第 03 章

LLM 作为可调用的零件 · API surface

6 节 · Token/Context 经济学 + API 三模式(REST/Streaming/Batching)+ Production prompt as spec + Function calling/Tool use/MCP + Embedding/RAG primitive。2 个 Mode B 视频站(Karpathy tokenizer + 3Blue1Brown vector)。

约 4.5 小时(2h 原创 + 2.5h 视频)
Stage 2 开篇 · LLM 作为可调用的零件

这一章是 Stage 2 · LLM 零件 的全部内容(约 4.5 小时,2 小时原创 prose + 2.5 小时必看视频)。它把 v1 的 4 章(03 token / 04 api+prompt / 05 function+tool / 06 embedding+rag)合并成一段完整的 API surface 学习。

读完后,你看着 OpenAI / Anthropic / DeepSeek 任意一家的 SDK 文档,能用自己的话讲清它在干嘛,不再是看代码会 vibe 但说不清为什么这么写。

6 个 sub-section + 2 个视频学习站:你会拿到 Token / Context 经济学的完整地图,看 Karpathy 实现一个 mini tokenizer 视频建立机制直觉,学 REST/Streaming/Batching 三种 API 模式,理解 production prompt 不是聊天而是 spec,拿 Function calling/Tool use/MCP 三个协议 primitive,拿 Embedding 向量空间直觉(配 3Blue1Brown 视频),学简单 RAG 流程。

边界 hard line(per cross-roadmap-check):本章只教 primitive。Agent harness 设计、context engineering 工程、multi-stage retrieval、re-ranking 等全归 #8 / #3。这一章给你 vocabulary 不给 engineering。

你的 Agent 上个月烧了多少钱?

让我用一个真实场景开篇。假设你写了一个 customer support agent,接 Claude Sonnet 4.6,一个用户跟它聊了 15 分钟,大概 20 轮对话。这一段对话 cost 你多少?如果你没做任何优化,这一段对话 cost 你大约 0.48美元;如果你做了一件事(我们这一章会讲),cost大约0.48 美元;如果你做了一件事(我们这一章会讲),cost 大约 0.05 美元,差 10 倍。把这个 10 倍 scale 到你的产品:假设你有 1 万活跃用户,每人每月 10 段这样的对话,一个月 10 万段对话。优化前 48,000美元,优化后48,000 美元,优化后 5,000 美元,一年下来差 50 万美元。

这个差距来自一件你没意识到的事 —— 一个 vibe coder 听过几十次但很少真正搞懂的概念:token。所有 LLM 系统的核心约束都在 token 这一层,成本按 token 算钱、context 按 token 限长、latency 按 token 累加、prompt cache 按 token 命中。这件事为什么现在才成为真问题,放到 Ch 1 时间线上看就清楚了:GPT-3 在 2020 年就开放了 API,但那时 LLM 还是少数开发者的玩具,几乎没人 care 一次调用花多少 token;直到 ChatGPT 把用户铺到亿级、agent 又把每个任务的调用从一次变成几十次,token 成本才从无人问津变成压在每个产品头上的真实约束。你跑 Agent 烧不烧钱,90% 取决于你 understand token,还是不 understand

这一站把 LLM 作为可调用零件 的全套概念立稳。学完后你看 Anthropic API doc / OpenAI API doc / DeepSeek API 任意一家,都知道每个字段在干嘛。我们从最底层的 token 开始。


1. Token / Context 经济学

1.1 Token 到底是什么?

Token 不是字符,不是单词,是模型对文字的分词。在英语世界里,1 个 token 大约是 0.75 个英文单词;在中文世界里,1 个 token 大约是 0.5 个汉字。给你一个具体数字:"hello world" 是 2 个 token,"你好世界" 是 4 个 token。同样意思,中文 token 数比英文多 100%,这就是为什么中文 prompt 通常更贵。更具体一点,如果你的 system prompt 是 "You are a helpful AI assistant.",这是 7 个 token;翻译成中文 "你是一个有用的 AI 助手。",这是 14-15 个 token。同样的语义,中文版每次调用多花 2 倍的 token cost。

这是 LLM 经济学的第一个反直觉点。你直觉上以为中文 prompt 跟英文 prompt 差不多,事实是中文显著更贵。如果你做的是中文产品,这一点会让你在成本曲线上比做英文产品的同行高一截,除非你 understand 这一层并在 prompt 设计时主动控制中文使用。第二个反直觉点:同样一段文字,OpenAI 和 Anthropic 切出来的 token 数不一样。各家用不同的 tokenizer 算法,所以"我这段 prompt 是 1000 token" 这种说法只在指定 provider 时有意义,换 provider 通常会让 token 数变化 5-15%。

为什么 token 是 LLM 的最小单位,不是字符或单词?这是一个 design choice 的结果。早期 LLM 用 character-level(每个字符一个 token,极慢)或 word-level(每个词一个 token,但词表爆炸)。现代 LLM 用 BPE(Byte Pair Encoding)算法,一种次词级的分词,平衡了词表大小和序列长度。Karpathy 那个 2 小时 tokenizer 视频(我们待会会看到)从代码层面带你走一遍这个机制。

1.2 Context window · 模型的工作记忆上限

Token 是单位。Context window 是上限。具体说,context window = 单次 API 调用里 input + output 加起来能塞的 token 上限。常见数字:

  • GPT-4 系列:128K tokens(约 19 万字)
  • Claude Sonnet 4.6:200K tokens(约 30 万字)
  • Gemini 1.5/2.x Pro:1M-2M tokens(约 150 万字)
  • DeepSeek V3:128K tokens

