收尾复盘

全书要点串联、与 llm-to-agent / Grok Build / OpenCode 的四方对照,以及可迁移的工程经验。

作者 David更新于 第 33 篇(共 33 篇)

全景回顾

一条 prompt 从终端到模型再回到屏幕,经过的层:

TUI(pi-tui 差分渲染)
  ↓ node-sdk:进程内 bootstrap 引擎
klient(契约 + zod;内存传输也做 JSON 往返)
  ↓ agentPromptService.submit
Agent scope:IAgentPromptChannel → AgentLoopService
  ↓ agent/loop/machine(适配层)
human 状态机:agent → turn → tool
  ├── generate  ← llmRequester → human/llm(四种协议基座)
  └── 工具批次  ← toolExecutor + ToolScheduler(按资源冲突并行/串行)
  ↓
事件 → IEventBus → SDK → TUI;记录 → wire.jsonl

十六条值得记住的设计

边界与契约

  1. human/ 是纯内核:不 import 任何 v2 领域代码,只有三个适配层能碰它的实现,lint:imports 强制。
  2. klient 每个线上方法都有 zod 契约,schema 手工镜像引擎类型,由编译期断言钉住漂移。
  3. 传输无关:内存传输也做 JSON 往返,与 ipc 返回字节一致——同进程不是走捷径的理由。
  4. 服务挂在哪一级 Scope 在注册时写死(registerScopedService(LifecycleScope.Agent, …)),看文件末尾就知道它的寿命。
  5. 功能经 Feature 接缝进来:features/ 下每个功能往贡献点挂配置、工具、命令,核心不必知道它们。

类型即约束

  1. 回合结果是 completed / failed / cancelled 的判别联合;请求层 generate 约定错误只经事件交付、从不抛出。
  2. 资源访问模型:读与搜索互不冲突,任一方写即冲突,all 与一切冲突;路径比较前小写折叠,在 Linux 上偏保守,方向安全。

并发与安全

  1. Edit 不唯一就报错,且错误信息带修复指引(设 replace_all=true,或给 old_string 多带上下文)。
  2. 权限的 fallback-ask 兜底保证默认安全。
  3. skill 与插件说明注入提示词前做 XML 转义防注入。
  4. bash 解析有确定性预算,中止或出错就当「无法分析」降级。

可恢复性

  1. wire 记录有完整的版本迁移链(v1.1 到 v1.5),旧会话能打开。
  2. 重试与恢复是纯策略:retry.ts / recovery.ts 给出方案,turn 状态机执行——先试凭证恢复,再试媒体降级等替换消息。
  3. 用量在模型请求结束时立即记录,不等工具执行。
  4. 事件发射器用 safelyCallListener 包住每个监听者,UI 出错不拖垮发射方。

工程纪律

  1. 无注释区 + 生成的清单 + 就近注册的 flag:三个包不许写注释,wire / state / config 三份清单由脚本生成,实验开关没有中央目录。

四方对照

维度llm-to-agentGrok BuildOpenCodeKimi Code
定位教学最小实现生产实现生产实现生产实现
语言教学用RustTypeScript + EffectTypeScript
规模小约 85 万行(非测试)/ 96 个 crate近 50 万行 / 36 个包(agent 相关约 17 万行)38 万行 / 19 个单元(引擎约 13.6 万行)
前端13(TUI、headless、ACP)8(TUI、Web、桌面、VS Code、ACP、GitHub、run、SDK)7(TUI、-p、Web、Remote Control、VS Code、ACP、SDK)
依赖组织无显式传参Effect 的 Service、Layer 与 LayerNodeVS Code 式 DI × Scope
TUI—自研OpenTUI + SolidJSpi-tui(vendored 自 pi-mono)
存储——SQLite + Drizzle + 事件投影wire.jsonl 追加日志 + minidb 索引
引擎代数112 代并存(V1 现役、V2 迁移中)2 代 → 1 代(v1 已删除)

最大的结构差异来自前端数量。Grok Build 以 TUI 为中心,headless 与 ACP 只是同一个二进制的另外两种入口,agent 核心可以与 UI 贴得很近;Kimi Code 要喂七种形态,于是必须有 SDK 宿主层、klient 契约、transcript 独立包。

同为 TypeScript,Kimi Code 与 OpenCode 的依赖组织走了两条路:一边是 VS Code 式的装饰器 DI 加三级 Scope,一边是 Effect 的 Service 与 Layer。更值得对照的是换代:OpenCode 还在 V1 现役、V2 迁移的中途,Kimi Code 已经走完——服务端先切(v0.24)→ CLI 默认新引擎、留回退开关(v0.33)→ kimi acp 不再认回退开关(v0.40)→ 到 v0.43.0 旧包与开关都已删除。这条路径正好可以拿来参照。

这不是谁更好,是约束不同。 读源码时若不理解这个前提,会觉得 Kimi Code「过度设计」。

可迁移的经验

不写 agent 也用得上的几条:

提示词是资源文件。 引擎里 63 个源文件以 ?raw 导入 .md,提示词可 diff、可 review、可由非工程同事编辑。

换代要有兼容层。 node-sdk 的 v2 客户端逐方法对齐 v1 语义,差异写进注释并「pinned in the migration tracker」,前端几乎无感地换了引擎。

边界要能被机器检查。 导入边界、无注释区都有脚本把关;AGENTS.md 还专门提醒 flake.nix 的同步检查只覆盖 CLI 的依赖闭包、其余必须手工维护——知道检查的盲区,和有检查同样重要。

配置不静默忽略。 改名的旧键会在启动时提示改名,永远匹配不到任何工具的 allow/deny 条目会告警;llm 设计指南的原则之一就是「No silent fallback」。

vendored 代码的本地改动要登记。 pi-tui 的 AGENTS.md 逐条列出与上游的差异,每条都有守护测试,重新同步上游时不会被悄悄覆盖。

文档也会漂移。 AGENTS.md 因为天天被用而大体准确,但这次复核仍发现它与代码有出入——所以仓库自己强调「代码才是真相」。

这份源码的短板

诚实地说几条:

  • 文档与代码有出入:根 AGENTS.md 写 agent-core-v2 有四级 LifecycleScope,枚举里只有三级;提到的 IWorkspaceLifecycleService.handlerFor 在引擎代码里找不到;minidb 的包描述还停在 KV 数据库。
  • v1 的残留:kosong、kaos 引擎已不用却仍在仓库(kaos 只剩类型引用);SDK 里还有「the v1 engine throws not_implemented」一类已无对应实现的说明。
  • 无注释的代价:13 万多行的引擎没有一行注释,新读者只能靠命名、类型和测试建立心智模型。
  • 巨型文件:kimi-tui.ts 4190 行、sdk-rpc-client-v2.ts 2807 行、loopService.ts 2260 行。
  • 自维护的底座(minidb、vendored 的 pi-tui、tree-sitter-bash)长期维护成本不低。

继续读什么

回到专栏首页可查任意章节。源码基准 ffa94fa(v0.43.0,2026-09-14)——从 v0.29.1 到这里只隔七周,引擎就完成了换代,读时请以当前 HEAD 为准。

本页目录