Agent
Agent 是决策层,不是工具本身。它既不是某个单独的模型,也不是某个单独的工具,而是一种“围绕目标持续做决策并调用能力”的运行方式。
本质:Agent = 模型 + 工具集 + 运行循环 + 当前状态。
- 模型(Model):负责理解用户目标、分析当前局面、决定下一步。
- 工具集(Tools):负责执行动作,例如查天气、查库存、调用 API、搜索文档、访问数据库。
- 运行循环(Loop):负责让模型不是只回答一次,而是可以“想一步、做一步、看结果、再决定下一步”。
- 状态(State):负责保存本轮对话、工具返回、中间结果,有时也包含短期记忆。
Agent最常见的工作机制:ReAct
ReAct = Reason + Act
- Thought / Reason(思考):当前要做什么,先想一步。
- Action(行动):如果需要外部能力,就调用某个工具。
- Observation(观察):拿到工具返回结果。
- 继续循环或结束:如果还没完成,就继续下一轮;如果信息足够,就给出最终答案。
它不一定会把“Thought”完整显示给你看。ReAct 更应该被理解成一种工作机制,而不只是某种固定的 Prompt 模板格式
Tool和Agent关系
Tool = 能力;Agent = 决策 + 使用这些能力。
Agent使用场景
workflow 的流程基本写死,重点是“按既定步骤执行”;agent 的流程不完全固定,重点是“根据上下文动态决策”。如果流程路径是固定的,优先考虑工作流 / 链,而不是 Agent。
Agent演变
V0.3/classic路线:显式组装
- 模型;2. 工具列表;3. Prompt 模板;4. create_tool_calling_agent(…);5. AgentExecutor(…)
Agent 决策,Executor 驱动循环
优点:结构清楚、很适合教学、容易看懂 Agent 和 Executor 是怎么配合的。
局限:代码更长、容易被 Prompt、scratchpad、executor 等概念同时压住、工程上需要手动拼接更多组件。
scratchpad:专供多轮工具循环使用,相当于 Agent 的“草稿区 / 中间步骤区”,用来承接:模型上一轮决定调用什么工具;工具返回了什么;下一轮模型基于这些信息继续推理。
"""
【案例】多工具并行调用与聚合回答(V0.3:Agent + AgentExecutor)
对应教程章节:第 21 章 - Agent 智能体 → 3、Agent 工作原理(V0.3)
知识点速览:
- Tool 与 Agent 关系:Tool 提供能力(如查天气),Agent 负责决策「何时用、用哪个、如何聚合结果」。
本案例中一次问题「北京和上海哪个更热」触发多次工具调用,再由 Agent 汇总比较。
- V0.3 流程:模型 + 工具 + 提示模板 → create_tool_calling_agent 得到 Agent → 用 AgentExecutor 执行,
对应教程「3、Agent 工作原理(V0.3 视角)」:Agent 只做决策,Executor 负责真正调用工具并把结果传回 Agent。
- 关键组件:ChatPromptTemplate 定义对话结构(含 `agent_scratchpad` 占位符)、AgentExecutor 驱动循环。
- `agent_scratchpad` 可以理解成 Agent 的“草稿区 / 中间步骤区”,没有它,classic 路线下的多步推理就很难成立。
- `AgentExecutor(verbose=True)` 很适合教学和排查,它相当于一个轻量级的执行日志窗口;新版教程里补充的
`stream()` / LangSmith 则是更偏 1.x 和工程化的观察手段。
- 这个文件的核心价值不是“天气查询”,而是帮助你看清 classic Agent 是如何围绕一次问题完成多次工具调用的。
"""
import json
import os
import httpx
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
load_dotenv()
from langchain_classic.agents import create_tool_calling_agent
from langchain_classic.agents import AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
@tool
def get_weather(loc):
"""
查询即时天气函数
:param loc: 必要参数,字符串类型,表示查询天气的城市名称;中国城市需用英文名,如 Beijing、Shanghai。
:return: OpenWeather API 返回的天气信息,JSON 序列化后的字符串。
"""
url = "https://api.openweathermap.org/data/2.5/weather"
params = {
"q": loc,
"appid": os.getenv("OPENWEATHER_API_KEY"),
"units": "metric",
"lang": "zh_cn",
}
response = httpx.get(url, params=params, timeout=30)
data = response.json()
print(json.dumps(data))
return json.dumps(data)
# 初始化大模型,用于理解用户问题并决定是否调用工具、如何组合结果
llm = ChatOpenAI(
model="qwen-plus",
api_key=os.getenv("aliQwen-api"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
# 定义 Agent 的对话结构:system 定角色,human 为用户输入,
# placeholder 供 Executor 填入中间推理与工具调用记录
prompt = ChatPromptTemplate.from_messages(
[
("system", "你是天气助手,请根据用户的问题,给出相应的天气信息"),
("human", "{input}"),
(
"placeholder",
"{agent_scratchpad}",
), # V0.3 必备:Agent 的「草稿本」,记录多轮推理与工具输出
]
)
tools = [get_weather]
# 将 LLM、工具列表、提示模板组装成「可做工具调用决策」的 Agent(尚未执行)
agent = create_tool_calling_agent(llm, tools, prompt)
# AgentExecutor 负责循环:调用 Agent → 执行其选中的工具 →
# 把结果写回 agent_scratchpad → 再交给 Agent,直到结束
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 一次问题触发多工具调用(北京、上海天气)并聚合回答
result = agent_executor.invoke(
{"input": "请问今天北京和上海的天气怎么样,哪个城市更热?"}
)
print(result)
"""
【输出示例】
> Entering new AgentExecutor chain...
"""
# Invoking: `get_weather` with `{'loc': 'Beijing'}`
# {"coord": {"lon": 116.3972, "lat": 39.9075}, "weather": [{"id": 804, "main": "Clouds", "description": "\u9634\uff0c\u591a\u4e91", "icon": "04d"}], "base": "stations", "main": {"temp": 10.49, "feels_like": 8.51, "temp_min": 10.49, "temp_max": 10.49, "pressure": 1024, "humidity": 35, "sea_level": 1024, "grnd_level": 1019}, "visibility": 10000, "wind": {"speed": 0.49, "deg": 203, "gust": 0.67}, "clouds": {"all": 100}, "dt": 1773385469, "sys": {"country": "CN", "sunrise": 1773354606, "sunset": 1773397087}, "timezone": 28800, "id": 1816670, "name": "Beijing", "cod": 200}
# {"coord": {"lon": 116.3972, "lat": 39.9075}, "weather": [{"id": 804, "main": "Clouds", "description": "\u9634\uff0c\u591a\u4e91", "icon": "04d"}], "base": "stations", "main": {"temp": 10.49, "feels_like": 8.51, "temp_min": 10.49, "temp_max": 10.49, "pressure": 1024, "humidity": 35, "sea_level": 1024, "grnd_level": 1019}, "visibility": 10000, "wind": {"speed": 0.49, "deg": 203, "gust": 0.67}, "clouds": {"all": 100}, "dt": 1773385469, "sys": {"country": "CN", "sunrise": 1773354606, "sunset": 1773397087}, "timezone": 28800, "id": 1816670, "name": "Beijing", "cod": 200}
# Invoking: `get_weather` with `{'loc': 'Shanghai'}`
# {"coord": {"lon": 121.4581, "lat": 31.2222}, "weather": [{"id": 800, "main": "Clear", "description": "\u6674", "icon": "01d"}], "base": "stations", "main": {"temp": 15.34, "feels_like": 13.5, "temp_min": 15.34, "temp_max": 15.34, "pressure": 1027, "humidity": 22, "sea_level": 1027, "grnd_level": 1026}, "visibility": 10000, "wind": {"speed": 3.06, "deg": 84, "gust": 2.32}, "clouds": {"all": 0}, "dt": 1773385631, "sys": {"country": "CN", "sunrise": 1773353249, "sunset": 1773396016}, "timezone": 28800, "id": 1796236, "name": "Shanghai", "cod": 200}
# {"coord": {"lon": 121.4581, "lat": 31.2222}, "weather": [{"id": 800, "main": "Clear", "description": "\u6674", "icon": "01d"}], "base": "stations", "main": {"temp": 15.34, "feels_like": 13.5, "temp_min": 15.34, "temp_max": 15.34, "pressure": 1027, "humidity": 22, "sea_level": 1027, "grnd_level": 1026}, "visibility": 10000, "wind": {"speed": 3.06, "deg": 84, "gust": 2.32}, "clouds": {"all": 0}, "dt": 1773385631, "sys": {"country": "CN", "sunrise": 1773353249, "sunset": 1773396016}, "timezone": 28800, "id": 1796236, "name": "Shanghai", "cod": 200}今天北京和上海的天气情况如下:
# - **北京**:阴,多云,当前气温为 **10.49°C**,体感温度约 **8.51°C**,湿度较低(35%),风速较小(0.49 m/s)。
# - **上海**:晴,当前气温为 **15.34°C**,体感温度约 **13.5°C**,湿度更低(22%),风速稍大(3.06 m/s),天空无云。
# **对比来看,上海更热**,当前气温比北京高约 **4.85°C**,且阳光充足,体感也更温暖。
# 如需未来几天预报或穿衣建议,欢迎随时告诉我! 😊
# > Finished chain.
# {'input': '请问今天北京和上海的天气怎么样,哪个城市更热?', 'output': '今天北京和上海的天气情况如下:\n\n- **北京**:阴,多云,当前气温为 **10.49°C**,体感温度约 **8.51°C**,湿度较低(35%),风速较小(0.49 m/s)。\n- **上海**:晴,当前气温为 **15.34°C**,体感温度约 **13.5°C**,湿度更低(22%),风速稍大(3.06 m/s),天空无云。\n\n**对比来看,上海更热**,当前气温比北京高约 **4.85°C**,且阳光充足,体感也更温暖。\n\n如需未来几天预报或穿衣建议,欢迎随时告诉我! 😊'}V1.x:create_agent一键创建
from langchain.agents import create_agent 很多原来要自己显式组装的部分,被封装进了更统一的运行时。
create_agent 背后使用的是基于 LangGraph 的 graph-based runtime。这里的 graph-based runtime,可以看成:Agent 的执行过程不再只是“一次函数调用”,而是由一个基于状态流转的运行框架来驱动。
关联:Agent 的运行循环、ReAct(Thought→Action→Observation→循环)、以及 LangGraph 的 graph-based runtime,本质都是状态机思想——在”状态”之间按条件流转。状态机是通用的计算机科学概念,不专属于 agent,详见
../computer-architecture/state-machine.md。
与其他能力的关系(tool / skill / mcp / workflow 都要吗?)
不需要。Agent 的本质是”模型 + 循环 +(可选)工具”,skill / mcp / workflow 都是在这个骨架上按需叠加的可选层,各自解决不同问题,不是 Agent 的必备零件。
| 机制 | 解决什么 | Agent 必须吗 | 典型场景 |
|---|---|---|---|
| tool call(工具调用) | 让模型能”做动作”——吐出符合 schema 的 JSON 触发函数 | 想让 Agent 影响外部世界才需要;纯聊天不需要 | 查天气、调 API、算数 |
| MCP | 把外部能力用统一协议暴露,模型运行时发现、跨客户端复用 | 否;只在”调用方是模型 + 想要标准化/可复用连接”时需要 | 多 AI 客户端共用一个浏览器/DB 工具 |
| skill | 把领域 know-how / 可复用流程打包成 Agent 可调用能力 | 否;只在需要复用专门流程/专业知识时 | ”按规范写 commit""做代码审查” |
| workflow | 把固定步骤 / 定时任务编排自动化,路径写死 | 否;路径固定时反而优先用 workflow 而非 Agent | 每天定时跑测试、定期生成报告 |
分层的直觉
- 最俭省的 Agent = 模型 + 循环,连工具都不用(如纯摘要 bot)。
- 要做事 → 加 tool call。
- 工具来自很多外部系统、想标准化复用 → 加 MCP(见
mcp.md)。 - 有成套领域流程想复用 → 加 skill(见
skills.md)。 - 流程固定且要定时 / 批量跑 → 用 workflow(甚至不一定要 Agent)。
一句话:按”需不需要”加,不为”完整”而加。一个 Agent 可能只用 tool call、一个都不碰;也可能同时用上全部四种。区分关键在调用方是谁、流程是否固定、能力要不要复用。
延伸阅读
- tool_calling — Agent 做动作的基础机制;见
tool_calling.md - mcp — 外部能力的统一接入协议;见
mcp.md - skills — 可复用的领域流程/专业知识包;见
skills.md - slash_commands — 另一种给 Agent 加能力的方式,与 skill 互补;见
slash_commands.md - state-machine — Agent 运行循环/ReAct/LangGraph 的本质都是状态机;见
../computer-architecture/state-machine.md