食谱开张:从一道"加强版 LLM"开始
Anthropic Cookbook 整本书在教什么 · 以及全书的最小可用单元——Augmented LLM
先回答一个问题:你打开 ChatGPT 问"今天上海下雨吗"——会发生什么?
它会说:"抱歉,我没有访问实时数据的能力。" 这一秒,你就触碰到了"裸 LLM" 的边界——它是一颗会说话的大脑,但被困在一个没有窗户、没有电话、没有日历的房间里。
Anthropic Cookbook 这本食谱整本书在教的,就是怎么给这颗大脑配上窗户、电话、日历、还有一个能干活的助手。
赤手空拳的攀岩者只能爬最简单的路线。给他/她加上四件套——绳索、保护塞、对讲机、教练——他/她突然就能去挑战更难的山壁。一个普通 LLM 调用就是"赤手攀岩",本课要教的 Augmented LLM(加强版 LLM)就是"装备齐全的攀岩"。
Augmented LLM 的"四件套"
Anthropic 工程师在论文 Building Effective Agents 里把"加强版 LLM"定义成基础单元——一个 LLM 配上以下四种装备的某些子集:
检索(Retrieval)
让 LLM 能从你自己的知识库里"翻书"——产品文档、客服 FAQ、内部 wiki。背后机制是 RAG。
工具(Tools)
让 LLM 能"叫别人去做事"——查天气、发邮件、调用计算器。LLM 自己不动手,它输出一个"请求工单",你的程序去执行。
记忆(Memory)
让 LLM 能"记住你昨天说过什么"——历史对话、用户偏好、长期事实。否则它每次都像初次见面。
系统提示(System Prompt)
给 LLM 一份"岗位说明书"——你是谁、说什么风格、不能碰哪些话题。本质上是把训练好的通用模型"角色化"成你的专属助手。
整本 cookbook 之后所有的复杂模式(链式、路由、Orchestrator、Agent SDK、SRE 自动修复),归根结底都是多个 Augmented LLM 之间的不同接线方式。先把这个最小单元吃透,整本书就是同一个乐高积木的不同组合。
看一段真实代码:7 行做一个能上网的研究员
这段代码来自 claude_agent_sdk/00_The_one_liner_research_agent.ipynb——整本 cookbook 的 "Hello World"。它是最小可用的 Augmented LLM:LLM + 一件工具(网页搜索)。
from claude_agent_sdk import ClaudeAgentOptions, query
messages = []
async for msg in query(
prompt="Research the latest trends in AI agents and give me a brief summary and relevant citations.",
options=ClaudeAgentOptions(model=MODEL, allowed_tools=["WebSearch"]),
):
print_activity(msg)
messages.append(msg)
第 1 行:从 Anthropic 官方 SDK 里把"提问"函数和"配置"工具拿进来。
第 3 行:准备一个空列表,待会儿 agent 跟我们说的每一句话都收进去。
第 4 行:开启异步流式接收——像看直播一样,agent 每说一句、做一步,你都能立刻看到。
第 5 行:我们的"任务订单"——研究 AI agent 最新趋势,给我摘要和引用链接。
第 6 行:关键配置——选用哪个模型,并且 只 允许它使用一个工具:网页搜索。这就是"Augmented LLM":一个 LLM + 一件工具。
第 8-9 行:把 agent 的每一条思考、每一次搜索、每一段答复都打印出来并存档。
整段共 7 行有效代码——一个能上网做调研的研究员。这就是 cookbook 的 Hello World。
看场对话:裸 LLM vs 加强版 LLM
同一个用户、同一个问题,两次对话——区别是有没有装备。
这本食谱里有什么——给你一张全景地图
整个 cookbook 仓库按"教学价值"重排后是这样的——后续 5 个模块,会按这张地图带你走完最重要的章节:
小测:你能认出哪一颗 LLM 装备齐了吗?
Q1. 你想让 Claude 写一首关于秋天的现代诗。最合理的做法是?
Q2. 你的 AI 助手反复说"抱歉我没有实时数据 / 我不知道现在几点 / 我访问不了你公司的内部 wiki"。你应该给它加什么?
Q3. 你公司的客服机器人每次新会话都要让用户重新报姓名、订单号、产品型号——同一个用户上次刚说过这些。最缺的是哪一件装备?
三种最基础的工序:链式 · 路由 · 并行
当一个 LLM 一次答不完,你怎么把多个调用编排起来——agentic 架构的第一道分水岭
把多个 LLM 编排起来——形状决定工序
上一模块我们看到 Augmented LLM 是基础单元。但一个单元能干的事有限。现在的问题是:当任务复杂到一个 LLM 一次答不完,你怎么把多个调用编排起来?
Anthropic 工程师在论文里给出了三种最基础的编排方式——本课用一家"现代化奶茶吧台"作贯穿隐喻:
链式 = 流水线:5 个工位前后接力,前一步的产物喂给下一步。
路由 = 分流员:店员看你点什么,把订单送到对应的专业工位。
并行 = 多窗口同时出杯:4 个工位同时开工,最后一起打包。
链式 Chain
何时用:任务能拆成"先 A 再 B 再 C" 的固定步骤。
例子:抽数据 → 转格式 → 排序 → 渲染表格。
不要用:任务步骤会随输入变化。
路由 Route
何时用:输入分多种类型,每种要不同的专门处理。
例子:客服工单分到账单 / 技术 / 安全三个专员。
不要用:所有输入处理逻辑都一样。
并行 Parallel
何时用:多个独立子任务可以同时进行。
例子:对 100 条评论同时打分;对一篇文章同时做"摘要 / 翻译 / 关键词"。
不要用:后一步依赖前一步结果。
工序一:链式 Chain——前一步的输出喂给下一步
来自 patterns/agents/basic_workflows.ipynb——整个 chain 函数 5 行有效代码:
def chain(input: str, prompts: list[str]) -> str:
"""Chain multiple LLM calls sequentially, passing results between steps."""
result = input
for i, prompt in enumerate(prompts, 1):
print(f"\nStep {i}:")
result = llm_call(f"{prompt}\nInput: {result}")
print(result)
return result
第 1 行:定义一个叫 chain 的"组装机"——吃进一段输入和一串 prompt 清单。
第 3 行:把"当前结果"初始化为最初的用户输入。
第 4 行:按顺序拿每一个 prompt——步骤 1、步骤 2、步骤 3……
第 6 行(关键):把"当前结果"塞给下一个 prompt 当输入,再调 LLM。前一步的输出 = 后一步的输入——这就是链式的全部魔法。
第 8 行:所有步骤跑完,把最终产物返回。
整个 chain 函数 5 行有效代码——这就是链式工作流的全部。
看一遍真实的链式跑一份业绩报告
cookbook 里给的实际例子:把一段杂乱业绩报告加工成 Markdown 表格,4 步流水线。点"下一步"逐步看:
工序二:并行 Parallel——同一份指令,N 份输入同时跑
def parallel(prompt: str, inputs: list[str], n_workers: int = 3) -> list[str]:
"""Process multiple inputs concurrently with the same prompt."""
with ThreadPoolExecutor(max_workers=n_workers) as executor:
futures = [executor.submit(llm_call, f"{prompt}\nInput: {x}") for x in inputs]
return [f.result() for f in futures]
第 1 行:定义"并发分工机"——吃进 1 个共用 prompt 和 N 个待处理输入。
第 3 行:开 3 个并发工位(线程池)。
第 4 行(关键):把 N 个任务一次性丢给工位——每位工人都拿到同一份 prompt但处理不同的输入。这是并行模式的核心约束。
第 5 行:等所有工位完工,结果按顺序收齐返回。
关键洞察:prompt 不变,input 变。适合"同样的指令处理一堆不同对象"——比如对 100 条评论同时打分、给一篇文章同时生成摘要 / 翻译 / 关键词三种产物。
顺序调 100 次 LLM,每次 2 秒 → 200 秒。开 10 个并发工位 → 约 20 秒。等待 LLM 回复的时间是浪费的,并行就是把这些等待时间叠在一起。账单不变,但用户感觉快了 10 倍。
工序三:路由 Route——分诊台 + 专科医生
路由的核心是两次 LLM 调用:第一次 LLM 当"分诊台"判断输入类型,第二次用对应的"专科 prompt"处理实际工作。
def route(input: str, routes: dict[str, str]) -> str:
selector_prompt = f"""
Analyze the input and select the most appropriate support team
from these options: {list(routes.keys())}
First explain your reasoning, then provide your selection in this XML format:
<reasoning>...</reasoning>
<selection>The chosen team name</selection>
Input: {input}""".strip()
route_response = llm_call(selector_prompt)
route_key = extract_xml(route_response, "selection").strip().lower()
selected_prompt = routes[route_key]
return llm_call(f"{selected_prompt}\nInput: {input}")
第 2-8 行:构造"分诊 prompt"——告诉 LLM 有哪些可选窗口、要先讲推理、再用 XML 标签 给出最终选择。
第 10 行:第一次 LLM 调用——分诊台开工。
第 11 行:用 extract_xml 把 <selection>标签里的内容拎出来,作为路由 key。
第 13-14 行:用 key 取出对应的"专科 prompt",第二次 LLM 调用——专员处理。
核心:两次 LLM 调用,第一次决定"去哪",第二次干活。
看一场真实的路由:客户报修工单
真实生产系统经常是 route → chain → parallel 的嵌套——先按工单类型路由,进了"账单专员"窗口后再走 "查订单 → 算金额 → 出退款单" 的链式,每个客户的退款邮件还可以并行批量发。先按形状判断,再按需要叠在一起。
小测:判断任务的形状
Q1. 你要做一个程序:客户上传 PDF 后自动生成"摘要 → 关键词 → 翻译成英文"。最适合的工序?
Q2. 你要给一条推文同时打 5 个独立维度的标签:情绪 / 话题 / 国家 / 是否广告 / 风险等级。最佳模式?
Q3. 用户上传一张图,可能是发票、可能是身份证、可能是收据——你要先判断类型,再用对应模板提取信息。最佳模式?
高阶工序:调度官 与 评审官
当任务的形状要看到才知道——动态拆解 与 互相博弈打磨的两种模式
现实里,有时候你看到任务的瞬间才知道该怎么拆
上一模块的三种工序都有一个共同假设——你提前知道任务的形状。但 chain 的步骤、parallel 的子任务清单、route 的窗口列表,都是在写代码时就敲死了的(俗称写死)。
现实世界里,很多任务一来你才知道要怎么拆。这一模块教两种处理"未知形状"任务的最重要工序——它们都比基础工序多了一层"元认知"。
Orchestrator-Workers(调度官 + 工人)= 总导演 + 摄影组:导演拿到选题后不预先写好剧本——看现场情况决定这场戏值得拍特写、空镜还是延时,再分别派摄影 A、B、C 去拍各自的镜头,最后剪辑成片。
Evaluator-Optimizer(评审官 + 优化器)= 编剧 + 责编:编剧写一稿初稿交给责编。责编不动笔,只给反馈:"角色动机不清楚 / 第二幕太拖"。编剧拿反馈再改一稿,再交。来回三五次直到 PASS。
调度官的核心 = 一个会现场拆任务的 prompt
看 cookbook 里的 orchestrator prompt——它的精髓是把"导演的元认知"用 XML 结构强约束出来:
ORCHESTRATOR_PROMPT = """
Analyze this task and break it down into 2-3 distinct approaches:
Task: {task}
Return your response in this format:
<analysis>
Explain your understanding of the task and which variations would be valuable.
</analysis>
<tasks>
<task>
<type>formal</type>
<description>Write a precise, technical version that emphasizes specifications</description>
</task>
<task>
<type>conversational</type>
<description>Write an engaging, friendly version that connects with readers</description>
</task>
</tasks>
"""
"Analyze this task and break it down into 2-3 distinct approaches"——让 Claude 当导演:看完任务后给我列 2-3 种切入角度。"distinct" 是关键词——必须有差异。
<analysis> 标签——强制导演先讲"为什么这么拆",避免它跳过思考直接出方案。这是 CoT 的一种实现。
<tasks> / <task> / <type> / <description> 嵌套——用 XML 强约束输出结构,方便程序解析成"工单清单"。
{task} 占位符——运行时把实际任务("为环保水壶写产品文案")填进去。
核心:具体拆几个 task、每个 task 是什么风格,全由 Claude 看到 task 那一刻自己决定——不是程序员写死的。这就是"动态拆任务"。
调度官的主循环:1 次导演 + N 次工人
第 1 次 LLM 调用——orchestrator 读 task,输出 <analysis> + <tasks> 工单清单。
用 extract_xml + parse_tasks 把 LLM 吐出来的 XML 转成程序能用的结构化清单。
对每张工单调一次 worker LLM——每个工人独立完成自己那一份。
把 N 个 worker 的结果收齐,作为最终产出返回。整个流程 = 1 次导演 + N 次工人 = N+1 次 LLM 调用。
class FlexibleOrchestrator:
def process(self, task: str, context: dict | None = None) -> dict:
# Step 1: Get orchestrator response
orchestrator_input = self._format_prompt(self.orchestrator_prompt, task=task, **context)
orchestrator_response = llm_call(orchestrator_input, model=self.model)
# Parse orchestrator response
analysis = extract_xml(orchestrator_response, "analysis")
tasks_xml = extract_xml(orchestrator_response, "tasks")
tasks = parse_tasks(tasks_xml)
# Step 2: Process each task
worker_results = []
for i, task_info in enumerate(tasks, 1):
worker_input = self._format_prompt(self.worker_prompt, original_task=task,
task_type=task_info["type"], task_description=task_info["description"])
worker_response = llm_call(worker_input, model=self.model)
worker_results.append({"type": task_info["type"], "result": worker_response})
return {"analysis": analysis, "worker_results": worker_results}
第 5 行:第 1 次 LLM 调用——导演看任务、决定怎么拆。
第 8-10 行:把导演吐出来的 XML 解析成程序能用的"工单清单"。
第 13 行:对每张工单派一个工人。
第 16 行:第 N 次 LLM 调用(N = 工单数)——每个工人独立完成自己那一份。
第 19 行:把所有产物加上 analysis(导演的解读)一起返回。
关键对比:parallel 的工人清单程序员写死。orchestrator 的工人清单导演现场决定。这一字之差,让系统从"流水线"变成了"团队"。
两个模式都是"同时跑多个 LLM"。差别在哪?parallel 的工人任务清单是写死的,orchestrator 的工人任务清单是导演现场决定的。如果你能提前列清楚要做哪几件事 → 用 parallel 省一次 LLM 调用;如果要做哪几件事得看具体输入 → 用 orchestrator。
评审官的循环:两个 LLM 互相博弈
这个模式叫 evaluator-optimizer——一个 LLM 写,一个 LLM 评,循环直到 PASS。
def loop(task, evaluator_prompt, generator_prompt):
memory = []
chain_of_thought = []
thoughts, result = generate(generator_prompt, task)
memory.append(result)
chain_of_thought.append({"thoughts": thoughts, "result": result})
while True:
evaluation, feedback = evaluate(evaluator_prompt, result, task)
if evaluation == "PASS":
return result, chain_of_thought
context = "\n".join(["Previous attempts:",
*[f"- {m}" for m in memory], f"\nFeedback: {feedback}"])
thoughts, result = generate(generator_prompt, task, context)
memory.append(result)
第 2 行:准备一本"草稿本"——记下每一稿。
第 5 行:编剧(generator)先写第一稿。
第 9 行:进入打磨循环。
第 10 行:责编(evaluator)看稿,吐出"通过 / 还要改"以及具体反馈。
第 11-12 行:责编满意了就交付,整个循环结束。
第 14-15 行:把所有历史草稿和最新反馈打包——告诉编剧"这些你已经写过了,现在按这个反馈改"。注意 memory 的累积——避免编剧在原地打转。
第 17 行:编剧带着批注再写一稿。循环直到 PASS。两个 LLM 互相博弈,自动改稿。
看一场真实的博弈:写一个 Min-Stack
cookbook 里给的实际例子——编剧(generator)和责编(evaluator)在写一个所有操作 O(1) 的栈:
真实生产里这个循环最大的危险是——evaluator 一直说 "NEEDS_IMPROVEMENT" 跑了 20 次还没 PASS,账单爆炸。必须设最大轮数,并且检测"反馈是否在重复"——如果第 5 次和第 3 次反馈几乎一样,说明陷入死循环,应该 break 出去交人工。
小测:架构决策题
Q1. 你要给同一个产品生成 3 种固定风格的营销文案:技术控版、文艺版、网红版。三种风格清单是产品经理写死的。最佳模式?
Q2. 你要让 AI 写一篇高质量的长文,并且严格控制语调("专业但不冷淡")。最适合的模式?
Q3. 你的 evaluator-optimizer 跑了 20 多轮还没 PASS,账单爆炸。最该做的两件事?
工具调用:让模型动手
Claude 自己不动手——它输出"工单"叫你的程序去做。这是 agent 唯一真的能改变外部世界的方式
Claude 不能查数据库、不能发邮件——但能"叫别人去做"
到上一模块为止,所有工序都还停留在"LLM 之间互相讲话"。Claude 自己看不见时间、查不了数据库、发不了邮件。但你可以教它"叫别人去做这些事"——这就是 tool use 的全部秘密。
大师(Claude)只能"想棋",不能离开桌子去查棋谱。你给他配几张"工具卡"——查开局库、查残局库、问引擎评分。每张卡上写清楚这张牌能干什么、需要什么参数。他想用哪张就抬手按下棋钟、说出工具名和参数。他不亲自查,他叫你去查——你查完把结果递回去,他接着想下一步。整盘棋下来按好几次钟——这就是 agentic loop。
Description 决定生死
Claude 看着工具的 description 决定"什么时候用 / 怎么用"。说明书写不清楚,工具就用不对。
JSON Schema 是合同
用 JSON Schema 描述参数形状——必填字段、类型、举例。这是 Claude 和你的程序之间的格式合同。
tool_result 必须回喂
工具执行完拿到结果后,必须把结果作为 tool_result 角色塞回对话历史,再调一次 Claude——否则 Claude 永远不知道工具结果。
max_iterations 是救命稻草
agent loop 可能跑飞——加个上限。同时检测"是不是反复调同一个工具"——这往往是死循环征兆。
第一步:写一份"工具说明书"
来自 tool_use/calculator_tool.ipynb——给 Claude 配一个简单计算器:
tools = [
{
"name": "calculator",
"description": "A simple calculator that performs basic arithmetic operations.",
"input_schema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "The mathematical expression to evaluate (e.g., '2 + 3 * 4').",
}
},
"required": ["expression"],
},
}
]
第 1 行:tools 是一个列表——意味着你可以一次给 Claude 配多张卡。
"name": "calculator"——工具的"卡名",Claude 会用这个名字来召唤它。
"description"(关键):这是 tool use 里最重要的一行——Claude 是看着这句话决定"要不要用这个工具"的。说明书写不清楚,Claude 就会用错或干脆不用。要写得像在跟一个聪明但没上下文的实习生交代任务。
"input_schema"——用 JSON Schema 描述参数形状:要一个叫 expression 的字符串。
"required": ["expression"]——强制 Claude 必须填这个参数,不许偷懒。
给 vibe coder 的一句话:tool 的 description 不是写给程序员看的,是写给 Claude 看的。
第二步:跑通 agentic loop
当 Claude 决定要用工具时,它不直接给答案——而是停下来,输出一段"工单",等你执行。然后你把结果回喂,它再继续。看一遍这个 loop 的 7 步动画:
message = client.messages.create(
model=MODEL_NAME,
messages=[{"role": "user", "content": user_message}],
tools=tools,
)
if message.stop_reason == "tool_use":
tool_use = next(b for b in message.content if b.type == "tool_use")
tool_result = process_tool_call(tool_use.name, tool_use.input)
response = client.messages.create(
model=MODEL_NAME,
messages=[
{"role": "user", "content": user_message},
{"role": "assistant", "content": message.content},
{"role": "user", "content": [{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": tool_result,
}],
}],
tools=tools,
)
第 1-5 行:第一次调 Claude,把"工具说明书清单"一起递过去。
第 7 行:Claude 没直接回答,stop_reason == "tool_use" 表示它要用工具——停下来等你执行。
第 8 行:从 Claude 的回复里挑出"要用工具的那块"——拿到工具名和参数。
第 9 行(真正干活):你的程序按 Claude 给的工具名和参数去执行——拿到结果。
第 11-22 行(关键):第二次调 Claude——把工具结果用 tool_result 这个特殊角色塞回对话历史。这一步是关键——必须保留完整对话上下文,Claude 才知道"刚才让查的结果回来了"。
真实场景里这个过程可能循环多次——直到 stop_reason ≠ "tool_use",才算完。
Spot the Bug:找出这段代码哪行写错了
下面是一段错误的 agentic loop。Claude 调用了工具但 agent 永远不会给最终答复——找出关键 bug 行:
message = client.messages.create(model=MODEL, messages=msgs, tools=tools)
if message.stop_reason == "tool_use":
tool_use = next(b for b in message.content if b.type == "tool_use")
tool_result = process_tool_call(tool_use.name, tool_use.input)
print(tool_result) # 拿到结果,打印就完事
return tool_result
"我让 AI 调一个工具,它好像调了,但最终答复还是没用工具的结果"——99% 是这个 bug:少了第二次 messages.create 把 tool_result 回喂给 Claude。print 工具结果不是答案——把结果塞回对话历史,再调一次 Claude,才有最终答复。
小测:调试 agent 的真实场景
Q1. 你给 Claude 配了一个 send_email 工具,但它从来不用——反而经常自己编邮件内容。最可能的原因?
Q2. 你的 agent 跑了 30 多次工具调用还没结束——账单爆了。最该加的两件事?
Q3. 用户问"订单 ABC123 怎么样了"。Claude 调用 get_order_details(order_id="ABC123"),但 ABC123 不存在。Claude 接下来最可能做的合理动作?
Tool use 是 Claude 唯一真的能改变外部世界的方式。它写一段文字 ≠ 它发了一封邮件——只有当你真的在 process_tool_call 里调用了发邮件的代码,邮件才会发出去。你才是动手的那个,Claude 只是动嘴。这一点理解了,你就理解了为什么所有 agent 框架的核心都是"工具循环",而不是"模型本身"。
Claude Agent SDK:从一行代码到 SRE 值班员
Anthropic 把 Claude Code 的内核以库的形式开放——同一个 SDK,从入门到生产
上一模块手写 70 行才能让 Claude 用一个计算器——Anthropic 自己也觉得太麻烦
所以他们把 agentic loop 打包成了 SDK——从 70 行变 7 行。但 SDK 真正的厉害不是省代码,而是它把 Claude Code 这个产品里所有的 agent 工程经验都开放给了你。
Notebook 00 = 第一首练习曲——右手单音、左手 do-mi-sol,5 分钟弹会。
Notebook 01 = 巴赫小品——左右手协奏,加上踏板(CLAUDE.md / Output Styles / Plan Mode / Hooks / Subagents)。
Notebook 03 = 拉赫玛尼诺夫——八度大跳、复杂踏板、还要现场即兴(MCP 工具 / 安全 hook / 远程修配置)。
同一架琴(Claude Agent SDK),同一双手,难度从入门到大师级。
WebSearch + 一句任务订单。能上网做调研。
ClaudeSDKClient + CLAUDE.md + Subagents + Output Styles + Plan Mode + Hooks。能当一家 50 人公司的参谋长。
+ MCP 服务器(Git / GitHub)+ 审计 hook。能监控 CI/CD、识别故障。
+ 写权限 + PreToolUse 安全钩子 + Prometheus + Docker 控制。能值守生产、自动诊断 + 修复 + 写 post-mortem。
第一首练习曲:7 行的 Research Agent
来自 claude_agent_sdk/00_The_one_liner_research_agent.ipynb——把模块 4 手写的 70 行 agentic loop 替换成这个:
from claude_agent_sdk import ClaudeAgentOptions, query
messages = []
async for msg in query(
prompt="Research the latest trends in AI agents and give me a brief summary and relevant citations.",
options=ClaudeAgentOptions(model=MODEL, allowed_tools=["WebSearch"]),
):
print_activity(msg)
messages.append(msg)
第 1 行:从官方 SDK 拿"提问"和"配置"两件事。
第 4 行:async for 流式接收 agent 的每一条动作和输出——像看直播一样。
第 5 行:任务订单(prompt)。
第 6 行(关键):allowed_tools 是 SDK 的安全围栏——你能精确控制 agent 能动什么、不能动什么。
第 8-9 行:把 agent 的所有动作打印 + 存档。
对比模块 4 手写 70 行的 agentic loop——SDK 把 while loop / tool dispatch / 对话历史管理全包圆了。
巴赫小品:Chief of Staff + 长期记忆
来自 claude_agent_sdk/01_The_chief_of_staff_agent.ipynb——给 50 人初创公司当 AI 参谋长:
async with ClaudeSDKClient(
options=ClaudeAgentOptions(
model=MODEL,
cwd="chief_of_staff_agent", # Points to subdirectory with our CLAUDE.md
setting_sources=["project"],
)
) as agent:
await agent.query("What's our current runway?")
async for msg in agent.receive_response():
print_activity(msg)
messages.append(msg)
第 1 行:用 ClaudeSDKClient——和上一段的 query() 不同,这个是有状态会话,能记住一整轮对话。
第 4 行(关键):cwd 把 agent 的"工作目录"指到一个子文件夹。关键魔法:那个文件夹里有一个 CLAUDE.md,agent 启动时会自动读它当背景知识。
第 5 行:告诉 SDK:去那个项目目录里把 .claude/output-styles/、.claude/commands/ 也一起加载。
第 8 行:像跟同事问话一样发问——"runway 还有多久?"
第 9 行:流式接收每一句回话。
核心洞察:CLAUDE.md = 项目级长期记忆。把公司财务数据、团队规则、历史决策写进去,agent 每次启动都自带这份"入职 onboarding 文档"——不用每次 prompt 重复。
看一场 Chief of Staff 内部分工
真实的 CoS 不是一个 agent 干所有事——它派子 agent (subagent) 去做专业领域的工作,自己只做汇总。看这场 CEO 让团队重组的对话:
SDK 的 6 大模块——你的"乐高积木"
query()
无状态一次性问答。最简单的入口,研究 agent / one-shot 任务用它。
ClaudeSDKClient
有状态会话——多轮对话、上下文累积。CoS 这种长会话场景必用。
ClaudeAgentOptions
配置中枢:model / allowed_tools / cwd / hooks / mcpServers / settings 全在这里。
CLAUDE.md
项目级长期记忆。把公司情况、规则、历史决策写进去——agent 自动加载。
Subagents
派生子 agent 处理专业领域。财务分析师、招聘官、法务顾问——每个有自己的 prompt 和工具。
Hooks
工具调用前后的拦截器——做审计、安全检查、限流。SRE agent 的"删数据库前必须人工确认"靠它。
你每天用 Claude Code 时见到的所有"魔法"——自动用工具、记得对话、规划再执行——其实就是这套 SDK。Anthropic 把内核拿出来开源后,意味着:你能把 Claude Code 那套"通用 agent 内核"复制到任何领域——AI 律师、AI 销售、AI 数据分析师……拿 SDK 嫁接,最快 1 天有 demo。
小测:从 SDK 选什么
Q1. 你想让 agent 在用一个"删数据库"工具前必须暂停让你确认。SDK 里用什么机制?
Q2. 你的 agent 经常忘记公司的"绝不直接联系客户"规则。最干净的解法?
Q3. 你看到 SRE agent demo 后想做"AI 律师助手"——查法规、起草合同、提交客户审批。最适合从哪个 notebook 起步?
进阶火候:思考 · 缓存 · Skills
三件训练外挂——把同一个 agent 从"能用"推到"生产可用"
前 5 个模块教的是 agent 的骨架——这一模块是训练外挂
到这里你已经能理解 cookbook 大部分章节。但实战里同一个 agent 经常被三个问题卡住:推理太浅 / 调用太贵 / 本领太散。这一模块的三件外挂分别治这三个症。
Extended Thinking = 起跑前的呼吸调整——赛前那 30 秒深呼吸,让大脑先预热再开跑。
Prompt Caching = 训练后的肌肉记忆——同一段路跑过 100 次,肌肉自动反应,不用再思考动作。
Skills = 教练给的训练计划包——一份现成的、能直接套用的 SOP("全马备赛 16 周计划")。
三件外挂对应三种"提速":思考更深、重复更快、本领更广。每一件都是任何 agent 都能加挂的。
Extended Thinking
治什么病:复杂推理一次想不清。
怎么打开:API 调用加一个 thinking 参数。
适合场景:数学 / 多步规划 / 风险分析。
注意:会增加输出 token 消耗。
Prompt Caching
治什么病:长前缀重复花钱。
怎么打开:API 调用加一个 cache_control 参数。
适合场景:固定 system prompt / 长文档反复问 / 工具列表。
效果:第二次起 input token 价格降到 1/10、延迟降一半。
Skills
治什么病:本领太散,复用难。
怎么打开:把"代码 + 提示词 + 资源"打包成 Skill。
适合场景:做 PPT / Excel / PDF 等专业产物。
意义:本领能跨项目、跨客户端复用。
第一件外挂:Extended Thinking——给模型一段不被打扰的草稿纸
Extended Thinking 不是模型变聪明了,是给它一段独立的草稿空间——它把推理过程写在 thinking 块里,最后才给结论。复杂推理任务直接受益。
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4000,
thinking={
"type": "enabled",
"budget_tokens": 2000
},
messages=[{
"role": "user",
"content": "Solve this puzzle: Three people check into a hotel..."
}]
)
第 1-3 行:普通的 API 调用——Sonnet 模型,最终输出最多 4000 token。
第 4-7 行(关键开关):打开扩展思考,并给它 2000 token 的"草稿纸预算"。模型会把推理过程写进一个独立的 thinking 块,再写最终答案。
第 8-11 行:你的实际问题。
调用回来后,response.content 里会有两类块:thinking 块(推理过程)和 text 块(最终答案)。
对调试 agent 行为价值巨大——你能看到模型的"内心独白",立刻知道它哪里想错了,是 prompt 没写好还是模型推理偏了。
看一遍模型的"内心独白"
cookbook 里给的经典例题——旅馆账单悖论。看 Claude 怎么用 thinking 块把推理过程摊开:
第二件外挂:Prompt Caching——一行参数省 90% 的输入 token
Prompt Caching 不是新模型,是 API 的一个 flag——把长的、不变的前缀(一本书、一份代码库、一份 system prompt)缓存住。第二次调用同一前缀时,输入 token 价格降到 1/10、延迟降一半。
write_response = client.messages.create(
model=MODEL_NAME,
max_tokens=300,
cache_control={"type": "ephemeral"}, # <-- one-line change
messages=[
{
"role": "user",
"content": str(TIMESTAMP)
+ "<book>"
+ book_content # 整本《傲慢与偏见》约 187k token
+ "</book>"
+ "\n\nWhat is the title of this book? Only output the title.",
}
],
)
第 4 行(关键):就这一行!cache_control={"type": "ephemeral"}。意思是"我允许系统把这次输入的可缓存部分缓存起来"。ephemeral 表示短期缓存。
第 9-11 行:把整本《傲慢与偏见》(约 187k token)塞进 prompt 里。如果不缓存,每次问问题都要重新输入这 187k token——账单会爆炸。
第 12 行:实际问题——只占几十 token。
第一次调用:缓存被写入,时间和不缓存差不多(baseline)。
第二次调用相同前缀:API 直接从缓存读,输入 token 价格降到 1/10、延迟降一半以上。
真实生产场景:你的 agent 每次都带相同的 system prompt(几千 token)和工具列表?打开 caching,立刻省钱。
假设你的 agent 系统每次带 10000 token 的 system prompt,每天 100 万次调用。
不开 caching:每次按全价付 10000 token 输入费 → 一天烧掉 100 亿 token 输入费。
开 caching:第一次写入按全价,之后命中按 1/10 → 一天烧掉约 10 亿 token 输入费。账单立省 90%。这是 CTO 级别的对话词汇——产品经理说"AI 太贵",你能立刻问"接 prompt caching 了吗?"
练习:把场景拖到对应的外挂上
把左边的"外挂方案"拖到右边对应的"真实场景"。
复杂数学推理 / 多步规划任务(一次答不准,需要深度思考)
客服 agent,每次对话开头都贴 5000 token 的产品 KB 当 system prompt
RAG agent:长文档(缓存友好)+ 复杂多跳问答(推理重)
一个超简单的"今天星期几" 一次性问答
小测:给真实场景挑外挂
Q1. 你的 agent 每次对话开头都要贴一份 8000 token 的 system prompt + 工具说明,对话本身只有几百 token。账单很贵。最优一招?
Q2. 你的 agent 在做"分析三季度财务报告,识别 5 个潜在风险"。回答经常浅尝辄止——只点 1-2 个明显的风险。最该试的开关?
Q3. 你想让 Claude 在网页应用里直接生成 PowerPoint 文件。最适合的机制?
整本食谱的脉络其实很简单——先认识 Augmented LLM 这个最小单元(模块 1),学会三种基础工序(模块 2)和两种高阶工序(模块 3),把"动手"机制(模块 4)打通后,用 SDK 把所有这些封装成产品级 agent(模块 5),最后用三件训练外挂(模块 6)把 agent 推到生产可用。
回到 cookbook 仓库后,你不再是看 random 一堆 notebook——你能认出每个 notebook 在这张地图上的坐标。看到 customer_service_agent → 这是 tool use 的实战。看到 contextual-embeddings → 这是 RAG 的优化变体。看到 sub-agents → 这是 orchestrator 的具体实现。
作为 vibe coder,你拿到的不只是知识——是和工程师对话的词汇:你能说出"这部分用 evaluator-optimizer 太重了,改成 chain 加 spot-check"或者"这个 agent 应该把规则放 CLAUDE.md 不要每次塞 prompt"。这就是这门课最大的价值。