第 5 节 · LLM + 外部支持:RAG、工具与上下文工程
LLM 的"天生不知道"

回忆第 1 节的类比:LLM 就像一个住在图书馆里、读过很多书但没有手机的人。只调用裸模型 API 时,下面四类信息它无法可靠获得:
| 类型 | 例子 |
|---|---|
| 实时信息 | 现在几点 / 今天天气 / 某只股票的当前价格 |
| 私域知识 | 你公司的内部文档 / 你仓库里的代码 |
| 训练截止后的事 | 最新的新闻、论文、政策 |
| 执行结果 | 跑一段代码会输出什么 / 调一个 API 返回什么 |
不管模型多大、多聪明,这四类信息都不应该靠"模型参数里会不会碰巧记得"来解决。工程上更可靠的做法是:把信息放进上下文,或者让模型调用工具去获取。
NOTE
这里说的是裸模型 API(如 chat.completions.create)。商用产品(ChatGPT、Gemini、Perplexity 等)通常已帮你接好搜索、代码执行等工具,所以用户感知上模型"好像什么都知道"——但底层仍然是"模型 + 工具"的组合。
2026 关键词:Context Engineering
现在业界越来越常用 Context Engineering(上下文工程) 来概括这件事:不是只写一句 prompt,而是系统地决定模型每次能看到什么、能调用什么、历史怎么压缩、结果怎么验证。RAG 和 Tool Use 都是上下文工程的一部分。
方式 1:把相关信息塞进 Prompt(朴素 RAG)
最直接的解法:你手动把外部信息拼进 user message,让模型基于它来回答。
# 示例:让模型回答内部政策问题
internal_doc = open("internal_policy.md").read()
messages = [
{"role": "user", "content": f"""
请根据以下内部文档回答问题。
【文档内容】
{internal_doc}
【问题】
报销流程是什么?
"""}
]模型本来不知道你公司的报销流程,但你把资料喂给它了,它就能准确回答。
这就是 RAG(Retrieval-Augmented Generation,检索增强生成) 的最朴素版本:
- Retrieval(检索):你手动找到相关文档
- Augmented(增强):把文档塞进 prompt
- Generation(生成):模型基于文档生成回答
更完整的 RAG 系统一般会自动完成这条流水线:
用户问题 → 检索知识库 → 取回相关片段 → 拼进上下文 → 让 LLM 基于证据回答优点
- 简单粗暴,立竿见影
- 任何 LLM 都支持(只要支持 chat 接口)
- 可以要求模型给出引用,方便回到原文核查
缺点
- 需要你手动决定塞什么信息
- 信息量大时会撑爆上下文窗口(回忆第 3 节)
- 模型自己无法主动"去找"它需要的信息
- 检索错了,模型会基于错误上下文认真回答
- citation 只能帮助核查,不等于答案一定正确
2026 年生产级 RAG 很少只做"向量库 top-k 检索"。常见做法会加入关键词检索、重排序、权限过滤、来源追踪、低置信度拒答等环节。
方式 2:让 LLM 自己调工具
更进一步:不是"你帮模型找信息",而是让模型自己决定该查什么工具。
这里的"工具"不是让模型随便访问世界,而是你的程序暴露出来的一组受控函数。它们大致分两类:
| 类型 | 例子 | 特点 |
|---|---|---|
| 查询型工具 | 搜索、读文件、查数据库、取当前时间 | 把外部信息带回上下文 |
| 行动型工具 | 发邮件、下订单、改文件、调用业务 API | 真的改变外部世界,必须更谨慎 |
流程大致是这样的:
用户: "现在是北京时间几点?"
↓
LLM 思考: "我不知道当前时间,但我有一个 get_current_time 工具可以用"
↓
LLM 输出: tool_call: get_current_time()
↓
你的程序执行工具,拿到结果: "2026-05-25 15:20:00 北京时间"
↓
把结果传回给 LLM
↓
LLM 组织自然语言回复: "现在是北京时间 15:20。"
↓
返回给用户这就是 Agent 的雏形——模型不再是被动等你喂信息,而是主动伸手向外部世界获取信息。
Day2 会详细实现这套机制(Function Calling 协议)。今天先理解"为什么需要它"。
工具标准化:MCP
Anthropic 在 2024 年底开源了 MCP(Model Context Protocol)。到 2025-2026 年,它已经成为 AI 应用连接外部工具和数据源的重要开放标准。MCP 定义了一套统一的工具、资源和提示模板接口,让同一套能力可以被不同客户端复用——类似于"USB 接口标准"让不同设备能插同一根线。
本课程的 Function Calling 实现与 MCP 的核心思想一致(工具注册 → 模型选择 → 执行 → 返回结果)。先学会最小工具调用,后面再理解 MCP 会轻松很多。
两种方式的对比

