运行中的修改,到底什么时候生效

拆解 pi(DeepSeek 开源终端 Coding Agent)v0.83.0 的 setter 与 pending writes:调用 setModel() 返回后,「新模型生效」有四个不同的观察面,其中两个还是旧的。源码依据:pi-internals 21 章

setter 返回了,然后呢

Agent 运行中,你调用了 await harness.setModel(second),它返回了。直觉上「模型已经是 second 了」。

但如果你同时问四个地方,会得到四个答案:

  • harness.getModel()second------setter 已更新 Harness 的最新配置
  • 已经发出的 provider request:旧模型------请求用的是 active turn state,不重读字段
  • 下一次 createTurnState()second------下一份快照会读到新值
  • session.buildContext()(保存点 flush 前):还是旧的 branch 配置------busy 时 setter 只把变更放进 pending queue

一次修改,四个观察面,其中两个还停在旧值。这不是 bug,是设计:当前请求必须用稳定的输入(turn snapshot),修改只对下一轮生效

setter 的两个时钟

以 setModel() 为例。它先保存 previousModel。phase 为 idle 时,等 appendModelChange() 成功;phase 非 idle 时,只向 pending queue 放入一条 model_change。接着无论哪条路径,都把 this.model 指向新模型,并发出 model_update 事件。

setThinkingLevel() 和 setActiveTools() 结构相同。setTools() 还会先校验工具名唯一、active name 都存在,再决定追加还是排队。setResources() 与 setStreamOptions() 不写 Session entry------它们只改变当前实例,进程重建时需要宿主重新提供。

pending write 为什么不能立即插队

这是这一章最值得细看的地方。assistant message 可能带多个 tool call。低层 loop 要先把 assistant message 和对应的 tool results 形成相邻、完整的对话片段,再进入下一轮。

如果一个 subscriber 在 message_end(assistant) 里追加 custom message,并且立刻写入 Session------这条消息就会落在 assistant 与 tool result 之间。下一次从树构建上下文时,模型 API 所需的工具配对可能被切断:模型看到 tool call,却看不到对应的 result。

所以 Harness 让运行中的 appendMessage() 只排队,idle 时才直接追加。flushPendingSessionWrites() 始终读数组首项,按 entry type 调 Session 对应方法,成功后才 shift()。某项失败不会越过它写后面的内容------简单的 FIFO,没有重试计数、没有 durable queue id、没有跨进程恢复。

Hook 是顺序的,不是并发的

subscribe() 注册全事件观察者,存放在专门的 handler set 里。emit 时按注册顺序等待每个 listener;任意一个抛错,错误被规范成 Hook error,后续 listener 不再执行。它不是 fire-and-forget 的埋点。

on(type, handler) 注册带类型的 Hook,同样顺序等待,保存最后一个非 undefined 结果。所以 context、tool_call、tool_result 这些 Hook 的默认 reducer 是 last-result-wins,不是自动合并所有对象。provider options 和 payload 是例外:链式处理,前一个 handler 改出的结果交给下一个。

失败不会回滚

message_end 到来时,Harness 先 session.appendMessage(event.message),成功后才通知 subscriber。subscriber 抛错,已经追加的消息不会从树上删除。setter 也是先提交自己负责的状态,再发 update 事件;update listener 失败会让 public Promise reject,却没有补偿性回滚。

拆完这章,我的感受是:Agent 框架的「修改」语义,比普通系统复杂得多------因为同一份状态同时活在内存配置、turn 快照、pending queue 和 Session storage 四个地方。pi 的处理是给每个观察面明确的语义:修改立即可见于内存,只对下一轮生效于请求,只在保存点落盘于存储。做 Agent 平台的人,最怕的就是把四个观察面混成一个「当前状态」------那才是真正找不到 bug 的根源。

想看完整拆解(setter 四观察面状态表与 Hook 失败语义源码),这里有 pi-internals 小册子

源码依据:packages/agent/src/harness/agent-harness.ts L225-L317、L442-L484、L512-L536、L538-L554、L884-L955、L961-L1023,pi v0.83.0,commit 845d6ff1。

相关推荐
echoVic1 小时前
Agent 的会话为什么是一棵树,而不是一串消息
架构·agent
ch8561 小时前
别再让 AI 直接报数了:思维链 CoT 全解——从“写步骤“到“自我拆解“,把推理拆到零件级
agent
echoVic1 小时前
Plan 模式为什么不能只是一个权限开关
架构·agent
echoVic1 小时前
为什么 Agent 的每个请求,都要先拍一张快照
架构·agent
烬羽2 小时前
组件拆了,逻辑没拆——自定义 Hook 才是 React 业务逻辑的正当归属
react.js·架构·typescript
小月土星2 小时前
自定义业务 Hook:从零解剖一个 React + TypeScript Todo 应用:用金字塔思维看透前端架构
react.js·架构·前端框架
AI 小老六2 小时前
Brainstorming 与 grill-me 在 AI 产品设计和工程决策中的分工边界
人工智能·ai·架构·创业创新
莫逸风3 小时前
【AgentScope 2.0】05-文件系统(Filesystem)详解
java·ai·agent·springai·agentscope
苏灿烤鱼4 小时前
今日 GitHub 热门|自改进 Agent 首日登顶,+2,180 项目却只排第四
agent