32 KiB
Grok Build 上下文工程与 Agent Runtime 源码笔记
研究对象:官方
xai-org/grok-build本地源码快照,SOURCE_REV=2ec0f0c8488842da03a71eeee3c61154957ca919。 范围:提示词与上下文装配、token 预算、历史裁剪/压缩、工具结果、缓存、会话恢复、子 agent、hooks/skills、可观测性与离线评估。 方法:只使用仓库内第一方源码和仓库自带的第三方归属声明,不以 README 宣传文字代替实现证据。
结论先行
Grok Build 最值得 Catty 学的不是某一个 prompt,而是以下 8 个机制组成的闭环:
- 把上下文做成可检查、可持久化的数据结构,而不是散落的字符串拼接。
PromptContext明确记录 audience、prompt mode、AGENTS.md、memory、role/persona、运行环境和构建时间,再统一渲染;父 agent 与子 agent 使用不同模板和目录信息,但项目指令保持一致。prompt/context.rs:79-151prompt/context.rs:160-171prompt/context.rs:251-297 - 在真正压缩前先做分层、可逆的减负。 超过 50% 才对请求副本裁剪旧工具结果;近 3 轮不动,较老大结果保留头尾,10 轮以前的结果只留占位;原始事件流仍保留用于重放。
request_builder.rs:20-108request_builder.rs:155-208types.rs:67-97mutations.rs:165-205 - 把两段式压缩的第一段放到后台提前跑。 达到正式压缩阈值前 10 个百分点时,后台总结约 95% 的历史;真正触发压缩时只需把 NOTE1 与最近约 5% 合并成最终摘要。缓存带前缀指纹与 model 标识,历史编辑、回退、分叉或切模型后自动失效。
compaction.rs:34-63compaction.rs:219-340compaction.rs:342-429two_pass.rs:1-20 - 压缩不是不可逆删除:摘要之外保留可检索的分段档案。
Summary、原始 transcript、Markdown segments 三种模式可选;segments 模式给后继 agent 一个索引和只读恢复路径,摘要不够时再按需读取精确代码、错误和工具输出。compaction_mode.rs:7-20compaction_mode.rs:51-77fork.rs:92-106 - 压缩后显式重新注入运行状态,而不是赌摘要记住一切。 新上下文重建时单独采集正在运行的终端任务、子 agent、改过的文件、MCP 服务、todo、skills、AGENTS.md、plan mode 和 memory,再生成 system reminder;这比把所有责任交给总结模型更可靠。
compaction.rs:1205-1350compaction.rs:1381-1499 - 所有工具调用历史都做结构完整性修复。 在恢复、下一轮写入和发请求边界去重重复 ToolResult、为悬空 tool call 补合成结果;另有显式 repair 路径移除会导致 provider 400 的孤儿结果。
mutations.rs:26-70mutations.rs:80-109 - 子 agent 是独立可恢复会话,不只是一次函数调用。 子 agent 有独立 session id、原始 transcript、tool state、model、cwd、能力与隔离模式;支持继续以前的子 agent、后台运行、父轮取消隔离、进度/用量拉取,并把用量按 model 汇总回父账单。
task/types.rs:29-68task/types.rs:84-108task/types.rs:304-335usage.rs:100-146 - 压缩路径本身是可观测、可离线重放的产品功能。 每次压缩记录触发比例、阈值、输入/输出 token、重试阶段、失败类别、TTFT、流耗时、最大 token 间隔、两段式命中/失效等;同时把“实际送给压缩模型的历史 + 返回摘要/错误”保存成 artifact,供离线迭代 prompt。
compaction.rs:800-864session_compact.rs:219-310persistence.rs:360-374
对 Catty 的优先级建议:先做 压缩后状态再注入 + 工具历史完整性修复 + 压缩 artifact/eval;随后做 可恢复 segments;最后用实验开关验证 后台两段式压缩。这些项的收益与风险边界最清晰。
1. 提示词与上下文装配
1.1 PromptContext 是正式协议
PromptContext 是可序列化的第一等对象,而不是最终 prompt 的临时参数。它包含 schema version、prompt mode、父/子 audience、可覆盖的基础模板、AGENTS.md 列表、memory 路径、role/persona、OS/shell/cwd/date 和 non-interactive 状态。prompt/context.rs:79-151
渲染统一走 ToolBridge 的模板引擎,因此工具名不是写死在 prompt 中,换工具集或兼容模式时仍能解析正确名称;Extend 支持基础模板 + 自定义 body,Full 支持完全替换。prompt/context.rs:233-297
父/子 agent 的差异被显式建模:子 agent 用紧凑模板、不接收 persona catalog,但仍接收完整 AGENTS.md,避免验证型子任务绕过项目约束。prompt/context.rs:68-77 prompt/context.rs:160-170 prompt/context.rs:205-220
Catty 可借鉴: 给现有 system prompt/context manager 增加一个可 dump、可版本化的 PromptContextSnapshot,让问题排查能回答“这轮究竟注入了什么、来自哪里、为何出现”。
1.2 项目规则有顺序、来源和幂等性
AGENTS.md/rules 的查找顺序是 global → repo root → cwd,越深的文件越晚出现、冲突时优先;兼容 Claude/Cursor 规则目录,并受 gitignore 过滤,最终按 canonical path 去重。agents_md.rs:66-77 agents_md.rs:87-168
每段规则保留源文件路径,rules frontmatter 被剥离;恢复会话时通过结构标签或 legacy 前缀识别已有项目指令,避免重复注入。agents_md.rs:186-229 prompt_build.rs:65-90
1.3 大用户输入采用“内联摘要 + 文件指针”
首轮大 prompt 超过 25 KB 时,不直接粗暴截掉尾部:会把全文写到 session 文件,内联内容按 query 80%、context 余量、skills 独立 4 KB 预算分配,并保留 head + tail,确保结尾真正问题仍在;写盘失败则改成无路径的诚实提示,避免模型追逐不存在的文件。prompt_build.rs:185-202 prompt_build.rs:203-276 prompt_build.rs:278-309
这和 Catty 已有的 tool output handle 思路相似,但 Grok 把同一模式也用于用户输入。值得统一成通用的“上下文外置对象”:有稳定 handle、摘要、大小、来源、读取工具和生命周期。
2. Token 预算与上下文计量
Grok 把 bytes/4 估算、图片固定成本、百分比、剩余量和阈值判断放在共享 crate,所有 UI、预检和自动压缩使用同一套整数语义;阈值是 >=,边界行为有测试固定。xai-token-estimation/src/lib.rs:1-32 xai-token-estimation/src/lib.rs:35-104 xai-token-estimation/src/lib.rs:188-207
运行时不是只信模型上次返回的 usage:get_estimated_total_tokens 会把上次模型总量与之后新增的工具结果估算相加,用于下一次请求前的 overflow 检查。handle.rs:403-419 mutations.rs:112-127
工具 schema 本身也进入压缩预算;输入溢出时采用 verbatim → fitted verbatim → lossy 的降级阶梯,fitted 为摘要预留 32,768 token,再扣除工具 schema token;lossy 最多使用窗口 70%。compaction.rs:879-890 compaction.rs:931-946 compaction.rs:1062-1116
Catty 可借鉴: 统一 tokenEstimator、UI context 指示、step pruning 与 413 预检的边界语义;把 tool schema、pending tool output、图片字节都纳入“下一请求成本”,而不是只看上一响应 usage。
3. 历史裁剪、压缩与可恢复性
3.1 三层减负
第一层是工具本身输出限额:一般工具默认 40 KB,终端结果默认 20,000 字符;完整终端输出写文件,模型收到头尾预览和文件路径。xai-grok-tools/src/lib.rs:5-16 types/output.rs:413-432 types/output.rs:1217-1238
第二层是请求副本 pruning:只在窗口超过 50% 后运行,近 3 个用户轮不动;较老且超过 4,000 字符的结果保留头尾各 1,500;10 轮以前只留 placeholder。request_builder.rs:155-208 types.rs:67-97
第三层才是整段 compaction。默认阈值 85%,可配模型、memory flush、5 分钟 wall-clock backstop,并可启用两段式模式。xai-grok-agent/src/compaction.rs:3-44
这种分层优于“每轮都压缩工具结果”:它刻意保护稳定前缀,避免频繁改写旧消息导致 KV cache miss。request_builder.rs:64-85
3.2 两段式后台预压缩
两段式先按估算 token 权重切分约 95%/5%,且切点会避开 assistant tool_calls 与对应 ToolResult,保证结构合法。two_pass.rs:29-50 two_pass.rs:52-139
NOTE1 最多 12,000 字符;优先取完整、足够长的 <summary>,否则使用原始输出。正式压缩前缓存必须同时满足 prefix_len、model slug、前缀 fingerprint 三项,任何不一致都退回单段压缩。two_pass.rs:14-20 two_pass.rs:141-187 compaction.rs:379-415
它还区分“后台已经完成的延迟”和“用户实际等待的延迟”,只有后者计入最终 TTFT;这是评估 speculative work 是否真的降低用户等待的正确方法。compaction.rs:342-355 compaction.rs:416-428
风险: 后台 pass1 会额外花 token,且 prefix fingerprint 目前只 hash item 类型和 text_content,没有显式 hash tool call arguments;如果 tool calls 的参数不在 text_content() 中,理论上可能出现缓存误命中。Catty 若实现,应使用完整 canonical serialization fingerprint,并先用命中率、浪费 token、同步等待下降三项实验数据验证。
3.3 压缩后恢复精确细节
CompactionMode 提供 summary-only、指向原始 updates.jsonl、以及 clean Markdown segment store 三种模式。后两者在摘要尾部告诉后继 agent 如何用 read/grep 找回精确内容。compaction_mode.rs:7-20 compaction_mode.rs:51-77
segment 有独立索引、关键词、turn/tool/file/error 统计和不同细节级别;fork 时连同 segments 一起复制,因此子分支不会因为父会话压缩失去早期证据。compaction_transcript.rs:75-140 compaction_transcript.rs:184-267 fork.rs:92-106
Catty 可借鉴: ToolOutputStore 解决的是大工具输出,segments 解决的是“摘要后整个旧对话”。两者可共用 handle/read 基础设施:压缩摘要携带结构化 archive manifest,按 segment/turn/tool/file 查询,而非只给一个巨大 transcript 路径。
3.4 压缩后重新建立“工作现场”
压缩成功后,Grok 不直接只留下 system + summary。它重新构造 AGENTS.md、skills、memory、计划模式、运行中的后台命令、活跃子 agent、改过的文件、MCP 和 todo,再对 compacted history 做 orphan ToolResult 清理与验证;若仍不合法,退回更小的安全历史。compaction.rs:1205-1350 compaction.rs:1425-1499 compaction.rs:1504-1548
这是对 Catty 最直接的改进点:现有 SessionState reinjection 可以扩展为正式的 ContinuationState,明确包含 active jobs、subagents、todo/plan、edited files、MCP/tool catalog version、skills/AGENTS snapshot、外置输出 handles,并有 schema/version 和恢复测试。
4. 工具结果与缓存
Grok 明确区分 ToolRunResult.output(干净、协议/序列化/追踪用)和 prompt_text(可附 reminder、专供模型),避免 UI/协议数据被模型提示加工污染。types/output.rs:128-145
对话完整性修复发生在确定的写边界,不在任意读取时运行,避免把仍在执行中的并行工具误判为悬空;修复会持久化,因而恢复后不会重复撞 provider 400。mutations.rs:26-43 mutations.rs:47-70
图片处理也考虑 cache:只有请求体接近 50 MB 才批量移除最旧图片,并一次降到 25 MB,形成迟滞区,避免每轮移一张、每轮破坏 KV 前缀;占位文案明确告诉模型图片已不可见,避免凭“记忆”幻觉描述。request_builder.rs:215-265
Catty 可借鉴: 所有会改写历史前缀的策略都应有 cache-cost 意识;用 high-water/low-water 批处理,而不是刚过线就做最小改写。并将“干净工具结果”和“给模型看的文本”拆为两个字段,防止 reminder、裁剪标记污染恢复/审计数据。
5. 会话恢复与分叉
本地会话只有存在 summary.json 才算可恢复,避免只有 images 的残缺目录劫持 resume;远端恢复会寻找同 cwd 下最新的本地 child,避免重复恢复。persistence.rs:395-419 persistence.rs:422-453
分叉会复制 chat、updates、plan state 和 compaction segments,记录 parent_session_id,并可指定 prompt index/model/cwd;磁盘复制放到 blocking pool,后台注册服务端,不阻塞本地 fork 的关键路径。fork.rs:64-113 fork.rs:115-163
恢复/子 agent spawn 对 system prompt 的策略不同:顶层 resume 保留历史 system;子 agent resume 继承 raw transcript 和 tool state,但用当前定义重新渲染 system,避免旧 persona/工具目录永久冻结。prompt_build.rs:92-110 task/types.rs:44-47
6. 子 agents
子 agent channel protocol 把身份、parent prompt、resume_from、cwd、runtime overrides、是否后台、是否向父模型展示完成事件、是否 fork parent context 都作为明确字段。task/types.rs:29-68
能力不是简单的“全工具/无工具”,而是 ReadOnly、ReadWrite、Execute、All 四档,并在移除所有能产生后台任务的工具时同步移除无意义的 get/kill 生命周期工具。task/types.rs:139-174 task/types.rs:189-300
父子共享 filesystem、terminal backend、memory、scheduler、hunk tracker、hooks 等运行资源,但子会话有独立的 model/context threshold/usage/session signals;背景子 agent 在父轮取消时继续,前台子 agent 才按 parent_prompt_id 取消。subagent/mod.rs:135-214 task/types.rs:39-58
用量账本区分 main-loop calls 与 subagent calls,能按 model 汇总 input/output/cached/reasoning/cost,并显式标记 incomplete;后台仍在跑时不伪造精确账单。usage.rs:1-26 usage.rs:31-89 usage.rs:100-146
Catty 可借鉴: 将 subagent completion 从一段自由文本提升为结构化结果:status、session id、turn/tool count、duration、tokens、worktree、archive handles;父上下文仅保留短摘要,细节通过 resume/read 获取。
7. Hooks 与 Skills
Hook 生命周期覆盖 session、turn stop/failure、pre/post tool、permission denied、prompt submit、notification、subagent 和 pre/post compact;envelope 包含 session/cwd/workspace/transcript/prompt id,tool payload 限制为 128 KB。event.rs:3-49 event.rs:152-172 event.rs:201-240 event.rs:322-340
只有 PreToolUse 是阻塞决策;hook 超时/崩溃采取 fail-open,并把失败展示与记录,而不是默默吞掉。event.rs:127-149 dispatcher.rs:15-35
Skills 采用渐进披露:启动时仅列名称/说明/路径,单条说明上限 400 bytes,整个 listing 预算由 context window 推导;实际 body 调用时再载入。skill_discovery_tracker/listing.rs:1-20 skill_discovery_tracker/listing.rs:79-120
运行期还会根据 read/list/edit/apply_patch 实际触达路径发现或激活 skills;I/O 在资源锁外执行,checked_dirs 回写避免重复 stat,公告由 session 统一排队去重。skill_discovery.rs:27-45 skill_discovery.rs:109-155 skill_discovery.rs:159-218
Catty 可借鉴: hook 应进入统一 AgentEvent trace;skill announcement 需要预算和去重,并在压缩后恢复“已宣布/已激活”状态,避免每次 compaction 后重复灌入。
8. 可观测性与离线评估
压缩 span 记录 trigger、使用比例、阈值、tokens before、attempts、degenerate/input-overflow/deterministic/transient rejection、TTFT、stream time、delta count、最大 inter-token gap、两段式是否使用、prefire hit/wait/stale 和 prefix release。compaction.rs:800-864 compaction.rs:1150-1202
compaction streaming timing 是 O(1) accumulator,不保存每 token 时间戳;能直接算 TTFT、流持续时间、delta 数和最大间隔。session_compact.rs:256-310
更关键的是持久化 compaction_requests/{id}.json:包含精确输入 ConversationItem、工具定义、模型、用户额外上下文、摘要或错误和每次尝试细节,注释明确说用于 offline prompt iteration。persistence.rs:360-368
这使“摘要质量”可以离线回放,而不是靠线上主观反馈。Catty 应补一套固定 eval:
- continuation state recall:active task/subagent/todo/edited file 是否完整;
- exact-detail recovery:摘要缺失时能否从 archive 找回具体错误、命令、路径;
- tool-call integrity:压缩/取消/恢复后无 dangling/orphan/duplicate;
- instruction retention:用户约束、AGENTS.md、skill 触发在多次压缩后仍有效;
- latency/cost:同步压缩等待、prefire 命中率、浪费 token、cached input 比例;
- continuation success:后继 agent 在不看原始 transcript 时能否完成下一步。
9. 原创实现与移植部分的边界
仓库的正式归属声明非常明确:从 OpenAI Codex 移植的是 xai-grok-tools/src/implementations/codex/ 下的 apply_patch、grep_files、list_dir、read_file;从 sst/opencode 移植的是 implementations/opencode/ 下 bash、edit、glob、grep、read、skill、todowrite、write。THIRD_PARTY_NOTICES.md:1-12 THIRD_PARTY_NOTICES.md:14-42
因此本笔记讨论的 PromptContext、chat-state actor、分层 pruning、图片迟滞、两段式 prefire compaction、segment archive、ContinuationState 重建、session fork/resume、subagent coordinator、usage ledger、hook runtime、compaction telemetry/artifacts,均不在声明的 Codex/OpenCode 移植目录中,应视为 Grok Build 自己的 runtime/context-engineering 实现。这里的“原创”只表示仓库归属证据显示不是那两组移植文件,不主张它在思想史上从未受其他 agent 产品启发。
需要特别避免误判:Grok 可以配置 Codex prompt profile,也能组合 OpenCode 工具集;这表示兼容/复用工具行为,不等于它的上下文 runtime 来自 Codex/OpenCode。prompt/context.rs:15-29 xai-grok-agent/src/config.rs:518-528
10. 给 Catty 的落地计划建议
P0:先补正确性与评估底座
- 定义版本化
ContinuationState,压缩后确定性重注入 active jobs、subagents、todo/plan、edited paths、MCP、skills/AGENTS、tool-output handles。 - 在开始新轮、取消完成、恢复、压缩替换四个边界运行 conversation integrity repair,并记录修复数与原因。
- 保存每次 compaction 的精确输入、输出、模型、token、重试与错误 artifact;建立 20–50 条真实长会话的离线 continuation eval。
P1:增加可恢复的压缩档案
- 在现有 ToolOutputStore 上增加 conversation segment handles 和索引;摘要只带 manifest/恢复提示,不塞回全文。
- 对工具结果采用“近轮保护、旧结果头尾裁剪、极旧占位”的分层策略,并保证原始 trace 仍可重放。
- 给大用户输入使用同一套外置 handle,不让首轮超长需求在进入 agent 前就丢失尾部。
P2:在实验开关下优化延迟与缓存
- 实现带完整 canonical fingerprint 的后台两段式 compaction;记录 hit/stale/wasted tokens/sync wait saved。
- 对会破坏 prompt cache 的历史改写采用 high-water/low-water 批处理,并比较 cached input tokens 的变化。
- 将 skill listing、tool catalog、MCP announcement 都纳入独立预算和持久化去重状态。
不建议直接照搬
- 不应直接采用 bytes/4 作为唯一 token 估算器;Catty 已有模型相关估算基础,应保留实际 tokenizer/usage 校正,只统一边界语义。
- 不应未经 eval 就开启后台 pass1;它可能增加费用且缓存失效会造成纯浪费。
- 不应把 50%/85%/95%、40 KB、10 轮等常数照抄;这些是 Grok 的模型和服务约束,应由 Catty 的 trace 分布校准。
- Hook 的 fail-open 是 Grok 明示的威胁模型选择,不适合作为所有安全策略的默认值;Catty 需要按 hook 类型区分“工作流扩展”和“安全门禁”。
最终判断
Catty 现有架构已经有 pre-turn compaction、step pruning、413 retry、SessionState reinjection、ToolOutputStore 和统一 AgentEvent,方向是对的。Grok Build 显示下一阶段最有价值的不是再加一种总结 prompt,而是把这些模块连成一个可恢复、可验证、可观测的上下文生命周期:压缩前分层减负,压缩时保存证据,压缩后重建现场,细节按需恢复,所有路径都能离线重放和量化。