Context window 像办公桌,只能放这么多张纸,要加新纸就得拿走旧的,或把旧纸的内容浓缩成一张便条。但能塞多少不是核心问题,核心问题是塞了什么后果。这三件事都和 token 数挂钩:第一是钱。input 和 output 的单价不同,长 prompt 烧钱。具体数字:Claude Sonnet 4.6 的 input 是 3每百万token,output3 每百万 token,output 是 15 每百万 token。你的 system prompt 加历史对话 = 1 万 token,平均回复 500 token,一次调用 cost = (10000 × 3+500×3 + 500 × 15) / 1M = 0.0375美元;乘以20轮对话,0.0375 美元;乘以 20 轮对话,0.75 美元,但这还没算每轮历史在累积。第二是延迟,token 越多算得越慢,前面 input token 的 prefill 阶段跟 token 数近线性相关,20K input token vs 5K input token,prefill 时间差大约 4 倍。第三是忘事,context 满了必须截断或 summarize,模型就开始忘了,如果你不主动设计 context 管理,context 一满系统就崩 —— 用户问"我前面说我叫什么?"模型答"你没告诉过我",因为那部分历史被截掉了。

回到开篇的 cost 例子。Agent 系统的成本爆炸 90% 来自不理解 token 经济。最常见的 production sin 是:把所有对话历史 + 工具结果 + 检索内容整段塞进每次 LLM 调用。第 1 轮 prompt 5K token,0.015;535Ktoken,0.015;第 5 轮 35K token,0.105;第 20 轮 180K token,$0.54 一次;第 25 轮超 context window,直接报错。月度账单超预算的根都在这里

1.3 Prompt cache · 同样的 prompt 前半截重复用,账单便宜 90%

但 LLM 经济学不是只有烧钱的故事,它也有一个省钱的核心机制。Provider(Anthropic / OpenAI / DeepSeek)会把你 prompt 中开头部分缓存起来,第二次调用如果开头一字不差地相同,缓存直接命中,这部分 token 只收 1/10 的钱,延迟也大幅降低。

让我用具体数字 unpack。假设你的 system prompt 有 10K token(详细的 agent 角色 + 工具说明 + 长 few-shot),用户每轮对话只加 200 token。没有 prompt cache 时,你每次都为 10,200 token 付全价,一段 20 轮的对话,平均每轮 cost ≈ 10,200 × 3/1M+500×3/1M + 500 × 15/1M = 0.038,20=0.038,20 轮 = 0.76。有 prompt cache 时,第一次付 10K + 200 全价,后面每次只为 200 token 付全价 + 10K 付十分之一价格,平均每轮 cost ≈ 200 × 3/1M+10,000×3/1M + 10,000 × 0.3/1M + 500 × 15/1M=15/1M = 0.011,差 3.5 倍。如果你的 system prompt 更长(50K token,production 常见),差距更大,接近 10 倍。这就是开篇 0.48vs0.48 vs 0.05 的来源。

但 prompt cache 不是免费午餐,有两个关键约束。第一,缓存有有效期,通常几分钟,不活跃就失效,所以如果你的 Agent 用户半小时回来继续聊,缓存可能已经失效,要重新付一遍全价。第二,缓存命中要求 prompt 开头部分一字不差,包括空格、换行、标点。所以不变的部分(system prompt、长例子)永远放最前,会变的部分(用户当前请求、工具结果)放最后。如果你把用户输入插在 prompt 中间,前后都没法 cache,整个 cache 机制失效。这两个约束对 Agent 设计的影响极大,Prompt cache 友好结构是 production prompt 设计的硬性要求,不是 nice-to-have。我们在 production prompt 是 spec 那一节(下面)会展开具体怎么写。

1.4 Latency · TTFT vs 总时间是两个维度

最后一个 token 经济学维度:时间。你按 Enter 到看见结果的时间,vibe coder 直觉上当成一个数字,但 production engineer 把它拆解成两个数:TTFT(Time To First Token)= 第一个 token 出现的时间,用户最关心这个;Tokens/sec = 一旦开始,每秒能出多少 token。总时间 ≈ TTFT + 输出 token 数 ÷ tokens-per-sec。

为什么要拆?因为用户体感主要来自 TTFT,不是总时间。具体类比:餐厅上菜时间 = 厨房接单(网络 + 排队)+ 烹饪时间(推理),其中第一个菜端上桌的时间对客人体感最关键 —— 只要先端上一道菜,客人就觉得开始上了,剩下慢慢端也能接受;如果你让客人干等 5 分钟看不到任何菜,即使后面菜来得很快,体感也是这家餐厅好慢。这就是为什么 ChatGPT / Claude 都用 streaming —— 它把 TTFT 从几秒降到 100ms,即使总时间一样,感觉快了 10 倍。

具体数字(2026 年常见):

  • Claude Sonnet 4.6: TTFT ~400ms · tokens/sec ~60
  • GPT-4 系列: TTFT ~500ms · tokens/sec ~50
  • DeepSeek V3: TTFT ~600ms · tokens/sec ~40
  • 小模型 / Haiku 类: TTFT < 200ms · tokens/sec > 100

记住数字本身不是目的,记住 TTFT vs 总时间是两个维度,做产品时要分别优化 才是目的。Streaming 优化 TTFT,但不省 token,不省 cost;模型选小一点可以同时降 TTFT、提 tokens/sec、降 cost,但牺牲质量。这就是 trade-off lens 的入门,Ch 7 我们会展开。

交互可视化 · agent loop
TaskLLMthinks · what now?Tool callsearch / read / writeObservationresult returnedfeed back → loop again, or stop if done
Figure · 最小 agent loop · 一个 token 反复流过三件东西

1.5 把四件合起来 · 上下文经济学

