第 01 章:第一次对话
所有 coding agent 的起点都朴素得惊人:一个 HTTP 请求。这一章我们写 70 行代码,实现一个能多轮对话的终端聊天程序。别小看它——理解这一章,后面的一切都是自然延伸。
核心认知:messages 数组就是全部状态
OpenAI 兼容的 chat completions 接口是无状态的。所谓"多轮对话",是客户端每轮把全部历史重新发给模型:
模型回复一条 assistant 消息,我们把它追加进 messages,下一轮再全部发出去。这个"追加 → 全量重发"的循环带来两个深远的影响,请记住它们,后面两章都与此有关:
- 上下文会无限增长——所以第 07 章需要压缩(compaction)。
- 历史必须由我们自己保管——所以第 06 章需要会话持久化。
pi 的 pi-ai 把这个结构抽象为 Context { systemPrompt, messages, tools },可以 JSON 序列化、可以跨 provider 交接(参见设计思想:LLM 抽象层)。我们的裸写版直接用这个 wire 格式本身,零抽象。
代码
src/main.ts(第 01 章完整版)
运行:
第二轮回答证明了:模型"记得"第一轮,是因为我们把第一轮的消息重发了一遍。
本章要点
- chat completions 是无状态 API;
messages数组是客户端维护的全部对话状态。 - 三种基本角色:
system(行为设定)、user(输入)、assistant(模型回复)。第 02 章会迎来第四种:tool。 - 没有任何 SDK——一个
fetch就是 LLM 调用的全部。
成本提示
全量重发意味着每轮的 input token 都在累加。真实 harness(包括 pi)会非常在意缓存命中(prompt caching)和上下文裁剪,这也是设计思想篇反复强调"token 是预算"的原因。