Skip to content

Agent 智能体

Tool VS Agent

Langchain 中的 Tool 和 Agent 是两个不同层次的概念,各自承担不同的职责

Tool:工具 = 能力的封装

Tool是一个可调用的函数,它封装了一个具体的能力,类似Java中的Util工具类:

  • 调用搜索引擎
  • 查询数据库
  • 运行python代码
  • 调用API

Tool 本身没有决策能力,它只是被动地等待被调用。

Agent:决策者 = 如何使用这些能力

Agent 是一个决策引擎,决定什么时候调用哪个 Tool,根据上下文决定下一步做什么,处理 Tool 返回的结果并决定是否需要继续调用其他 Tool。

Agent 的核心是 推理 + 行动(Reason + Act),也就是 ReAct 模式

Agent = LLM + Memory + Tools + Planning + Action

image-20260512145428037

python
import os

from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from langchain.tools import tool
from pydantic import SecretStr
from dotenv import load_dotenv

load_dotenv()

# 模拟产品数据库
PRODUCT_DATABASE = {
    "无线耳机": [
        {"id": "WH-1000XM5", "name": "索尼 WH-1000XM5", "popularity": 95, "price": 299},
        {"id": "QC45", "name": "Bose QuietComfort 45", "popularity": 88, "price": 329},
        {"id": "AIRMAX", "name": "苹果 AirPods Max", "popularity": 92, "price": 549},
        {"id": "PXC550", "name": "森海塞尔 PXC 550", "popularity": 76, "price": 299},
        {"id": "HT450", "name": "JBL Tune 760NC", "popularity": 82, "price": 99},
    ],
    "游戏鼠标": [
        {"id": "GPW", "name": "罗技 G Pro 无线", "popularity": 90, "price": 129},
        {"id": "VIPER", "name": "雷蛇 Viper V2 Pro", "popularity": 87, "price": 149},
        {"id": "DAV3", "name": "雷蛇 DeathAdder V3", "popularity": 85, "price": 119},
    ],
    "笔记本电脑": [
        {"id": "MBP14", "name": "MacBook Pro 14英寸", "popularity": 94, "price": 1999},
        {"id": "XPS13", "name": "戴尔 XPS 13", "popularity": 89, "price": 1299},
        {"id": "TPX1", "name": "ThinkPad X1 Carbon", "popularity": 86, "price": 1499},
    ],
}

# 模拟库存数据库
INVENTORY_DATABASE = {
    "WH-1000XM5": {"stock": 10, "location": "仓库-A"},
    "QC45": {"stock": 0, "location": "仓库-B"},
    "AIRMAX": {"stock": 5, "location": "仓库-C"},
    "PXC550": {"stock": 15, "location": "仓库-A"},
    "HT450": {"stock": 25, "location": "仓库-B"},
    "GPW": {"stock": 8, "location": "仓库-C"},
    "VIPER": {"stock": 12, "location": "仓库-A"},
    "DAV3": {"stock": 3, "location": "仓库-B"},
    "MBP14": {"stock": 7, "location": "仓库-C"},
    "XPS13": {"stock": 0, "location": "仓库-A"},
    "TPX1": {"stock": 4, "location": "仓库-B"},
}


# 工具1:搜索产品
@tool
def search_products(query: str) -> str | None:
    """搜索产品并返回按受欢迎度排序的结果"""
    print(f"🔍 [工具调用] search_products('{query}')")

    # 关键词映射,支持多种中文表达方式
    keyword_mapping = {
        "无线耳机": ["无线耳机", "蓝牙耳机", "头戴式耳机", "耳机"],
        "游戏鼠标": ["游戏鼠标", "电竞鼠标", "鼠标"],
        "笔记本电脑": ["笔记本电脑", "笔记本", "手提电脑", "电脑"],
    }

    # 查找匹配的类别
    matched_category = None
    for category, keywords in keyword_mapping.items():
        if any(keyword in query for keyword in keywords):
            matched_category = category
            break

    if matched_category and matched_category in PRODUCT_DATABASE:
        products = PRODUCT_DATABASE[matched_category]
        # 按受欢迎度排序
        sorted_products = sorted(products, key=lambda x: x["popularity"], reverse=True)
        result = f"找到 {len(sorted_products)} 个匹配 '{query}' 的产品:\n"

        for i, product in enumerate(sorted_products, 1):
            result += f"{i}. {product['name']} (ID: {product['id']}) - 受欢迎度: {product['popularity']}% - ¥{product['price']}\n"

        return result