把 Token / Context window / Prompt cache / Latency 四件合起来,LLM 系统的上下文经济学就清晰了:

维度由什么决定怎么优化
每次调用成本input/output token 数 × 单价短 prompt + cache 友好结构 + 选小模型
每次调用延迟input token 数(预填)+ output token 数 ÷ speedstreaming + 控制 output 长度 + cache 命中
能塞多少历史context window 上限选大窗口模型 OR summarize 旧历史
多轮对话累积成本是否 prompt cache 命中不变的放最前 + 把动态内容放最后

这个表是 production LLM 系统的根。后面 Stage 3 / Stage 4 的大量决策都最终落到上下文经济学。每次你设计或 review 一个 LLM 系统方案,脑子里跑一遍这 4 个维度:它在 cost 轴上选了什么?在 latency 轴上选了什么?context budget 怎么管的?prompt cache 友好吗?这就是开始具备 production engineer 判断力的起点。

视频学习站 · Mode B
Let's build the GPT Tokenizer · 视频缩略图

Andrej Karpathy · Let's build the GPT Tokenizer

2h13m · YouTube · 2024
⚓ 为什么这里看

你已经知道 token 是 LLM 经济学的根。Karpathy 这一段 2 小时 13 分钟的视频把 tokenizer 内部怎么工作讲清楚,从 BPE 算法到为什么中文比英文更费 token、为什么 hello 比 Hello 多一个 token、tokenizer 怎么影响模型行为。

这一段是 Karpathy "Neural Networks: Zero to Hero" 系列的一部分,他从代码层面带你实现一个 mini tokenizer。看完你对 tokenizer 不再"知道有这东西",而是真懂它怎么工作。对 Stage 2 后面所有讨论(production prompt / function calling / RAG)都是底层 mental model

注意 Karpathy 用很多具体数字 —— 数字背后的物理直觉(13 万 token 是 6000 GPUs × 12 天的训练产物 / 2 字节每参数 / 70 亿参数等)是你建立 LLM scale intuition 的最佳起点。这种数字 + 单位 + 比较的讲解风格,本身就是你在后续章节读懂 production engineer 表达的训练。


2. API call 三种模式

视频看完了。现在你对 token 这一层有了机制级理解,不只是模型有 token 这个东西,而是字节怎么被 BPE 切成 token,token 怎么被嵌入向量空间,向量怎么被送进 transformer。下一步,我们把这层 token 经济学落到代码层:LLM API 实际长什么样,你怎么调它。LLM API 有三种主流调用模式,它们不是不同 provider 的不同 API —— 同一个 provider 的同一个 LLM 通常同时支持这三种,你按场景选。

2.1 REST · 请求 → 等结果 → 拿完整 response

REST 是最基本的 API 调用模式。你发一个 HTTP POST,等服务器算完返回完整结果,期间客户端挂着。代码长这样:

python
# Anthropic SDK 风格(简化)
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello"}]
)
print(response.content[0].text)  # 一次性拿到完整回复

这种模式的关键特征:TTFT 即总时间。用户从按 Enter 到看到任何东西,要等模型把整段话算完。如果模型在算一段长回复(2000 token,30 秒),用户在前 30 秒看不到任何反馈。听起来 UX 很差,为什么 REST 还普遍存在?因为不是所有场景都需要用户实时看到模型在打字。那么什么时候用 REST?短回复(< 200 token)TTFT 本身就快,差异不大;后台任务不需要 UI 显示模型在干嘛;简单脚本、数据处理、批量分类;测试、debug 时代码简单。什么时候不用 REST?用户直接交互的长回复,用 streaming 替代。

2.2 Streaming · 边生成边推 token

Streaming 是 REST 的改进版。模型每生成一个 token 立刻推送给客户端,客户端边接边显示。代码长这样:

python
# Anthropic SDK streaming 风格
with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "讲个故事"}]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)  # 一个一个 token 打印

Streaming 的核心价值是 TTFT 优化。它不让总生成时间变快,但让用户从等 5 秒看到一坨变成等 200ms 开始看到第一个字,体感快 10 倍。回到 1.4 节的餐厅类比 —— streaming 像厨房做好第一道菜就立刻端上来,不等所有菜做完再一起上。客人坐下 30 秒就开始吃,体感快;即使整餐 30 分钟 vs 没 streaming 也 30 分钟,体感完全不同。

何时用 streaming?用户直接交互(ChatBot / Cursor / Claude Code 等)必用;长回复必用;复杂 reasoning(让用户看到 think 过程)必用。基本上只要用户在屏幕前等,就该 streaming。实现复杂度上,前后端都要支持,后端用 SSE(Server-Sent Events)或 WebSocket 推 chunks,前端按 chunk 渲染,这比 REST 复杂一些但 production app 必备。最后一个关键反直觉:streaming 不省钱,token 数完全一样,它只省等待感。如果你听到有人说"streaming 让模型更快了",纠正他 —— streaming 让用户体感更快,模型 inference 的总时间不变。

2.3 Batching · 批量提交 + 异步等结果 · 50% 折扣

Batching 是用 latency 换 cost 的极端方案。你把多个独立请求打包提交,服务器异步处理,完成后你回来取结果,延迟换成本,通常 50% 折扣。主流 provider 都提供 Batch API:Anthropic Message Batches 让你一次提交几百到几千个请求,服务器在几小时内处理完,你 poll status 或等 webhook;OpenAI Batch API 是类似机制。那么何时用 batching?不在意延迟(几小时内出结果即可)+ 大批量(几百到几百万请求)+ 想省 50% 成本(对照常规 API 单价)。典型场景:跑历史数据分析、批量分类、大规模标注、eval pipeline、离线 report 生成。何时不用 batching?用户实时等结果(他不会等 6 小时);量小(< 50 个,常规 API 跑就行,不值得用 batch API)。

