源码调查日期:2026-09-26。本文的“Pi goal”特指第三方扩展 code-yeongyu/pi-goal,并非 Pi 核心自带的一套唯一实现。DSH 指 deepseek-ai/deepseek-harness。以下结论来自固定提交的静态源码检查;操作步骤未调用付费模型做端到端实测。

先给结论

/goal 不是让模型突然拥有可靠的“长期记忆”。它通常由五部分组成:目标状态、模型可见的目标工具、人类命令、空闲后的下一轮调度、完成或阻塞的退出规则。模型仍在普通 agent turn 中读文件、改代码、运行工具;框架负责在 turn 结束后判断能否再送入一条续跑消息。完成判断主要仍由模型提出,因此“状态是 complete”不能代替验收证据。

Codex、Pi goal 与 DeepSeek Harness 的 Goal 状态和自动续跑路径Codex、Pi goal 与 DeepSeek Harness 的 Goal 状态和自动续跑路径
三种 Goal 实现的状态存储与续跑入口;点击可查看 SVG 原图

图中三条路线分别标出状态存储和续跑入口;各节点依据下文所列源码。

调查范围与复核方法

项目固定提交本地源码主要入口
Codexe72da2b53805894878023d01949a25a082e0a5cbsources/codexext/goal、GoalStore
Pi 核心d6af72e1857cfb10b41d8ff8e69f0d72b4cf6d31sources/pi扩展 API 文档
Pi goal 扩展c2e1e732fa12eec1da2a47170c59dfa61fc547c9sources/pi-goalsrc/index.ts
DSH477b4f420553e8a52c2fbccc464d7561b239c443sources/deepseek-harnesspackages/goal

下载时采用浅克隆,以下命令可在空目录复现。若仓库 main 已前进,需先抓取表中的提交,再切换到它;仅在新的浅克隆上直接 git checkout <旧 SHA> 可能失败。

bash
1
2
3
4
5
6
7
8
git clone --depth 1 https://github.com/openai/codex.git
git clone --depth 1 https://github.com/earendil-works/pi.git
git clone --depth 1 https://github.com/code-yeongyu/pi-goal.git
git clone --depth 1 https://github.com/deepseek-ai/deepseek-harness.git
# 对每个仓库分别执行:
git -C codex fetch --depth 1 origin e72da2b53805894878023d01949a25a082e0a5cb
git -C codex checkout --detach FETCH_HEAD
# 其余三个仓库用表中的 SHA 同样操作

这里的 Pi 主仓库以当前 pi.dev 安装页指向的 @earendil-works/pi-coding-agent 为准。Pi 提供 registerCommand、registerTool、sendMessage 和 appendEntry 等扩展原语;本文锁定的 /goal 行为由第三方扩展注册。Pi 的官方扩展文档还特别提醒:若状态应跟随会话分支,需要从活动分支重建状态。依据:扩展 API 与状态章节。

一、Codex:线程 SQLite 状态加空闲续跑

入口。 TUI 的 /goal 受 Feature::Goals 控制;本次提交把 goals 标成稳定功能且默认开启。裸 /goal 显示目标摘要与可用命令,/goal edit、pause、resume、clear 由 TUI 分发;模型侧另有 get_goal、create_goal、update_goal 三个工具。Goal 要求持久线程,临时线程不能创建它。依据:功能开关、TUI 分发、摘要呈现、持久线程要求、工具定义。

状态。 一条线程对应 thread_goals 表的一行,保存 goal_id、目标、状态、token 预算、已用 token 与耗时。insert_thread_goal 的 SQL 只允许替换已完成目标,所以并发创建未完成目标时数据库会拒绝第二次写入。模型工具允许创建、完成、阻塞;“暂停须由用户明确请求”是工具说明中的模型行为规则,handle_update 本身只校验状态枚举,没有验证请求来源。恢复与预算限制由宿主控制。依据:表结构、条件插入、工具说明、工具执行。

