关上源码,自己动手

源码篇读完,小A合上 pi-mono 的代码,长长舒了口气:“现在我至少看得懂一个真实 Agent 是怎么搭起来的了。”
老Z却把电脑转过来,给他看一个空目录:“那现在,你自己写一个。”
小A愣住了:“照着 pi 抄一个?那得抄到什么时候。”
“不是抄。”老Z说,“源码篇让你看见一个真实工程如何切分边界;实现篇要求你为自己的需求做取舍。下一步,是先决定你的 Agent 不做什么。”
我们要造什么
小A:“那到底造多大?总不能把 pi 重写一遍。”
老Z给他列了一张交付清单:
本篇交付什么
所有章节的代码必须组成同一份可运行产物,而不是彼此独立、无法拼接的片段。最终产物会随章节逐步具备:
- 可调用模型并报告流式事件、错误和中止;
- 可维护消息状态、停止原因、工具调用与轮次预算;
- 可顺序执行受 schema 和工作区边界约束的
read、write、bash工具;runAgent可在调用方传入sessionPath/contextMaxChars时保存、恢复和进行确定性字符裁剪,并另行提供可诊断的命令行交互;- 可在风险动作前触发 Hooks 或审批,并明确沙箱、权限和敏感信息处理的真实边界。
完成标准:验证,不是“看起来合理”
小A:“那我怎么知道写完了、写对了?”
老Z敲了敲桌子:“‘看起来合理’不算完成标准。每个阶段都要给出可执行的验证命令、预期结果和已知限制;最后一章会用完整代码、测试与实际运行结果检验这些零件是否能够协同工作。验证覆盖正常路径,也覆盖至少必要的错误、中断或边界路径。”
“还有没有别的坑?”小A追问。
老Z:“有一个坑,得先说清楚:”
必须区分库能力与入口装配
当前
main.ts和runCli()都没有向runAgent传入sessionPath或contextMaxChars。因此npm start与当前runCli()调用不会持久化、恢复或裁剪会话;这些能力只能通过直接调用runAgent并显式传参使用。
小A有点心虚:“那我能做到什么程度?会不会被说‘这不算 Agent’?”
“本篇不承诺生产级通用平台。真实 Provider 不做端到端验收;MCP、Skill 和多 Agent 只保留被需求证明必要的扩展点;不在范围内的能力会说明限制,不会拿空接口假装已实现。”老Z顿了顿,“同一份实现放在 examples/mini-agent,章节会逐步补全其中的代码与验证步骤;尚未核验的运行结果、测试数量、性能数据,这里一律不预报。”
“那这篇解决什么,又暂不解决什么?”小A问。
本篇解决什么
读完实现篇,你能用 TypeScript 从零搭出一个可运行、可验证、范围受控的 Agent:接一条模型流,跑一个循环,让工具真正落地,让会话能续接,给 CLI 一个可靠的入口,并在执行前拦住风险。它的价值不在功能齐全:每一条控制流、每一个数据类型、每一道测试边界,你都能同时读到。
这里暂不解决生产级平台的问题:重试、REPL、OS 级沙箱、网络控制、多 Provider 端到端验收都不在范围内;MCP、Skill、多 Agent 只讲接口推演,不进
src。
小A又接了一句:“那这十二章,是不是每章都往产物里加一块?”
“对,而且顺序就是依赖顺序。”老Z说,“先决定不做什么(范围),再接通模型流(管道),把下一步交给循环(控制),让工具落地(能力),让上下文续接(状态),给人可靠入口(交互),在执行前设卡(安全),为变化留接口(扩展),最后把零件拼成机器(装配)。每一章加一块,前一块是后一块的前提,跳着读会接不上。”
小A想了想:“那我怎么确认每一章真的做对了?”
“每章结尾都有阶段验收。”老Z说,“它不认‘你觉得差不多了’,只认可执行的验证:命令是什么、预期输出是什么、失败长什么样。先写验收条件,再写代码。代码是为了满足验收才存在的。很多教程教你写个 Agent,本篇是和你一起写一个每一行都有验证的 Agent。”
为什么现在读这一篇
小A收拾了一下桌面,忽然问:“那我们从哪开始?”
老Z笑了笑:“先别急着写代码。还是那句话,先决定不做什么。往后每个选择,咱们都用设计讨论、结对编程和故障排查来推进:先写验收条件,再让代码与验证结果说话。”
从这里开始:实现篇第1章 白纸与边界——Scope