2.4 三种模式综合对照

三种模式不是替代关系,是按场景选。同一个 Agent 应用里可能同时用到三种:

维度RESTStreamingBatching
TTFT高(等完整)极低(~100ms)不适用(异步)
总时间= 总 token / speed相同数小时
成本全价全价50% 折扣
实现复杂度中(前后端都得改)中(异步 job + retrieve)
典型场景短回复 / 后台用户直接交互大批量 / 不急

你写一个 customer support agent,用户交互用 streaming,日志总结用 REST(后台跑,简单),每周分析一万条历史对话用 batching(50% 折扣)。三种模式各管一摊。


3. Production prompt 不是聊天,是 spec

知道怎么调 API 后,下一个问题是:API 调用里的那段 prompt 怎么写?这是 vibe coder 和 production engineer 差距最大的一个环节。Vibe coder 写 prompt 像跟 AI 聊天 —— "帮我写一个函数,接收用户问题,调 search 工具,返回答案"。Production engineer 写 prompt 像给演员写剧本 —— 角色、能力、行为、边界、安全、输出格式全部精确说明。DeepSeek 的 Agent Harness PM JD 把 Prompt Engineering verbatim 列为独立子学科,跟 Context Engineering 和 Harness Engineering 平级,这意味着行业认知里,production prompt 是一门工程,不是一种聊天技巧。

3.1 三种角色 · system / user / assistant

LLM 多 turn 对话由三种角色组成,这三种角色的语义你必须 grounded:system 是你(开发者)给模型的元指令,定义模型的角色、行为边界、输出格式、安全规则;user 是真实用户的输入,用户实际问什么;assistant 是模型的回复,多 turn 历史里,过往的模型回复。典型 production prompt 长这样:

json
{
  "system": "你是一个代码 review 助手。规则:1. 只评论 PR 涉及的文件 2. 输出用 markdown 3. ...",
  "messages": [
    {"role": "user", "content": "<第 1 轮用户输入>"},
    {"role": "assistant", "content": "<第 1 轮模型回复>"},
    {"role": "user", "content": "<第 2 轮用户输入>"}
  ]
}

注意三件事。第一,每次调 API,整段历史都得重发,因为 LLM stateless(见 Ch 2),多 turn 越长,token 累积越多,cost 越高。第二,system prompt 在最前面且不变,每次调用 system 一样,变的只是 messages 里的最后一段 user input,这种结构正是 prompt cache 想要的形态。第三,assistant 角色的内容是模型自己生成的,不是你写的,但你在每次调用时要把它原样带回去 —— 模型需要看到自己上一轮说了什么,才能 contextually 接下一轮。

3.2 System Prompt · 模型的 operator manual

System prompt 是最高优先级的指令,在所有用户输入之前生效。Production system prompt 通常包括 6 个部分。Role 是你是谁,"你是一个代码 review 助手",简单但必需,模型需要知道扮演什么。Capability scope 是能做什么、不能做什么,比如"你只评论 Python 代码,JavaScript / TypeScript 你说不在我能力范围",明确边界让模型行为可预测。Behavior bounds 是语气、形式、安全规则,"用专业但友好的语气。不指责式表达。涉及 security issue 时用 ⚠ 标记"。Output format 是 JSON schema、Markdown、纯文本或任何其他你后续代码要 parse 的格式,这个非常重要 —— 如果模型输出格式飘忽,你的下游 parsing 会崩。Few-shot examples 是几个标准输入输出对照,让模型学到 pattern,具体说在 system prompt 里嵌 2-5 个完整 example("用户问 X,你回答 Y"),模型学会模仿。Edge cases 是遇到 X 怎么办,"如果用户问的代码超过 500 行,告诉用户太长无法 review,建议拆成多个 PR",提前覆盖边界 case。

Production system prompt 经常很长 —— 5K-50K token,因为要覆盖各种 case。所以 prompt cache 命中率是 production prompt 设计的核心 KPI(回到 1.3 节那个 cache 机制)。类比一下:System prompt 像新员工入职手册,把角色、权责、标准动作、边界、例外一次说清,以后干活按这本手册自行决定。手册写得清,新员工干活稳定且不需要每次问主管;手册写得含糊,新员工每次行为都不一样。

3.3 Vibe prompt vs production prompt · 同任务的差距

把同一个任务,vibe coder 的 prompt 和 production 的 prompt 摆一起,差距一眼可见。Vibe 版(你用 Claude Code 写代码时随口 prompt):

text
帮我写一个函数,接收用户问题,调 search 工具,返回答案。

模型可能输出:能跑,但不稳定 —— 出错了不知道怎么 retry、没考虑工具失败、输出格式每次不同。Production 版:

markdown
# Role
你是一个研究助手,职责是基于真实检索结果回答用户问题。

# Tools available
- search_web(query: str) -> List[Result]
- read_url(url: str) -> str

# Behavior
1. 先 search,看 top-5 结果摘要
2. 如果摘要够,直接回答 + 列引用
3. 如果不够,read_url 看 1-2 个最相关页面
4. 最多调 8 次工具,超过就告知用户"无法找到充分资料"

# Safety
- 不回答与检索结果矛盾的内容
- 如果用户问题涉及医疗 / 法律 / 金融,必须加免责声明

# Output format
{
  "answer": str,
  "citations": [{"url": str, "snippet": str}],
  "confidence": "high" | "medium" | "low"
}

# Few-shot
[1-2 个完整示例,展示标准输入输出]