续跑。 扩展接到 thread idle 事件后,先读持久状态、检查延迟标记和当前线程,再调用 start_turn_if_idle 投入一条内部目标消息。普通会话恢复时,如果数据库状态仍为 active,运行时会重新标记目标并允许之后续跑。fork 有一个默认关闭的实验选项 deferGoalContinuation:仅在调用方启用它、存在持久 rollout 路径且 Goals 启用时,fork 才继承目标并写入延迟标记,阻止自动续跑直到首次显式 turn 启动。目标正文在模板中被 XML 转义,并明确声明为用户数据;模板要求逐项检查交付物后才标记完成。模型自行报告“同一阻碍持续三轮”由工具说明和提示词约束;另有执行失败、空 turn 等宿主自动阻塞路径,不能把三轮规则理解成所有 blocked 状态的统一硬门槛。依据:生命周期钩子、fork 参数、继承条件、延迟写入、显式 turn 清除、空闲调度、续跑模板、自动阻塞计数。

二、Pi:扩展拼装 Goal,本文实例用会话目录 JSON

入口。 pi-goal 在扩展入口分别注册命令、工具和生命周期处理器。/goal <objective>、/goal、pause、resume、clear 由命令解析;模型工具有 create_goal、get_goal、update_goal。模型可设置 complete 或 blocked,人类命令负责暂停与恢复。依据:扩展入口、命令解析、工具注册。

状态。 扩展把目标写到会话目录下、以 session ID 编码命名的 .json 文件;无持久会话时回退到 Pi 配置目录,但每次新会话仍有新 ID,不能据此推断跨进程会自动恢复。readGoal → 修改 → writeFile 是普通文件读写,代码没有在这些函数中使用文件锁、版本 CAS 或原子重命名。这个事实意味着并发写入可能丢更新,但本文未做并发运行试验,故这是源码推出的风险,而非已复现缺陷。依据:路径、读写与更新、会话 ID 生成。

续跑。 session_start 读到 active 目标且 agent 空闲、没有待处理消息时,以及 agent_end 后目标仍 active 且没有待处理消息时,扩展用 pi.sendMessage({display:false}, {triggerTurn:true, deliverAs:'followUp'}) 排入隐藏消息。暂停的目标在交互式 resume 时询问用户。中断 turn 被扩展识别为 blocked 并停止续跑;README 自述扩展 API 缺少中断来源,因此也可能把系统中断标成“用户中断”。依据:生命周期、排队条件、投递、项目说明。

三、DSH:事件溯源状态、进程内授权和有编号的续跑轮

DSH 将功能拆成四个包:goal 管状态、tool-goal 供模型调用、command-goal 供人类使用 /goal、goal-round-driver 负责续跑。完整功能要求宿主与 agent 预设的组合中四包齐备;Web 组合把 goal 和 driver 装在宿主层,standard 预设提供命令和模型工具。依据:goal 包组、宿主配置、Web 组合调整、standard 预设。

持久层。 每次 goal 变更写入 Session 的 goal/change 事件;投影重放得到当前目标。更新必须带当前 {id, revision},旧 revision 会被拒绝。armed/disarmed 则只存在进程内:新 agent 或恢复、fork 后,即使持久 phase 是 active,也不会自动续跑,必须明确 resume 重新授权。依据:投影与事件、激活与修订检查、CAS 检查。

调度层。 driver 等整个 agent idle;若有待持久化检查点,先 flush Session,然后为下一轮预留 {goalId, revision, round},用 agent.followup 排队;agent/pre-step 再核对预留是否仍有效,用户消息或目标变更能使过期轮失效。真正进入会话的 goal 消息才消耗轮次。目标仍 active + armed 且下一次调度时,达到 maxGoalRounds 才写入 round-limit 阻塞;默认上限为 256。宿主发起 pause 会取消正在运行的 turn,模型自己在 turn 内 pause 则正常结束该 turn。依据:调度与预留、竞争与过期校验、默认上限。