# 工具2:检查库存
@tool
def check_inventory(product_id: str) -> str:
    """检查特定产品的库存状态"""
    print(f"📦 [工具调用] check_inventory('{product_id}')")

    if product_id in INVENTORY_DATABASE:
        stock_info = INVENTORY_DATABASE[product_id]
        status = "有库存" if int(stock_info["stock"]) > 0 else "缺货"
        return f"产品 {product_id}: {status} ({stock_info['stock']} 件库存) - 位置: {stock_info['location']}"
    else:
        return f"未找到产品ID: {product_id}"


# 创建代理
api_key = os.getenv("QWEN_API_KEY")
model = ChatOpenAI(
    model="qwen-plus",
    api_key=SecretStr(api_key) if api_key else None,
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

agent = create_agent(
    model,
    tools=[search_products, check_inventory],
    system_prompt="""你是电商助手,遵循ReAct模式:
    1. 先推理用户需求
    2. 选择合适的工具执行操作
    3. 基于工具结果进行下一步推理
    4. 重复直到获得完整答案

    保持推理步骤简洁明了。""",
)

# 测试案例1:无线耳机搜索
result1 = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "查找当前最受欢迎的无线耳机并检查是否有库存"}
        ]
    }
)

print("\n" + "=" * 40)
print("📊 最终结果:")
for msg in result1["messages"]:
    if hasattr(msg, "content"):
        print(f"{msg.__class__.__name__}: {msg.content}")
print("=" * 40)

print()
print()


# # 详细追踪ReAct循环过程
def track_react_cycle(messages):
    print("ReAct循环步骤分析:")
    step = 1
    for i, msg in enumerate(messages):
        msg_type = msg.__class__.__name__
        if msg_type == "AIMessage" and hasattr(msg, "tool_calls") and msg.tool_calls:
            print(f"\n🔄 步骤{step}: Reasoning + Acting")
            for tool_call in msg.tool_calls:
                print(f"   🛠️  工具调用: {tool_call['name']}({tool_call['args']})")
            step += 1
        elif msg_type == "ToolMessage":
            print(f"   📋  观察结果: {msg.content[:80]}...")
        elif msg_type == "AIMessage" and not (
            hasattr(msg, "tool_calls") and msg.tool_calls
        ):
            print(f"\n✅ 最终回答: {msg.content}")


# 追踪案例1的ReAct循环
track_react_cycle(result1["messages"])

为什么有了Tool 还需要 Agent?

image-20260512145529084

image-20260512145606080

  • Tool 就像“工具箱里的螺丝刀、锤子”

  • Agent就像“一个有判断力的工匠”,他知道什么时候用螺丝刀,什么时候用锤子,甚至知道先用螺丝刀再用锤子。

Agent演变过程

两者对比

image-20260512145710533

第一个智能体组装

image-20260512145738730

v0.3版本的工作原理

image-20260512145808281

在LangChain的Agents实际架构中,Agent的角色是接收输入并决定采取的操作,但它本身并不直接执行这些操作。这一任务是由AgentExecutor来完成的。将Agent(决策大脑)与AgentExecutor(执行操作的Runtime)结合使用,才构成了完整的Agents(智能体),其中AgentExecutor负责调用代理并执行指定的工具,以此来实现整个智能体的功能。

