# 组装 · 把每一格拼成一个能跑的 agent · Agent 实战 · 从零构建完备智能体

**作者** Aklman · **章节** 13 / 19 · **首发** 2026.07 · **语言** 中文为主, 中英双语
**原文** https://library.aklman.com/books/build-agent/assemble
**全书 markdown** https://library.aklman.com/books/build-agent/llms.md
**上一章** https://library.aklman.com/books/build-agent/streaming/llms.md
**下一章** https://library.aklman.com/books/build-agent/coding-agent/llms.md

> 本章来自 Aklman · Library, 完整免费阅读。允许 AI 摘读、引用、问答; 转载请保留作者署名与原文链接 (CC BY-NC-ND 4.0, https://library.aklman.com/license)。

---

> 前面每章加一格，这一章把它们拼成一个你能真跑的 Python 工程 —— 离线一条命令，接上 key 就换真实 Claude。

前面每一章都在给那个研究助手加一格 —— 搜索、抓取、记忆、推理、沙箱、护栏、trace。这一章把它们拼成一个你能真跑的 Python 工程：离线一条命令就跑，接上 key 就换成真实的 Claude。读完你手里不再是十几段散落的片段，是一个核心部件齐全、能跑能改的基础版 agent。

工程在 `content/books/build-agent/agent/`，一个叫 `research_agent` 的小包。下面拆三件事：心脏为什么还是第二章那个循环、怎么跑、以及它为什么是「完备基础版」而不是生产级。

### I · 心脏没变，还是那个循环

加了这么多格，中心那个 while 一行没变。这正是全书的论点：agent 就是 LLM 在循环里用工具、读反馈、定下一步；其余都是挂在这个循环上的格子。

Anthropic 把基本积木叫「augmented LLM」—— 检索、工具、记忆三样增强；agent 就是它在循环里跑。[^1]OpenAI 的说法一样直白：单 agent 是一个 run loop，跑到触发退出条件为止，「这个 while 循环是 agent 运转的核心」。[^2]我们的 `loop.py` 就是这句话的代码版，把后面各章的格子接上去：

_loop.py 的核心：还是第二章那个循环，各格挂在上面_
```text

```python
for step in range(cfg.max_steps): # 停止条件（ch2）
 budget.check(tracer, cfg) # 预算闸（ch11）：超了就熔断
 messages = compact(messages, threshold) # 上下文减法（ch4）
 reply = model.generate(SYSTEM, tools, messages) # 真 Claude 或离线 stub（ch2）
 messages.append({"role": "assistant", "content": reply.content})
 if reply.stop_reason != "tool_use": # 模型给出最终答案 → 结束
 return final_text(reply)
 results = run_calls(reply.tool_uses) # 工具+并行（ch3）·护栏（ch9）·沙箱（ch7）
 messages.append({"role": "user", "content": results}) # 结果回灌；错误即纠错信号（ch5）
```

```

模型层是那把让它「能跑」的钥匙：`model.py` 把 provider 藏在一个接口后面，**有 `ANTHROPIC_API_KEY` 就接真实的 Claude，没有就退回一个离线 stub 模型**。[^4]stub 不联网、不要 key，按一条 search → fetch → run_python → 报告 的固定轨迹走 —— 它存在的意义，是让你在零依赖下把整台机器跑起来、看清每一格，也让这套 harness 可回归测试。

---

### II · 跑起来：离线一条命令，真实多两步

先证明它真能跑 —— 不要 key、不联网、不装任何包，离线 stub 模式一条命令就把整条链路走通。

#### 离线（零依赖）

```bash
cd content/books/build-agent/agent
python -m research_agent --offline "比较 RAG 与长上下文，各自适合什么场景"
```

#### 真实 Claude

```bash
cd content/books/build-agent/agent
pip install anthropic
export ANTHROPIC_API_KEY=sk-... # PowerShell: $env:ANTHROPIC_API_KEY="sk-..."
python -m research_agent "比较 RAG 与长上下文，各自适合什么场景"
```

离线那条真跑出来是这样（进度走 stderr，每一步都看得见）—— 注意 `run_python` 是在沙箱里真执行的，不是装样子：

_离线 stub 模式的真实输出（节选）_
```text

```text
[model: offline-stub]
▶ 开始：比较 RAG 与长上下文，各自适合什么场景
 → web_search({"query": "比较 RAG 与长上下文各自适合什么场景"})
 → fetch_page({"url": "https://zhuanlan.zhihu.com/p/8280024582"})
 → run_python({"code": "vals = [3, 7, 2, 8, 5]\nprint('sum=', sum(vals))…"})
■ 完成：4 步 · 3 次工具 · ~987 tok · ≈$0.0066 · cost-of-pass ≈$0.0066
```

```

工程的好处是每一格都是一个文件，名字就对着书里的章 —— 你能一眼看出哪段代码解决的是哪一章的问题：

- **`loop.py`**: 循环（ch2）—— 心脏，其余都挂在它上面。

- **`model.py`**: 模型层（ch2）—— 真实 Claude / 离线 stub，一个接口。

- **`tools.py`**: 工具（ch3）—— schema、错误回传、并行。

- **`memory.py`**: 记忆（ch4）—— 上下文减法 + 笔记 + tiny RAG。

- **`sandbox.py`**: 代码执行（ch7）—— 一次性工作区、超时、最小环境。

- **`guardrails.py`**: 安全（ch9）—— 最小授权 + 不可信标记 + 输出校验 + 人闸。

- **`trace.py`**: 可观测性与成本（ch10/11）—— trace + cost-of-pass + 预算闸。

一张图看清它们怎么咬合 —— 中间是循环，各格挂在上面，trace 和预算闸贯穿每一步：

_组装好的 agent：循环居中，各格在侧_
```text

```text
 question
 │
 ▼
 loop (ch2) ──→ model.generate ──→ 只有文本? ──是──→ 报告 + cost-of-pass
 ▲ │
 │ tool_use
 │ ▼
 上下文减法(ch4) 护栏(ch9) → 工具·并行(ch3) → 沙箱(ch7)
 │ │
 └──── 结果回灌 ◀────┘

 贯穿每一步：trace + 预算闸(ch10/11) · 事件级进度(streaming)
```

```

> 先用 `--offline` 跑，盯着 stderr 把四步看完一遍 —— 你会比读十章更快地「看见」这个循环。没设 key 时它会自动退回 stub，所以这条命令永远能跑，不会因为缺 key 报错。

---

### III · 它是完备基础版，不是生产级

核心部件齐全、能跑能改 —— 但「能跑」不等于「能上生产」。把它的边界说清楚，每一条都在对应章里展开过，也都是你下一步该补的地方。

- **沙箱是 subprocess，不是真隔离**: 超时 + 一次性工作区 + 最小环境够挡误操作，但不隔离文件系统 / 网络 / 内核。跑任意不可信代码要上容器 / gVisor / microVM（ch7）。

- **「RAG」是关键词重叠**: 真实提升在 contextual chunk + 重排的检索管线里，不在这个最小骨架（ch4）。

- **循环是同步的**: 进度用回调流出；真要做流式 UI + 可中断，要上 async（streaming 章）。

- **原则化自查默认关**: 那道护栏的裁判得是个真实模型，离线 demo 跑不了（ch9 · Constitutional AI）。

怎么知道哪一格该补、补了值不值？看 trace 摘要里那个 `cost-of-pass`（成本 ÷ 成功率）。Efficient Agents 的发现是很多模块边际递减，复杂度该按任务难度配。[^3]所以加一格之前先量一下：成功率涨了多少、成本涨了多少 —— 用数字判断，别凭感觉。这正是全书第一章那句话在工程上的兑现：先做最简的，只在被证明值得时才加复杂度。

所以你现在手里有一个能跑的 agent。下一章把这套同样的机器对准最难、也最成熟的用例 —— 编码；再下一章回答：手写过一遍之后，何时该用框架、何时不必。

动手 · 把这个 agent 接到你自己的一个真问题上：

- **先离线跑一遍，看清每一步**:
 在 `agent/` 里 `python -m research_agent --offline "你的问题"`，对着 stderr 看它搜、抓、算、报。

- **设上 key，换真实模型**:
 `pip install anthropic`、设 `ANTHROPIC_API_KEY`，跑你自己真要查的问题，看真实 Claude 怎么自己决定调哪个工具。

- **改一格，用 cost-of-pass 判值不值**:
 在 `tools.py` 加一个你自己的工具，或在 `config.py` 收紧白名单 / 预算。改前改后各跑一次，对比 trace 摘要里的 cost-of-pass —— 用数字决定这一格留不留。

> 把每一格拼起来，
> 心脏还是第二章那个循环。

## 引用与参考

01 · Anthropic · 「Building Effective Agents」（2024-12-19）—— 基本积木是「augmented LLM」（检索 / 工具 / 记忆）；agent 就是「LLM 在循环里用工具、读环境反馈、决定下一步」。组装这一章正是把这句话落成代码。  (Anthropic · Building Effective Agents)
02 · OpenAI · 「A Practical Guide to Building Agents」（2025-04）—— 单 agent 就是一个 run loop，跑到触发退出条件（最终输出 / 无工具调用 / 出错 / 步数上限）；原话「这个 while 循环是 agent 运转的核心」。  (OpenAI · A Practical Guide to Building Agents)
03 · Wang et al. · 「Efficient Agents」（2025，arXiv:2508.02694）—— 用 cost-of-pass = 成本 ÷ 成功率 度量每一格值不值，复杂度按任务难度配。trace 摘要里那个数，就是给「加东西前先量」一个抓手。  (arXiv · Efficient Agents (2508.02694))
04 · anthropic-sdk-python —— 官方 Python SDK，真实后端用 messages.create；本章的 model.py 在「有 ANTHROPIC_API_KEY 时接 Claude、没有时退回离线 stub」之间切换。引用发布版本而非 main。  (GitHub · anthropic-sdk-python (releases))

---

*组装 · 把每一格拼成一个能跑的 agent · Agent 实战 · 从零构建完备智能体 · Aklman 著 · CC BY-NC-ND 4.0 · https://library.aklman.com/books/build-agent/assemble*