权限。 DSH 允许模型从明确的人类长期请求推断建 Goal;create_goal 会调用 requireDirectHuman,更新使用 ID/revision。自动 goal round 报 blocked 时,代码硬检查累计已进入会话的 goal 轮数,默认至少 3 轮;没有代码验证同一阻塞条件连续存在三轮,这部分仍靠提示词。直接人类轮次不受该轮数检查限制。依据:创建策略和配置、创建权限、阈值执行、直接人类轮次测试。

四、差异一览

维度CodexPi + 本文扩展DSH
功能归属主仓库 Goal 扩展,默认稳定开关Pi 扩展 API + 第三方插件独立 goal 插件组
状态源SQLite thread_goals会话目录 JSON 文件Session 事件日志及投影
自动续跑thread idle 后 start_turn_if_idleagent_end / session_start 后隐藏 followUpagent idle,预留并验收 goal round
恢复会话普通 active 会话可续跑;启用实验选项的 fork 才继承并延迟目标active 且空闲、无待处理消息时排队;fork 通常另用新 IDactive 但 disarmed,待明确 resume
预算token 阈值、使用量、耗时;当前 turn 可能超预算记录 token 和耗时;当前命令与 create 工具没有预算参数轮数上限,默认 256;goal 子系统不计 token 预算
并发防护创建有 SQLite 条件写入、续跑有 idle 检查;模型更新无版本 CAS目标文件读改写,无显式 CASgoal ID/revision、预留身份、前置校验
模型报告 blocked“同一阻碍三轮”主要由提示词约束;宿主错误另有自动阻塞“同一阻碍三轮”由工具描述约束自动 goal round 硬检查累计轮数 ≥3;不检验阻碍是否相同

最后一行只比较本文检查到的模型报告路径,不表示其他宿主层一定没有附加约束。DSH 的直接人类轮次还可在累计 goal 轮数为 0 时报告 blocked。Codex 工具 schema、Pi 工具注册、DSH 阈值执行和测试。

五、操作手册

Codex

启动 Codex CLI,在已经开始的会话输入:

text
1
2
3
4
5
6
/goal 为 API 的错误返回补齐设计、实现和回归验证;完成条件:指定测试通过且逐项检查边界情形
/goal
/goal edit
/goal pause
/goal resume
/goal clear

裸 /goal 显示当前摘要与可用命令,/goal edit 打开编辑入口。也可直接明确要求 agent 创建 Goal;模型工具接受 objective 和可选 token_budget,工具说明要求模型仅在用户明确提出预算时设置参数,但宿主配置 goals.max_goal_token_budget 也可能成为默认预算。请在持久会话中使用;临时线程会拒绝 Goal。预算是停止后续工作的阈值,并非当前 turn 的严格用量上限。若 /goal 不出现,先核对所运行版本的 goals 功能开关;本文固定版本默认开启。依据:TUI 分发、工具 schema、预算默认值、预算记账。

Pi 与 pi-goal

先按 Pi 安装页安装 Pi,再在 Pi 环境运行:

bash
1
2
pi install npm:pi-goal
pi

进入交互界面后:

text
1
2
3
4
5
/goal 修复登录过期后的重复刷新,并用真实回归用例证明无死循环
/goal
/goal pause
/goal resume
/goal clear

扩展支持 pi -e ./src/index.ts 从本地检出的扩展源码加载。目标超过 4,000 个 Unicode 码点时会被截断;长需求宜写进仓库文件,并在简短目标中引用文件和验收条件。第三方扩展可执行代码,安装前检查来源与锁定版本。依据:扩展 README、截断实现、Pi 官方包文档。

DSH

按 官方仓库说明使用 Node.js 运行 npx @deepseek-ai/dsh web,或从源码 pnpm install && pnpm run build && pnpm dsh web。Web 默认 standard 预设提供命令与工具,宿主组合提供状态服务和续跑 driver;使用包含这些能力的组合。依据:默认预设

