Appearance
从零手写一个 Agent
这是全书最后一关。我们把前面每个模块的工程化 Demo 串成一个完整的最小 Agent。它零依赖、可运行,是 @pi/agent-core + @pi/ai 的浓缩复现。
它包含哪些"pi 概念"
| 概念 | 在 pi 里的位置 | 在工程化 Demo 里 |
|---|---|---|
| 消息模型 | @pi/ai types | src/ai/types.ts |
| 事件流 | @pi/ai EventStream | src/ai/event-stream.ts |
| StreamFunction | @pi/ai | src/ai/stream-function.ts |
| 工具契约 | @pi/agent-core AgentTool | src/agent/types.ts |
| Agent 循环 | agent-loop.ts | src/agent/agent-loop.ts |
| 生命周期事件 | types.ts AgentEvent | src/agent/types.ts |
| steer/followUp | agent.ts | src/agent/agent.ts |
工程化结构(完整项目)
text
pi-principles/
├── package.json # scripts:cli / chat / agent / app / repl / ai ...
├── cli.ts # CLI 入口(pi-demo)
├── src/ # 模块源码(镜像 pi 的包)
│ ├── config.ts (.env 配置)
│ ├── ai/ (消息/事件流/StreamFn/Faux + api/openai-responses)
│ ├── agent/ (AgentTool/循环/MiniAgent/decider)
│ ├── tools/ (calculator/bash/read)
│ ├── protocol/ (定帧/协议消息)
│ ├── transport/ (服务端/客户端/管道)
│ ├── tui/ (组件/布局/差分渲染/session-view)
│ └── cli/ (args/main/chat/agent/app/repl)
└── play/ 模块化离线入口
├── ai.ts / mini-agent.ts / protocol.ts / transport.ts / tui.ts综合入口:play/mini-agent.ts
它把第 1、2 部分的模块缝合起来:
- 用
@pi/ai的StreamFn形状驱动循环。 - 用
agent/agent-loop.ts的 ReAct 循环做"思考→行动→观察"。 - 挂两个工具(
calculator/echo)。 - 演示
steer(中途插话)与followUp(完成后追加)。
ts
import { MiniAgent } from "../src/agent/agent.ts";
import { createDeciderStreamFn } from "../src/agent/decider.ts"; // 离线假模型
import type { AgentTool } from "../src/agent/types.ts";
const tools: AgentTool[] = [
{ name: "calculator", description: "两数相加", execute: ({ a, b }) => `= ${Number(a) + Number(b)}` },
{ name: "echo", description: "原样返回", execute: ({ text }) => text ?? "" },
];
const agent = new MiniAgent({ streamFn: createDeciderStreamFn(), systemPrompt: "你是一个只会做加法的教学 Agent", tools });
agent.subscribe((e) => { /* 观察事件 */ });
await agent.prompt("计算 2 + 3");
agent.steer({ role: "user", content: "改成计算 2 + 5", timestamp: Date.now() });
agent.followUp({ role: "user", content: "最后把答案乘以 10", timestamp: Date.now() });
await agent.prompt("继续");想用真实模型?把
createDeciderStreamFn()换成createOpenAIStreamFn(loadConfig())(需 .env),或直接bun cli.ts agent "计算 2 + 3"。
运行
bash
cd pi-principles
bun play/mini-agent.ts # 或 npm run mini-agent输出(注意工具被调用、steer 中途采纳、followUp 最后处理):
== agent_start ==
[assistant] (调用 calculator:{"a":2,"b":3})
→ 工具: calculator
[toolResult] = 5
[assistant] 答案:= 5
== agent_end ==
== agent_start ==
[user] 改成计算 2 + 5 ← steer 中途注入,再次触发工具
→ 工具: calculator
[toolResult] = 7
[assistant] 答案:= 7
[user] 最后把答案乘以 10 ← followUp 最后处理
[assistant] 答案:最后把答案乘以 10
== agent_end ==扩展练习(自己做)
- 加一个工具:在
tools数组里加一个reverse工具(反转字符串),看模型能否调用。 - 改判定逻辑:修改
createDeciderStreamFn,让它识别"乘法"而调用calculator。 - 联动 transport:把
MiniAgent的 transcript 用protocol/framing.ts编码后经transport推送,让它"跑在远程"。
这本书至此,你已经掌握了
@pi/ai:统一消息模型、事件流、Model/Provider、StreamFunction 契约。@pi/agent-core:Agent 循环、工具、生命周期事件、steer/followUp 队列。@pi/protocol:定帧、CBOR 编解码、消息 Schema。@pi/server+@pi/client:远程会话、握手、快照与增量。@pi/tui:组件模型、布局、差分渲染。@pi/coding-agent:会话持久化、内置工具、上下文压缩、扩展系统、SDK。
从"一段字节"到"一个能干活、能存盘、能远程连接、有界面的 Agent",整条流水线你已经走过一遍。下一步回顾全书层级关系。 → 总结与层级关系