差别一目了然:vibe 是"做 X"让模型按自己理解做,production 是角色、工具、步骤、安全、格式、例子让模型按精确 spec 执行。这 6 倍长度不是装饰,每一段都在收紧模型行为的不确定性。Production system 要求可预测的输出,可预测性直接来自 spec 的精度

3.4 Cache-friendly prompt 结构

回到 1.3 节:prompt cache 让重复的前缀只付 1/10 价格,所以 production prompt 的结构顺序决定了 cache 命中率。永远不变的内容放最前:system role 定义(几乎永不变)、tool schema(版本稳定时不变)、few-shot examples(稳定不变)、业务规则与安全规则(稳定不变)。会变的内容放最后:用户当前的具体问题、工具的临时结果。

错误写法(每次 cache miss):

text
You are X. The user is asking: <user input>. Use these tools: <tool schema>.
                              ↑ 用户输入在中间,后面的 tool schema 没法 cache

正确写法:

text
You are X. Use these tools: <tool schema>. <few-shot>. <safety rules>.
---
User question: <user input>

把分隔线后的内容当作动态末段,前面全部稳定 cache,这样多 turn 跑下来,前 4 项保持 cache 命中,只为最后那部分付全价。这是 production prompt 设计的硬性要求,不是 nice-to-have。回到开篇的 0.48vs0.48 vs 0.05 例子 —— 那个 10 倍差距的核心来源就在这。


4. Function calling / Tool use / MCP 协议入门

到这里你已经知道 token / API / prompt 这三层。但 Agent 系统的核心能力不止回话,是用工具改变世界。这就是 function calling / tool use / MCP 这一组 protocol primitive 解决的事。放到 Ch 1 的时间线上,这一组协议是 2023 年那次工具化转向的产物(节点 ⑤:知识从全塞进权重,转向按需检索 + 工具获取)—— OpenAI 2023 年 6 月推出 function calling 之后,工具调用才从各家自己拼的 hack 变成 API 的一等公民。

本节边界 · protocol 不是 framework

关键边界(per cross-roadmap-check):本节只教 vocabulary primitives —— function calling 怎么工作、tool use 长什么样、MCP 协议是什么。不教 harness design / tool ecosystem orchestration / agent loop engineering,那归 #8 Agent 工程

如果你已经会用 LangChain / OpenAI Agents SDK 跑 tool use,本节可能感觉浅。但你需要这一节是因为后续 #8 假设你对 protocol 本身有清晰理解 —— 不是依赖框架黑盒,是看见原始协议长什么样。

4.1 Function calling · LLM 不真的调函数,它写一张想调函数的便条

这是 LLM 系统里最反直觉的一件事,所以我把它放在最显眼位置。LLM 不会真的执行任何代码,它只会输出 JSON 格式的"我想调这个函数 + 这些参数",具体执行的是你的代码。

完整流程展开成 4 步。第一步,你在 prompt 里告诉模型有什么工具,每个工具 = 名字 + 描述 + 参数 JSON Schema。第二步,模型读用户请求后,如果觉得要用工具,输出里带一段 tool_use block —— 工具名 + 填好的参数。第三步,真正执行工具的是你的代码,你读到 tool_use block 后,你的代码调真函数(可能调网络、数据库、系统命令)。第四步,执行结果作为 tool_result 塞回 prompt,模型据此决定下一步 —— 再调一个工具、直接回答、或停下。

LLMwrites tool_usetool_useyour code真正执行工具tool_resultTool resultback into promptloop until LLM says done
Figure · Agent loop 的本质就是这一圈反复

类比:像戏剧导演喊"道具组,递茶杯给演员"。导演不亲自拿茶杯,他喊出指令,真递茶杯的是道具组 —— LLM 是导演,你的代码是道具组。理解这一层,几件 production 上极重要的事情就清楚了。工具是不是 idempotent、有没有权限、会不会超时、retry 不 retry,完全在你这边决定。LLM 没法直接调用任何东西,所以也没法直接产生副作用 —— 一切副作用都经过你的代码。LLM 输出的 tool 调用是建议不是命令,你的代码可以拒绝执行、改参数、改顺序,这给了你完全的控制权。一切 Agent 安全、权限、cost 控制,都落在执行前你截断这层 —— 这不是模型的责任,是你的责任。

4.2 Tool Schema · 工具的 spec

工具的 schema 是模型决定要不要调 + 怎么填参数的全部依据。典型 JSON Schema 格式:

json
{
  "name": "search_web",
  "description": "Search the web and return the top 5 result summaries. Use this when the user asks about current events or anything outside your training data.",
  "input_schema": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "Search query in natural language"
      },
      "num_results": {
        "type": "integer",
        "default": 5,
        "minimum": 1,
        "maximum": 20
      }
    },
    "required": ["query"]
  }
}

模型看到这个 schema 后,能自己决定:何时调这工具(根据 description)+ 怎么填参数(根据 input_schema)。写好工具 schema 的几条 production 经验。Description 比 name 更重要 —— 模型主要靠 description 判断现在该不该调,一个清楚的 description("Use this when the user asks about current events or anything outside your training data") 比好的命名更影响模型行为。参数 schema 越具体越好,限定 enum / 给 default / 写 description,减少模型填错,如果参数有合法取值范围,在 schema 里限定。除了单个工具的 schema,工具集整体也有讲究:工具数量不要超过 ~30,太多模型 attention 散开,容易选错工具;production 系统经常组织一个 router agent 先决定大类,再让 specialist agent 用更少的工具集。互斥工具不要并存,save_filewrite_file 都有时模型摇摆,合并或明确区分语义。这些只是入门,production tool schema design 归 #8(包括 tool composability、schema versioning、测试覆盖等)。