| 朴素 RAG(塞进 prompt) | Tool Use(模型自己调) | |
|---|---|---|
| 谁决定查什么 | 开发者/检索系统 | 模型自己 |
| 灵活性 | 低(写死了查什么) | 高(模型按需选择) |
| 复杂度 | 极低 | 需要实现工具调度 |
| 适用场景 | 固定文档问答、政策解释、资料总结 | 实时查询、精确字段、执行动作 |
| 主要风险 | 检索不到 / 检索错 / 上下文太长 | 选错工具 / 参数错 / 行动越权 |
在实际工程中,两种方式经常组合使用,这种融合常被称为 Agentic RAG:
- 模型自己决定什么时候需要检索、检索什么关键词
- 检索不到时自动切换到其他工具(搜索引擎、代码执行、API 调用)
- 检索到的内容不够好时,模型会重新构造查询再试一次
传统 RAG 是"开发者写死检索逻辑",Agentic RAG 是"模型自主驱动检索循环"——后者正是本课程要构建的 Agent 能力。
但 Agentic RAG 不是魔法。它仍然需要权限控制、循环上限、结果核查和可观测日志,否则就会变成"模型带着工具乱跑"。
核心类比:LLM 是大脑,工具是手脚
| 角色 | 类比 |
|---|---|
| LLM | 一个聪明但被关在房间里的人——能推理、能总结,但看不到外面 |
| 工具 | 给他一部手机(查天气)、一个文件柜(读文档)、一台电脑(跑代码) |
| Agent | 聪明人 + 一套工具 + "自己决定该用哪个" 的能力 |
整个 Agent 开发的核心工作就是:
- 给 LLM 配什么工具
- 怎么描述这些工具让 LLM 能正确选择
- 怎么管理上下文不让它爆
- 怎么限制危险动作、验证工具结果并记录日志
动手试试
运行 demo_05_with_tool.py,会看到两组前后对比实验:
实验 1:内部知识
- ① 直接问"课程截止时间" → 裸模型没有这份内部文档
- ② 把内部文档塞进 prompt → 模型立刻精确回答
实验 2:当前时间
- ① 只问 LLM"现在几点" → 它无法从参数里知道当前时间
- ② 给它
get_current_time()工具 → 模型自己决定调用工具再回答
看到的关键结论:LLM 不是不能解决这些问题——它只是需要可靠上下文,或者需要受控工具去获取上下文。
回顾:Day1 的完整拼图
1. Next-token prediction → 解释了 LLM "怎么思考"
2. 三种角色 + messages 协议 → 解释了 "怎么跟它说话"
3. Token + 上下文窗口 → 解释了 "成本与物理限制"
4. 幻觉 → 解释了 "它的弱点"
5. 外部支持 → 解释了 "怎么补强弱点" ← 通往 AgentDay1 完整回答了"LLM 是什么、能做什么、不能做什么"。
从 Day2 开始,我们把第 5 节中"手动喂信息"的过程自动化——让 LLM 自己决定调哪个工具、怎么调、调完怎么用。这就是 Function Calling 协议,也是 Agent 的核心机制。
小结
| 概念 | 一句话理解 |
|---|---|
| LLM 的天然边界 | 不知实时、不知私域、不知训练后的事、不能执行 |
| 朴素 RAG | 手动把信息塞进 prompt |
| Tool Use | 让 LLM 自己决定调什么工具 |
| Context Engineering | 管理模型能看到、能调用、能记住和能验证的信息 |
| MCP | 连接模型应用与外部工具/数据源的开放协议 |
| Agent | LLM + 工具 + 自主决策能力 |
下一步(Day2):实现 Function Calling——让 LLM 自己选工具、调工具、用结果回答用户。