工作流程可以分为以下步骤:

  1. 输入解析:语言模型分析用户输入,理解任务目标。

  2. 推理规划:

    1. 使用推理框架(如 ReAct)生成操作计划。ReAct(Reasoning + Acting):结合推理和行动,模型在每次迭代中思考(生成推理) 并执行(调用工具)
    2. 决定是否调用工具、调用哪些工具以及调用顺序。
  3. 工具调用:

    1. 根据推理计划调用工具,传递输入并获取结果。
    2. 工具结果反馈给语言模型
  4. 迭代推理

    1. 语言模型根据工具结果更新推理,可能触发更多工具调用。
    2. 循环直到任务完成或达到终止条件。
  5. 语言模型综合所有信息,生成最终答案。

python
import json
import os
import httpx
from langchain_openai import ChatOpenAI
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
from pydantic import SecretStr
from dotenv import load_dotenv

load_dotenv()

"""
多工具并行调用,一次性发起了同一个外部函数的两次调用请求并将最后结果聚合
一次问题 → 多工具调用 → 聚合回答
"""


@tool
def get_weather(loc):
    """
    查询即时天气函数

    :param loc: 必要参数,字符串类型,用于表示查询天气的具体城市名称。
                注意,中国的城市需要用对应城市的英文名称代替,例如如果需要查询北京市天气,
                则 loc 参数需要输入 'Beijing'/'shanghai'。
    :return: OpenWeather API 查询即时天气的结果。具体 URL 请求地址为:
             https://api.openweathermap.org/data/2.5/weather。
             返回结果对象类型为解析之后的 JSON 格式对象,并用字符串形式进行表示,
             其中包含了全部重要的天气信息。
    """
    # Step 1. 构建请求 URL
    url = "https://api.openweathermap.org/data/2.5/weather"

    # Step 2. 设置查询参数,包括城市名、API Key、单位和语言
    params = {
        "q": loc,
        "appid": os.getenv("OPENWEATHER_API_KEY"),
        "units": "metric",  # 使用摄氏度
        "lang": "zh_cn",  # 输出语言为简体中文
    }

    # Step 3. 发送 GET 请求获取天气数据
    response = httpx.get(url, params=params, timeout=30)

    # Step 4. 解析响应内容为 JSON 并序列化为字符串返回
    data = response.json()
    print(json.dumps(data))
    return json.dumps(data)


api_key = os.getenv("QWEN_API_KEY")

# 初始化模型实例,用于处理自然语言任务
llm = ChatOpenAI(
    model="qwen-plus",
    api_key=SecretStr(api_key) if api_key else None,
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 创建聊天提示模板,定义agent的对话结构和角色
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是天气助手,请根据用户的问题,给出相应的天气信息"),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"),
    ]
)

# 定义可用工具列表,包含获取天气信息的工具函数
tools = [get_weather]

# 创建工具调用agent,整合语言模型、工具和提示模板。该agent能够根据用户问题调用相应工具获取天气信息
agent = create_tool_calling_agent(llm, tools, prompt)

# 创建agent执行器,负责协调agent和工具的执行流程
# agent参数指定要执行的agent实例
# tools参数提供agent可调用的工具列表
# verbose参数设置为True,启用详细输出模式便于调试
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# 执行agent,处理用户关于北京和上海天气的查询请求
result = agent_executor.invoke(
    {"input": "请问今天北京和上海的天气怎么样,哪个城市更热?"}
)

# 输出执行结果
print(result)

v1.0版本的工作原理

image-20260512150024535

python
import os
import json
import httpx
from typing import TypedDict

from langchain.agents import create_agent
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from pydantic import SecretStr
from dotenv import load_dotenv

load_dotenv()


# 1.Tool 定义
@tool
def get_weather(loc: str):
    """
    查询即时天气函数

    :param loc: 必要参数,字符串类型,用于表示查询天气的具体城市名称。
                注意,中国的城市需要用对应城市的英文名称代替,例如如果需要查询北京市天气,
                则 loc 参数需要输入 'Beijing'/'shanghai'。
    :return: OpenWeather API 查询即时天气的结果。
    """
    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, ensure_ascii=False, indent=2))
    return json.dumps(data, ensure_ascii=False)


