# 21 - Agent 智能体

---

**本章课程目标：**

- 理解 **Agent（智能体）** 到底是什么、适合解决什么问题，以及它与 [Tool](17-Tools工具调用.md)、Function Calling、[RAG](19-RAG检索增强生成.md)、[MCP](20-MCP模型上下文协议.md) 的关系。
- 掌握 Agent 的核心工作方式：**围绕目标持续推理、决定是否调用工具、接收结果、继续决策，直到满足结束条件**。
- 理解 LangChain 中 Agent 的两条学习主线：**V0.3 / classic 的 Agent + AgentExecutor**，以及 **V1.x 的 `create_agent` + LangGraph 运行时**。
- 跑通并理解本章全部案例：`AgentSmartSelectV0.3.py`、`AgentSmartSelectV1.0.py`、`AgentReact.py`、`Agent2Agent.py`、`McpClientAgent.py`。

**学习建议：** Agent 这章先抓一句话：Agent 是决策层，不是工具本身。读的时候先看它如何决定下一步，再看 Tool、RAG、MCP、Function Calling 分别给它补了什么能力。代码部分重点比较旧的 AgentExecutor 和新的 `create_agent` 思路；读完后要能判断一个需求到底该用普通链、工具调用，还是 Agent。

**官方文档与资源**：详见 [工具导航与参考资料索引 - 工具调用、MCP与智能体](工具导航与参考资料索引.md#工具调用、MCP与智能体)。

---

## 1、Agent 简介

### 1.1 定义

先给初学者一句最重要的话：**Agent 不是某个单独的模型，也不是某个单独的工具，而是一种“围绕目标持续做决策并调用能力”的运行方式。**

如果只看最小本质，可以把 Agent 看作：**Agent = 模型 + 工具集 + 运行循环 + 当前状态**。

这里的四个部分分别代表：

- **模型（Model）**：负责理解用户目标、分析当前局面、决定下一步。
- **工具集（Tools）**：负责执行动作，例如查天气、查库存、调用 API、搜索文档、访问数据库。
- **运行循环（Loop）**：负责让模型不是只回答一次，而是可以“想一步、做一步、看结果、再决定下一步”。
- **状态（State）**：负责保存本轮对话、工具返回、中间结果，有时也包含短期记忆。

这也是本章首先要修正的一个常见误区：**不是所有 Agent 都必须同时具备“长期记忆、复杂规划、多智能体协作”。**

对很多入门场景来说，一个最简单的 Agent 只要能：1. 读懂用户目标；2. 知道什么时候调工具；3. 能根据工具结果继续判断；4. 最后给出答案。就已经是 Agent 了。

上面的公式是帮助你先抓住重点的“最小理解版”。如果把 Agent 再进一步展开，它在更完整的架构图里还可能包含记忆、规划、反思等能力。所以下图更适合理解为“扩展后的能力拼图”，而不是和上面四项定义做一一对应。

![Agent 扩展架构示意：中心为 Agent，周邻 Memory（短/长期）、Planning（反思、自评、思维链）、Tools（日历、计算、搜索等）与 Action，强调多模块协同而非最小四元组一一对应（中文）](images/21/21-1-1-2.png)

![Agent 扩展架构示意：中心为 Agent，周邻 Memory（短/长期）、Planning（反思、自评、思维链）、Tools（日历、计算、搜索等）与 Action，强调多模块协同而非最小四元组一一对应（英文）](images/21/21-1-1-1.jpeg)

### 1.2 Agent 最常见的工作方式：ReAct

**ReAct = Reason + Act。**中文可以看作：**先推理，再行动。**

这是 Agent 最经典的一种运行循环：

1. **Thought / Reason（思考）**：当前要做什么，先想一步。
2. **Action（行动）**：如果需要外部能力，就调用某个工具。
3. **Observation（观察）**：拿到工具返回结果。
4. **继续循环或结束**：如果还没完成，就继续下一轮；如果信息足够，就给出最终答案。

所以 ReAct 的本质就是：**先推理，再行动；根据行动结果，再继续推理。**

这点和普通“只回答一次”的 LLM 调用差异非常大。普通对话通常是：用户提问、模型直接回答。而 Agent 更像：用户提问、模型判断要不要查工具、调工具、看结果、再判断要不要继续、最终回答。

下图正好对应这条循环：

![ReAct 模式：思考（Thought）→ 行动（Action）→ 观察（Observation）→ 根据结果继续循环或给出最终答案的闭环示意](images/21/21-1-2-1.jpeg)

这里还要补一个现实中的重要认知：**现代 Tool Calling Agent 不一定会把“Thought”完整显示给你看。**

很多时候你在代码里真正看到的是：

- `AIMessage` 里出现 `tool_calls`
- 工具执行后得到 `ToolMessage`
- 最后再出现一个普通 `AIMessage`

所以在今天的 LangChain / Tool Calling 语境里，ReAct 更应该被理解成一种**工作机制**，而不只是某种固定的 Prompt 模板格式。

**Agent 不只有 ReAct 这一种工作方式。**

ReAct 只是最经典、最适合入门的一种理解方式，因为它最容易让人看懂“为什么 Agent 不只是一次回答”。但在真实项目里，常见的 Agent 形态还包括：

- **Plan-and-Execute**：先整体规划，再按计划逐步执行。
- **Router / Supervisor**：先判断该把任务交给哪个工具、哪个子 Agent、哪个流程。
- **Workflow + Agent 混合**：固定部分用工作流，遇到不确定节点时再让 Agent 决策。
- **Multi-Agent / A2A**：多个 Agent 分工协作，由一个主 Agent 或协调器统筹。
- **Human-in-the-loop**：关键步骤需要人工确认，Agent 不是全自动闭环。

所以更准确的结论是：

- **ReAct 是 Agent 最经典的一种工作机制**
- **不是 Agent 的唯一形态**
- **本章先用 ReAct 入门，是因为它最容易把 Agent 的核心价值讲清楚**

### 1.3 Tool 与 Agent 的关系

这一节是整章最容易混淆的地方。

**Tool（工具）** 是能力封装。

例如： `get_weather()`、`search_products()`、`check_inventory()`、`book_flight()`。它们都只是“能做一件事”的函数或接口。

**Agent** 则是决策者。

它负责判断：现在要不要调用工具、调哪个工具、先调哪个、后调哪个、一个工具够不够、工具失败了要不要重试或换路线、什么时候可以停止并给最终答案。

所以两者关系可以概括成：**Tool = 能力；Agent = 决策 + 使用这些能力。**

| 对象      | 本质定位 | 一句话理解                         |
| --------- | -------- | ---------------------------------- |
| **Tool**  | 能力层   | 我能做什么                         |
| **Agent** | 决策层   | 我什么时候、按什么顺序去用这些能力 |

也正因为如此，**不是“有 Tool 就有 Agent”**。比如“查一下北京天气”这种一步问题，直接调一个天气工具就够了，完全可以不需要 Agent。

但如果问题变成：“帮我找最热门的无线耳机，再查库存，再告诉我哪款可以买”；“先订机票，再订酒店，再预约打车”；“先查知识库，再看是否需要调外部 API，再整理成报告”。

这时就不是某个单独 Tool 能解决的了，而是需要有人负责多步决策，这时 Agent 才有价值。

### 1.4 Agent 的使用场景

这一点结合官方关于 **workflow vs agent** 的区分来理解最清楚。这里的 **workflow**，中文通常可以理解成：**工作流 / 固定流程**。指的是一条**步骤基本提前确定、执行顺序相对稳定**的处理路线，例如先做 A，再做 B，再做 C，中间不太需要模型临场决定“下一步该怎么走”。

先把它和 Agent 粗略区分开会更清楚：**workflow** 的流程基本写死，重点是“按既定步骤执行”；**agent** 的流程不完全固定，重点是“根据上下文动态决策”。**如果流程路径是固定的，优先考虑工作流 / 链，而不是 Agent。**

比如：

- 固定先做文本切分，再做向量化，再写入向量库
- 固定先分类，再摘要，再存库
- 固定按 A → B → C 的顺序执行

这类场景更像 [第 15 章 LCEL 与链式调用](15-LCEL与链式调用.md) 或后续 LangGraph 的**工作流**问题。

**如果下一步怎么做不固定，需要模型根据中间结果动态决定，就更适合 Agent。**

比如：

- 用户问题不确定，需要模型决定先查库还是先调 API
- 工具很多，模型要自己选哪个
- 可能要调一次工具，也可能要调很多次
- 工具返回后还要继续推理

所以更贴近实际项目的判断标准可以写成：

| 场景                                                   | 更适合什么  |
| ------------------------------------------------------ | ----------- |
| 路径固定、步骤明确、顺序可提前写死                     | 链 / 工作流 |
| 路径不固定、工具选择依赖上下文、中间结果会影响后续决策 | Agent       |

这也是为什么今天很多企业项目其实是：**Workflow 负责固定骨架，Agent 负责不确定节点的决策。**

从 2025-2026 年的真实项目看，Agent 的典型场景也在从“会调几个工具”扩展到更完整的任务闭环。比如：

- **浏览器自动化 / Computer Use Agent**：根据目标理解页面、点击按钮、填写表单、滚动页面，并通过截图或页面状态校验结果。
- **长任务研究代理**：围绕复杂问题拆分子任务，多轮搜索、交叉验证资料，最后生成带来源的结构化报告。
- **代码库维护代理**：读取代码库、定位相关文件、制定修改计划、编辑代码、运行测试，并根据失败结果继续修复。
- **文件 / 数据分析代理**：读取表格、PDF、日志或业务数据，调用代码执行、SQL、统计或可视化工具，输出分析结论和图表。

这些例子仍然符合上面的判断标准：如果流程完全固定，用链或工作流更稳；如果每一步都要根据中间结果继续判断，才更适合交给 Agent。

---

## 2、演变过程：从多步组装到一步创建

### 2.1 V0.3 / classic 路线：显式组装 Agent

在较早的 LangChain Agent 写法中，通常要显式准备这些部分：1. 模型；2. 工具列表；3. Prompt 模板；4. `create_tool_calling_agent(...)`；5. `AgentExecutor(...)`。

也就是说，Agent 本身只负责“出主意、决定调用什么”，真正驱动循环、执行工具、把结果写回上下文的，通常是 **AgentExecutor**。

这一套写法的优点是：结构清楚、很适合教学、容易看懂 Agent 和 Executor 是怎么配合的。

它的局限是：代码更长、容易被 Prompt、scratchpad、executor 等概念同时压住、工程上需要手动拼接更多组件。

仓库里的 `AgentSmartSelectV0.3.py` 就是这条路线的典型案例。

### 2.2 V1.x 路线：create_agent 一步创建

到了 LangChain 1.x，官方更推荐的入口是：

```python
from langchain.agents import create_agent
```

然后直接把模型、工具、系统提示等交进去，得到一个可运行的 Agent。

这种方式的核心变化不是“Agent 不循环了”，而是：**很多原来要自己显式组装的部分，被封装进了更统一的运行时。**

根据当前官方文档，`create_agent` 背后使用的是**基于 LangGraph 的 graph-based runtime**。这里的 **graph-based runtime**，可以看成：**Agent 的执行过程不再只是“一次函数调用”，而是由一个基于状态流转的运行框架来驱动。**

可以把它看成这样：

- 当前有哪些消息和中间结果，这是**状态**
- 下一步是继续让模型推理、去调工具，还是结束输出，这是**状态流转**
- 整个过程像一张“节点 + 连线”的执行图，所以叫 **graph-based**

这里不用先深入 LangGraph 细节，先抓住一句话就够了：

**`create_agent` 虽然看起来像一步创建，但底层仍然有一套负责“循环、状态、工具调用”的运行时在支撑它。**

这意味着：Agent 仍然在循环，仍然会调工具，仍然会维护消息状态。只是这些动作不再要求你手动拼出一套 `Agent + AgentExecutor + scratchpad` 才能跑。

### 2.3 这两条路线的意义

对你这套教程来说，这两条路线都值得保留，因为它们分别承担不同教学价值：

- **V0.3 / classic 路线**  
  帮助你理解 Agent 内部到底怎么转：Prompt、工具、Executor、循环是怎么配合的。

- **V1.x / `create_agent` 路线**  
  帮助你掌握当前官方更推荐的写法，更接近真实项目开发。

所以这一章不是在教你背两个 API，而是在教你：**经典写法怎么看**，**当前写法怎么用**，**二者背后的 Agent 本质其实是同一件事**。

下图就可以把这种“从组件输入到 Agent 创建”的感觉建立起来：

![三步组装智能体示意：① 定义工具（如 `tools = [get_weather]`）② 构建系统提示 ③ 调用 `create_agent(model, tools, system_prompt)` 将 Tools / Prompt / Model 收口为可运行 Agent](images/21/21-2-1-1.jpeg)

### 2.4 V0.3 与 V1.x 的对比速览

| 维度       | V0.3 / classic                                | V1.x / `create_agent`  |
| ---------- | --------------------------------------------- | ---------------------- |
| 核心入口   | `create_tool_calling_agent` + `AgentExecutor` | `create_agent`         |
| 代码组织   | 手动拼更多组件                                | 统一入口更简洁         |
| 学习价值   | 更容易看清内部结构                            | 更接近当前官方主线     |
| 运行方式   | Agent 决策，Executor 驱动循环                 | graph runtime 驱动循环 |
| 更适合     | 理解原理、维护旧案例                          | 新项目、快速搭建       |

还有一个实践补充：

**当前官方 1.x 文档更强调以 `messages` 状态作为 Agent 的统一输入。**但本教程仓库中为了教学连续性，仍保留了部分 `{"input": "..."}` 风格示例。读代码时先抓住入口含义：**它们都是在给 Agent 一个“新的用户请求”**，只是不同版本、不同适配层的调用方式略有差异。

---

## 3、Agent 工作原理（V0.3）

这一节聚焦本教程里的 classic 路线，因为它最适合拆开理解 Agent 的内部工作机制。

### 3.1 保留 V0.3 的原因

虽然今天新项目更常直接用 `create_agent`，但 classic 路线依然有学习价值。它能让你看清楚：Agent 自己负责什么，AgentExecutor 负责什么，为什么 Prompt 里要有 `agent_scratchpad`，工具结果是怎么“再喂回去”给模型继续推理的。

### 3.2 Agent 与 AgentExecutor 的职责分工

在 V0.3 / classic 路线下，职责可以直接拆成：

- **Agent**：负责分析输入、决定下一步动作
- **AgentExecutor**：负责执行动作、拿结果、继续驱动循环

也就是说：**Agent 更像大脑，AgentExecutor 更像执行器和循环调度器。**这也是为什么单独有 Agent 通常还不够，还要再交给 Executor 去跑。

下图正好适合理解这一点：

![AgentExecutor 工作流示意：左侧为消息流（Input、模型回复、History）；中间为 Agent 链（Prompt、LLM、输出解析）决定下一步；右侧为可调用的 Tool 1…n，执行结果回传并形成循环直至结束](images/21/21-3-2-1.jpeg)

### 3.3 agent_scratchpad 重要性

你在 classic 路线里经常会看到 Prompt 里有这样一个占位：

```python
("placeholder", "{agent_scratchpad}")
```

其中 `ChatPromptTemplate`、占位符与消息结构，与 [第 13 章 提示词与消息模板](13-提示词与消息模板.md) 一脉相承；这里多出来的 `{agent_scratchpad}` 专供多轮工具循环使用。

这不是装饰，它的作用非常关键。它相当于 Agent 的“草稿区 / 中间步骤区”，用来承接：模型上一轮决定调用什么工具；工具返回了什么；下一轮模型基于这些信息继续推理。

如果没有这块区域，模型就很难在多步循环里“记住刚刚自己做过什么”。

所以在 classic 路线里，`agent_scratchpad` 不是一个边角知识点，而是 **ReAct 循环能成立的重要拼图**。

### 3.4 结合案例理解

【案例源码】`案例与源码-2-LangChain框架/12-agent/AgentSmartSelectV0.3.py`

[AgentSmartSelectV0.3.py](案例与源码-2-LangChain框架/12-agent/AgentSmartSelectV0.3.py ":include :type=code")

这个案例适合用来理解 classic Agent 的完整链路，因为它把这些部分都摆出来了：

1. 定义 `get_weather` 工具
2. 定义 `ChatPromptTemplate`
3. 在 Prompt 里保留 `agent_scratchpad`
4. 用 `create_tool_calling_agent` 得到 Agent
5. 用 `AgentExecutor` 驱动执行

当用户问：请问今天北京和上海的天气怎么样，哪个城市更热？

这个案例里的 Agent 并不是“一次回答完”，而是更像这样：

1. 先判断需要查天气
2. 调一次 `get_weather(Beijing)`
3. 再调一次 `get_weather(Shanghai)`
4. 根据两次工具结果做比较
5. 最后输出总结

所以它真正演示的不是“天气查询”，而是：**一个 classic Agent 如何做多步工具调用，并把多次结果汇总成最终回答。**

---

## 4、Agent 工作原理（V1.0）

这一节对应当前官方主线，也就是你项目里更现代的 Agent 入口。

### 4.1 create_agent 的意义

`create_agent` 的最大价值不是“少写几行代码”，而是：**把 Agent 的常见配置收口到一个统一入口里。**

你通常会把这些东西交给它：模型，工具，系统提示，有时再加结构化输出、状态、中间件等。这样你可以更专注于业务目标，而不是一开始就陷进大量底层组装细节里。

![Agent 工作循环：用户目标进入 Agent，模型判断下一步，必要时调用工具并根据观察结果继续循环](images/21/21-4-1-1.png)

### 4.2 V1.x 的核心输入

在入门阶段，先看 `create_agent` 常见的 6 个参数：

| 参数              | 是否常见     | 作用                                         |
| ----------------- | ------------ | -------------------------------------------- |
| `model`           | 必备         | 让谁来做推理与决策                           |
| `tools`           | 很常见       | 给 Agent 哪些可调用能力                      |
| `system_prompt`   | 很常见       | 约束 Agent 的角色、风格和工作规则            |
| `response_format` | 很常见       | 让最终结果更适合结构化输出                   |
| `checkpointer`    | 工程化能力   | 保存运行状态，可支撑短期记忆                 |
| `middleware`      | 工程化能力   | 在模型调用、工具调用等阶段插入自定义控制逻辑 |

放到项目开发里，可以这样对应：

- `model`：模型能力怎么选
- `tools`：Agent 可以用哪些外部能力
- `system_prompt`：行为规则怎么约束
- `response_format`：输出要不要便于程序直接接收
- `checkpointer`：跨多轮调用时，状态和短期记忆怎么保留
- `middleware`：要不要加入守护、拦截、动态控制

其中前四个最适合先入门；后两个更偏工程化能力。

### 4.3 结构化输出在 Agent 中的理解

本教程里的 V1.0 案例保留了一个很有价值的知识点：**Agent 不一定只能返回自然语言，它也可以返回结构化结果。**实际项目里，我们常常不只是想“让模型说一段话”，而是希望它最终给出：JSON、TypedDict、Pydantic 对象、便于程序直接消费的字段结构。

官方文档也把 `response_format` 作为 `create_agent` 的重点能力之一。所以你可以把这个能力理解成：**Agent 不只是会调用工具，它还可以把最终答案整理成程序更容易接收的格式。**

在当前 1.x 文档语境里，`response_format` 常见有三种理解方式：

- 直接传 schema 类型：例如 `TypedDict`、Pydantic 模型，框架会按模型能力自动选择更合适的结构化输出策略。
- 显式 `ProviderStrategy(schema)`：当底层模型原生支持结构化输出时，更贴近 provider 原生能力。
- 显式 `ToolStrategy(schema)`：把结构化输出当成一次工具调用式约束，兼容性通常更好。

在入门阶段，可先把握一个基本判断：**`response_format` 不是“把字符串转 JSON”的后处理，而是在 Agent 最终输出阶段，把“结果长什么样”提前约束清楚。**

### 4.4 checkpointer、thread_id 与短期记忆

如果你给 `create_agent` 传入 `checkpointer`，底层运行时就可以把 Agent 的状态保存下来。这时 Agent 不再只是“当前这一轮问什么答什么”，而是可以在多轮调用之间保留上下文。

这里可以先把三个词对应起来：

- **checkpointer**：状态保存器，负责把运行过程中的状态记下来
- **thread_id**：一次会话或一条对话线程的标识
- **短期记忆**：在同一个 `thread_id` 下，多轮消息和状态能够被延续使用

所以可以概括成一句话：**checkpointer 决定“状态能不能存下来”，thread_id 决定“这些状态属于哪一条会话”。**

![Agent 短期记忆：thread_id 区分会话线程，checkpointer 按线程保存 messages 和 state，支撑多轮延续](images/21/21-4-4-1.png)

实际调用时，`thread_id` 通常通过运行配置传入，例如：

```python
config = {"configurable": {"thread_id": "user-001"}}
```

> **版本说明：** 在 LangChain 1.x 语境里，`thread_id` 主要服务于 checkpointing 和多轮状态隔离；如果要传用户资料、租户信息、权限上下文等静态运行时信息，还需要关注 `create_agent` 的 `context` 等上下文机制，不要把所有业务信息都塞进 `thread_id`。

这一点和 [第 16 章 记忆与对话历史（含Redis基础）](16-记忆与对话历史（含Redis基础）.md) 是连着的。第 16 章更偏“记忆是什么、怎么存历史消息”，而这里更偏“在 `create_agent` 这条 1.x 主线上，状态和短期记忆是怎么接进 Agent 运行时的”。

### 4.5 运行过程怎么观察：stream 与 LangSmith

在真实项目里，只会 `invoke()` 还不够，因为 Agent 往往不是一步完成的。

#### 4.5.1 stream() 的作用

如果 Agent 要经历：模型判断、工具调用、工具返回、再次判断、最终输出。那么直接 `invoke()` 往往要等到最后才看到结果。而 `stream()` 的价值就在于：**把中间进展实时暴露出来。**

这对实际项目非常有用，因为它能帮助你：

- 看到 Agent 到底卡在模型还是工具上
- 看到是否发生了多轮工具调用
- 给前端提供更好的交互体验
- 调试“为什么这次 Agent 没按预期走”

所以这里的区别很直接：`invoke()` 更像等最终结果，`stream()` 更像看 Agent 执行过程。

#### 4.5.2 LangSmith 与 Agent 的关系

Agent 的问题，往往不是“有没有报错”这么简单，而是：

- 为什么调了这个工具而不是那个
- 为什么多调了一轮
- 为什么结构化输出没按预期生成
- 为什么这一步耗时特别长

这类问题只看最后一句回答，通常是不够的。也是为什么官方文档会把 **LangSmith** 和 Agent 经常放在一起讲。可以把 LangSmith 先看作：**用于追踪、调试、测试、评估 Agent 运行过程的可观测平台。**

这里不用立刻深入平台细节，只要先知道：`stream()` 让你在代码层面看到过程，**LangSmith** 让你在可视化层面看到过程，两者都能帮助 Agent 开发。

### 4.6 结合案例理解

【案例源码】`案例与源码-2-LangChain框架/12-agent/AgentSmartSelectV1.0.py`

[AgentSmartSelectV1.0.py](案例与源码-2-LangChain框架/12-agent/AgentSmartSelectV1.0.py ":include :type=code")

这个案例和 V0.3 做的是同一类业务：都是围绕“北京和上海谁更热”来调用天气工具。

但它的教学重点已经变了，不再强调 Prompt + Executor 的手工组装，而是在强调：

- `create_agent(...)` 的统一创建方式
- `response_format` 带来的结构化输出
- V1.x Agent 的更简洁用法

所以读这个案例时，建议把关注点放在两件事上：

1. **为什么代码明显更短了**
2. **为什么最后可以直接拿到 `structured_response`**

这两点正好代表了 V1.x 路线的两个现实价值：更适合新项目快速搭建；更适合把 Agent 输出接进后端业务逻辑。

另外，虽然本教程里的 `AgentSmartSelectV1.0.py` 重点放在 `invoke()` 和结构化输出上，但如果放到真实项目里，你通常还会继续关心：

- 要不要用 `stream()` 展示中间进展
- 要不要用 `checkpointer` 保留短期记忆
- 要不要通过 `middleware` 做动态控制和守护

这也是为什么 `create_agent` 看起来只是一个函数，背后却能一路延展到更完整的 Agent 工程化能力。

### 4.7 V0.3 与 V1.x 结合案例对比

总体差异已在上文 **2.4 节（V0.3 与 V1.x 对比速览）** 从 API 与运行时角度概括过；下表仅对照本仓库**同一业务（双城气温比较）**下的两个文件，便于你并排阅读代码。

| 维度     | `AgentSmartSelectV0.3.py`               | `AgentSmartSelectV1.0.py`            |
| -------- | --------------------------------------- | ------------------------------------ |
| 教学重点 | 看懂 Agent + Executor 怎么配合          | 看懂 `create_agent` 统一入口         |
| 代码风格 | 显式组装                                | 一步创建                             |
| 中间机制 | `agent_scratchpad`、Executor 循环更明显 | 运行时被更高层封装                   |
| 输出重点 | 最终自然语言回答                        | `structured_response` 更适合程序处理 |

一句总结：**V0.3 更适合理解“Agent 是怎么转起来的”，V1.x 更适合理解“今天项目里通常怎么写”。**

### 4.8 Middleware 与人类审核

如果说 `tools` 是给 Agent 增加能力，`middleware` 更像是给 Agent 增加运行边界。它可以在模型调用、工具调用、状态更新等阶段插入控制逻辑。

![Agent Middleware 概念：在模型调用、工具调用和运行过程之间插入守护、改写、审核与日志逻辑](images/21/21-4-8-1.png)

常见用途包括：

- **动态提示词**：根据用户、租户、权限或场景调整 system prompt。
- **工具调用守护**：高风险工具调用前先检查参数、权限或业务规则。
- **人类审核**：删除、支付、发消息、批量修改这类动作，不让 Agent 静默执行。
- **日志与监控**：记录关键决策、工具参数、异常和耗时。

所以生产级 Agent 的重点不是“工具越多越好”，而是：能力越强，边界越要清楚。后续 LangGraph 章节会继续展开中断、人工确认和复杂状态控制。

---

## 5、实操与案例

前面 3、4 节已经把两条 Agent 主线讲清楚了，这一节把所有案例重新放回到清晰的位置里。

### 5.1 全部案例先建立一张总览表

| 案例                      | 主要学习点                         | 工具来自哪里 | 更适合放在什么语境里理解 |
| ------------------------- | ---------------------------------- | ------------ | ------------------------ |
| `AgentSmartSelectV0.3.py` | classic Agent + AgentExecutor      | 本地 `@tool` | 理解 Agent 内部工作机制  |
| `AgentSmartSelectV1.0.py` | `create_agent` + 结构化输出        | 本地 `@tool` | 理解当前 1.x 主线        |
| `AgentReact.py`           | ReAct 循环、多步工具调用、消息轨迹 | 本地 `@tool` | 理解 Agent 的动态决策    |
| `Agent2Agent.py`          | 多智能体协作 / A2A                 | 本地 `@tool` | 理解多角色分工           |
| `McpClientAgent.py`       | Agent + MCP 工具接入               | MCP 服务     | 理解外部工具接入         |

也就是说，这一章不是在用 5 个案例重复讲一件事，而是在从 5 个不同角度，把 Agent 的完整能力拼出来。

### 5.2 ReAct 实操

【案例源码】`案例与源码-2-LangChain框架/12-agent/AgentReact.py`

[AgentReact.py](案例与源码-2-LangChain框架/12-agent/AgentReact.py ":include :type=code")

这个案例可以用来建立“**Agent 会自己连续做多步决策**”的直觉。

它定义了两个工具：

- `search_products`
- `check_inventory`

用户的问题不是简单的“查某个 ID 是否有库存”，而是：查找当前最受欢迎的无线耳机并检查是否有库存。

这个问题天然要求两步：

1. 先搜索产品，找出哪个最热门
2. 再根据搜索结果，决定去查哪个产品的库存

这里最关键的地方不是工具本身，而是：**第二步依赖第一步的结果。**  
如果流程完全固定，普通工作流也能完成这件事；本案例使用 Agent，是为了观察模型如何根据第一步返回内容，自己决定第二步该查哪个产品、该调用哪个工具。

这个案例还有一个很值得保留的学习点：它会展示 `result["messages"]`，让你看到：`AIMessage`、`tool_calls`、`ToolMessage`、最终 `AIMessage`。

所以这个案例不仅适合学 ReAct，也适合学：**现代 Agent 在消息层面到底长什么样。**

---

### 5.3 A2A 实操

【案例源码】`案例与源码-2-LangChain框架/12-agent/Agent2Agent.py`

[Agent2Agent.py](案例与源码-2-LangChain框架/12-agent/Agent2Agent.py ":include :type=code")

这个案例对应的是广义上的多智能体协作，也就是 **A2A（Agent-to-Agent）**。这里的 A2A 指“Agent 之间分工协作”的思想，不特指某一个外部通信协议。

它的教学价值非常高，因为它把“一个 Agent 解决所有问题”换成了另一种思路：携程 Agent 只负责机票；美团 Agent 只负责酒店；滴滴 Agent 只负责打车；最上面再有一个总协调逻辑负责调度。

这和真实项目非常贴近。因为很多企业级系统里，往往不是“一个大而全的 Agent”，而是：

- 一个总入口
- 多个领域专长子 Agent
- 各自有边界
- 最后再汇总结果

和 LangChain 官方多智能体资料对照时要注意：**官方常见做法是把 subagent 包装成 tool，再交给主 Agent 调用。**

而本教程里的这个案例，使用的是：

- 子 Agent 链
- `RunnableLambda` 协调
- 显式顺序调度

这不是错误，而是另一种更适合教学的表达方式。它的好处是你可以更清楚地看到：子 Agent 如何单一职责；总协调如何串联业务顺序；A2A 不一定非要从“主 Agent 调子 Agent 工具”这一个套路入门。

所以这个案例更适合帮你先抓住一个重点：**多智能体协作的关键不是 API 长什么样，而是“分工 + 协调”。**

---

### 5.4 Agent + MCP 实操

【案例源码】`案例与源码-2-LangChain框架/11-mcp/McpClientAgent.py`

[McpClientAgent.py](案例与源码-2-LangChain框架/11-mcp/McpClientAgent.py ":include :type=code")

这一节把第 20 章 MCP 和本章 Agent 接上了。前面几个案例里的工具，基本都是当前进程里直接写的 `@tool`。而这个案例演示的是另一件更接近真实项目的事：**工具不一定定义在本地代码里，也可以来自外部 MCP 服务。**

它的大致链路是：

1. 读取 `mcp.json`
2. 用 `MultiServerMCPClient` 连接 MCP 服务
3. 获取 MCP 暴露出来的工具列表
4. 把这些工具交给 LangChain Agent
5. 让 Agent 在对话中继续像本地 Tool 一样使用它们

所以这个案例真正学的不是“怎么聊天”，而是：**Agent 的工具来源可以被标准化外置。**

这在实际项目里意义很大，因为很多企业系统都会走这条路线：

- 能力由后端团队封装成 MCP 服务
- Agent 侧只负责接入和使用
- 这样一个工具集可以被多个 AI 应用复用

也正因为如此，本章和 [第 20 章 MCP 模型上下文协议](20-MCP模型上下文协议.md) 是强关联的：第 20 章解决“工具怎么标准化暴露”，第 21 章解决“Agent 怎么把这些能力真正用起来”。

---

## 6、小结：Agent、Tool、Function Calling、RAG、MCP 的区别与联系

这是本章最后必须收口的一节，因为这些概念最容易混。

> **术语约定：** 为和前文保持一致，本章这里写 **Function Calling** 是因为它在很多官方资料里仍然常见；如果你更习惯 [第 17 章](17-Tools工具调用.md) 的说法，也可以直接把它理解成 **Tool Calling 这层调用机制**。

### 6.1 一张总表先记住

| 概念                 | 它解决什么问题                      | 一句话理解   |
| -------------------- | ----------------------------------- | ------------ |
| **Tool**             | 系统有哪些可调用能力                | 能力层       |
| **Function Calling** | 模型怎么把“调工具”表达出来          | 调用机制     |
| **RAG**              | 模型缺知识时怎么拿上下文            | 上下文增强   |
| **MCP**              | 工具 / 资源 / Prompt 怎么标准化接入 | 连接协议     |
| **Agent**            | 什么时候用什么能力、按什么顺序做    | 决策与编排层 |

### 6.2 在真实项目里怎么配合

一个更接近真实项目的完整链路通常像这样：

1. 用户提出一个目标
2. **Agent** 先判断该怎么做
3. 如果缺知识，就先走 **RAG**
4. 如果缺动作能力，就通过 **Function Calling** 去调 **Tool**
5. 这些 Tool 可能是本地写的，也可能来自 **MCP**
6. Agent 再根据返回结果继续判断，直到最终完成任务（需设好迭代上限与超时，避免死循环）

所以关系可以简写成：

- **Agent = 决策层**
- **Tool = 能力层**
- **Function Calling = 调用机制**
- **RAG = 上下文增强**
- **MCP = 外部能力接入协议**

---

**章节思考题：**

1. 一个任务是否需要 Agent，你会看哪些信号？

   **参考思路：** 看任务是否步骤不固定、需要多次决策、需要选择工具、需要根据中间结果调整路线。如果只是固定输入到固定输出，链或工作流通常更简单。

2. Agent、Tool、RAG、MCP 放在一起时，各自的位置是什么？

   **参考思路：** Agent 负责决策和推进任务，Tool 提供可执行能力，RAG 提供外部知识上下文，MCP 提供标准化接入方式。它们不是互相替代，而是在不同层协作。

3. 为什么 Agent 系统要设置迭代上限、超时和失败处理？

   **参考思路：** Agent 会根据中间结果继续决策，如果没有边界，可能循环调用、成本失控或执行危险动作。工程上需要有停止条件、错误兜底和日志追踪。

4. 旧的 AgentExecutor 和新的 `create_agent` 思路，你会怎样看待它们？

   **参考思路：** 旧写法有助于读懂历史代码，新写法更贴近当前 LangChain / LangGraph 主线。学习时不必纠结“谁完全替代谁”，要看项目版本、依赖和维护成本。

**本章小结：**

- **Agent 的定位是决策层**：它不是多几个 API，也不是多几个 Tool，而是让系统围绕目标判断下一步做什么。Tool 是能力层，Agent 负责协调这些能力。
- **理解 Agent，可以放回 ReAct 循环里看**：观察问题、决定动作、调用工具、拿回观察结果、继续决策。这样再去看 classic 路线里的 scratchpad / AgentExecutor，以及 1.x 路线里的 `create_agent`，就不会只剩 API 记忆。
- **LangChain 1.x 的主线要抓住三个工程点**：`response_format` 负责把最终输出结构化，`checkpointer` 负责线程状态持久化，`thread_id` 负责多轮对话和会话隔离。它们共同决定 Agent 能不能从“演示能跑”走向“系统可用”。
- **什么时候不该上 Agent 也要明确**：如果步骤固定、路径稳定、可控性要求高，优先用 LCEL 或 Workflow；只有当任务真的需要动态选工具、临场分解步骤、边执行边调整时，Agent 才值得它带来的复杂度。
- **可上线的 Agent，重点在边界而不只是能力**：工具白名单、参数校验、人审或二次确认、超时重试、日志与状态持久化，往往比“再多接几个 Tool”更重要。

**建议下一步：** 先在本地依次运行 `AgentSmartSelectV0.3.py`、`AgentSmartSelectV1.0.py`、`AgentReact.py` 和 `Agent2Agent.py`，对照文档理解 [Tool](17-Tools工具调用.md)、Agent、AgentExecutor 的配合。若要把外部能力接入 Agent，再回看 [第 20 章 MCP 模型上下文协议](20-MCP模型上下文协议.md) 中的 `McpClientAgent.py`。链式固定流程与 Agent 的取舍，可对照 [第 15 章 LCEL 与链式调用](15-LCEL与链式调用.md) 和本文 **1.4 Agent 的使用场景**。