4.3 Tool Use · 模型决策 + 你执行的协议循环

把 function calling + tool schema 拼起来,完整 tool use 一次循环长这样:

python
# 1. 准备工具
tools = [
    {"name": "search_web", "description": "...", "input_schema": {...}},
    {"name": "read_file", "description": "...", "input_schema": {...}}
]

# 2. 让模型决策
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "今天 OpenAI 发了什么新闻?"}],
)

# 3. 检查模型是不是要调工具
if response.stop_reason == "tool_use":
    tool_block = next(b for b in response.content if b.type == "tool_use")
    tool_name = tool_block.name      # "search_web"
    tool_args = tool_block.input     # {"query": "OpenAI news today"}

    # 4. 你的代码真去执行
    result = call_actual_tool(tool_name, tool_args)

    # 5. 把结果塞回去,继续 loop
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        tools=tools,
        messages=[
            {"role": "user", "content": "今天 OpenAI 发了什么新闻?"},
            {"role": "assistant", "content": response.content},  # 包含 tool_use block
            {"role": "user", "content": [{
                "type": "tool_result",
                "tool_use_id": tool_block.id,
                "content": result,  # 工具的实际返回
            }]}
        ],
    )

# 6. 模型基于工具结果回答(可能再调工具,或 final answer)

这是协议层。Production Agent 不会这么写,会包成 framework 的 agent loop(LangChain / OpenAI Agents SDK / claude-agent-sdk 等)。但理解协议层是看穿 framework 黑盒的基础 —— 当 framework 出 bug 时你知道在哪一层,当 framework 升级时你知道核心是否变了。10 年内 framework 会换好几代,但 function calling / tool use 协议的核心形态会稳定。投资理解协议本身,而不是某个 framework 的 API

4.4 MCP · Model Context Protocol · 工具生态的统一协议

MCP(Model Context Protocol)是 2024-2025 年由 Anthropic 推出的开放协议,让 LLM host(Claude Desktop / Cursor / Codex 等)和工具、数据源之间通信标准化。为什么需要它?没有 MCP 时,每个 host 自己定义工具接口,你在 Claude Desktop 写的工具不能直接给 Cursor 用、给 Codex 用,生态碎片化,工具作者要为每个 host 写一遍 adapter。有 MCP 后:你写一次 MCP server(暴露你的工具或数据源),所有支持 MCP 的 host 都能用 —— 一次写、多家用,等于 USB 之于硬件,等于 LSP 之于编辑器。2026 现状:Anthropic Claude / Claude Code 原生支持,OpenAI Codex 原生支持,Cursor 支持,DeepSeek Agent Harness PM JD verbatim 列 MCP 为 PM baseline。主流 AI host 都在跟进。

一个最小 MCP server 长这样(伪代码):

python
from mcp import Server, Tool

server = Server(name="my-tools")

@server.tool(
    name="get_weather",
    description="Get current weather for a city",
)
async def get_weather(city: str) -> str:
    return f"{city}: 25°C, sunny"

server.run()

启动后,在 Claude Desktop / Cursor / Codex 等配置里加上这个 server URL,这些 host 全部能直接调 get_weather 工具,不需要任何额外集成代码。

三个关键反直觉点。第一,MCP server 不是给 LLM 调的,是给 host(Claude / Cursor 等)调的,host 把 MCP server 暴露的工具注册到模型可见的 tool schema 列表里,然后走 4.3 节那个 tool use 协议循环。第二,MCP 不是替代 function calling,是建立在 function calling 之上的 —— MCP server 暴露的还是工具(函数),只是用标准协议让 host 都能发现。第三,MCP 也支持 resources(数据源)和 prompts(模板),不只工具,所以 MCP 协议入门严格来说包括三种 primitive,只是工具部分最常用。MCP server 怎么 production 设计、怎么 orchestrate 多个 MCP server、怎么管理 MCP server 安全权限,这些归 #8 Agent 工程。foundations 只让你听到 MCP 不再脸盲。


5. Embedding · 把一句话压成一个高维向量

到这里你已经拿到 LLM API surface 的核心(token / API / prompt / tool)。最后一组 primitive 是 embedding 和 RAG,它们是 Agent 系统跟真实知识库对接的桥梁。Embedding 是一个独立的小模型,输入文本,输出一个固定维度的向量,常见 768 / 1536 / 3072 维。关键性质:意思相近的文本,它们的 embedding 向量距离也近,通常用 cosine similarity 衡量。"猫坐在垫子上" 和 "垫子上有只猫" 的 embedding 几乎重合;但和 "今天股市跌了 5%" 的 embedding 离得很远。

① "猫坐在垫子上"② "垫子上有只猫"③ "股市跌 5%"embeddingtext → vectorVECTOR SPACE↑ dim 2dim 1 →d ≈ 0.1(近 · 含义相近)d ≈ 0.9(远 · 含义无关)
Figure · 意思近的句子在向量空间聚成一团 · 实际 1536 维,这里投影到 2D

类比:像给每个句子盖一枚指纹,指纹是 1536 维的数字组合,意思像的句子指纹也像,可以直接用数学算距离。两个关键反直觉点。第一,embedding 不是 LLM —— 它只输出向量,不生成文字;模型小得多、便宜得多(Anthropic 的 voyage-3-large 大约 LLM 的 1/30 价钱)。第二,距离近不等于意思一定相同,只是足够相关,真要判断是不是同一件事,还得跟一遍 LLM —— embedding 给你 candidate,LLM 做最终判断。