# 2 结构化输出(推荐)
class WeatherCompareOutput(TypedDict):
    beijing_temp: float
    shanghai_temp: float
    hotter_city: str
    summary: str


# 3 模型(OpenAI Compatible)
api_key = os.getenv("QWEN_API_KEY")
model = ChatOpenAI(
    model="qwen-plus",
    api_key=SecretStr(api_key) if api_key else None,
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)


# 4 创建Agent
agent = create_agent(
    model=model,
    tools=[get_weather],
    system_prompt=(
        "你是天气助手。"
        "当用户询问多个城市天气时,"
        "你需要分别调用工具获取数据,并进行比较分析。"
    ),
    response_format=WeatherCompareOutput,
)

# 5 调用Agent
result = agent.invoke({"input": "请问今天北京和上海的天气怎么样,哪个城市更热?"})
print(result)

print()

print(json.dumps(result["structured_response"], ensure_ascii=False, indent=2))

Agent2Agent 案例

基于 Python3.12 和 LangChain1.0 实现Agent-to-Agent(A2A) 协作案例,

模拟携程订机票、美团订酒店、滴滴打车的跨平台智能协作流程,核心是让不同领域的专属 Agent 分工协作、完成完整的出行服务闭环,模拟用户 “从北京飞上海、订浦东机场附近酒店、从机场打车到酒店” 的完整需求:

核心设计思路

拆分专属 Agent:

  • 按业务领域拆分为机票 Agent(携程)、酒店 Agent(美团)、打车 Agent(滴滴),

  • 每个 Agent 仅负责自身领域的任务,保证专业性;

主协调 Agent:新增出行总协调 Agent,作为入口接收用户需求、调度各专属 Agent、整合协作结果、反馈最终结论;

LangChain1.0 核心组件: 使用AgentExecutor实现 Agent 执行、ChatOpenAI作为大模型驱动、Tool封装各 Agent 的核心能力、HumanMessage/AIMessage实现 Agent 间的消息通信;

模拟业务能力:因无真实平台接口,用模拟函数实现订机票 / 酒店 / 打车的核心逻辑(可直接替换为真实 API)

python
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda
from langchain.tools import tool
from pydantic import SecretStr
from dotenv import load_dotenv

load_dotenv()

# ===================== 通义千问配置 =====================
api_key = os.getenv("QWEN_API_KEY")
llm = ChatOpenAI(
    model="qwen-plus",
    api_key=SecretStr(api_key) if api_key else None,
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
output_parser = StrOutputParser()


# ===================== 模拟业务函数(@tool装饰器) =====================
@tool(
    "CtripBookFlight",
    description="预订机票的唯一工具,必须调用,参数是departure出发地、arrival目的地、date出行日期(格式2026-02-01)",
)
def ctrip_book_flight(departure: str, arrival: str, date: str) -> str:
    """携程订机票:固定返回测试结果"""
    return f"【携程机票预订成功】\n出发地:{departure}\n目的地:{arrival}\n出行日期:{date}\n航班号:CA1885(北京首都T3→上海浦东T2)\n起飞时间:14:00\n降落时间:16:30\n座位:经济舱34A\n电子客票号:999-1234567890\n舱位等级:经济舱超级经济座"


@tool(
    "MeituanBookHotel",
    description="预订酒店的唯一工具,必须调用,参数是city城市、near_by附近地标、check_in入住日期、check_out离店日期",
)
def meituan_book_hotel(city: str, near_by: str, check_in: str, check_out: str) -> str:
    """美团订酒店:固定返回测试结果"""
    return f"【美团酒店预订成功】\n城市:{city}\n位置:{near_by}附近\n入住日期:{check_in}\n离店日期:{check_out}\n酒店名称:上海浦东机场铂尔曼大酒店\n房型:豪华大床房(含双人自助早餐)\n房号:1508\n预订号:MT20260201001\n入住人:张三\n退房政策:入住后24小时内可免费取消"


@tool(
    "DidiBookTaxi",
    description="预约打车的唯一工具,必须调用,参数是start起点、end终点、time用车时间",
)
def didi_book_taxi(start: str, end: str, time: str) -> str:
    """滴滴打车:固定返回测试结果"""
    return f"【滴滴打车预约成功】\n起点:{start}\n终点:{end}\n用车时间:{time}\n车型:滴滴快车(舒适型)\n司机姓名:王师傅\n车牌号:沪A12345\n司机电话:13800138000\n预估费用:35元(券后立减5元,实付30元)\n预计接驾时间:16:35\n车型空间:5座,可放2件24寸行李箱"


# ===================== 专属Agent(工具绑定逻辑) =====================
def create_ctrip_agent(llm):
    llm_with_tools = llm.bind_tools([ctrip_book_flight])
    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "你是专业的工具调用助手,只能调用CtripBookFlight工具,"
                "调用格式必须正确,"
                "直接传入参数:departure='北京', arrival='上海', date='2026-02-01',"
                "调用后直接返回工具执行的完整字符串结果,不能有任何其他内容,不能留空!",
            ),
            ("human", "{input}"),
        ]
    )
    return prompt | llm_with_tools | output_parser


