goal:三处没接上的线
十一期间研究 agent 长程任务时,我碰上一个挺有意思的项目:metacodes。
10 月 17 日,metask-ai/metacodes 的维护者 wayfind 在 #277 里说了一件事:他们 10 月刚合入的 goal,在连续跑一周以上、迭代上千轮的长任务里,目标本身开始拖代码的后腿。原话是:“现在的 goal 在长任务场景里面存在问题,在连续运行的一周以上的长任务里,goal 的目标已经影响到迭代上千轮后的代码继续演进了。”
做长程 agent 的人大概都撞过这堵墙,但愿意公开承认的极少:Codex 和 Claude Code 的 goal 保证都以会话为界,千轮尺度上肯说“我这层设计不够”的,我没见过第二个。
他的解决办法不是往续跑提示词里再加规则,而是用 TinyKG 把任务拆成 DAG,每轮只注入当前节点到根的路径摘要;还专门说了一句,做完会严格评估。
metacodes 是一个 Zig 写的 agent runtime,把运行时能力封装成 C ABI(AgentCore)交给宿主应用:Session、Run、压缩、权限,以及 2026-10-17 刚合入的 goal(PR #286,+2923 行,ABI Revision 23)。
这个仓库的 goal 代码和 PR 记录我完整过了一遍。读下来我的判断是:这个问题的缘由不在于目标写得不够好。goal 现在只有一根线连着外面,它不知道任务图存在,任务图不知道验收存在,验收又不驱动 goal 的迁移。 那个预告中的 DAG PR 真正要做的不是发明 DAG,是接线。
一、现行 goal:一套边界很清楚的设计
状态机有六个状态(active / paused / blocked / usage_limited / budget_limited / complete),Host 和用户能发的动作只有四个(pause / activate / complete / block),迁移表很短:
| 动作 | 源状态 | 到 | 拒绝 |
|---|---|---|---|
| pause | active, paused | paused | 其他 → GoalNotActive |
| activate | 任意 | active | 预算已尽 → BudgetExhausted |
| complete | 任意 | complete | 不拒绝 |
| block | 非 complete | blocked | complete → GoalComplete |
表里 pause 允许 paused → paused,这不是迁移表冗余,而是有意的幂等 no-op:状态、预算、目标都没变时,update() 的 unchanged 检查直接返回——不刷新 updated_at,不产生第二次迁移。Host 重复暂停不必先查当前状态。
两个细节:usage_limited 是个预留的死状态,全仓库没有任何代码路径设置它(ABI 文档自己写明 “never sets it”);update 把改目标、改预算、发动作三件事原子化——“提高预算并恢复”是一次 update,要么全应用要么全不变。
Meter 只对一种 Run 计费:Run 开始时 goal 是 active 的那种。它计每个 provider 响应的 (input − cached) + output 和 Run 的墙钟时间,无论 Run 以什么方式结束。这里要分清口径:缓存命中不计预算,是项目自选的预算记账口径,不是 provider 替它扣了什么——真实成本里 cache read 按 0.1× 收费、cache write 还有溢价,预算表和成本账是两本账。
这也是 PR #286 修掉的存量 bug:旧实现只在 REPL 计费、且只计成功的 Run。不计费的也写得清楚:子 agent 默认不计(SpawnOptions.goal_meter 默认 null),模型以工具调用形式 fork 的 Skill 不计(可能与其他工具并行,共享 Meter 会竞态),paused 起跑的 Run 不计。唯一的例外是作为 Run 本身、在 Run 线程上同步执行的 AgentCore Skill 调用——它复用父 Meter,因为不存在并发。
预算中途耗尽的时序写得很精确:响应到达 → 记账 → 翻转 budget_limited → 发事件 → 不切断当前轮 → 下一个 turn 边界注入一次“收尾提醒”(持久 user 消息,只注一次)→ Run 结束结算墙钟再发一次事件 → 此后新 Run 不再计费、续跑被拒(GOAL_CONFLICT)。软着陆,不拉闸。
checkpoint 的定宽段设计也很能代表这个仓库的手法:goal 段里除了 objective 全部定宽,所以计费永远不改变 checkpoint 的字节尺寸——持久化预算的准入检查只需要发生在 set/edit/clear 三个 idle 时刻。不变量不刻在流程里,刻在数据布局上。
ABI 上,goal 的 set/update/clear 全部仅 idle 可用;Host 主动发起非法迁移时不静默纠正,返回 GOAL_CONFLICT。
上面这些全是边界问题:谁能动目标、什么时候能动、动多少、动完怎么记账、账要不要落盘。边界清楚到能列成一张表,这是百轮尺度的正确答案。
二、它跟 Codex 的三处不同
PR #286 自己声明对标 Codex 的 thread goals(六状态、计费公式、软着陆都一致),有三处刻意不同。
第一处:拒绝即报错,还是静默 coerce? Codex 倾向静默 coerce,由运行时把状态压到它认为合理的格子,不把冲突抛回给 Host;metacodes 相反,返回 GOAL_CONFLICT。我站 metacodes。这里有个边界:预算耗尽时的自动翻转两边都做(这格不算差异),差异只在“Host 的非法主动动作能不能拿到显式错误”。长任务里,“Host 以为操作成功了”的错误会复利。
第二处:模型侧给不给 goal 工具? Codex 给模型留着 create_goal 和 update_goal(complete |
blocked 两值),metacodes 干脆不给:模型只能在回复里说目标完成了,标记权归用户或 Host。续跑提示词里 “marking the goal is the user’s decision, not yours” 把这条写死了,比我上次文章里 Codex 的版本更激进,是彻底的“运动员不许当裁判”。这条我站,但它的代价正是下一节的第三个因素。 |
第三处:时间按什么算? Codex 计闲置时间,metacodes 计被计费 Run 的墙钟时间。这一处我认可:长任务里 agent 大半时间在等 provider,按闲置算会把等待也记进消耗。
另外还有一条,写在 PR 的 Not done 里:子 agent 用量不 roll up。 Codex 会沿 agent 树把子线程用量滚入 root 预算,防的就是逃逸;metacodes 反过来,默认不滚。对可能并行的子循环,共用 Meter 会竞态,所以这是设计上的明确选择,不是漏接。上面那个同步内联 Skill 的例外只覆盖 AgentCore 自带上下文的调用,模型自己发起的 Task / 后台子 agent 仍然不计。这个缺口是真的,只是口径要写准:不是“全仓库从不滚”,而是“并发子 agent 默认滚不进来”。
这一条我不认可。前两处是把错误的操作变难,这一条是把真实的花费变看不见;跑到周尺度,这笔花费是实打实花出去的,而且有可能很高。
三、问题从哪来:三个机制因素
维护者说“上千轮后目标妨碍代码演进”。这句话能从代码里拆成三个机制因素,权重不同。
结构根因是目标不可分层。Goal 就是一个扁平字符串、一个状态、一个账本。注入文本里唯一的“进度”信号是 “Tokens used toward it so far: X of Y”——那是预算表,不是任务进度表。系统表达不了“七步完成了三步”,每一轮续跑都得从(可能被压缩过的)会话里重新推导“做到哪了”,冻结的原文帮不上忙。
放大器是冻结原文的每轮重注入。每个续跑 Run 注入一次完整目标原文,AgentCore 路径还持久追加。compaction 对 user 消息只有 20K token 的逐字保留窗——旧副本被摘要掉,最新副本永远在场,陈旧锚点被不断刷新,而不是老化退出。提示词里还写着 “Do not start unrelated work”,压着模型重新解释目标的余地。

逐字保留窗之外的目标副本被摘要,最新副本永远在场——锚没有跟船走,就成了压舱石。
注入路径倒有两条,污染级别不一样。同一段续跑提示词(<objective> 包裹的目标原文 + “Do not start unrelated work”),在 CLI 的 /loop 里只作为该 Run 第一个 turn 的 API 负载、不落持久会话;在 AgentCore 的 RUN_INPUT_GOAL_CONTINUATION 里则作为持久 user 消息写进会话、跟着 checkpoint 走。同一套 goal 语义,两种历史污染级别。跑一周的托管任务走的是后一条。
第三个因素是修复无法传播,它的原因正好是上一节我认可的第二处。改目标只能由用户/Host 在 idle 时做;editObjective 原地改、无修订号、无历史。百轮尺度这是防漂移的优点,千轮尺度合在一起就是负债:目标跟不上演进,还带着“不许改”的提示词边界。
这里有点意思:不给模型 goal 工具,防的是运动员当裁判;代价是执行者对自己的目标没有话语权,而长任务的演进恰恰需要这种话语权。
外部也有一个印证,但只是单源口述。Theta Software 的人在 AI Engineer 频道讲长程任务时说,真正的难点是序列复杂度——早期一个坏查询会级联放大,而不是“把短任务链起来”。这段话没有预印本或文字稿,转引请保留这个证据等级。冻结目标每轮重注入,等于把 day-0 的 framing 变成每一轮的“早期输入”。它支撑的是失效类型——早期错误在长程会复利——不是“DAG 能解决它”这个结论。
他自己仓库里还有一条旁证。learning turn 的设计文档(§10)引了 stall gate 的实测数据——文本 nudge 在长上下文里会被忽略,所以经验注入走确定性通道,不走提醒。这跟“冻结原文每轮重注入”是同一类提示层手段在不同位置的表现,可以旁证提示层在长程会衰减;两者不是同一个干预,严格说不算直接证据。但方向是清楚的:这个仓库自己也认为,长程里能靠的是确定性通道,不是提醒。他据此选的结构解是 DAG 投影。
四、仓库里藏着半个答案
读下来最意外的发现是,DAG 的下半截已经建好了,只是没接上去。
TinyKG 任务协议是现成的:TaskCreate / TaskList / TaskGet / TaskUpdate 一套模型可见工具,open/claimed/completed/failed 生命周期,认领时下发 task_packet(父目标、依赖、证据链接),TaskList 的叶子行带 path 面包屑,依赖只在 completed 后解锁。
还有一个没接线的文件:plan_commit.zig 能把批准的计划文本落成任务 DAG(contain/depends_on 边),能力完整,但它的唯一调用方 plan 模式在 PR #260 被有意删掉。维护者的立场是 plan 模式有意去掉、以后都不需要;我在同一个线程里补了一句:plan 什么都不持有,放进 core 只会扩大 API 面。
此外还有两件半成品。check_gate 是宿主 pinned 的确定性验收命令,verdict 落图;learning turn 的 lesson supersede 走 append-only,写新教训、命名被替代者、召回时过滤。
零件都在,缺的是三处没接上的线,而且每处都能对上前面拆出来的因素:
- goal → 任务图。goal 是个字符串,它不知道图存在。前面拆出的第一、二个因素,都长在这根线的断口上:表达不了进度,就只能重注原文。
- 任务图 → 验收。check_gate 能判,判完 verdict 落进图里就停了,不驱动 goal 的迁移。
- 验收 → goal。完成判定没有卡口。模型说完成,或者 Host 说完成,goal 就到 complete。而“目标跟不上演进”本来应该由验收结果反向推一次。
五、接线要回答的三个权责问题
那个预告中的 DAG PR,真正要做的不是“发明 DAG”,而是把已有两层接通,再回答三个权责问题:
- 分解由谁 authoring。 它拒绝过模型侧 goal 工具,但任务协议的工具本来就是模型可见的——这不是矛盾,是一道必须划清的授权切分:模型可以 authoring 执行层节点,目标与验收来源的权威必须留在人/Host 一侧。DAG PR 要做的就是把这层 authority 分级在协议里写死。
- 节点完成由谁裁决。 check_gate 是现成答案,只差接线。
- 续跑要不要 LoopX 式的前置判据。 现在 ABI 的口径是“续跑次数与时机完全由 Host 决定”,没有任何确定性判据。
验收这条有个外部旁证。Theta 在同一集里说,软验收场景下 judge 自己必须是 agent——只读权限检查最终环境状态(GitHub、CloudWatch),用子 agent 查询长轨迹。这跟 check_gate 的 pinned 命令、以及仓库里还是 design-only 的 isolated verifier 设计——fresh context、只读快照、只能降级不能放行——是同一条收敛线。
顺带说一句,他在 #277 里挂着的另一个决策——Swarm 的 teammate 线程归谁——已在 PR #294 以 host-scheduled 落地;Ona 的人在同一频道论证“runtime 和编排都已解决,缺的是 swarm 协调原语”,正好给那个决策提供了外部词汇表。
六、这个形状,行业里已经有人走过
这个方向不孤独,甚至可以说是 2026 下半年的行业收敛点。状态外置 + 层次投影 + fresh-context 执行 + 独立验收——LongHorizon-Harness(manager 持有外置任务状态、fresh-context executor、只读 auditor,只有被独立验证的事实才能跨轮)、InfiAgent、OneDayAgent(显式分解成有界可执行单元)几篇论文,和 Devin、LoopX 的产品实践,都收敛在这个形状上。
最接近他预告方案的是 Mage(arXiv 2606.06090):两层状态树,底层是逐步的 action-observation 原文,顶层在子目标与决策边界生成摘要——几乎就是“当前任务 + root 到当前节点的路径摘要”,连摘要触发点都一样。TME 把状态树从 tree 推广到 DAG;ReAcTree 的 agent 树带控制流节点,形状接近“任务图 + Swarm”合体。他要做的结构在相关论文里都有近似原型。
风险也在这里:两个开放问题,恰好都是方案的核心。
第一个开放问题是层级摘要的保真度没有评测方法。Mage、HORMA 这类层次摘要工作都假设摘要是好的,没有失真检测器。DAG 化等于把“每轮注入原文”换成“每轮注入路径摘要”——每层蒸馏丢了什么约束,没有任何检测手段。这是方案的头号新失效模式。
第二个是 replan 语义。父目标变更后,已展开的子树如何失效、如何对账,论文和 Devin 都没有公开解法。这个仓库现行 goal 连 objective 的版本号都没有。
这里还有个前车之鉴。Box 的 CTO Ben Kus 在 AI Engineer 频道的《The Half Life of Agent Infrastructure》(AI Engineer World’s Fair 2026,7 月 1 日)里,公开修正了自己去年在同一大会《Building an Agentic Platform》上推荐的图式 agent 架构:他仍喜欢这个模式,但认为它“有点过时”。
他的原话是“why are you bothering to make graphs when you could actually have an agent just figure out what to do? Make a plan.”(4:30 起;视频 / 文字稿)。
他的核心判断是“the half-life of agent infrastructure is now measured in months”——这讲的是基建整体折旧。至于“图式方案特有的脆弱点在检索质量、TinyKG 建在图检索上因此风险真实”,是我的推断,不是他的结论,两件事别焊在一起。
七、它的胜算在评估文化
风险说完了。为什么我还看好它?因为能外部审计。
我判断一个长程项目值不值得投入只看两件事:撞墙之后它改结构还是改提示词,以及它的宣称有没有留给外人证伪的接口。前者决定问题会不会再次出现,后者决定我能不能在它失败之前知道。这个仓库两条都过了。
改法在 #277 里看得很清楚,没人去调提示词,换的是注入物。能不能信,看它的流程:
每个 PR 都带三样东西:本地门禁证据(多少测试过、多少跳过)、变异测试清单、对 Lean 形式化镜像的维护。变异测试清单会点名每改一条规则哪个测试会失败——比如‘activate 忽略已耗尽的预算 → 迁移表测试失败’。Lean 镜像在 control-plane/lean/ 下,二十多个 .lean 文件,检查门、预算、学习回合各有一份形式模型。
评估是预注册制:evals/experiments/ 里的实验文件分 Claim、Cohort、Arms、Stage A/B、Stopping rule、Claim boundary、Authorization 几栏,没有 owner 签字和预算上限,实验不许跑。它给 learning turn 做的实验设计里,“对照臂用 observe 模式记录实验臂本会做什么”这种细节都想好了。wayfind 说“做完之后也严格评估”——在别的仓库这是公关辞令,在这个仓库这是一句有文件格式和门禁兜底的话。
当然,DAG 方案还没开工,摘要失真是真实风险,所以这不代表一定能成。只是眼下大家都没有更好的解法,多一种实验方向,就多一种可能。
如果 DAG PR 要配一个最小可行实验,骨架现成:chained runner(它正在 #297 里为 learning turn 建造)跑 K 次连续续跑、attempt 之间强制压缩模拟历史更替、链中点做一次目标编辑模拟 replan——断言落在末段相位的隐藏 validator 通过率、约束探针违规率、作废/孤儿节点计数上。ground truth 来自任务作者冻结的分相位验证器,不来自模型自评。这个实验同时检验方向(节点粒度新鲜度是否优于扁平重锚)和失效模式(摘要丢约束、父变更对账)。
两个设计约束必须先钉死,不然会重蹈我之前批评过的对比陷阱:一是等预算——DAG 臂与扁平重注臂按同一 token 预算停,不然结果只是花钱排名;二是隐藏 validator 自己先过变异——向各相位产出注入已知缺陷,validator 必须判死,判不出来的相位不得计入 ground truth。这个仓库恰好有变异测试文化,第二点几乎是为它的流程定制的。
结尾
上一篇我说,控制权的最后一站是目标本身的生命周期管理。这个项目正在给这句话做对照实验:一边是被冻结目标拖垮的千轮任务,一边是还没开出来的 DAG PR 和一份等着的预注册表。
但这个问题比“目标要不要分层”更具体。goal 现在是一个字符串,它看不见任务图,看不见验收结论,也看不见自己走过的历史,唯一看得见的是“用了多少 token”。
三根线接上之前,DAG 只是把每轮注入的东西从原文换成摘要,同样的失效会换一个位置出现。接上之后,“目标妨碍代码演进”才第一次变得可以测。
大部分项目回答“你怎么知道完成了”的方式是提高模型的自信,这个项目的方式是管理自己的不确定:每条规则的反面写成变异测试,每个宣称写成预注册实验,“我们不知道”写进文档的 Claim boundary。
目标会不会分层、摘要会不会丢约束,DAG PR 开出来才知道。
引用与出处
下面按类型分开列。
仓库与讨论
- metacodes:Zig 写的 agent runtime,运行时能力封成 C ABI(AgentCore)。
- #277 Expose Goal, Swarm, and Plan capabilities through AgentCore:维护者承认长任务目标失效、给出 TinyKG DAG 解法的那条评论就在这个线程里(2026-10-17)。
- #286 feat(goal): one set of goal rules at every entry point; AgentCore Revision 23:+2923 行。
- #260 refactor(core)!: remove plan mode:
kg/plan_commit.zig在这里失去唯一的生产调用方。 - #294 feat(swarm): host-scheduled teams + AgentCore Revision 24 team ABI:Swarm 的 teammate 线程归属,按 host-scheduled 落地。
- #297 Chained-attempt eval runner for learning-turn-v1:实现
evals/experiments/learning-turn-v1.md的第一个前置条件。 - LoopX
- Codex:
codex-rs/ext/goal,thread goals 的参考实现。
论文
- MAGE: Beyond Semantic Organization — Memory as Execution State Management for Long-Horizon Agents(arXiv:2606.06090,EMNLP 2026):正文里的「Mage」。
- LongHorizon-Harness: Advancing Long-Horizon Agents for Real-World Tasks(arXiv:2608.01964)
- 正文点了名但没给编号的:InfiAgent、OneDayAgent、TME、ReAcTree、HORMA。手头没有能确认的 arXiv 号,先不挂链接,免得挂错。
演讲与视频