第三课:Loop Engineering —— 自动化你的所有 Bullshit Work¶
第 0 章:开篇 —— 蒸汽机到 Generator-Verifier 原则¶
从蒸汽机说起¶
自动化是人类发展至今的美好愿景。但历史反复告诉我们一个教训:当我们从大自然得到一股强大而狂野的力量后,第一件要思考的事情不是"怎么用它",是"怎么驾驭它"。
先从一个最接地气的例子说起。你每天骑的电瓶车,动力来自后轮那颗电机——通电就转,力气不小。但如果只有电机:没有车把控制方向,没有油门转把调速,没有刹车停下来——它不是交通工具,是一颗失控的炮弹。你平时骑的车之所以安全,靠的不是电机,是车把、油门、刹车这片"控制层"。它们不提供动力,但决定了动力往哪使、使多少、什么时候停。
再看蒸汽机——更大尺度上的同一个道理。
1712 年,纽科门造出了第一台实用蒸汽机——给煤矿抽水用的,力量巨大但不可控制:转速没法调,输出没法稳。后来人们试着把蒸汽动力接到纺纱机上,机器瞬间被撕碎。

瓦特用飞球调速器解决了这个问题:利用离心力自动调节进气阀门,转太快关小,转太慢开大。不需要人手调节,系统自己感知偏差、自己矫正——这是控制论历史上一次天才的闭环设计。
动手体验:瓦特蒸汽机调速器
拖动蒸汽压力和机器负载滑块,观察调速器如何通过负反馈自动维持转速稳定。然后想想:你的 AI 项目里,"调速器"在哪里? 点此全屏体验
蒸汽机还催生了一种新工种:调校引擎的人——不直接制造产品,但决定了工厂能不能稳定生产。今天,LLM 是新的蒸汽机——裸用的 LLM 像没有调速器的纽科门引擎,力量狂暴,方向随机。同样的转变正在软件行业重演:从"亲手写每一行代码"转向"搭建闭环让 AI 稳定输出代码"。
RIPER 与 Loop 的关系¶
上节课我们学了 RIPER——通过角色切换来控制 AI 在单轮对话中的行为范围。但 RIPER 有一个天花板:你不可能 24 小时坐在 AI 旁边告诉它每一步该干什么。Loop Engineering 的答案是:让 AI 自己跑完完整的"感知 → 规划 → 行动 → 验证"循环。你把驾驭逻辑写在工具、规则和流程里,它自己转。
两个词:Harness 和 Loop
业界对"驾驭 AI"这套方法的称呼经历过一次演化。Harness engineering(挽具工程)是 2026 年初由 Anthropic、OpenAI 先后带热的伞概念——指 agent 外围的整个执行环境:工具、权限、上下文、验证、人机切换,目标是"让 agent 在真实系统里稳定跑"。Loop engineering 是 2026 年年中出现的新叫法,聚焦 harness 里最核心的那个闭环:感知 → 规划 → 行动 → 验证,循环往复直到任务完成。
两者是包含关系:loop 是 harness 的核心,harness 是 loop 的载体。这节课聚焦闭环设计(Generator-Verifier、红灯循环、对抗验证),也会讲到 harness 的经典护栏(worktree、precommit、CI)。读外部资料时两个词都要认识。
Generator-Verifier 原则¶
在拆解这个循环之前,我们先建立这节课最重要的一个概念:Generator-Verifier 原则。
想象一个场景。你让 AI 帮你写一个爬虫解析器。AI 写好了,代码看起来挺工整。你怎么知道它真的能解析目标网页?你怎么知道它不会把标题解析成正文?你有两个选择:第一,让 AI 自己检查一遍自己的代码,然后告诉你"没问题";第二,写一个测试,跑一遍,看实际解析结果。
选项一的成功率极低。因为 AI 检验自己产出的质量,就像老师让自己出的考卷自己批——它永远给自己打 A。这不是 AI 不诚实,是它和同一个思维框架共谋。它写出"解析标题逻辑"的那段代码,和它检查"解析标题逻辑是否正确"的判断,用的是同一个内部表征——它能发现"明显"的错误(变量未定义),但发现不了"合理但错误"的假设(HTML 结构变了导致选择器失效)。
所以任何有效的 AI 工作流,都天然地分成两个半场:
| 半场 | 职责 | 特点 |
|---|---|---|
| Generator(生成器) | 产出代码、文档、方案 | 可廉价反复运行,成本越低越好 |
| Verifier(验证器) | 评判产出是否有价值 | 必须独立于生成器,决定循环是否继续 |
核心论点一句话:验证器是循环的瓶颈,不是模型本身。 你的 AI 生成的代码质量不取决于它有多聪明——取决于你的验证器能不能准确判别"对"和"错"。验证器越精准,AI 就越能在错误的道路上及时刹车、调头。
验证器主要有三种形态,这也是这节课后面几章要展开的:
- 确定性裁判:测试、lint、类型检查——规则是死的,判断是机械的,不需要模型参与。跑不过就是跑不过。后面要讲到的 precommit hook 和 CI 不是另一种裁判,而是这类裁判的两个执行位置:一个在本地提交前拦截,一个在云端 push 后拦截。
- LLM 裁判:让另一个模型来评判——对抗验证(从不同角度反驳结论)、视觉审图(截图交给有视觉能力的模型打分)。用模型审模型。
- 人类裁判:机器裁判判不了的问题交给人——PR review、方案确认、多个机器裁判结论冲突时的仲裁。人不参与每一轮循环,但保留一票否决权。
思考:你的 AI 工作流里,Generator 和 Verifier 分开了吗?
回想一下你最近让 AI 做的一项开发任务。AI 产出的代码,谁来验证它的正确性?是你自己跑了一遍测试?还是你看了一眼代码说"差不多"?还是你让 AI 自己说"对了"?
"让 AI 自己说对了"是最危险的验证方式——因为它一定会说对。
主线案例:AI 行业信息助手¶
这节课我们全程围绕一个真实项目展开——AI 行业信息助手,它自动爬取 AI 行业最新资讯,通过多角度对抗验证去伪存真,最终生成结构化的 PPT 报告。
它有三个子系统:
| 子系统 | 功能 | 课堂方式 |
|---|---|---|
| 爬虫 | 自动爬取小红书 AI 话题 + 公开 AI 媒体 | 现场搭建——模块化 parser、多源并行、cache fallback |
| 信息整合 + 对抗验证 | 多源信息聚合 → subagent 交叉验证 → 置信度评分 | 现场搭建——prompt 引导 agent 规划,打包成 Skill |
| PPT 报告生成 + 视觉修复 | Markdown → HTML → 截图 → Gemini 审图 → 迭代 | 现场展示——Skill 提前写好,演示运行流程 |
接下来的每一章,我们都在给这个项目装上闭环的每一环。信源文档定义(感知层)→ grilling 和 plan.md(规划层)→ 爬虫搭建(行动层)→ 测试和 domain validation(确定性裁判)→ 对抗验证和 PPT 视觉审图(LLM 裁判)→ precommit 闸门和 CI(裁判的执行位置)。案例打散到各章,每章回扣主线项目的具体对应部分。
学完这节课,你应该能做到:
- 用 CLAUDE.md + LLM Wiki 搭建渐进式的项目感知体系
- 用 prompt 引导 agent 做多角度需求分析和规划
- 用 Git worktree 和 precommit hook 给 AI 的操作装上安全护栏
- 让 AI 写出能自动回归的测试,形成"红灯 → 修复 → 绿灯"闭环
- 搭建对抗验证管线,用 LLM 裁判发现 Generator 发现不了的问题
- 把反复使用的流程封装成 Skill,让 AI 一句话触发
第 1 章:感知 —— CLAUDE.md + LLM Wiki¶
对应 AI 信息助手:信源配置文档、爬虫规则文档、数据 schema 定义。Agent 启动时读 CLAUDE.md 目录,按需检索 LLM Wiki 获取具体的信源规则和解析 schema。
痛点:每次开新对话,AI 都是新来的¶
你一定经历过这个场景。昨天和 Claude Code 协作了一下午,对话里积累了丰富的上下文——项目结构、技术栈、之前的决策、踩过的坑。今天打开一个新会话,AI 像一个刚入职的新人,什么都不知道。
你赶紧把项目的文档丢给它,好了一点。但项目越来越大,文档也越来越胖。两个月后,你发现 AI 开始漏掉关键信息了——因为它的注意力分散在几千字的指令里,真正的任务反而被挤到角落。
这是两难:信息太少,AI 猜错;信息太多,AI 眼花。
方案一:渐进披露 —— CLAUDE.md 只做路由器¶
先回答一个问题:CLAUDE.md 的受众是谁? 不是人类开发者,是 AI Agent。人类可以扫读目录、跳着看、凭经验判断哪些重要。AI 做不到——它要么全看,要么全不看。
所以 CLAUDE.md 的核心职责只有一个:告诉 AI 去哪找信息。它是路由器,不是全书。
一个健康的 CLAUDE.md 长这样:
# Project Guide
## Project Overview
AI 行业信息助手——自动爬取 AI 资讯、对抗验证去伪、生成 PPT 报告。
## Repository Structure
ai-info-assistant/
├── docs/vc_llm_wiki/ # LLM Wiki 知识库(信源配置、爬虫规则、数据 schema)
├── crawlers/ # 爬虫模块(小红书、AI 媒体)
├── pipeline/ # 信息整合 + 对抗验证管线
├── reports/ # PPT 报告模板 + 视觉修复循环
└── CLAUDE.md # 本文件
## Key Documents
| Path | Description |
|------|-------------|
| docs/vc_llm_wiki/sources.md | 信源列表和爬取规则 |
| docs/vc_llm_wiki/schema.md | 数据格式定义和验证规则 |
| docs/lessons/ | 历史踩坑记录 |
关键设计点:
- 文件路径索引:告诉 AI 想要什么信息时去哪个目录找。AI 会按需用 Glob 和 Grep 检索,而不是一次性把所有文件塞进上下文。
- 高风险禁区:那些不能碰、不能跳过的约束放在显眼位置。模型对开头和结尾内容最敏感(U 型记忆曲线),重要约束放这两处。
- 篇幅控制:CLAUDE.md 超过约 2500 token(约两千个汉字),AI 自己都记不住了。在文件里放一条自检指令:
当 AI 准备往 CLAUDE.md 加内容时,它会先自检:当前多大了?超过就自动把冗余内容迁移到子文档。
方案二:规则外化 —— 从"每次强调"到"写下来就生效"¶
你是不是经常这样:AI 生成代码后,你说"不要用 var,用 const",AI 改了。第二天换了一个会话,AI 又用了 var。你再纠正一次。第三天你烦了,不纠正了,bug 就埋下了。
把"每次都要说"的规则写进 CLAUDE.md:
## 编码规范
- JavaScript:用 const/let,禁止 var
- 函数命名:动词开头(fetchUser, parseConfig)
- 文件命名:kebab-case(user-profile.ts)
规则外化的本质是把"每次强调"变成"写下来就生效"。这和汉谟拉比法典的逻辑一样——把口头习惯固定成成文法,减少了法官判案的随意性。对你的 AI 来说,CLAUDE.md 就是法典的总目。
常见坑:别用全大写命令 AI
不要把规则写成 "ALWAYS" 和 "NEVER"。AI 不会被大写说服——它会被理由说服。对比:
# 差的写法
NEVER use console.log in production code. ALWAYS use the logger utility.
# 好的写法
生产代码中使用 logger 工具而非 console.log。console.log 不会写入日志文件,
出问题时找不到调用栈,且在生产模式下不会被 tree-shaking 移除。
当 AI 理解了"为什么",而不是机械服从"禁令",它才能在新情境下自己做正确判断。
方案三:LLM Wiki —— 规模化管理项目知识¶
CLAUDE.md 做目录,具体内容放文档目录。但文档多了,冒出两个新问题:AI 不知道哪份文档是相关的;文档之间没有连接,知识是孤岛。
LLM Wiki 的思路:给每份文档加上标准化元数据,让 AI 先读元数据筛选,再按需读全文。文档末尾用 wikilink 互相连接。
以 AI 信息助手的信源配置文档为例:
---
brief: 小红书 AI 话题页的爬取规则,含 cookie 刷新策略和反爬处置方案
confidence: verified
updated: 2026-06-20
tags: [crawler, xiaohongshu, ai-topic]
---
# 小红书 AI 话题爬取规则
## 目标 URL
https://www.xiaohongshu.com/topic/ai
## 反爬策略
1. 每次请求间隔 >= 3 秒
2. Cookie 有效期约 2 小时,需在失效前 10 分钟刷新
3. 单 IP 每日请求上限约 500 次
## 数据 schema
参考 [[data-schema]]
## See also
- [[ai-media-sources]]
- [[anti-crawl-strategies]]
AI 用 Grep 扫一遍 frontmatter 就能快速判断哪些文档相关。100 份文档的 wiki,AI 一次只需要读 3-5 份。这就是渐进披露的技术实现。
对应主线:AI 信息助手的感知层¶
回到我们的 AI 行业信息助手。在 CLAUDE.md 的指引下,AI 启动时会自动检索以下文档:
| 文档 | 内容 | AI 如何使用 |
|---|---|---|
sources.md |
信源列表(小红书 AI 话题、机器之心、量子位等) | Grep 找到信源,按优先级爬取 |
crawler-rules.md |
爬虫行为规范(请求间隔、cookie 策略、反爬处置) | 爬虫模块遵守这些约束 |
data-schema.md |
统一数据格式:{source, title, url, summary, timestamp, tags} |
不同信源爬到数据后统一转换到此格式 |
pipeline-config.md |
信息整合流程:聚合 → 去重 → 对抗验证 → 输出 | Agent 按此流程组装管线 |
AI 不需要把这些文档全读进上下文。它只在需要的时候检索需要的文档。这就是感知层要做的事:不是喂饱 AI,是让它知道去哪找吃的。
自检:你的 CLAUDE.md 够精简吗?
回头看看你项目的 CLAUDE.md。能在三句话以内说清项目是什么吗?如果不行,里面太多内容了。把细节下放到 docs/ 子目录,CLAUDE.md 只留路径索引。
第 2 章:推理 + 规划 —— grilling、探索、定格步骤¶
对应 AI 信息助手:对 agent 说"我想做一个 AI 行业信息助手,帮我 grilling 一下需求"——agent 从多个角度追问和分析,输出结构化计划,定格为
plan.md。
痛点:你有一个模糊的想法,但不知道怎么变成可执行的计划¶
"我想做一个 AI 行业信息助手"——这是一个好想法。但接下来呢?你要从哪个信源开始爬?爬回来的数据存什么格式?怎么判断两条信息说的是同一件事?对抗验证要验证哪些维度?PPT 报告的受众是谁?
你可能想了两分钟,觉得脑子里的问题比答案多,就先放一边了。或者你跟 AI 说"帮我做",AI 写了一个能跑但方向偏了八百里的东西——因为它不理解你的真实意图,你也没说清楚。
传统产品经理的解决方案是写一份 PRD。但对个人项目来说,PRD 太重了。你需要的是一个快速、高质量的需求澄清过程——它应该比"自己随便想想"更系统,但比"写完整 PRD"更轻便。
方案:用 Prompt 引导 Agent 做多角度 grilling¶
这里有一个关键技术:grilling prompt。它不是问 AI "你想做什么"——那是被动收集。Grilling 是让 AI 反过来审问你——追问你的假设、敲打你的逻辑、逼你回答你没想过的问题。
现场演示。对 Claude Code 说:
我想做一个 AI 行业信息助手,自动爬取 AI 行业最新资讯,经过信息整合和对抗验证后,
生成结构化的 PPT 报告。
在开始写代码之前,先帮我 grilling 一下这个需求——从多个角度追问和分析,
帮我理清:目标用户是谁、核心价值是什么、信源选哪些、验证哪些维度、
PPT 报告给谁看、技术选型上有什么约束。问到你认为这已经是一个可执行的需求为止。
这个 prompt 的关键设计点:
- 明确 grilling 的角色:"先帮我 grilling 一下"——告诉 AI 你现在是审查官,不是执行者。
- 给出分析维度:目标用户、核心价值、信源选择、验证维度、受众、技术约束——这 6 个维度是思考的脚手架。没有脚手架,AI 的追问会散乱无章。
- 设定停止条件:"问到你认为这已经是一个可执行的需求为止"——避免无限追问。Agent 自己判断何时追问完毕。
Agent 的追问可能长这样:
- "这个助手的目标用户是你自己,还是你团队里的其他人?如果是团队用,他们对 AI 行业的了解程度一样吗?"
- "你关心的'AI 资讯'范围多大?大模型、AI 编程工具、AI 政策监管都算吗?还是只关注某个细分方向?"
- "PPT 报告的汇报对象是谁?老板需要的是趋势判断和商业洞察,同事需要的是技术细节和可复现的实验结果——这两个方向的 PPT 结构完全不同。"
- "信息更新频率是什么?日报、周报还是实时监控?这决定了爬虫的触发机制和验证管线的时间预算。"
- "你是打算自己部署还是在本地跑?如果本地跑,Chrome 的内存占用你可能承受不了——要考虑 headless browser 在低配机器上的表现。"
思考:为什么这个 prompt 要写「从多个角度追问」,而不是「问问我有什么需求」?
"问问我有什么需求"是开放式的——AI 会问一些宽泛的问题("你想做什么?""你的目标是?""还有什么补充的吗?"),这些问题你自己已经想过了。多角度框架(用户、价值、信源、验证、受众、约束)给了 AI 具体的进攻方向——它逼你回答那些你没想过的问题。
点击查看对比
没有框架的追问:AI 问"你的目标是什么",你答"自动获取 AI 资讯并生成报告",AI 回"好的"。有框架的追问:AI 问"PPT 报告给谁看——老板看趋势,同事看技术细节",你愣住了——你之前没想过这个问题。这种"愣住"的时刻,正是 grilling 在起作用。
一轮 grilling 下来,你会得到一份比你独自思考系统得多的需求分析。但现在是口头约束——下一步要把它定格。
定格:从对话到 plan.md¶
把规划结果写成文件,不是一个形式问题——是一个工程问题。对话是流动的,session 一关就丢了。文件是固定的,你和 AI 都可以反复参考。更重要的是,文件是 AI 自己写下来的——每次新对话,AI 读这份文件就知道"我们在做什么、已经决定过什么、为什么这样决定"。
让 AI 输出一份 plan.md:
基于刚才的 grilling 结果,生成一份 plan.md,包含:
1. 项目目标和成功标准(可验证的指标,不是模糊描述)
2. 信源列表和优先级(哪些先爬、哪些后加)
3. 数据流通架构(爬虫 → 清洗 → 整合 → 验证 → 报告)
4. 分阶段执行计划(Phase 1/2/3,每阶段的可交付物)
5. 技术选型决策和理由
6. 风险和假设(哪些决定现在还拿不准、需要验证的)
plan.md 的价值不仅是记录——它是后续所有 AI 操作的"起点文档"。新开一个会话,AI 先读 plan.md,然后就知道该干什么。你不需要再解释一遍。
并行分派:Subagent 的工作分配¶
plan.md 成型后,下一步是把工作拆成可并行执行的任务。这里有一个实际约束:有些任务天然是串行的(你得先有爬虫数据,才能做信息整合),有些是可以并行的(多个信源的 parser 可以同时写)。
一个好的 subagent 分派策略:
| 任务 | Agent | 依赖 | 并行 |
|---|---|---|---|
| 小红书 parser | subagent-1 | 无 | 可与 #2, #3 并行 |
| 机器之心 parser | subagent-2 | 无 | 可与 #1, #3 并行 |
| 量子位 parser | subagent-3 | 无 | 可与 #1, #2 并行 |
| 信息整合 + 去重 | subagent-4 | #1, #2, #3 完成 | 不可并行 |
| 对抗验证管线 | subagent-5 | #4 完成 | 不可并行 |
| PPT 报告模板 | subagent-6 | 无 | 可与 #1-#5 并行 |
关键决策点在依赖链的边界。parser 之间没有依赖——三个 subagent 同时开工,互不干扰。信息整合依赖所有 parser 完成——它必须等。PPT 报告模板完全独立——可以在任何阶段并行推进。
这个分派逻辑,你用自然语言告诉 AI 就行:
先启动 3 个 subagent 并行写 parser:小红书、机器之心、量子位。
等三个 parser 都跑通测试后,启动信息整合 agent。
同时可以让 PPT 模板 agent 提前开始——它不依赖任何 parser。
打包成 Skill:把 grilling 流程固化¶
这次 grilling → 规划 → 分派的流程是完整的。但你不可能每次都重新写一遍 prompt。把这件事封装成一个 Skill。
以这节课为例,我们创建一个 plan-griller Skill:
---
name: plan-griller
description: 需求 grilling → 多角度追问 → 输出结构化 plan.md → 分派 subagent。当用户描述了新的项目想法或功能需求时使用。
---
# 需求规划师
## 执行流程
1. **Grilling 阶段**:从以下 6 个维度追问用户,直到需求可执行
- 目标用户画像
- 核心价值主张
- 信息/数据来源
- 验证和评判标准
- 产出物和受众
- 技术约束和边界
2. **定稿阶段**:输出 plan.md,包含成功标准、架构、分阶段计划、技术决策、风险假设
3. **分派阶段**:分析任务依赖 DAG,标注并行/串行,推荐 subagent 分配方案
4. **确认阶段**:展示 plan.md 摘要,等待用户确认后进入执行
从此以后,你对任何新想法说"帮我 grilling 一下",AI 自己知道该怎么做。花 10 分钟写一次 Skill,后面每次省 20 分钟——这笔账怎么算都是赚的。
第 3 章:行动 —— 沙盒 + 脚本 + 爬虫¶
对应 AI 信息助手:用 Git worktree 隔离开发环境,按模块化 parser 架构搭建爬虫 mini 版,用 browser-use 爬取小红书 AI 话题。工程方法驱动:写 parser → 跑测试 → domain validate → 修 → 再跑。
Git Worktree:让 AI 在沙盒里干活¶
在让 AI 动手写爬虫之前,先解决一个安全问题:AI 改代码的时候,你的主工作区不该被波及。
上一课讲的 Git checkpoint 能让你回退,但还有一层问题:你想让 AI 试一个风险比较高的爬虫方案——用 browser-use 自动登录小红书——但你又不确定它会不会把项目依赖搞乱,会不会不小心改了其他模块的代码。
Git worktree 是这个问题的标准答案。它在同一个仓库里开出多个独立的工作目录,每个目录有自己的分支。AI 在一个 worktree 里折腾爬虫,你在另一个里正常开发,互不干扰。
# 创建一个隔离的工作树,让 AI 在 feature/crawler-v1 分支上干活
git worktree add ../ai-assistant-crawler-dev -b feature/crawler-v1
AI 在 ../ai-assistant-crawler-dev 里随便试。如果方向对了:
如果方向错了:
干净利落,主工作区毫发无伤。Claude Code 也支持 --worktree 参数,启动时直接创建隔离环境。
常见坑:Worktree 里不要手动改配置文件
worktree 继承主仓库的 .gitignore 和 Git 配置,但不会继承 .env 文件(它已在 .gitignore 里)。AI 在 worktree 里跑爬虫时可能因为缺少环境变量而报错。正确做法:在 worktree 目录里手动复制一份 .env,或用脚本自动生成。
脚本封装:确定性流程写成脚本,AI 调用¶
前面规划章提过一个原则:凡是流程确定的事情,不要让 AI 手动做,写成脚本让 AI 调用。
爬虫项目里最典型的确定性流程:环境检查。你需要确认 Chrome 已安装、browser-use 依赖完整、网络能访问小红书。这个流程永远不会变——不需要 AI 每次推理一遍。
写成脚本:
#!/bin/bash
# scripts/check-crawler-env.sh —— 验证爬虫运行环境
echo "[check] 检查爬虫运行环境..."
# 1. Chrome 是否可用
if command -v google-chrome &>/dev/null || command -v chromium &>/dev/null; then
echo "[pass] Chrome/Chromium 已安装"
else
echo "[fail] 未找到 Chrome,请先安装" && exit 1
fi
# 2. Python 依赖是否完整
pip show browser-use &>/dev/null || { echo "[fail] browser-use 未安装"; exit 1; }
echo "[pass] browser-use 已安装"
# 3. 网络连通性
curl -s --connect-timeout 5 https://www.xiaohongshu.com >/dev/null || \
{ echo "[fail] 无法访问小红书"; exit 1; }
echo "[pass] 网络连通正常"
echo "[ok] 环境检查全部通过"
AI 不需要理解这个脚本——它只需要在动手前跑一遍。Skill 里写一句 bash scripts/check-crawler-env.sh,AI 自动执行。
爬虫 Mini 搭建:现场实战¶
现在到了这节课最核心的动手环节——搭一个能跑的小红书 AI 话题爬虫。
爬虫的核心架构思路:模块化 parser、多源并行、cache fallback。每个信源对应一个独立的 parser 模块,输出统一的数据格式。主调度器负责并发控制和 cache 管理。
目录结构:
crawlers/
├── main.py # 主调度器
├── parser_base.py # parser 基类(定义统一的 parse() 接口和数据 schema)
├── parsers/
│ ├── xiaohongshu.py # 小红书 parser
│ ├── jiqizhixin.py # 机器之心 parser
│ └── liangziwei.py # 量子位 parser
├── cache/
│ └── cache_store.py # 简单的文件 cache(避免重复请求)
└── tests/
├── test_xiaohongshu.py
├── test_schema.py
└── test_domain.py
Parser 基类定义统一的接口。每个 parser 不管内在逻辑多复杂,对外只有一种姿势:
# crawlers/parser_base.py
from dataclasses import dataclass
from typing import List
from datetime import datetime
@dataclass
class Article:
"""所有 parser 产出的统一数据格式"""
source: str # 信源标识:xiaohongshu / jiqizhixin / liangziwei
title: str
url: str
summary: str
timestamp: datetime
tags: List[str]
class BaseParser:
"""所有 parser 的基类——定义契约"""
def validate_source(self) -> bool:
"""子类必须实现:验证信源可访问"""
raise NotImplementedError
def parse(self) -> List[Article]:
"""子类必须实现:爬取并返回 Article 列表"""
raise NotImplementedError
关键设计决策:接口先于实现。 先把数据格式和接口定下来,写测试,再让 AI 填具体逻辑。你能少踩很多坑。
以小红书 parser 为例。不能用 requests 直接抓——小红书是 SPA 渲染,HTML 由 JS 动态生成。这里用 browser-use:给它一个自然语言任务,它自己驱动 Chrome 打开页面、渲染、提取内容:
# crawlers/parsers/xiaohongshu.py
import asyncio
import json
from datetime import datetime
from browser_use import Agent, Browser, ChatAnthropic
from crawlers.parser_base import BaseParser, Article
class XiaohongshuParser(BaseParser):
SOURCE = "xiaohongshu"
TOPIC_URL = "https://www.xiaohongshu.com/topic/ai"
def validate_source(self) -> bool:
"""快速验证:URL 可访问"""
import requests
try:
r = requests.head(self.TOPIC_URL, timeout=10)
return r.status_code < 500
except Exception:
return False
def parse(self) -> list[Article]:
# browser-use 是异步 API,在同步接口里用 asyncio.run 包一层
raw_items = asyncio.run(self._extract_with_agent())
return [self._to_article(item) for item in raw_items]
async def _extract_with_agent(self) -> list[dict]:
"""用 browser-use Agent 驱动 Chrome 渲染页面并提取笔记。
注意:提取方式是自然语言任务,不是写死的 CSS 选择器——
页面改版后 Agent 仍能找到笔记卡片,这是它和
requests + 选择器方案的本质区别。
"""
agent = Agent(
task=(
f"打开 {self.TOPIC_URL},等页面渲染完成,"
"提取前 20 条笔记的标题和链接,以 JSON 数组返回,"
'格式 [{"title": "...", "url": "..."}],'
"url 补全为完整链接,不要输出其他内容"
),
llm=ChatAnthropic(model="claude-sonnet-4-6"),
# 也可 ChatGoogle / ChatBrowserUse,配好对应的 API key
browser=Browser(),
)
history = await agent.run()
return json.loads(history.final_result())
def _to_article(self, item: dict) -> Article:
return Article(
source=self.SOURCE,
title=item["title"],
url=item["url"],
summary="",
timestamp=datetime.now(),
tags=["ai"]
)
注意:包名与导入名不同
pip 安装时包名是 browser-use(带连字符),但 Python 代码中导入时用 browser_use(下划线)。这是 Python 生态常见的命名差异。
常见坑:browser-use 的 Chrome 内存开销
每个 browser-use Agent 背后都是一个真实的 Chrome 进程,单个实例动辄几百 MB 内存。批量跑任务时别一口气起十几个并发 Agent——机器会先被 Chrome 拖垮。先跑通单个任务,再把并发控制在 2-3 个;跑完后顺手 ps aux | grep -i chrome 确认没有残留进程。
工程方法驱动:写 parser → 跑测试 → domain validate → 修 → 再跑¶
爬虫最容易出问题的不是语法错误——是"页面结构变了,选择器失效了"这类运行时错误。这些错误 compile 检查不出来,只有实际跑了才知道。
所以我们的工作流程不是"写代码 → 提交"——是"写代码 → 跑测试 → 验证数据 → 修 bug → 再跑"。
第一步:先写测试,再写实现。 别跳步——爬虫是最经不起"写完再看"的代码类型。
# crawlers/tests/test_xiaohongshu.py
import pytest
from crawlers.parsers.xiaohongshu import XiaohongshuParser
def test_validate_source_accessible():
"""信源必须可访问——这是爬虫最基本的前提"""
parser = XiaohongshuParser()
assert parser.validate_source(), "小红书 AI 话题页无法访问"
def test_parse_returns_non_empty():
"""解析器必须返回数据——空列表说明选择器可能过期"""
parser = XiaohongshuParser()
articles = parser.parse()
assert len(articles) > 0, "爬取结果为空,可能页面结构已变化"
def test_parse_returns_valid_articles():
"""返回的 Article 对象必须字段完整"""
parser = XiaohongshuParser()
articles = parser.parse()
for article in articles:
assert article.source == "xiaohongshu"
assert article.title, f"文章缺少标题: {article.url}"
assert article.url.startswith("https://"), f"URL 格式异常: {article.url}"
第二步:domain validation。 这是爬虫特有的验证——数据字段没问题,但内容对不对?URL 是不是真的来自小红书?
# crawlers/tests/test_domain.py
def test_article_urls_match_expected_domains():
"""所有文章的 URL 必须来自预期的信源域名"""
parser = XiaohongshuParser()
articles = parser.parse()
for article in articles:
assert "xiaohongshu.com" in article.url, \
f"URL 域名不匹配: {article.url}"
def test_articles_are_recent():
"""爬取的文章应该是近期的(不是半年前的旧文章)"""
from datetime import datetime, timedelta
parser = XiaohongshuParser()
articles = parser.parse()
one_week_ago = datetime.now() - timedelta(days=7)
recent = [a for a in articles if a.timestamp > one_week_ago]
assert len(recent) > 0, "没有最近一周内的文章,可能取到了过期数据"
第三步:跑测试 → 看红灯 → 修 → 再跑。 这个循环不需要你在场——告诉 AI:
这就是 Generator-Verifier 原则在爬虫开发中的落地:Generator(AI 写 parser)→ Verifier(pytest 红灯/绿灯)→ 反馈回路自动运转。
第 4 章:桥梁 —— Precommit Hook¶
对应 AI 信息助手:爬虫改完代码 → precommit → lint + 测试通过 → 提交。不通过就不能进版本库。
为什么需要一座桥¶
在讲验证之前,我们需要一把锁——一个横跨"行动"和"验证"之间的闸门。precommit hook 就是这个闸门。
它做的事情极其简单:每次 commit 之前,自动跑一遍检查。通过了才能提交,没通过就堵住。 简单到你会觉得"这有什么好讲的"。但恰恰是这种简单的东西,在实际工作中被严重低估。
上一章你还在兴冲冲地搭爬虫——AI 写代码、你跑测试、修了再跑。但你没注意到一个隐患:你总有一次会忘记跑测试就提交了。 可能是急于上线,可能是深夜太累,可能是"这次改动很小应该不会出事"。然后有一天,broken code 进了 main 分支,后面的人基于它开发,炸了一地。
precommit hook 不给你忘了的机会。它在 commit 这个动作上装了一道自动闸——就像高铁闸机,不刷票就过不去。你不需要提醒自己"记得跑测试",机器替你记。
配置¶
在项目根目录创建 .git/hooks/pre-commit:
#!/bin/bash
# .git/hooks/pre-commit —— commit 前的自动检查
echo "[pre-commit] 检查中..."
# 1. Lint 检查(快速,5 秒内完成)
ruff check crawlers/ 2>/dev/null || {
echo "[pre-commit] ruff 检查失败 —— 代码风格不符合规范"
exit 1
}
# 2. 类型检查
mypy crawlers/ --ignore-missing-imports 2>/dev/null || {
echo "[pre-commit] mypy 类型错误 —— 可能有运行时炸雷"
exit 1
}
# 3. 爬虫 domain validation(这是爬虫项目特有的)
pytest crawlers/tests/test_domain.py -q 2>/dev/null || {
echo "[pre-commit] domain 验证失败 —— URL 域名或数据时效性异常"
exit 1
}
# 4. 快速冒烟测试(只跑关键路径,不跑全量——全量放 CI)
# --timeout 参数需要 pytest-timeout 插件:pip install pytest-timeout
pytest crawlers/tests/ -q --timeout=30 2>/dev/null || {
echo "[pre-commit] 关键测试失败"
exit 1
}
echo "[pre-commit] 所有检查通过,允许提交"
别忘了执行权限:chmod +x .git/hooks/pre-commit。
三道闸门¶
precommit hook 本质上做了三道拦截:
AI 写代码
→ Lint 检查(风格和明显错误)
→ 不通过 → 拒绝提交,AI 看报错修
→ 通过 → 类型检查(防止 None.foo() 这种运行时炸雷)
→ 不通过 → 拒绝提交
→ 通过 → 关键测试(URL 域名对吗?数据格式对吗?)
→ 不通过 → 拒绝提交
→ 通过 → 绿灯,允许 commit
三道闸门的顺序有讲究。Lint 最快——0.5 秒出结果,所以放第一道。类型检查稍慢——2 秒。测试最慢——所以只跑关键路径,不超过 30 秒。三道全过的 commit,比"人眼扫一眼觉得差不多"的 commit 靠谱一百倍。
回到 AI 信息助手的工作流:爬虫改完 parser → git add && git commit → precommit 闸门自动检查 → lint 红灯?AI 修 → 类型报错?AI 修 → domain test 失败?说明小红书改版了,选择器过期了,AI 更新选择器 → 全部通过 → commit 进入版本库。
整个过程你只在旁边看。它自己写、自己交、被挡住了自己修、修完再交——这就是闭环。
常见坑 1:别在 precommit 里跑完整测试套件
precommit 追求"快而关键"——lint + 类型检查 + 关键测试,控制在 30 秒内。完整测试放 CI(下一章讲)。precommit 超过 1 分钟,你会下意识找办法跳过它——那它就没价值了。
常见坑 2:Hook 脚本不是跨平台的
.git/hooks/pre-commit 在不同机器上不共享——它是本地文件,不在 Git 管理范围内。团队成员需要各自配置。解法是用一个脚本(如 scripts/install-hooks.sh)自动生成 hook 文件,放在仓库里,每个成员首次 clone 后跑一遍。
第 5 章:确定性验证 —— 测试 + Lint + CI¶
对应 AI 信息助手:爬虫 parser 测试、数据格式验证、CI 自动部署爬虫任务。这些验证都不依赖模型——规则是死的,判断是机械的。
痛点:AI 改得很快,但改对了还是改坏了你不知道¶
第 4 章的 precommit hook 里我们跑了两类检查:lint 和类型检查。它们属于"静态验证"——不运行代码,只检查代码本身。这能挡住很多低级错误,但挡不住逻辑错误。
AI 写了一个爬虫 parser,lint 过了、类型也对,但实际跑起来——返回的列表是空的。为什么?因为小红书改了页面结构,选择器 '.note-item' 失效了。这不是代码格式问题,是运行时行为问题。Lint 发现不了,类型系统发现不了,AI 自己更发现不了——它在写代码的时候并不知道小红书改版了。
这才是爬虫最常出的问题——不是语法错,是"环境变了,假设不成立了"。你需要的不是更聪明的 AI,是能自动发现"假设不成立"的验证系统。
方案一:测试闭环 —— AI 写、AI 跑、AI 修¶
这就是第 3 章结尾讲的循环,现在把它正式化:
这个循环不需要你在场。它本质上是把一个确定性的裁判(pytest)嵌入 AI 的工作流。你和 AI 的对话变成了:
给小红书爬虫加一个"按标签筛选"功能,指定只爬 #大模型 标签的文章。
动手前先更新测试:确认筛选逻辑正确、筛选后数据格式不变、没有匹配标签时返回空列表。
改完代码后跑 pytest crawlers/tests/ -v,确认全部通过。
如果测试失败,根据报错修复,再跑测试。
AI 会执行: 1. 分析现有测试,补充新的筛选功能测试 2. 运行测试——预期新的筛选测试红灯(功能还没实现) 3. 修改 parser 代码,加入筛选逻辑 4. 再跑测试——预期全部绿灯 5. 如果红灯,看报错、修、再跑——直到绿灯
你在这个过程中做的事情:写了一句话。AI 做的事情:写测试 → 跑 → 红灯 → 写代码 → 跑 → 绿灯 → 提交。你撬动了 10 倍于你输入的产出。
方案二:Lint + 类型检查 —— 概率性遵守变成确定性检查¶
你可以在 CLAUDE.md 里写"Python 代码用 ruff 格式"、"函数参数和返回值必须有类型注解"。但 AI 在长对话里注意力会衰减——第 15 轮时它可能随手写了个无类型注解的函数。你 review 时不一定能发现——盯着一百行 diff,大脑已经疲劳了。
但 ruff 和 mypy 不会累。每一行都检查,每一次都检查。这里有一个重要的概念区分:
CLAUDE.md 是立法——告诉 AI 规则是什么。Linter 是执法——不管 AI 记没记住,不符合就报错。 立法没用,除非有执法。
Python 项目的标准配置(pyproject.toml):
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I", "N", "W"] # 错误、格式、导入排序、命名、警告
[tool.mypy]
strict = true
ignore_missing_imports = true
这两行配置的价值:从此你不必在 code review 里说"这里少了个类型注解""那里 import 没排序"。机器替你说,而且每次都记得。
方案三:CI 自动触发 —— push 即验证¶
precommit hook 解决的是"提交前检查",但它有一个盲区:你没法保证团队所有人都装了 hook。总有人会 git commit --no-verify 跳过检查。
CI 填上了这个盲区。precommit 是本地闸门,CI 是云端闸门——push 到远程仓库后自动触发,没有"跳过"的选项。
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint-and-type:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: pip install ruff mypy pytest
- name: Lint
run: ruff check crawlers/
- name: Type check
run: mypy crawlers/ --ignore-missing-imports
test:
runs-on: ubuntu-latest
needs: lint-and-type
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: pip install -r crawlers/requirements.txt
- name: Run tests
run: pytest crawlers/tests/ -v
domain-validate:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: pip install -r crawlers/requirements.txt
- name: Domain validation
run: pytest crawlers/tests/test_domain.py -v
注意 job 的依赖链:lint -> test -> domain-validate。Lint 最快(30 秒),不通过就不跑后续——省计算资源,也省你的等待时间。三层检查,每一层都是一个 Verifier。任何一个红灯,CI 发通知,AI 修代码,再 push,再跑——自动化闭环。
对应主线:AI 信息助手的验证体系¶
汇总一下,AI 信息助手的确定性验证体系:
| 验证层 | 工具 | 检查什么 | 运行时机 | 失败后果 |
|---|---|---|---|---|
| Lint | ruff | 代码风格、明显错误 | precommit + CI | 拒绝提交/构建失败 |
| 类型检查 | mypy | 类型安全、接口契约 | precommit + CI | 拒绝提交/构建失败 |
| Parser 单元测试 | pytest | 解析逻辑正确性 | precommit(关键路径)+ CI(全量) | 拒绝提交/构建失败 |
| Domain validation | pytest | URL 域名、数据时效性、格式合规 | precommit + CI | 拒绝提交/构建失败 |
| Schema 验证 | pydantic | 数据格式与定义的 schema 一致 | CI | 构建失败 |
五层验证,全是确定性的。没有一个依赖"AI 自己觉得对不对"。这就是 Generator-Verifier 原则里的"确定性裁判"——规则是死的,判断是机械的,成本接近于零,公平性几乎完美。
第 6 章:LLM 作为裁判 —— 对抗验证 + 视觉审图¶
对应 AI 信息助手:信息整合对抗验证(现场搭)、PPT 视觉修复循环(现场展)。Generator 负责产出,Verifier 负责评判——但这里的 Verifier 也是 LLM,关键设计是让它和 Generator 不共谋。
为什么确定性裁判不够¶
上一章的测试、lint、CI 能验证"格式对不对""逻辑通不通""数据合规不合规"。但爬虫爬到数据之后,还有一个问题它们回答不了:这条信息可信吗?
你从小红书爬到一条帖子说"OpenAI 明天要发布 GPT-6"。你的 parser 测试全部通过——数据格式完美,URL 来自 xiaohongshu.com,时间戳是今天的。测试全绿。但这不代表那条帖子说的是真的。它可能是一个营销号在蹭热点,可能引用了一篇三周前已经被辟谣的"新闻",可能把"内部人士透露"当成了"官方公告"。
这类判断需要的不是规则——是常识、是交叉验证、是对信源可信度的历史认知。确定性裁判在这类问题上完全失效。你需要一个不同的裁判。
方案一:信息整合对抗验证(现场搭建)¶
核心思路:Generator 生成摘要,多个 Verifier 从不同维度反驳,综合得出置信度评分。
现场演示。对 Claude Code 说:
我们刚刚从小红书、机器之心、量子位爬到了今天的 AI 行业资讯(约 50 条)。
现在需要做信息整合和对抗验证。
1. 先用 DeepSeek V4 把所有资讯按主题聚合,每个主题生成一段摘要和关键主张列表
2. 对每条关键主张,启动 3 个 subagent 从不同维度进行对抗验证:
- Agent A(信源可靠性):查这条主张的来源是否权威、是否有历史翻车记录
- Agent B(交叉验证):搜索其他信源是否有独立验证、是否有矛盾报道
- Agent C(逻辑一致性):检查主张内部的逻辑是否自洽、是否有夸大或选择性引用
3. 综合三个 Agent 的发现,对每条关键主张给出置信度评分(1-5 分)
4. 最终输出:高置信度信息简报 + 低置信度信息并标注原因
这个流程的设计关键:
Generator 和 Verifier 使用不同的系统指令。 Generator(生成摘要)的系统指令是"忠实地概括信息,不要添加或删减"。Verifier(对抗验证)的系统指令是"假设这条主张可能有问题,找出它的漏洞"。两个角色用不同的 system prompt 实例化,从不同的认知框架出发,降低了自我验证的共谋风险。
多维度攻击同一个目标。 单独一个 Agent 验证"信源可靠性"可能漏掉"逻辑不一致"的问题——因为它在关注信源的时候,不会深究逻辑链条。三个 Agent 从不同角度进攻同一条主张,任何一个方向发现漏洞都能亮红灯。
置信度评分不是"对或错",是"有多可信"。 现实中很少有完全虚假的信息,更多的是"部分真实但不完整""引用了真实数据但得出了错误结论"。1-5 分的量表比二元判断更有实用价值。
打包成 Skill¶
和规划章一样,这套验证流程值得封装。创建一个 adversarial-verifier Skill:
---
name: adversarial-verifier
description: 对信息集进行多角度对抗验证,输出置信度评分。当有新的信息聚合结果需要验证时使用。
---
# 对抗验证器
## 执行流程
1. **聚合阶段**:Generator(DeepSeek V4)按主题聚类,生成摘要和主张列表
2. **验证阶段**:对每条主张启动 3 个 subagent 并行审查
- 信源可靠性 Agent:来源权威度、历史准确率
- 交叉验证 Agent:其他信源是否独立验证、是否有矛盾
- 逻辑一致性 Agent:推理链是否自洽、是否有逻辑跳跃
3. **综合评分阶段**:汇总三个维度的发现,给出 1-5 分置信度
4. **输出阶段**:高置信度简报 + 低置信度清单(附驳回理由)
## Subagent 配置
- 生成摘要: DeepSeek V4(成本低、中文能力强)
- 信源验证: Claude Sonnet(对权威来源的辨别力强)
- 交叉验证: 能 web search 的 agent(需要实时查证)
- 逻辑审查: DeepSeek V4(成本低、逻辑推理稳定)
常见坑:对抗验证不等于让 AI 吵架
三个 subagent 的结论可能冲突——A 说信源可靠,B 说另一个信源有相反报道。这时候不能让它们"相互辩论"来达成一致——AI 辩论最终会收敛到最长的那个回复,而不是最正确的那个。正确做法:三个结论原样汇总,由第四个 Agent(仲裁者)基于具体证据做最终判断。
方案二:PPT 视觉修复循环(现场展示)¶
对抗验证确保信息可信。但最终的产出物是 PPT——一份视觉上能给人看的报告。这里有一个反直觉的设计:PPT 的内容生成和视觉评判用不同的模型。
为什么?因为 DeepSeek V4(我们用来写内容、生成 HTML 的模型)没有视觉能力——它能把文字写得很好,但不知道自己在页面上放出来的效果好不好看。一个按钮重叠了,一段文字溢出到下一页了——DeepSeek V4 看不到这些问题。
解决方案:Generator = DeepSeek V4(生成内容和 HTML 结构),Verifier = Gemini(审截图布局)。 Gemini 有视觉能力,能看到渲染出来的页面是什么样的。
管线设计如下:
Markdown 报告内容
→ slide-plan.json(定义每页的内容分区)
→ HTML 渲染(DeepSeek V4 生成 HTML + CSS)
→ Headless Chrome 截图
→ Gemini 审图:评估布局、对齐、留白、字号层级
→ 不合格(<8分)→ Gemini 输出 CSS 修改建议 → 修 HTML → 重新截图
→ 合格(>=8分)→ 进入下一张
关键设计决策:Gemini 不总结内容,只评视觉。 Gemini 的系统指令里明确写:
你是一个 PPT 视觉审查器。你的唯一任务是评估截图的视觉质量。
不要总结内容,不要评价信息的正确性。只评:布局、对齐、留白、颜色对比度、
文字大小层级、视觉层次。
输出格式:对每个视觉问题,给出具体坐标和建议。
格式:位置(左上x%, 左上y%, 宽w%, 高h%) → 具体问题 → 修改建议。
示例:位置(15%, 30%, 70%, 40%) → 正文区行间距过小,文字拥挤 → 将 line-height 从 1.4 调到 1.8。
为什么要限制 Gemini 只评视觉?因为如果让它评内容——"这个标题不够吸引人""这句话可以更有力"——它就和 DeepSeek V4 抢活了。两个模型互相纠正,迭代发散。把两个模型的职责严格分开:DeepSeek V4 管内容 + 布局实现,Gemini 管视觉评判。各走各的赛道,迭代收敛速度大幅提升。
这个管线 Skill 已经提前写好,现场展示运行流程——给一份 Markdown 报告,看它怎么自动变成一份视觉上过关的 PPT。
第 7 章:HTML 可视化 —— 辅助方法¶
人在 Loop 外的介入手段¶
前面六章在讲如何让 AI 自己转起来。但有一个问题:当你需要介入决策时,怎么高效地和 AI 对齐?
和 AI 讨论技术方案的时候,最花时间的往往不是"做决策"——是"让对方理解你在说什么"。你说"我想把爬虫扩展成支持动态页面渲染的版本",AI 回复你一套文字方案:模块拆成 Crawler Core、Render Engine、Parser Registry 三层。你看完觉得不太对——"不是,我的意思是 render engine 应该是可插拔的,支持从 puppeteer 切换到 playwright"。AI 再回一套文字。来来回回,五次对话、二十分钟过去了,方案还没定下来。
问题出在哪?文字是低带宽的。它是一个线性的信息流——你必须逐行阅读,在大脑里把文字重新组装成对架构的理解。这个"组装"过程消耗大量脑力,而且容易出错。
方案:让 AI 直接生成 HTML,把讨论变成"看演示"¶
实操上极其简单。和 AI 讨论任何稍微复杂的方案时,加一句:
不需要指定用什么框架。AI 会用纯 HTML + CSS + 内嵌 JS——零依赖,浏览器即打开。
你看到的东西如果不对,在页面上比划着说"这个模块应该放在 pipeline 前面,不是后面""这里的交互不对,应该是 hover 触发而不是 click"。AI 改 HTML,你刷新浏览器,一秒验证。这就是"先对齐,再动工"。
效果:从三十分钟讨论到五分钟对齐¶
回到 AI 信息助手的信息整合管线。最初用纯文字讨论架构设计:多源数据进入 → 去重 → 聚类 → 摘要生成 → 对抗验证 → 评分排序 → 输出。听起来清晰,但一到细节就开始拉扯——去重是在聚合前做还是聚合后做?对抗验证的结果怎么影响最终评分?评分排序的权重怎么分配?
换了 HTML 方式。对 AI 说"直接用 HTML 画一个信息整合管线的架构图"。AI 在几十秒内生成了一个页面:左侧是输入源(小红书、机器之心、量子位),中间是管线的每一步(带数据流箭头),右侧是最终输出(置信度排序的简报 + 标记了低质量信息的红色区块)。
我一看——去重放在聚类之前不对,应该先聚类再组内去重;评分排序的输入应该是"置信度"和"信息新鲜度"两个维度加权,不是单一维度。两个问题,30 秒内全部发现并确认。AI 改完 HTML 后再确认一遍,一分钟。从需求到方案对齐,总共 5 分钟。
| 讨论方式 | 信息载体 | 平均对齐时间 | 典型偏差 |
|---|---|---|---|
| 纯文字 | 自然语言 | 20-30 分钟 | "我以为你说的是..." |
| HTML 可视化 | 交互页面 | 3-5 分钟 | 一眼看出位置/交互/流程偏差 |
不是"AI 变聪明了"——AI 的能力是一样的。改变的是沟通的带宽。
判断标准和边界¶
不是所有讨论都需要 HTML。一句话能说清的事,不值得写一个页面。
| 值得用 HTML | 用文字就够了 |
|---|---|
| 讨论 UI 布局和交互流程 | 确认一个变量命名 |
| 对齐多模块之间的数据流 | 询问 API 参数格式 |
| 比较两个架构方案的优劣 | 检查一个 bug 的原因 |
| 确认用户操作路径和体验 | 代码层面的实现细节 |
| 可视化复杂状态机或业务流程 | 简单的 CRUD 逻辑 |
判断标准一句话:如果这个讨论让你在脑子里构图超过 3 秒,就让 AI 把图直接画出来。
常见坑:HTML 原型不是产品代码
讨论阶段生成的 HTML 是沟通工具,不是代码产出。不要把它的 inline style 和原生 JS 直接复制到项目里——会和项目的 CSS 体系冲突、无响应式布局在生产环境崩溃。对齐之后,还是要按项目的技术栈正经写代码。
另一个极端:让 AI 在 HTML 上过度雕花。讨论方案用的 HTML,重点在于把关系画清楚,不在于配色好不好看。如果 AI 花了 10 分钟给 HTML 加 CSS 动画和渐变效果,你该喊停了——工具成了目的。
第 8 章:课后讨论¶
Loop Engineering 为什么近几周才火¶
理解了前面七章的内容后,你可能会问:Generator-Verifier 原则、测试闭环、对抗验证——这些概念似乎不是新东西,为什么这套方法论现在才火起来?
严格说,它不是一夜之间出现的。2026 年初,Anthropic 和 OpenAI 先后发表长文讨论 harness engineering——怎么给 agent 搭外围执行环境;到年中,LangChain 等开始用 loop engineering 专指其中的闭环设计。名词在演化,核心问题没变:怎么让 AI 自己跑完一个完整的循环。而这个问题,是最近才具备解决条件的。
答案在三个齿轮上:模型能力、上下文窗口、成本。
第一个齿轮:模型能力越过"能用"的临界点。 两年前的模型生成 200 行代码可能编译不过,你需要频繁介入、手动修正。那时的 AI 更像是"辅助编码",不是"独立开发"。Agent 自己跑完一个完整循环的前提是——它在每一步都能产出至少及格的产物。Claude Sonnet 4.5 和 DeepSeek V4 这代模型在代码生成上达到了这个门槛:写测试像样了,修复 bug 能定位到根因了,看报错能推断出大概哪里出问题了。
第二个齿轮:上下文窗口从焦虑到缓解。 Claude Sonnet 4.5 有 20 万 token 上下文——看起来很大,但在一个完整的开发生命周期里很快就被吃光。项目文档、代码文件、测试输出、错误日志、几轮对话下来,窗口满了。Agent 开始"忘事"——早期的决策、踩过的坑、刚才修的 bug 原因,全被挤出了上下文窗口。模型只能在窗口内的信息上做推理,而窗口外的东西对它来说不存在。
2026 年初,百万 token 上下文从 beta 走向正式可用——2 月 Claude Opus 4.6 发布,3 月 Opus 4.6 和 Sonnet 4.6 的 1M 上下文正式 GA 且不加价。这是一个工程上的质变:不只是窗口大了五倍,更是"被清出窗口"的概率从"经常"降到了"偶尔"。Agent 能在一个连续上下文里完成更复杂的任务链,不需要人类在每次窗口满了之后重新输入状态。
第三个齿轮:成本从"何不食肉糜"到"可以规模化"。 旗舰模型的能力领先,但价格令人难以接受——跑一次完整的 Loop 任务可能要几美元,跑十次就是几十美元。这个成本对个人开发者和大规模应用都不现实。
DeepSeek V4 的出现改变了游戏规则。它的代码生成能力在很多任务上接近甚至持平 Sonnet,价格却低得多——输入 token 约为 Sonnet 的六成,输出 token 约为四分之一。Loop Engineering 的核心运作方式是反复运行 Generator——每次验证不过就要重新生成。如果 Generator 很贵,你不敢让它多跑。如果 Generator 便宜,你可以让它跑十次、取最好的结果。DeepSeek V4 以数倍之低的成本让"反复运行"从奢侈变成了标配。
三个齿轮咬合在一起:模型能用了、上下文够用了、成本能承受了。Loop Engineering 从"理论上有道理"变成了"工程上能落地"。
全自动闭环长什么样¶
今年早些时候,Anthropic 和 OpenAI 先后公开了各自内部的全自动开发实践。典型形态是:项目托管在 GitHub,issue 打上约定标签(比如 ai-ready)后,Agent 自动领取、triage、开发、写测试、写文档、commit、提交 PR。整个过程没有人类参与——你睡觉的时候 Agent 在后台默默把 PR 推上来了。
这件事的意义不在于"AI 自己写了个 PR"——在于整个链路是闭环的。从"有人提了个需求"到"代码合入 master",中间的每一步都有对应的 Verifier:triage 阶段有需求可行性检查、开发阶段有测试红灯、commit 阶段有 precommit hook 闸门、PR 阶段有 CI。每一步出了错,对应的 Verifier 打出红灯,Agent 自动修复,红灯转绿灯后流程继续。
这和我们这节课搭的 AI 信息助手是同构的。爬虫 = issue 采集,信息整合 = 需求 triage,对抗验证 = 多角度审查,PPT 报告生成 = 最终交付。
背后的机制:动态编排(Dynamic Workflow)¶
上面这个闭环的底层机制,代表着 Coding Agent 领域的一个新方向:动态编排(dynamic workflow)——把多 Agent 编排这件事,从"人手工搭"变成"AI 自己生成"。
这不是一个修辞上的区别。用过 Dify、Coze 这类低代码 Workflow 平台的人都有体会:你拖了一堆节点——LLM 调用、条件分支、代码执行、循环——看起来很直观,但一旦要实现稍微复杂的逻辑,就发现节点不够用,你开始手写 Python 脚本塞进"代码节点"里。更难受的是,你没法让 AI 帮你写这个脚本——因为编排平台本身不是 AI-native 的,AI 进不去,帮不了你。绕了一圈,你还是在手工搭流水线,只不过从"写代码"变成了"拖节点 + 写脚本",生产力没有质变。
新一代 Coding Agent 走的是另一条路:你说"帮我审计整个代码库的安全漏洞",Agent 自己决定编排方案——哪些检查并行、哪些串行、派多少个子 agent、谁验证谁、什么时候停。你不需要拖任何一个节点,编排方案还可以固化下来重复运行。
这才是 Workflow 该有的样子。随着 Coding Agent 越来越强,模型越来越适应 agentic coding 这个场景,人手工编排 Workflow 这件事本身就成了瓶颈——你编排的速度和复杂度,怎么可能比得上 AI 自己生成的速度?Dify/Coze 式的低代码编排不是在推动这个趋势,是在阻碍它。真正的趋势是:人只需要说清楚"要做什么"和"怎么判断做对了",AI 自己生成执行计划、自己调度子 Agent、自己验证结果。
人类从流水线上的操作工,升级成了调度台后面的决策者。Workflow 不是让你画流程图的工具——是 AI 替你画、替你跑、替你修的东西。
全自动不是从零造产品¶
但别误读这件事。"Agent 能独立完成一个增量 PR"和"AI 能自己造出一个产品"是完全不同的两件事。
Agent 能闭环的前提是:已经有一个完整的代码库、一套验证系统、一份 CLAUDE.md 文档体系作为基础设施。 它是在"已有的系统"上做增量开发,不是从零开始。代码库提供了代码风格锚点(AI 生成的代码不会和现有风格打架)、测试体系提供了安全网(改错的地方会被抓住)、文档体系提供了上下文(不用每次都从头解释)。
你去读一读开源项目的 issue——大量的 feature request 和 bug fix 其实都是"写得清楚、范围明确、有现有代码可以参考"的增量任务。这些任务过去需要人花时间写,现在 Agent 可以在你睡觉的时候做完,推到你面前等你 review。Review 的难度远低于"从零开发",而且你是在一个已经跑通 CI 的 PR 上做 review——不是在一片白纸上做。
人类远没有退场¶
最后,人类在 Loop Engineering 中扮演什么角色?
搭建 Loop 的人。 你设计了 CLAUDE.md 的渐进披露体系,决定了哪些规则写进文档、哪些做成 Skill、哪些挂进 precommit hook。你定义了 Generator 和 Verifier 的配对方式——确定性裁判管什么域、LLM 裁判管什么域、人在哪些节点介入。这些设计决策决定了 AI 能跑多稳、多快、多准。
做出"做什么"决定的人。 AI 能执行"怎么做",但"做什么"仍然是你说了算。对抗验证能告诉你一条信息的置信度是 2 分(低),但它不能告诉你"置信度 2 分的信息要不要也放进报告里(作为待验证线索)"——这是你的判断。
当 Verifier 产生分歧时的最终仲裁者。 三个对抗验证 Agent 的结论冲突了、Gemini 审图说 7 分但你觉得效果还不错——机器裁判不是最终裁判。你保留一票否决权和一票放行权。
总结一句话:人类第一次可以在睡觉时让 AI 替自己开发。我们没有退场——我们退出了流水线,升到了调度台。这不是"AI 取代人类",是"一个人加上一个能自转的 AI 系统,能做的事情多了一个数量级"。
课后任务¶
这节课的实践作业是 给 AI 信息助手搭建完整的 Loop 系统。详细要求见 assignment3.md。
简要概括你需要交付的东西:
- CLAUDE.md + LLM Wiki:至少包含信源配置文档、数据 schema 文档、爬虫规则文档,结构遵循渐进披露原则。
- 爬虫模块:至少实现 2 个信源 parser(建议选小红书 + 一个 AI 媒体),包含对应的单元测试和 domain validation。
- 信息整合 Skill:一个完整的
adversarial-verifierSkill,实现聚合 → 对抗验证 → 置信度评分的完整管线。 - PPT 报告管线的 HTML 原型:用 HTML 画出从数据到 PPT 的完整数据流和页面布局方案。
完成这门课后,你可以回头看看自己的日常开发流程。问自己三个问题:
- 有哪些"每次都要重新说一遍"的指令,值得写进 CLAUDE.md 或封装成 Skill?
- 有哪些"AI 改完代码、我只能凭感觉判断对不对"的环节,可以加上一个确定性 Verifier?
- 有哪些"需要多个角度审查"的产出(代码 review、方案评估、信息验证),可以用 LLM 裁判来辅助?
回答了这三个问题,你就完成了从"用 AI"到"驾驭 AI"的认知跃迁。下一步就是把答案变成你项目里实际运转的 Loop。