def create_meituan_agent(llm):
    llm_with_tools = llm.bind_tools([meituan_book_hotel])
    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "你是专业的工具调用助手,只能调用MeituanBookHotel工具,调用格式必须正确,"
                "直接传入参数:city='上海', near_by='浦东机场', check_in='2026-02-01', "
                "check_out='2026-02-02',调用后直接返回工具执行的完整字符串结果,"
                "不能有任何其他内容,不能留空!",
            ),
            ("human", "{input}"),
        ]
    )
    return prompt | llm_with_tools | output_parser


def create_didi_agent(llm):
    llm_with_tools = llm.bind_tools([didi_book_taxi])
    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "你是专业的工具调用助手,只能调用DidiBookTaxi工具,调用格式必须正确,"
                "直接传入参数:start='上海浦东机场T2', end='上海浦东机场铂尔曼大酒店', "
                "time='2026-02-01 16:40',调用后直接返回工具执行的完整字符串结果,"
                "不能有任何其他内容,不能留空!",
            ),
            ("human", "{input}"),
        ]
    )
    return prompt | llm_with_tools | output_parser


# ===================== 总协调Agent =====================
def create_travel_coordinator_agent(llm, ctrip_chain, meituan_chain, didi_chain):
    """总协调:按顺序调用+空值兜底+打印详细测试"""

    def a2a_schedule(input_dict):
        print("🔍 开始执行A2A协作测试,依次调用各业务Agent...\n")
        ctrip_func = ctrip_book_flight.func  # 获取携程工具原始函数
        meituan_func = meituan_book_hotel.func  # 获取美团工具原始函数
        didi_func = didi_book_taxi.func  # 获取滴滴工具原始函数

        # 1. 携程Agent调用
        print("1. 调用【携程机票Agent】>>>")
        try:
            ctrip_result = ctrip_chain.invoke({"input": "订机票"})
        except:
            ctrip_result = ""
        if not ctrip_result.strip():
            ctrip_result = ctrip_func("北京", "上海", "2026-02-01")  # 替换为原始函数
        print(f"✅ 携程测试结果:\n{ctrip_result}\n" + "-" * 80 + "\n")

        # 2. 美团Agent调用
        print("2. 调用【美团酒店Agent】>>>")
        try:
            meituan_result = meituan_chain.invoke({"input": "订酒店"})
        except:
            meituan_result = ""
        if not meituan_result.strip():
            meituan_result = meituan_func(
                "上海", "浦东机场", "2026-02-01", "2026-02-02"
            )
        print(f"✅ 美团测试结果:\n{meituan_result}\n" + "-" * 80 + "\n")

        # 3. 滴滴Agent调用
        print("3. 调用【滴滴打车Agent】>>>")
        try:
            didi_result = didi_chain.invoke({"input": "预约打车"})
        except:
            didi_result = ""
        if not didi_result.strip():
            didi_result = didi_func(
                "上海浦东机场T2", "上海浦东机场铂尔曼大酒店", "2026-02-01 16:40"
            )  # 替换为原始函数
        print(f"✅ 滴滴测试结果:\n{didi_result}\n" + "-" * 80 + "\n")

        # 整合最终报告
        total_report = f"""
📋 【携程-美团-滴滴 A2A协作测试最终报告】
{("=" * 90)}
📌 测试状态:本地运行成功,所有Agent均返回完整结果(含兜底保障)
📌 协作流程:携程订机票 → 美团订酒店 → 滴滴打车(按业务顺序执行)
📌 测试环境:Python3.12 + LangChain1.0 + 通义千问qwen-plus + @tool装饰器(修复可调用问题)
{("=" * 90)}
【1. 携程机票预订结果】
{ctrip_result}

【2. 美团酒店预订结果】
{meituan_result}

【3. 滴滴打车预约结果】
{didi_result}
{("=" * 90)}
💡 测试结论:A2A协作逻辑正常,@tool装饰器集成成功,无报错!
"""
        return total_report

    return RunnableLambda(a2a_schedule)


