Files
deepseek-harness/docs/rfc/implemented/feature/2026-07-14-time-context-plugin.zh.md
2026-07-14 17:44:32 +08:00

5.8 KiB
Raw Blame History

RFC:可选时间上下文插件

Status: implemented

English | 中文

问题

如果部署方既未在提示词中提供时钟,也未给模型提供查询工具,agent(智能体)请求就无法获得实时准确的时间。静态文本会变得陈旧,而对于日期、截止时间或闲置时长等常规推理,调用工具会增加开销。缺少已经过去的时长时,模型无法区分紧接着发送的消息与上一条消息几小时后才发送的消息。

提示词组装流程可以在每个步骤中根据持久会话时间戳派生这两项信息,请求头日志则可以记录实际渲染的确切值。在会话历史中累积陈旧读数或唤醒空闲 agent 都会违反现有请求生命周期。

决策

@deepseek-ai/dsh-time-context 是位于 packages/context/time-context/、需要显式启用的函数插件。context/ 产品分组用于容纳既不定义工具、也不定义服务的有界请求上下文增强。dsh-agent-core 和仓库提供的示例都不会加载该 package;只有当 token 与信息披露成本可接受时,部署方才显式挂载它。

该插件注册顺序值为 10 的全局系统提示词区段 context:time,位置在部署方角色设定之后、工具指导之前。对于活跃轮次,它会输出带数字 UTC 偏移和 IANA 时区、形似 ISO 的时间戳,以及从轮次开始前最后一条模型可见消息起算的紧凑整秒时长。未绑定 agent 或 agent 处于空闲状态时,该区段为空。

上一条消息基线

在轮次首次组装时,提供方会在 turn/start 之前查找最近的 user/message、assistant/message、tool/result、context/message 或 steering/message。它会排除当前提示词,使时长表达轮次间隔,而不是接近零。同一轮次中的每次刷新都保留这条基线;首个轮次报告 unavailable (no earlier message in this session)。

基线采用会话事件的追加时间,而不是日志中不存在的客户端时间戳。因此,恢复和 fork 行为可以从持久日志中确定性重现,模型可见值也无需新增事件即可重建。系统挂钟向后调整时,插件会将时长钳制为零。

刷新策略

refreshIntervalMs 默认值为 60,000,并且必须是非负安全整数。每个轮次的首次请求都会刷新。同一轮次中的后续组装会复用该区块,直至其存在时间达到该间隔;设为 0 时每个步骤都刷新。刷新仅由请求驱动,因此在模型调用、工具运行或空闲期间,计时器不会创建任务。

timeZone 默认为 UTC,插件加载时会校验它是否为 IANA 标识符。形似 ISO 的本地时间戳包含已解析的时区及其当前数字偏移,使夏令时变化保持显式可见。

日志与 token 形态

agent loop(智能体循环)会在发送前通过 request/header 和 request/header-delta 记录时间区块,从而满足可重建请求契约。每个请求只携带一个当前区块;先前的读数不会保留在会话历史中。该插件拥有时间信息,并按照提示词变量 RFC通过提示词注册表贡献该信息,无需为循环添加特殊分支。

测试

单元测试固定格式化、基线、刷新策略、校验、逐 agent 状态和资源释放行为。使用真实 agent loop 的测试固定实际发送的提示词和 request/header-delta;Loader 测试固定命名导出路径。默认快照组合不包含该插件,因此其中的 transcript(文本记录)fixture(测试前置数据)不包含时间区块。

考虑过的替代方案

  • 每个轮次或每次刷新都追加一条 context/message——不予采纳,因为读数和 token 成本会在历史中累积。替换先前的表层节点会保留其旧位置,而替换尾部节点会隐藏中间的会话内容。
  • 使用 agent/session-prefix——不予采纳,因为会话期间保持稳定的前缀无法表示逐轮次或逐步骤变化的时钟。
  • 在 agent/request 中修改请求——不予采纳,因为该边界在消息边界之后塑造调用配置;插入模型可见内容会绕过提示词压力核算和请求头日志。
  • 注册独立的 {{current_time}} 和 {{elapsed}} 变量——不予采纳,因为独立提供方可能在不同时间点采样,并且需要共享缓存。单个区段会以原子方式记录两项信息,也不需要部署方编写时间模板。
  • 通过后台计时器刷新——不予采纳,因为请求组装之外没有消费新值的对象。由计时器驱动 agent.inject() 会创建轮次,并且只为报告时间流逝就唤醒空闲会话。
  • 在 dsh-agent-core 中挂载插件——不予采纳,因为时区、信息披露、token 预算和新鲜度都属于部署策略。选择加入能保持默认上下文稳定。
  • 将 package 放入 core/——不予采纳,因为 core/ 负责产品 API 主干,而该插件是没有服务键的可选叶节点。

后果

  • 选择加入的模型无需调用工具,即可获得分区时钟和轮次间隔时长。每个请求的系统提示词成本固定,不会随会话增长。
  • 刷新会改变请求头,并可能新增 request/header-delta。refreshIntervalMs 用新鲜度换取持久增量记录的数量;设为 0 时,每个整秒渲染结果发生变化的步骤都会记录新值。
  • 系统不会仅为刷新时间而创建请求。长时间运行的工具会保留先前读数,直至下一步骤开始组装。
  • 时长反映持久追加边界处的 harness 处理时间,不包含消息进入日志之前的客户端网络延迟。若要保留客户端来源时间戳,需要单独的持久输入契约。