一部从 Transformer 内部机制走到多智能体系统的完整技术栈手册——先直觉、后形式化,面向实现,对「什么有效」诚实相告。
HAGGAI ROITMAN 著 · ← → 翻页 · T 目录 · F 全屏
全书的三大支柱:Transformer 内部如何处理语言、让训练与推理成为可能的硬件与系统,以及支撑一切对齐方法的经典强化学习。
文本→Token→表征→Token→文本:一条流水线看清 LLM 如何读入、如何计算、如何预测;同一个主干,换一个预测头就是另一个任务。
预训练目标只有一句话:预测下一个 token。之后的每个阶段——更好的优化器、低秩适配、稀疏专家——都在为它省算力。
概率分布已定,选哪个 token?如何把模型塞进显存、把解码提速 3×?最后是幻觉与安全——工程落地的最后一公里。
| 方法 | 确定性 | 多样性 | 最佳适用 |
|---|---|---|---|
| Greedy | 是 | 无 | 代码、事实问答 |
| Beam(B=4–8) | 是 | 低 | 翻译、摘要 |
| Top-k(k=50) | 否 | 中 | 通用生成 |
| Top-p(p=0.9) | 否 | 自适应 | 开放式默认 |
| Min-p(p=0.1) | 否 | 自适应 | top-p 稳健替代 |
LLM 训练在 GPU 上快 100-1000×:不是因为核多,而是因为矩阵乘天然并行。而真正的瓶颈往往不是算力,是数据搬运——读懂内存层级,后面每一章的优化技术才有支点。
智能体与环境试错交互、最大化长期累积奖励——不像监督学习需要标签,RL 从经验中学习。本章从 MDP 五元组与 TD 误差,走到策略梯度与 GAE,为 RLHF 筑底。
完整的「面向 LLM 的 RL」工具箱:从 PPO 的逐步推导,到 DPO、GRPO 与 20+ 种对齐算法;从奖励模型到大规模基础设施,再到轨迹级的智能体训练。
SFT 教模型模仿,天花板是训练数据本身;RL 让模型超越示范。本章把文本生成建模为 MDP,再走通 RLHF 流水线:SFT → 奖励模型 → RL 优化。
朴素策略梯度一次坏 batch 就可能崩溃。PPO 用一阶 clipped 目标复现 TRPO 的稳定性,实现简单 10×——GPT-4 与 Claude 背后的主力算法。
L = L_CLIP − c1·LVF + c2·H[π] L_CLIP = E[ min(r·Â, clip(r, 1−ε, 1+ε)·Â) ] ε = 0.2 · c1 = 0.1 · c2 = 0.01 — value loss 亦被 clip · 熵奖励保探索
| 关键超参 | 典型值 | 设错的影响 |
|---|---|---|
| cliprange | 0.2 | 过低学不到东西 · 过高不稳定 |
| init_kl_coef | 0.01–0.1 | 过低 reward hacking · 过高卡在 SFT |
| ppo_epochs | 4 | 过多过拟合 · 过少浪费生成算力 |
| learning_rate | 1–5×10⁻⁶ | 过高:灾难性遗忘 |
DPO 从 RLHF 目标的解析解出发,把「reward 最大化 + KL 惩罚」变成偏好对上的监督 loss:4 个模型变 2 个,跳过 RL 与在线采样,回到稳定监督学习。
DPO 简单但陷阱密布:不掩码的 prompt、均值代求和、过期的 reference 都会静默失败;八个变体针对各自的失败场景对症下药。
| 变体 | 核心改动 | 解决的失败场景 |
|---|---|---|
| f-DPO | f′ 替换对数比,推广 f-散度 | 反向 KL 寻峰,多样性差 |
| Robust DPO | (1−ε)L(w,l)−εL(l,w) 除以 1−2ε | 标签噪声、标注翻转 |
| TR-DPO | EMA 周期更新 reference,α 混入 policy | reference 过期,KL 主导早平台 |
| EXO | 对调 πθ 与 π_ref 角色,纠正反向 KL | loss 实为前向 KL,方向错 |
| NCA | 加绝对似然项,防被选概率同跌 | 似然崩塌:被选也被压低 |
| SLiC-HF | hinge 损失 max(0, δ−margin) | 大 margin 时 log-sigmoid 收敛慢 |
| Iterative RPO | 被选上加 NLL:λ1·L_DPO+λ2·L_NLL | 会判别不会生成,推理退化 |
| SimPO | 免 reference:β/|y|·log πθ + margin γ | reference 显存翻倍、复杂 |
书 6.5.7 的 PyTorch 风格伪代码:policy 对 chosen/rejected 各算一次序列对数概率,ref 在 no_grad 下给基线,β 缩放差值进 logsigmoid——DPO 的全部数学就这几行。
def dpo_loss(model, ref_model, batch, beta=0.1): # ① batch:chosen/rejected 两条序列,prompt 掩为 -100 logps_chosen = get_sequence_logprob(model, batch["chosen"]) logps_rejected = get_sequence_logprob(model, batch["rejected"]) with torch.no_grad(): ② 参考模型:预计算或在此计算 ref_logps_chosen = get_sequence_logprob(ref_model, batch["chosen"]) ref_logps_rejected = get_sequence_logprob(ref_model, batch["rejected"]) chosen_rewards = beta * (logps_chosen - ref_logps_chosen) ③ β 乘在 logp 差上 rejected_rewards = beta * (logps_rejected - ref_logps_rejected) loss = -F.logsigmoid(chosen_rewards - rejected_rewards).mean() return loss def get_sequence_logprob(model, sequences): """仅在回答 token 上求和的对数概率。""" outputs = model(sequences["input_ids"], attention_mask=sequences["mask"]) logits = outputs.logits[:, :-1, :] ④ 为 next-token 预测做位移 labels = sequences["labels"][:, 1:] # 位移后的标签 log_probs = F.log_softmax(logits, dim=-1) token_logps = log_probs.gather(-1, labels.unsqueeze(-1)).squeeze(-1) mask = (labels != -100).float() ⑤ 只对回答 token 求和 return (token_logps * mask).sum(dim=-1) # 形状:[batch_size]
DeepSeek 在 DeepSeekMath 提出、R1 放大:不训练 critic,对同一 prompt 采 G 条响应,以组内 reward 统计量作基线——显存去掉一整个模型,往往还优于 PPO。
| 组大小 G | 信号质量 | 使用场景 |
|---|---|---|
| 4 | 中等 | 快速实验(通过率>50%) |
| 8 | 良好(标准) | 默认,多数任务的良好平衡 |
| 16 | 极佳 | 困难任务(通过率<20%) |
基础 GRPO 暴露四类失败:预训练偏差稀释梯度、对称 clipping 限制探索、组大小浪费、多目标 reward 失衡——变体家族逐一击破,TRL 大多 loss_type 一键切换。
| 变体 | 解决什么 | 关键机制 | 何时用 |
|---|---|---|---|
| DAPO | 对称 clip 限探索;全对全错组零梯度 | Clip-Higher εhigh=0.28 · token 级 loss · 动态采样 | 长推理;GRPO 不稳定时首选 |
| GSPO | off-policy 下逐 token ratio 乘积爆炸(1050) | 序列级几何平均重要性采样,整序列只 clip 一次 | steps_per_generation>1 |
| Dr. GRPO | 预训练常见 token 稀释梯度 | 按 (1−πref) 降权,聚焦有信息 token(需 ref_model) | 词表分布严重不匹配 |
| 2-GRPO | 大 G 生成算力浪费 | G=2,隐式 DPO 式对比;生成省 8×、端到端 4-6× | 通过率适中的推理任务 |
| SAPO | 硬 clip 的梯度悬崖效应 | sigmoid 软门控,温度 τ±=1.0/1.05;τ→0 回 PPO | clip 边界训练不稳 |
| TIS / MIS | vLLM 与训练前向概率不匹配 | 截断修正(C=5)/ 超阈整条置零(C=3) | 用 vLLM 生成时建议常开 |
| VESPO | 启发式改 clip 缺乏原理 | 变分核:平滑、非对称、指数衰减陈旧轨迹 | 异步 / off-policy 训练 |
| DPPO | ratio clip 对低概率 token 过罚 | 直接 TV/KL 散度做信赖域(binary/topk mask) | 研究阶段;概率高度偏斜任务 |
| CISPO | 组内基线不稳,单一 prompt 主导 | batch 级 reward 缩放 + token 级 loss 协同 | 大规模 RL 推荐默认(ScaleRL) |
| GDPO | 高方差 reward 主导,advantage 坍塌 | 每项独立归一化,再按权重 wn 聚合 | 多目标(正确性+格式) |
| GOPO | RM 绝对分数幅度无意义 | 只用组内序数 rank 作 advantage,对单调变换不变 | 不可验证奖励(摘要/聊天) |
书 7.3 最小可运行示例:每条 prompt 采 8 条响应、组统计量当 baseline,critic 从显存中消失;reward 就是一个普通 Python 函数。
from trl import GRPOConfig, GRPOTrainer model = AutoModelForCausalLM.from_pretrained( "Qwen/Qwen2.5-7B-Instruct", torch_dtype=torch.bfloat16, attn_implementation="flash_attention_2") grpo_config = GRPOConfig( num_generations=8, temperature=1.0, ① G=8 · 高温保组内多样 max_completion_length=2048, beta=0.04, ③ KL 惩罚系数 learning_rate=1e-6, per_device_train_batch_size=2, ② 每设备 2 prompt ×8 = 16 响应 use_vllm=True) ④ 8 倍生成,速度至关重要 def reward_fn(completions, prompts, **kwargs): ⑤ Python 函数即 reward """返回 float 列表:正确为 1.0,错误为 0.0。""" rewards = [] for completion, prompt in zip(completions, prompts): answer, expected = extract_answer(completion), get_ground_truth(prompt) rewards.append(1.0 if answer == expected else 0.0) return rewards trainer = GRPOTrainer(model=model, args=grpo_config, reward_funcs=[reward_fn, format_reward_fn], ⑤ 可组合多个,多目标 train_dataset=math_dataset, tokenizer=tokenizer) trainer.train()
五种变体各补 offline DPO 一块短板:Online 消分布偏移,KTO 凭前景理论只吃二值反馈,IPO 锁死 margin 抗过拟合,ORPO 免参考模型,Best-of-N 零训练成强基线。
| 方法 | 所需信号 | reference 模型 | 何时选 |
|---|---|---|---|
| Online DPO | 每步从 πθ 新鲜采样,RM 打分取最高/最低成 pair | 要 · 3 模型 | DPO 停滞、需要 on-policy 探索 |
| KTO | 未配对点赞/点踩(label true/false) | 要 · 2 模型 | 生产二值反馈、类别不平衡、噪声标签 |
| IPO | 离线成对偏好(与 DPO 同格式) | 要 · 2 模型 | 噪声数据、多 epoch、DPO 过拟合 |
| ORPO | 离线 pair,chosen 需高质量 | 不要 · 1 模型 | 显存受限、从 base 一步 SFT+对齐 |
| Best-of-N | RM 给 N=4-64 条候选打分,取最优 | 不要 · 1+RM | 最强基线、RFT 迭代造数据 |
奖励模型把人类偏好变成 RL 训练信号,训练不佳就是奖励黑客的源头:BT 成对推导定框架,PRM 逐步打分补稀疏,RLVR 用规则防作弊,PL 列表排序补校准。
SFT 是 RLHF 的地基:RL 只能放大已有行为,无法引入 SFT 中完全不存在的能力——打包、掩码与数据混合决定这条上限。
书第 10 章两件套:packing 收回浪费在 padding 上的 50-80% 算力,completion-only mask 把 gradient 聚焦到 assistant 回应——一个管效率,一个管信号。
from trl import SFTConfig, SFTTrainer, DataCollatorForCompletionOnlyLM from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B-Instruct") def formatting_func(example): ① chat 模板拼接对话 messages = [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": example["instruction"]}, {"role": "assistant", "content": example["response"]}] return tokenizer.apply_chat_template(messages, tokenize=False) response_template = "<|start_header_id|>assistant <|end_header_id|>" collator = DataCollatorForCompletionOnlyLM( ② 此 token 后才开始算 Loss response_template=response_template, tokenizer=tokenizer) config = SFTConfig( max_seq_length=4096, packing=True, ③ 启用序列打包 per_device_train_batch_size=4, learning_rate=2e-5) trainer = SFTTrainer(model=model, args=config, train_dataset=dataset, data_collator=collator, ④ 仅对 completion 计算 Loss formatting_func=formatting_func) trainer.train()
RLHF 是系统工程题:四个模型同驻显存、训练循环摊到数百卡——沿四个本质不同的维度切分,再按通信层级组合起来。
生成被显存带宽卡死,训练看 MFU;数值稳定是 RL 的隐形地基——FP32 master 权重与梯度裁剪缺一即崩。
| 方法 | LR | 调度 | 要点 |
|---|---|---|---|
| DPO | 5e-7 | 常数 · 50 步 warmup | β=0.1 · 1 epoch |
| PPO policy | 1e-6 | 常数 · 20 步 warmup | 自适应 KL · target 6 |
| PPO critic | 1e-6 | 常数 | 与 policy 同步 |
| GRPO | 1e-6 | 常数 | 组基线 · 免 critic |
AdamW · β1=0.9 · β2=0.95(二阶矩窗口 ~20 步) · ε=1e-8 max_grad_norm=1.0 · weight_decay=0(KL 惩罚即正则) LR 比 SFT 小 10-100× · 临界 batch Bcrit ≈ 1-4M token
标准 RLHF 只优化单轮应答;自主智能体要跨 10–100+ 次工具调用规划,reward 稀疏且来自真实执行——轨迹缓冲区 e=(S,A,R,S′) 存下整条轨迹,再经三种范式转化为能力。
| 方法 | 核心思想(一句话) | 信号来源 |
|---|---|---|
| STaR | 迭代 SFT:在自身成功轨迹上自举推理;失败题以正确答案为条件「合理化」,3–5 轮收敛 | 答案 / 测试正确性 |
| Reflexion | 上下文 RL:语言式自我批评存入情节记忆(m≤3),零权重更新 | 评估器二元成败 |
| ReAct | Prompt 范式:单次生成内交错「思考」与「行动」;格式惩罚写入 reward | 环境观测(逐步) |
| LATS | 树搜索:动作序列上做 MCTS(UCB1)反向传播;WebShop 40%→75% | 终端 reward 反向传播 |
| AgentQ | 离策略 RL:MCTS 产出多样轨迹,按 reward 构造偏好对做 DPO;WebShop 50%→82% | 同任务轨迹排序 |
| Voyager | 技能库:存取已验证代码函数,检索组合复用,终身学习且单调改进 | 环境执行验证 |
| RLEF | 在线 RL:从执行反馈学习,r=通过测试数/总测试数,零 reward 噪声 | 测试套件(确定性) |
| SWE-Agent / OpenHands | GRPO:每组 8–16 轨迹组归一化,解 GitHub Issue(15–50 动作);SWE-bench 30%→55% | 回归测试二元 reward |
同一配方跑通两个用例:SFT 示范打底,模拟环境中 GRPO 在线强化,执行 reward 与人类反馈共同塑形——生产力副驾与研究 Agent。
仅凭可验证的奖励信号,RL 就能教会模型推理本身——思维链、回溯与自我验证自发涌现。DeepSeek-R1-Zero 的「aha moment」与 OpenAI o1 证明了这一点。
推理即搜索:多步推理是在部分解构成的树上导航。测试时扩展(Test-Time Scaling)用推理算力换准确率——小模型多「想」几步,即可匹敌更大的模型。
| 方法 | 结构 | LLM 调用 | 奖励信号 | 最适用 |
|---|---|---|---|---|
| 思维链 CoT | 线性链 | 1 | 无 | 简单—中等难度 |
| 自洽性 SC | 并行链 · 全并行 | N · T=0.7–1.0 | 多数投票 = 隐式 ORM | 答案离散的数学题(GSM8K 74.4%) |
| Best-of-N + RM | 并行链 | N+1 / N+N·K | ORM / PRM | 有良好 RM;p=0.3,N=10 → 97% |
| 思维树 ToT / 思维图 GoT | 树 BFS·DFS / DAG | 2kbd | LLM 作判官 | 24 点 74% vs CoT 4%;合并省 62% |
| 蒙特卡洛树搜索 MCTS | 树 + 价值网络 | O(Nsim·d) | 价值网络 + Rollout | 困难证明、编程 · PUCT |
| 步级束搜索 / Self-Refine | 前缀束 / 线性迭代 | O(kd) / 2T | 价值模型 / 自我评判 | 正确性+流畅性 / 开放式生成 |
DeepSeek-R1 两阶段全透明:冷启动 SFT + GRPO;可验证奖励(RLVR)下仅结果奖励即足以涌现推理——aha 时刻自然发生。
测量前面一切是否真的有效:从 BLEU 到 Pass@k,从 ELO 排名到 LLM-as-Judge——以及当指标成为目标时,如何被 Goodhart 定律背叛。
评估 LLM 难在:输出空间无界、质量多维、评估本身也是语言任务。先定方案再采数:分类法定测量,合成数据补规模,指标跟着任务类型走。
| 任务 | 代表指标 | 人类相关 |
|---|---|---|
| 翻译 | BLEU:n-gram 精度+简短惩罚 BP | 低–中 |
| 摘要 | ROUGE:召回导向,L 用 LCS | 中 |
| 通用 NLG | BERTScore:上下文嵌入+IDF | 高 |
| 语言模型 | 困惑度 PPL:同分词才可比 | 低 |
| 代码生成 | Pass@k:n=200 样本无偏估计 | 极高 |
| Agent | TSR · 轨迹效率 η=L*/L · 工具调用 TUA | Oracle |
评估本身也是语言任务:强模型当裁判可规模化,却自带位置与冗长偏置。交换顺序、面板投票、概率加权可校正;污染与 Goodhart 仍在腐蚀评估。
| 指标 | 验证评判者是否可靠 |
|---|---|
| 一致率 | 与人类标注一致的样本比例 |
| Cohen's κ | 偶然修正一致率;κ>0.6 且一致率>80% 即可靠 |
| Spearman ρ · Kendall τ | 与人类分数的秩相关,适合序数评分;τ 对平局更鲁棒 |
从训练好的模型到被部署出去的自主系统:检索、记忆、上下文与循环、协议(MCP/A2A)、多智能体协作、框架与界面——Agent 在真实世界中运作所需的一切。
LLM 把知识压进权重,难逃幻觉、过时、领域盲区。RAG 配上动态可更新的外部记忆——对检索证据边缘化,让响应有据可查、可验证。
高级 RAG 在检索前后做文章:查询变换、重排、压缩与自我反思;智能体 RAG 更进一步——把何时查、查哪里、查几次变成可学习的序贯决策。
书 16.7:路由器用一次结构化输出为每个子查询挑选知识源;LangGraph 把规划—检索—评估—生成串成条件循环——检索从一次性前缀,变成可迭代的序贯决策。
def route_query(query: str, llm) -> RouteDecision: ① LLM 选源=一次「动作」 prompt = f"""Given the query: "{query}" Decide which knowledge source to use: - vector_db: for internal documents, policies, past reports - web_search: for current events, recent information - sql_db: for numerical data, statistics, structured records Return a JSON with: source, refined_query, reasoning.""" return llm.with_structured_output(RouteDecision).invoke(prompt) def retrieve_node(state: AgentState) -> AgentState: ② 逐子查询扇出→融合 new_docs = [] for sq in state["sub_queries"]: new_docs.extend(retrieve_from_source(sq, route_query(sq))) return {**state, "retrieved_docs": new_docs, "iterations": state["iterations"] + 1} def should_retrieve(state: AgentState) -> str: ③ 充分?重检索? if state["context_sufficient"]: return "generate" if state["iterations"] >= state["max_iterations"]: return "generate" # 预算耗尽:放弃检索,用现有内容生成 return "retrieve" # 换精炼子查询,回环再检索
LLM 本质无状态:GPT-4o 上下文 128K Token,而一本典型书籍约 670K——数日运行的 Agent 累积的观察必然溢出。记忆系统是对这一物理约束的工程回应。
记忆不是被动仓库,而是认知的主动参与者:读—行动—反思—写入循环;CoALA 给出设计语言,前沿正从反应式检索走向预判式注入。
| 记忆类型 | 内容 | 机制 | 例子(软件开发 Agent · p318) |
|---|---|---|---|
| 工作记忆 | 当前任务的活动信息 | 上下文内 · 0 ms | 正在编辑的文件与刚收到的报错 |
| 情景记忆 | 具体过往事件与轨迹 | 向量检索 · 时间索引 | 「上周在模块 X 修过类似 NPE」 |
| 语义记忆 | 通用事实与领域概念 | 知识图谱 · 结构化查询 | asyncio.gather 的异常传播规则 |
| 程序记忆 | 自动化技能与流程 | 权重 θ · 动作序列 | 调试流程:复现→隔离→假设→验证→修复 |
书 17.10:记忆存成带元数据的 Embedding,读时以「余弦相似 × 时间衰减」打分取 top-k,再注入 system prompt——写什么、怎么读、怎么进上下文的最小闭环。
class VectorMemoryStore: def write(self, content, importance=0.5, check_duplicates=True): if check_duplicates and self._is_duplicate(content): ① 写入过滤 return None # 余弦相似度>0.95:近似重复,直接跳过 if len(self.entries) >= self.max_entries: self._evict() # 满:逐出「最不重要×最久未访」条目 self.entries.append(MemoryEntry(content=content, embedding=self.embed_fn(content), importance=importance)) def retrieve(self, query, k=5, recency_boost=True): dense_scores = self._cosine_similarities(self.embed_fn(query)) for entry, d_score in zip(self.entries, dense_scores): ② 相似×时近 age_h = (datetime.now() - entry.timestamp).total_seconds()/3600 recency = np.exp(-self.decay_rate * age_h) # 每小时衰减 score = (1 - self.recency_weight) * d_score \ + self.recency_weight * recency ... # 组合分降序取 top-k;更新 access_count/last_accessed class MemoryAugmentedAgent: def step(self, user_message: str) -> str: ③ 记忆进 prompt memories = self.memory.retrieve(user_message, k=5) system = self.SYSTEM_PROMPT + f"\n\n{self._format_memories(memories)}" ... # + get_hot_context() 热层;最近 6 轮 → llm → 执行记忆命令
模型与真实任务之间隔着一层基础设施:harness 管记忆、路由工具、跟踪状态、强制安全——设计糟糕会抵消最强 LLM,设计良好则放大中等模型。
编排决定 Agent 如何决定下一步:探索用 ReAct,结构化用 Plan-and-Execute,可分解用多 Agent;生产系统还需管好状态、错误、延迟与成本。
| 框架 | 灵活 | 复杂 | 生产 | 多Agent | 最适合 |
|---|---|---|---|---|---|
| LangChain | H | H | M | M | 快速原型·链式 |
| LangGraph | H | H | H | H | 复杂有状态工作流 |
| AutoGen | M | M | M | H | 多 Agent 对话 |
| CrewAI | M | L | M | H | 角色制团队 |
| OAI Assistants | L | L | H | L | 托管 Agent |
| OAI Swarm | M | L | L | H | Handoff 演示 |
| 自研 Custom | H | H | H | H | 完全控制·无锁定 |
书 18.10:system 消息与四段 token 预算、按名分发的工具执行器、ReAct 主循环——模型→解析工具调用→执行→回填观测,四重终止条件兜底,永不静默死循环。
class AgentHarness: def __init__(self, model, system_prompt, tools, approval_cb=None): self.executor = ToolExecutor(tools, approval_cb) ① 工具分发表 self.ctx_mgr = ContextManager(model, max_tokens=128_000) self.ctx_mgr.system_msg = Message(Role.SYSTEM, system_prompt) async def run(self, user_input: str) -> str: # user 消息先入 context tool_defs = [t.to_api_dict() for t in self.tools] tool_tokens = sum(self.ctx_mgr.count_tokens( ② 组装时计预算 json.dumps(t)) for t in tool_defs) for iteration in range(self.MAX_ITERATIONS): # 硬上限 50 if not self.ctx_mgr.preflight_check(tool_tokens): return "I've run out of context space." ③ 每轮预算闸 response = await self.client.chat.completions.create( model=self.model, messages=self.ctx_mgr.build_messages(), tools=tool_defs, tool_choice="auto", temperature=0.0) choice = response.choices[0] if choice.finish_reason == "stop" or not choice.message.tool_calls: return choice.message.content or "Task complete." ④ 终止 results = await self._execute_tool_calls(choice.message.tool_calls) for tid, result in results.items(): ⑤ 观测回填 self.ctx_mgr.add_message(Message( Role.TOOL, result, tool_call_id=tid))
提示工程、上下文工程、Harness 工程之后的第四层:设计迭代控制结构,让 Agent 无需逐轮指令、自主朝目标前进。精心设计的循环就是推理时运行的强化学习——只是权重不更新。
st = context(st-1, at-1, ot-1) at = πθ(st) ot = env(at) rt = verify(st, at, ot)
书 19.3-19.4:五个原语(自动化·工作树·技能·连接器·子 Agent)外加外部状态持久化,装配成一段恒温器式的规范循环——推理、行动、回填、终止判定。
def agent_loop(goal: str, max_steps: int = 50, budget: TokenBudget = None): """The canonical agent loop structure.""" state = initialize_state(goal) ① 加载外部状态和目标 for step in range(max_steps): thought = model.reason(state) ② 推理(ReAct 风格) action = model.choose_action(state) observation = environment.execute(action) state = update_state(state, thought, action, observation) if state.token_count > CONTEXT_BUDGET * 0.8: state = compact(state) 汇总旧步骤 if verifier.passes(state, goal): ③ 确定性检查 persist_state(state, status="success") return Success(state) if no_progress_detected(state, window=3): ④ 无进展 persist_state(state, status="stuck") return escalate_to_human(state) if budget and budget.exhausted(): persist_state(state, status="budget_exceeded") return escalate_to_human(state) persist_state(state, status="max_steps") ⑤ 硬性上限 return escalate_to_human(state)
架构决定 Agent 是否可靠、可调试且成本可控。工作流预定义控制流——可预测、可测试、更便宜;Agent 由 LLM 动态决策——灵活、应对新情况。从工作流开始,确需自适应才升级。
| 模式 | 复杂度 | LLM 调用 | 最适用于 |
|---|---|---|---|
| Prompt 链 | 低 | N(固定) | 顺序任务、内容流水线 |
| 路由 | 低 | 1+1 | 多类型输入、分诊 |
| 并行化 | 低 | N(并行) | 独立子任务、投票 |
| Orchestrator-Workers | 中 | 可变 | 未知的分解结构 |
| Evaluator-Optimizer | 中 | 2-10(循环) | 高质量要求的输出 |
| ReAct | 中 | 3-25(循环) | 通用工具使用、探索 |
| 规划型 Agent | 高 | 5-50+ | 长时程、多步任务 |
Agent 必须在世界中行动、观察并调整,单次回复无法刻画其质量。环境就是 LLM 的 RL Gym——让探索安全、评估可复现、课程由易到难。
书 21.8 的 FileEditEnv:Agent 面对一个含 bug 的文件与失败的测试,不断编辑直到通过;观察与动作均为纯字符串,奖励来自真实运行测试而非 LLM 裁判。
@dataclass class StepResult: observation: str 输入给 LLM 的文本 reward: float 0.0 或 1.0 terminated: bool 任务解决或达最大步数 truncated: bool 预算超出被截断 class FileEditEnv: 动作: view | edit | run_tests | submit def reset(self, seed: int | None = None): ① 新回合 self._workdir = Path(tempfile.mkdtemp(prefix="fileenv_")) (self._workdir / "solution.py").write_text(self.buggy_code) self._step_count = 0 return self._build_observation(...), {"step": 0} def step(self, action: str) -> StepResult: ② 一步交互 self._step_count += 1 if action.startswith("edit"): self._action_edit(action) test_output = self._run_tests() 隔离子进程 + 超时 passed = "passed" in test_output and "failed" not in test_output reward = 1.0 if passed else 0.0 ③ 基于执行的奖励 terminated = passed or action.startswith("submit") truncated = self._step_count >= self.MAX_STEPS ④ 步数上限 return StepResult(self._build_observation(action, test_output), reward, terminated, truncated)
每个新框架都在重写工具连接器。MCP(Anthropic 2024)以 JSON-RPC 2.0 统一接口,把 N×M 集成压成 N+M——如 USB 之于设备。
MCP 运行在分层信任之上:Host 执行同意,Server 条件信任。对训练者,它更是标准化的动作空间与轨迹来源——LLM 时代的 gymnasium。
| 场景 | 选 MCP | 选自定义集成 |
|---|---|---|
| 多厂商 / 多框架协同 | 一次实现,跨厂商即插 | 锁定单一提供方 |
| 工具分发给他人使用 | 开放标准 + 生态注册表 | 自建分发与维护 |
| 需会话·订阅·推送 | 协议原生支持 | 需自行实现 |
| 延迟敏感·接口特殊 | 协议开销难承受 | 直连更快更省 |
@mcp.tool() def create_note(title: str, content: str) -> str: ... # inputSchema 由注解+docstring 生成 · mcp.run() 自选传输
书 22.7.1 的完整记笔记 server:装饰器声明 tool 与 resource,Schema 与传输全自动——这就是「N×M 集成问题」的标准答案长什么样。
from pathlib import Path from mcp.server.fastmcp import FastMCP mcp = FastMCP("notes-server") ① 一个实例=一个 server NOTES_DIR = Path.home() / ".notes" @mcp.tool() ② 声明式 tool def create_note(title: str, content: str, tags: list[str] | None = None) -> str: """Create a new text note. Use this when the user wants to save information.""" ③ docstring 给 LLM 看 safe = "".join(c if c.isalnum() or c in "-_" else "_" for c in title).strip() ④ 输入校验:防路径穿越 p = NOTES_DIR / f"{safe}.md" p.write_text(f"---\ntitle: {title}\n---\n\n" + content, encoding="utf-8") return f"Note saved to {p}" @mcp.resource("notes://{title}") ⑤ URI 路由的只读数据 def get_note(title: str) -> str: return (NOTES_DIR / f"{title}.md").read_text("utf-8") if __name__ == "__main__": mcp.run() ⑥ stdio / Streamable HTTP 自动选择
左侧:最小 Client 的握手—发现—调用四阶段;讲解卡:书中 22.7.3 的 MCPHost 连接池——多 server 时工具如何统一路由。
import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main(): params = StdioServerParameters( ① 如何拉起 server 子进程 command="python", args=["notes_server.py"], env=None) async with stdio_client(params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() ② 能力协商握手 tools = await session.list_tools() ③ 工具发现 for t in tools.tools: print(f" - {t.name}: {t.description[:60]}") result = await session.call_tool( ④ 执行(先经用户同意) "create_note", arguments={"title": "MCP Architecture Notes", "content": "MCP uses JSON-RPC 2.0.", "tags": ["mcp"]}) print(f"Tool result: {result.content[0].text}") asyncio.run(main())
Skill 思路由 Voyager 在 Minecraft 中推广:可执行代码技能不断入库、验证、复用。如今 skill 以可版本化的包封装领域专长——不重新训练即可加载、组合与替换,可扩展性远超任何单个 prompt。
单一通才 Agent 无法兼顾知识广度与能力深度。Google 于 2025 年 4 月发布的开放协议 A2A,让异构 Agent 相互发现、委派与协作——不透明执行:不探查对方内部,只共享能力与任务。
从请求-响应到合同网与拍卖,A2A 提供一整套通信与协调语汇;它与 MCP 纵横互补——MCP 纵向连工具,A2A 横向连 Agent,生产系统两者并用。
| 维度 | MCP(纵向) | A2A(横向) |
|---|---|---|
| 参与方 | Agent ↔ 工具/资源 | Agent ↔ Agent |
| 智能性 | 仅 Agent 端推理 | 双方均能推理规划 |
| 状态性 | 无状态 Tool 调用 | 带生命周期的任务 |
| 流式传输 | 有限(Tool 结果) | 一等 SSE 流式 |
| 发现机制 | Tool 清单 | Agent Card |
| 典型用例 | 查库·运行 SQL | 委派给专家 Agent |
MCP 纵向连工具与数据源,A2A 横向连 Agent——互补而非竞争— 书 p433
书 24.9:OrchestratorAgent 借 A2A 把研究问题拆解后派给远程 ResearchAgent——发现、派发、等待、收集四步全走开放标准,不碰对方内部实现。
# -- A2AClient(REST 绑定,节选)------------------ async def get_agent_card(self) -> dict: ① 发现能力卡片 url = f"{self.agent_url}/.well-known/agent.json" async with httpx.AsyncClient() as client: return (await client.get(url, headers=self.headers)).json() async def send_task(self, message: Message, metadata: dict) -> Task: payload = {"id": str(uuid.uuid4()), "message": message.model_dump(), "metadata": metadata} ② 提交任务 ... # POST {url}/tasks/send(Bearer 头,30s 超时)→ 初始 Task async def wait_for_completion(self, task: Task) -> Task: while task.status.state not in {"completed", "failed", "canceled"}: await asyncio.sleep(2.0) ③ 轮询至终止态 task = await self.get_task(task.id) return task class ResearchOrchestrator: async def run(self, research_question: str) -> str: ④ 分解→并行派发→综合 tasks = await asyncio.gather(*[self.research_client.send_task( Message(role="user", parts=[Part(type="text", text=q)]), metadata={"workflowId": self.workflow_id, "subQuestion": i}) for i, q in enumerate(self._decompose(research_question))]) ... # 逐任务 wait_for_completion;取 artifacts 综合成报告 return self._synthesize(research_question, findings)
单个 LLM 再大也是通才,也是单点故障。专门化、并行化、鲁棒性、涌现能力——四大动机推动单体走向 Agent 社会;问题不在是否用多个,而在如何组织与协调。
拓扑之上是交互模式:辩论、反思、流水线等七种模式组装 LLM 团队;RL 训练引入非平稳性,CTDE 是合作场景的最佳实践;协调有代价,安全须显式设计。
| 多 Agent RL | 要点 |
|---|---|
| 独立学习 | 视他为环境,非平稳易震荡 |
| CTDE | 集中 critic 训练·去中心执行(主流) |
| 通信学习 | 可微消息向量,端到端优化 |
| 涌现通信 | 自发符号协议,可解释性差 |
| 自博弈 | 对手是过去的自己,自动课程 |
| 种群 PBT | 多样种群,劣者被变异替换 |
| Nash 均衡 | 单方偏离无法提高回报 |
在 notebook 里搭一个能干活的 Agent 不难,难的是边界情况、故障恢复、负载扩展与持续改进。五阶段生命周期与七种主流框架,正是跨越这条工程鸿沟的脚手架。
| 框架 | 抽象核心 | 最适合 |
|---|---|---|
| LangGraph(LangChain) | 有向图·State·条件边·子图 | 显式控制·检查点·人机协同 |
| AutoGen(Microsoft) | 可对话实体·群聊·沙箱代码执行 | 多智能体协作 |
| CrewAI | 角色·目标·背景故事·Process | 最少样板的角色团队 |
| OpenAI Agents SDK | Handoff 交接·Guardrail | OpenAI 生态多智能体 |
| DSPy | Signature·优化器编译 Prompt | 有指标的自动优化 |
| Semantic Kernel(Microsoft) | 插件架构·企业连接器 | .NET/Azure 企业集成 |
| NVIDIA OO Agents(NOOA) | Agent 即 Python 对象·代码即动作 | 极小抽象·研究级 |
Agent 行为非确定、有状态、多步骤:测试金字塔下层数量多而快、上层可信度高;Trace-Metric-Log 三支柱让失败可分类、可重放;队列化部署扛住规模化。
| 你的需求 | 选型 |
|---|---|
| 对 Agent 流程显式控制 | LangGraph |
| 带代码执行的多 Agent 系统 | AutoGen |
| 最少样板的角色制 Agent | CrewAI |
| 基于 OpenAI 生态开发 | Agents SDK |
| 自动化优化 Prompt | DSPy |
| 企业级 .NET/Azure 环境 | Semantic Kernel |
书 26.9:研究 Agent 的推理/检索/审阅被编译成图——节点是函数、State 是唯一载体、条件边画出自批评回路,checkpoint 负责可恢复。
class ResearchState(TypedDict): messages: Annotated[List[BaseMessage], add_messages] ① 节点间只传 State error_count: int tool_call_count: int status: Literal["researching", "done", "failed"] def route_after_llm(state: ResearchState) -> str: ② 条件边路由 if state["error_count"] >= 5 or state["tool_call_count"] >= 15: return "fail" 预算熔断 last_message = state["messages"][-1] if hasattr(last_message, "tool_calls") and last_message.tool_calls: return "tools" return "research" 无工具调用 → LLM 继续推理 async def build_graph(db_url: str) -> CompiledStateGraph: ③ 图即控制流 checkpointer = AsyncPostgresSaver.from_conn_string(db_url) ④ 检查点 builder = StateGraph(ResearchState) ... # add_node ×4;START→research;tools→check;fail→END(略) builder.add_conditional_edges("research", route_after_llm, {"tools": "tools", "research": "research", "fail": "fail"}) builder.add_conditional_edges("check", lambda s: "end" if s["status"] == "done" else "research", {"end": END, "research": "research"}) 审阅未过 → 打回重做 return builder.compile(checkpointer=checkpointer)
聊天气泡只传达结果,Agent 界面必须传达过程——推理、工具与决策。六大范式沿「对话 → 自主」光谱展开,配套七大关键组件与快速成熟的框架生态。
| 框架 | 语言 | 生成式 UI | 定位与亮点 |
|---|---|---|---|
| Vercel AI SDK | TypeScript | ✓ | useChat 等流式 Hook + RSC 组件流,生产级 Web Agent UI 应用最广 |
| Chainlit | Python | 部分 | LangChain / LlamaIndex 步骤自动渲染为可折叠树,内置认证与反馈采集 |
| Gradio | Python | ◦ | ChatInterface 极少代码原型,Hugging Face Spaces 零配置分享 |
| Streamlit | Python | × | st.metric 仪表盘 + st.write_stream 流式,@st.fragment 局部重跑 |
| LangGraph Studio | Python/TS | × | 桌面可视化 IDE:状态检查、时间旅行调试、任意节点人机协同断点 |
模型不仅决定说什么,还决定如何呈现:工具调用直接渲染为界面组件。流式是基线,审批门控必须分层,信任则是涌现的属性——北极星是「透明协作者」。
| 层级 | 策略 | 示例 |
|---|---|---|
| Tier 1 自动批准 | 低风险·可逆·例行,记录备审计 | 网页搜索、读取文件 |
| Tier 2 通知 | 非阻塞通知,短窗口(30 秒)内可取消 | 向草稿箱发送草稿邮件 |
| Tier 3 要求批准 | 阻塞门控:批准 / 拒绝 / 修改 | 发邮件、采购等不可逆操作 |
书 27.9:FastAPI 生成器产出 SSE 事件流;不可逆操作先推审批事件,再在 asyncio.Event 处挂起——流不断、Agent 停,人点头后才继续。
async def agent_stream(session_id: str, user_message: str): def sse(event_type: str, data: dict) -> str: return f"data: {json.dumps({'type': event_type, **data})}\n\n" approval_id = f"{session_id}_email_001" ② Tier 3 审批门控 approval_store[approval_id] = ev = asyncio.Event() yield sse("approval_required", {"approval_id": approval_id, "tool": "send_email", "tier": 3, "risk": "irreversible"}) try: await asyncio.wait_for(ev.wait(), timeout=300) ③ 挂起整条流 ... 批准 → 执行 send_email;拒绝 → action_rejected except asyncio.TimeoutError: yield sse("approval_timeout", {"approval_id": approval_id}) yield sse("token", {"content": "I've completed the research. "}) @app.get("/chat/stream") ① SSE 主通道 async def chat_stream(session_id: str, message: str): return StreamingResponse(agent_stream(session_id, message), media_type="text/event-stream") @app.post("/chat/approve") ④ 批准端点 → 恢复 async def handle_approval(req: ApprovalRequest): approval_results[req.approval_id] = {"approved": req.approved, "parameters": req.parameters} approval_store[req.approval_id].set() set() 唤醒挂起的流
覆盖全部主题的 108 道详细测验题与完整解答;一章整合公式、超参表与失败模式诊断的速查手册;以及领域走向何方的总结。
四道精选测验覆盖基础、算法、系统、实战四个层次——先遮住要点自己作答,再对照检查。题目与解答忠实取自第 28 章(书 p516 起)。
临考速查:三条核心公式、六组关键超参、一张方法选择决策卡与五种常见失效模式——调试 PPO/DPO/GRPO 流水线时,先翻这一页。
L = E[ min(rₜÂₜ, clip(rₜ, 1±ε)Âₜ) ] rₜ = πθ(aₜ|sₜ) / πold(aₜ|sₜ)
L = −E[ log σ( β·log πθ(yw|x)/πref(yw|x)
− β·log πθ(yl|x)/πref(yl|x) ) ]Âᵢ = (rᵢ − μG) / σG → 组内归一化后做 PPO clip(无 critic)
| 症状 | 根因 | 修复(29.12) |
|---|---|---|
| 奖励升 · 质量降 | 奖励黑客 | RM 集成、长度惩罚、增大 β |
| KL 爆炸(>15) | LR 过高 / 模式崩溃 | 降 LR、回滚 checkpoint |
| 熵塌缩 | 过早收敛 | 增大熵系数、提高温度 |
| 5K 步后无提升 | Prompt 分布不当 | Goldilocks 过滤(通过率 20–80%) |
| 基准性能回退 | 对齐税 | 减 RL 预算、改用 LoRA、混入 SFT |
全书一句话:从 Transformer 基础到自主智能体——对齐是系统工程,没有单一最优方法,推理可从奖励中涌现,评估驱动一切,简单才能扩展。
本指南的核心论点:构建优秀的 AI 系统需要理解整条流水线,而不仅是某一层。按你的角色,可以从任一码头重新启航。