# ===================== 主程序=====================
if __name__ == "__main__":
    try:
        # 初始化各专属Agent
        print("🔧 初始化携程/美团/滴滴专属Agent...")
        ctrip_chain = create_ctrip_agent(llm)
        meituan_chain = create_meituan_agent(llm)
        didi_chain = create_didi_agent(llm)
        print("✅ 所有Agent初始化完成!\n" + "=" * 90 + "\n")

        # 初始化A2A总协调Agent
        print("🔧 初始化A2A总协调Agent(调度核心)...")
        coor_chain = create_travel_coordinator_agent(
            llm, ctrip_chain, meituan_chain, didi_chain
        )
        print("✅ 总协调Agent初始化完成!\n" + "=" * 90 + "\n")

        # 执行A2A协作核心测试
        print("🚀 携程-美团-滴滴 A2A协作测试正式开始 🚀")
        final_result = coor_chain.invoke(
            {"input": "安排2026-02-01北京飞上海的完整行程"}
        )

        # 打印最终完整测试报告
        print("\n" + "=" * 90)
        print(final_result)
        print("=" * 90)

    except Exception as e:
        print(f"❌ 全局运行异常:{type(e).__name__} - {str(e)[:100]}")
        print(
            "💡 快速排查:"
            "1. 通义密钥是否正确 2. 网络能否访问阿里云 3. LangChain版本是否为1.0.0"
        )

案例总结:

简单说:A2A 调度 = 多个功能单一的 Runnable 子 Agent 链 + 一个控制调用逻辑的总协调器

模板核心固定规范(LangChain 1.0 A2A 调度最佳实践)

以下规范是模板能稳定运行的关键,无需修改,严格遵循即可:

  1. 子 Agent 规范
  • 单一职责:一个子 Agent 只负责一个业务,只绑定一个专属工具;

  • 统一接口:所有子 Agent 都封装为Prompt | 绑定工具的LLM | output_parser的 Runnable 链,对外仅暴露invoke()方法;

  • 明确 Prompt:必须指定专属工具、参数值、强制返回结果,避免大模型歧义。

  1. 总协调 Agent 规范
  • 统一调度:所有子 Agent 的调用都由总协调 Agent 控制,子 Agent 之间不直接交互;

  • 稳定性保障:每个子 Agent 调用都加try-except,且对空结果做兜底(调用@tool的原始函数);

  • 统一输入:所有 Agent 的调用参数均为{"input": 字符串},符合 LangChain 1.0 的 Runnable 规范。

  1. 工具封装规范
  • 装饰器必用:用@tool(工具名, description=工具描述)封装业务函数,替代旧版Tool类;

  • 原始函数提取:兜底时通过工具对象.func获取原始可调用函数,解决StructuredTool不可调用问题;

  • 描述准确:工具的description必须明确参数名、参数说明,大模型通过描述识别参数。

Released under the MIT License.