典型用途:RAG(知识检索)用户问题 → embedding → 在文档库里找 cosine 最近的 N 段 → 把这 N 段塞进 prompt 给 LLM 回答;语义去重与聚类看两段内容是不是实质相同;语义记忆让 Agent 把历史压缩成 embedding 存起来,需要时按相关度检索。为什么这件事在 Agent 系统里关键?你听到的向量数据库 / Vector DB / Pinecone / Weaviate / Chroma / Milvus / Qdrant 这一整套基础设施,都是为了存储和检索 embedding 服务的。理解 embedding 这一个概念,后面架构层"向量数据库该怎么选"就有了判断锚,而不是只能照搬别人的方案。

视频学习站 · Mode B
What does it mean for computers to understand language? · 视频缩略图

3Blue1Brown · What does it mean for computers to understand language?

22min · YouTube · 2024
⚓ 为什么这里看

你已经知道 embedding 把文本压成向量、意思近的距离近。3Blue1Brown 这一段 22 分钟视频从几何直觉角度讲清楚 —— embedding 空间里的方向对应语义维度(king - man + woman ≈ queen 这种经典展示就在这里),为什么 cosine similarity 是衡量距离的标准。

视频是 "But what is a GPT?" 系列的一部分,Grant 的可视化是业界最佳的几何直觉教学。看完你对 vector space 的几何感会从知道有这么个东西升级到能想象 1536 维空间里 embedding 是怎么排布的 —— 这对接下来 RAG 流程的理解很重要。


6. 简单 RAG 流程

本节范围 · primitive 不是 production system

本节教 RAG 三步最小流程。teaching-tokens cross-roadmap discipline 要求 foundations 教 primitive,不教 production engineering。

具体:教 ✅ RAG 三步流程、一个最小例子、常见反 pattern 列表;不教 ❌ multi-stage retrieval、re-ranking、hybrid search、向量数据库选型、检索质量 eval。后者属于 #3 数据工程 + #8 Agent 工程。

6.1 为什么需要 RAG

回到 Ch 2 的 LLM 心智模型:LLM 不知道事实,只按概率续写,所以会 hallucinate;LLM 的训练数据有 cutoff,不知道训练后发生的事;LLM 不知道你公司的内部知识、你的项目 README、你的 PR 历史。这三件事是 LLM 自身能力的硬上限。RAG(Retrieval-Augmented Generation · 检索增强生成)是绕过这三个上限的标准方法 —— 你把相关资料找到,作为 context 塞进 prompt,LLM 看着资料生成,不再凭空猜。RAG 不是天生的标准做法:它的核心想法早在 2020 年就有论文(Lewis et al),但真正从研究想法变成 production 标配,是 2023 年大量产品撞上这两堵墙(训练 cutoff + 幻觉)之后——对应 Ch 1 节点 ⑤ 那次工具化转向。类比:RAG 像带书的开卷考试,LLM 是学生,但允许翻参考资料(top-K chunks),学生答题时引用资料 = grounding。这跟闭卷凭记忆答题形成对照,后者就是 LLM 默认行为,经常 hallucinate。

6.2 RAG 的三步最小流程

text
1. 索引(offline 一次性 / 增量)
   文档 → 切片(chunk)→ embedding → 存入 vector store

2. 检索(每次提问)
   用户问题 → embedding → vector store 找 top-K 最近的 chunk

3. 生成
   把 top-K chunks + 用户问题塞进 prompt → LLM 生成答案

把它落到代码层,一个最小例子(伪代码):

python
# 1. Index — offline
documents = load_my_pdfs("./company_docs")
chunks = [chunk for doc in documents for chunk in split_into_chunks(doc, size=500)]

embeddings = embedding_model.embed_batch([c.text for c in chunks])
vector_store.upsert(chunks, embeddings)

# 2. Retrieval — 每次提问
def answer(user_question):
    query_embedding = embedding_model.embed(user_question)
    top_chunks = vector_store.search(query_embedding, k=5)

    # 3. Generation
    prompt = f"""
    根据下面的资料回答用户问题。如果资料里没有,说"不知道"。

    资料:
    {format_chunks(top_chunks)}

    问题:{user_question}
    """
    return llm.complete(prompt)

这是 RAG 的骨架。Production RAG 会比这复杂很多,检索可能多路(BM25 + 向量 + metadata filter)+ re-ranking + 引文标记 + 去重;生成可能多轮 refinement + structured output + 引用回填;评测可能 retrieval recall/precision + 生成质量 eval + 回归测试。但这些都是 #3 数据工程 + #8 Agent 工程的内容。foundations 给你心智模型,具体 production 模式以后再学。

6.3 RAG 的几个常见反 pattern

你设计 RAG 时如果出现这几个,需要意识到不对。第一,整段文档塞进 context(没切 chunk),浪费 token + retrieval 失去意义,文档太长应该切成 200-500 token 的 chunks,然后只取相关的几个。第二,chunk 大小固定不变(没考虑文档语义结构),该切的地方不切、不该切的地方切,比如把一段完整代码切成两半,模型看不全这段代码,production RAG 用 semantic chunking(按段落、heading、代码块边界切)。第三,top-K 一刀切 K=10(没看检索分布),有时 top-2 就够,有时 top-20 才覆盖,盲取一个数往往不对。第四,检索结果不去重,5 个 chunk 全来自同一段,信息冗余,production 系统通常加一层 dedup 或 diversity filter。第五,没记录 retrieval 哪些被命中,模型答错了不知道是 retrieval 错,还是 generation 错,observability 是 RAG debug 的核心(Ch 6 我们会展开 observability)。这 5 个反 pattern 你不要立刻解决,但要记住它们存在 —— 后面 Stage 4 的 evals + observability 会教你怎么发现 + 评估这些问题。

