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路线:显式组装

  1. 模型;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、一个都不碰;也可能同时用上全部四种。区分关键在调用方是谁、流程是否固定、能力要不要复用

延伸阅读