📑 本页目录(点开跳转)
17 · 实战项目
⏱ 三个项目,每个 1–3 天 | ⭐ 不做项目 = 没学过
🎯 一句话
三个递进的项目:单 Agent → 带验证循环 → 多 Agent。做完你就有了完整的手感。
💰 成本提示:项目一用第 1 章的
MockModel可以零成本开发调试,逻辑通了再换真模型。 项目二三建议用中档模型,全部做完大概几美元。
🧠 ADHD 专属:怎么保证做得完
项目是最容易半途而废的东西。三条规则:
- 先跑通最烂的版本。 别一开始就想做完美。烂版本能跑 = 已经赢了 80%
- 每个项目切成 ≤1 小时的任务(下面已经切好),完成一个打勾
- 不允许优化「还没跑通的东西」。跑通 → 打勾 → 才准优化
🚫 必须避免:花两天配环境。用最简单的方式跑起来,工程化以后再说。
🥉 项目一:终端研究助手(1 天)
目标:一个能「搜资料 → 读内容 → 给出带引用的回答」的命令行 Agent。
练什么:第 1 章的循环 + 第 7 章的工具设计。
任务清单
- [ ] T1 (45min) 搭骨架:复用第 1 章的
run_agent循环,先用 MockModel 跑通 - [ ] T2 (60min) 实现两个工具:
search_web(query)—— 可用duckduckgo-search库或维基百科 APIread_page(url)—— requests + 粗提取正文,截断到 3000 字并注明已截断- [ ] T3 (45min) 系统提示词:要求回答必须带来源 URL;资料不足要明说,不许编
- [ ] T4 (45min) 测试 5 个问题:2 个简单事实、2 个需要多跳搜索、1 个资料里根本没有答案的
- [ ] T5 (30min) 打磨工具返回:搜索结果只给标题+摘要+URL(第 7 章:别把整页塞给模型)
- [ ] T6 (30min) 加护栏:
max_steps+ token 计数器,每次任务结束打印成本
🔨 关键代码片段
def search_web(query: str, max_results: str = "5") -> str:
"""搜索网页。返回标题、摘要和链接,不返回正文。
需要正文时用 read_page(url)。"""
results = do_search(query, int(max_results))
if not results:
return f"没有找到关于「{query}」的结果。试试更宽泛的关键词。"
return "\n".join(
f"[{i}] {r.title}\n {r.snippet[:150]}\n {r.url}"
for i, r in enumerate(results)) # ⭐ 用短索引不用 UUID(第7章)
def read_page(url: str) -> str:
"""读取网页正文。内容会被截断。"""
text = extract_main_text(url)
LIMIT = 3000
if len(text) > LIMIT:
return (text[:LIMIT] +
f"\n\n[正文已截断,原文共 {len(text)} 字。"
f"如需后续内容,说明你要找什么,我会重新提取相关段落]")
return text
✅ 通关标准
- 多跳问题能自主「搜→读→再搜」至少两轮
- 找不到答案时明说,而不是编造 ⭐ 这条最容易翻车
- 每个结论都有来源 URL
🎁 加分
- 遇到搜索结果互相矛盾时,让 Agent 指出矛盾而非硬选一个
- 加一个
response_format参数(第 7 章),对比 token 消耗
🥈 项目二:带验证循环的代码修复 Agent(2 天)
目标:给它一个带失败测试的 Python 小项目,它自主「跑测试 → 读报错 → 改代码 → 再跑」直到全绿。
练什么:第 5、12 章的验证循环 —— 这是 Agent 编码能力的核心机制。
任务清单
- [ ] T1 (45min) 准备靶场:写一个 100 行左右的小模块(如日期处理工具)+ 10 个 pytest 用例,故意埋 3 个 bug 让 4 个用例失败
- [ ] T2 (60min) 工具三件套:
read_file(path)edit_file(path, old, new)—— 精确字符串替换,old 不唯一时报可行动的错误run_tests()—— subprocess 跑 pytest,返回精简后的失败摘要,不是全量输出- [ ] T3 (45min) 循环策略写进系统提示词:每次只改一处 → 立即跑测试 → 读结果决定下一步;修好之前不许宣布完成
- [ ] T4 (60min) 跑通全流程,观察它的轨迹:它先看了什么?误改过吗?怎么发现的?
- [ ] T5 (60min) 加验证分离(第 12 章):完成后由一个全新上下文的「审查 Agent」检查 diff——它不知道修复过程,只看结果和测试,试图推翻结论
- [ ] T6 (45min) ⭐ 故意给一个测试本身就错的靶场,看 Agent 会不会盲目迎合错误测试
🔨 关键:run_tests 的返回设计
def run_tests() -> str:
"""运行测试套件,返回失败摘要。"""
r = subprocess.run(["pytest", "-q", "--tb=short"],
capture_output=True, text=True, timeout=120)
if r.returncode == 0:
return "✅ 全部测试通过。"
# ⭐ 只返回失败部分,不返回全量输出(第4章:控制上下文)
fails = extract_failures(r.stdout) # 解析出失败的测试名 + 简短 traceback
return (f"❌ {len(fails)} 个测试失败:\n\n" +
"\n\n".join(f"{f.name}\n {f.short_trace}" for f in fails[:5]) +
(f"\n\n[还有 {len(fails)-5} 个失败未显示]" if len(fails) > 5 else ""))
✅ 通关标准
- 4 个失败用例全部修复,且没有改坏原本通过的
- Agent 至少完成 3 轮「改→测→读」循环,全程无人工干预
- 审查 Agent 至少抓出过一次问题(没抓出就自己埋个陷阱验证它在干活)
- T6 的发现写进笔记——这是你对「测试质量就是基础设施」的第一手体感
🥇 项目三:多 Agent 文档流水线(2–3 天)
目标:编排器-工作者架构——输入一个主题,产出一份多来源、经过审查的研究报告。
练什么:第 13 章的多 Agent 协作 + 第 4 章的上下文隔离。
架构
编排器(大模型)
│ 拆解主题 → 3-5 个子问题(结构化委派)
├──► 研究工作者1(小模型,独立上下文)──► 500字摘要+来源
├──► 研究工作者2 ──► 摘要+来源
├──► 研究工作者3 ──► 摘要+来源
│ 综合成初稿
▼
审查者(独立上下文):核查引用、找矛盾、按 rubric 打分
│ 不合格 → 带意见退回编排器(最多 2 轮)
▼
最终报告
任务清单
- [ ] T1 (60min) 编排器提示词:产出结构化子任务(第 13 章)
- [ ] T2 (60min) 工作者:复用项目一的研究 Agent,包一层「只返回 500 字摘要 + 来源列表」
- [ ] T3 (45min) 并行跑工作者(
concurrent.futures就行),聚合前先定聚合策略(第 6 章的忠告) - [ ] T4 (60min) 审查者 rubric(第 12 章:把「好不好」变成可回答的问题)
- [ ] T5 (45min) 退回机制:审查不过 → 意见给编排器 → 只重跑有问题的部分(不从头来)
- [ ] T6 (60min) 全流程跑 3 个主题,记录:总 token、耗时、审查退回了几次、每次退回抓的是什么
- [ ] T7 (45min) ⭐ 等预算对照实验(第 13 章):把多 Agent 花掉的总 token 全给单 Agent,让它多轮迭代,比较质量
🔨 关键:结构化委派
subtasks = [
{"goal": "找出 2026 年 Q2 三家主要竞品的定价变化",
"output_format": "每家一段,含具体价格和变化时间,300字以内",
"tools": ["search_web", "read_page"],
"boundaries": "只看官方定价页,不要看第三方评测;不要分析原因(别人在做)"},
...
]
🔨 关键:审查 rubric
REVIEW_RUBRIC = """按四个维度审查报告,每项 0-25 分:
- 引用完整:每个事实结论都有来源吗?
- 引用有效:来源真的支持该结论吗?(抽查 3 条)
- 覆盖完整:子问题都被回答了吗?
- 内部一致:有互相矛盾的说法吗?
输出 JSON:{"引用完整":分,"引用有效":分,"覆盖":分,"一致":分,
"必须修复":["..."], "通过":true/false}
低于 70 分不通过。"""
✅ 通关标准
- 编排器的委派是结构化的,工作者之间没有重复劳动
- 审查者至少成功退回过一次,且第二轮真的变好了
- T7 的对照实验有结论——亲手验证「15 倍成本」值不值
📝 项目 README 模板(作品集用)
# 项目名
## 它做什么(一句话 + 一张架构图)
## 关键设计决策
- 为什么这里用工作流不用自主 Agent(附降级测试数据)
- 工具返回为什么这么设计(附 token 对比)
- 验证为什么分离(附审查者抓出的真实案例)
## 成本与表现
| 任务 | 轮数 | token | 成本 | 结果 |
## 踩过的坑 ← ⭐ 这一节最加分
1. ...
🔑 「踩过的坑」最加分——它证明你真的做过,而不是抄的教程。
🔗 这一章连到哪里
| 去哪 | 为什么 |
|---|---|
| Kaggle 25 | ⭐「先跑通最烂的版本,再逐环替换」—— 这套做法的方法论出处 |
| 上线之后 20 | 项目跑通之后的下一半:上线、监控、发现它悄悄变差 |
| 全景导论 16 | 做完这几个项目,用它来决定下一个方向挖哪里 |
✅ 检查点
- 三条通用规则是什么?为什么「先跑通最烂版本」排第一?
- 研究助手项目里,「找不到就明说,不许编」为什么必须写进提示词?
- 工具返回内容为什么要截断?不截断会怎样?
- 代码修复 Agent 的
run_tests为什么只返回失败摘要,而不是完整输出? - 为什么审查 Agent 必须用独立上下文?
- T6「故意给一个错的测试」这个实验在测什么?
- 项目三的 T7 等预算对照实验回答了什么问题?为什么这个问题重要?
👀 答案
- ①先跑通最烂版本 ②任务切成 ≤1 小时的块 ③没跑通不准优化。第一条排第一是因为一条能从输入跑到输出的完整链路,会立刻告诉你瓶颈在哪——而半成品连"哪一环最弱"都看不出来。
- 因为模型的默认倾向是"给出一个答案"而不是"承认不知道"。不显式授权它说"找不到",它就会编一个看起来合理的。这是幻觉最常见的来源之一。
- 因为工具返回的内容会全部进入上下文。一个返回 5 万字网页正文的工具,两次调用就把预算吃光了。不截断的直接后果是上下文腐烂,Agent 在第 5 轮就开始忘记任务目标。
- 同理——完整测试输出里绝大部分是"通过"的噪声。只返回失败摘要,既省上下文,又让模型的注意力集中在真正要修的地方。
- 因为共享上下文的审查会被"生成过程"污染——它看过那些推理,会倾向于认同。独立上下文的审查者只看产出物,判断更接近真实的第三方视角。🔗 这是第 12 章「生成与评估分离」的落地。
- 测模型会不会盲目迎合。给一个明显错误的测试,正确的行为是指出测试本身有问题,而不是改代码去迁就它。这直接对应第 12 章说的谄媚倾向。
- 回答「多 Agent 那 15 倍的 token 消耗,换来的效果提升值不值」。重要是因为多 Agent 的成本是隐性的——不做对照实验,你只会看到"效果好像更好了",而看不到代价。很多任务用单 Agent + 好工具就够了。
🛑 完成了?
三个高难度挑战在等你,每个都对准一个「普通项目练不到」的核心矛盾:
- ⚙️ 挑战A · 手搓 mini Agent 框架 —— 不用任何 SDK,从零造带上下文压缩和子 Agent 的框架
- 📏 挑战B · 评测驱动开发 —— 先建评测再写 Agent,戒掉玄学
- 🛡️ 挑战C · 提示注入攻防 —— 攻破自己的 Agent,再分层防住
⚡ 走神救援
三个递进项目:①研究助手(第1章循环+第7章工具设计,关键是"找不到就明说不许编"(模型默认倾向是给答案而不是承认不知道)+工具返回要截断(不截断会直接吃光上下文预算→第5轮就忘了任务目标))②代码修复Agent(验证循环改→测→读,
run_tests只返回失败摘要——完整输出95%是"通过"的噪声;加独立上下文的审查Agent(共享上下文的审查会被生成过程污染,倾向于认同);T6故意给错的测试看它会不会盲目迎合——正确行为是指出测试有问题)③多Agent流水线(结构化委派防重复劳动+rubric审查+退回只重跑有问题的部分;⭐T7等预算对照实验回答"15倍token换来的提升值不值"——多Agent的成本是隐性的,不做对照你只会看到"好像更好了")。三规则:先跑通最烂版本(完整链路能立刻告诉你瓶颈在哪)、切成≤1小时任务、没跑通不准优化。