6.4 Embedding / RAG 之外

本章只是 vocabulary primitive。完整的 retrieval engineering 包括:

Topic归属
Multi-stage retrieval (BM25 + vector + filter)#3 数据工程
Re-ranking models(用更强 model 重排 top-K)#3 数据工程
Hybrid search 设计#3 数据工程
Retrieval quality eval(recall / precision / NDCG)#3 数据工程 + #6 后训练
RAG fine-tuning(基于反馈训练 embedding 或 reranker)#6 后训练
Vector DB 选型决策 / 容量规划#2 系统设计
Agent 的 long-term memory 设计(用 embedding 当 memory)#8 Agent 工程

学完 foundations 后这些方向都对你开放。


收官 · 你现在在哪 + 下一站

回头看看你这一章走了什么。你拿到了 LLM 经济学的核心 —— token 是单位,context window 是上限,prompt cache 是省钱机制,latency 拆 TTFT 和总时间,这套上下文经济学是 production LLM 系统所有决策的根。你看了 Karpathy tokenizer 视频,把 token 是分词这一抽象具体到代码层面,知道 BPE 怎么工作、为什么中文比英文更费、tokenizer 怎么影响模型行为。你拿到了 API 三种调用模式 REST / Streaming / Batching 各管一摊,能根据场景选对。你理解了 production prompt 是 spec 不是聊天 —— 6 部分结构 + cache-friendly 顺序,这是 vibe coder 到 production engineer 转型的核心心智改变。你拿到了 function calling / tool use / MCP 三个协议 primitive —— LLM 不真的调函数它写想调便条,完整 tool use 一次循环长什么样,MCP 让生态统一。你看了 3Blue1Brown 向量空间视频,有了 embedding 的几何直觉,你拿到了简单 RAG 三步流程 —— 索引 + 检索 + 生成,以及 5 个常见反 pattern。

这是 Stage 2 的核心 vocabulary 全部完成。你看着任何一家 LLM API 文档应该都能 grounded,不再陌生。Karpathy 视频带给你的 mechanism 直觉 + 3Blue1Brown 视频带给你的几何直觉,加上 4 个 sub-section 的 production engineering 概念,合起来是"LLM 作为可调用零件"这一层的完整图景。

下一章(Ch 4)进入 Stage 2 的最后一章 · Agent 范式入门 · 词汇 + 三子学科。我们把单次 LLM call(Ch 3)升级到自主决定下一步动作的循环系统。13 个 Agent vocabulary primitives + Prompt / Context / Harness Engineering 三子学科定位 —— 只教 vocabulary,engineering 归 #8。

你完成了 foundations 的第 2 个心流断点。合上书,去练 —— 用 Karpathy 视频教的机制重读你之前写过的几个 prompt,看看哪里 cache 不友好;用 RAG 三步流程试着搭一个最简单的 doc query 工具。下次回来,带着扎实的 API surface 直觉进 Ch 4。


本章术语速查

Token / Context 经济学:Token 模型最小单位 · Context window 单次调用 token 上限 · Prompt cache Provider 缓存前缀,命中 1/10 价 · TTFT 首 token 时间 · 上下文经济学 Token/Context/Cache/Latency 联动

API 模式:REST 同步等结果 · Streaming 边生成边推 · SSE streaming 常用底层 · Batching 异步批量 50% 折扣 · Multi-turn 多轮对话,每次重发完整历史

Production prompt:System / User / Assistant role 三种角色 · System Prompt 高优元指令 · Few-shot prompt 里的示范示例 · Production Prompt 写成 spec · Cache-friendly Prompt 不变内容放最前

Tool use:Function Calling LLM 输出想调函数的 JSON · Tool Schema 工具的 spec · JSON Schema 描述结构的标准 · tool_use block / tool_result · Tool Handler 执行工具的代码 · Structured Output 让模型按 schema 输出 · MCP Model Context Protocol · MCP Server / Host 暴露/消费工具的两端

Embedding / RAG:Embedding 文本压成向量 · Vector 数字列表 · Cosine similarity 距离度量 · Vector Database 存检 embedding 的库 · RAG 检索 + 生成 · Chunk 文档切片 · Top-K 取最近 K 个 · Grounding 让回答基于真实来源

进阶词汇(harness / context engineering / re-ranking 等)归 #8 / #3,见 术语表 /glossary


参考文献

Mode B · 必看视频(本章路径必经)

  • Andrej Karpathy · Let's build the GPT Tokenizer · 2h13m · YouTube · 2024 Tokenizer 内部怎么工作 + BPE 算法 · 代码层面带你实现 mini tokenizer · 看完对 Stage 2 后续所有讨论(prompt / tool use / RAG)都有底层 mental model

  • 3Blue1Brown · What does it mean for computers to understand language? · 22min · YouTube · 2024 Embedding 几何直觉 + vector space 的方向语义 · "But what is a GPT?" 系列一部分

Mode A · 引用出处(正文 inline,集中列在此)

深 dive 资源(可选 · 想往下走再看)

Prompt Engineering 进阶:

Tool Use 进阶:

Embedding / RAG 深 dive(归 #3 数据工程,这里仅 reference):

  • "Designing Data-Intensive Applications"(Kleppmann) · 数据系统设计经典
  • Vector DB 各家文档 · Pinecone · Weaviate · Chroma · Qdrant

下一章

下一章(Ch 4)收官 Stage 2 · LLM 零件 · Agent 范式入门 · 词汇 + 三子学科。13 个 DeepSeek JD 显式列的 Agent vocabulary primitives + Prompt / Context / Harness Engineering 三子学科定位。本章只教 vocabulary,engineering 归 #8。