text
1
2
3
4
5
6
/goal 修复登录过期后的重复刷新,并检查所有相关调用路径
/goal
/goal edit 修复登录过期后的重复刷新,并补足并发请求验证
/goal pause
/goal resume
/goal clear

/goal 状态页应同时显示 phase、Rounds: 已用/上限、Activation: armed/disarmed。会话恢复后如果是 active + disarmed,由人明确 /goal resume;单看 active 不代表自动执行中。若命令不存在,核查预设是否装入 command-goal;若模型没有目标工具,核查 tool-goal;若不续跑,核查宿主组合的 goal-round-driver。依据:命令实现、Web 预设、宿主组合。

六、问题与坑:排查时先看哪一层

  1. 目标明明 active,却没有继续。 Codex 检查线程是否 idle、功能开关、延迟标记和 live thread;Pi 扩展检查是否有待处理消息;DSH 还检查 armed、全 agent idle、轮数上限与预留是否失效。先读状态,再找调度入口,别只改提示词。Codex、Pi、DSH。
  2. 恢复后意外继续或意外停下。 Codex 普通 active 会话恢复后可进入续跑;只有启用默认关闭的实验选项 deferGoalContinuation 且满足持久化条件的 fork 才继承并延迟目标。本文 Pi 扩展在空闲且无待处理消息时续跑,Pi fork 通常换新 session ID;DSH 恢复后主动 disarm。跨项目迁移时先核对这些条件。Codex fork 参数、继承条件、延迟写入、Pi 排队条件、DSH。
  3. Pi 分支与目标文件不一致。 树分支导航仍使用同一 session ID,本文扩展的目标文件可能沿用其他分支的目标;fork 通常新建 session ID,因此更可能读不到父目标。无持久会话每次新建 ID,也不能因为配置目录中残留目标文件就认为能自动恢复。切换分支或 fork 后应重新核对 /goal 与当前任务。这是源码推论,未做分支运行复现。Pi 状态指南、会话 fork、扩展存储路径。
  4. “完成”过早。 三种实现都会要求模型检验目标,但源码没有展示一个能理解所有交付物的独立验收器。把目标写成可核查的交付物、命令和结果;结束时亲自查看当前文件、测试范围和生成物。通过狭窄测试不能证明宽泛目标完成。Codex 完成审计提示、Pi 完成审计提示、DSH 续跑提示。
  5. 预算口径混淆。 Codex 的 token_budget 是停止续跑的目标阈值,当前 turn 可能超出;DSH 的 max_goal_rounds 是自动续跑轮数,两者无法直接换算。Pi 本文扩展会记录用量,但公开的 /goal 与 create_goal 入口没有预算参数。迁移配置时先写清楚限制的是 token、轮数、时间还是费用。Codex 记账、DSH goal 限制、Pi 工具参数。
  6. Pi 的长目标被截断。 本文扩展把超过 4,000 个 Unicode 码点的目标截断;全文另存文件,续跑提示却只放截断后的正文。把长需求与验收条件写进仓库文件,在短目标中明确引用该文件,并在结束时检查全文。截断规则、续跑提示。

若自己实现 Goal,最小可行设计

把“当前目标”做成持久记录,至少含 goalId、objective、status、revision;把“是否允许自动继续”作为独立授权状态。所有更新验证旧 revision;每次准备续跑时再读状态,并在真正进入模型请求前复验目标 ID 与 revision。只在整个 agent 空闲、没有更优先的用户消息时排队;请求结束后依据明确的完成证据、阻塞规则与预算停止。为目标内容标注低信任来源。最后设计会话恢复与 fork 的行为:自动续跑还是等待人类重新授权,必须是显式产品决定。DSH 的 状态服务和预留校验可作为并发设计参考;Codex 的 条件写入和 idle hook则是更紧凑的宿主集成路径。