Appearance
关上源码,自己动手
源码篇读完,小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笑了笑,丢给他一个问题:“先别急着写代码。**先决定不做什么。**小a与老z会用设计讨论、结对编程和故障排查推进每一个选择:先写验收条件,再让代码与验证结果说话。”
从这里开始:第30章 白纸与边界——Scope