Appearance
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

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?


Tool 就像“工具箱里的螺丝刀、锤子”
Agent就像“一个有判断力的工匠”,他知道什么时候用螺丝刀,什么时候用锤子,甚至知道先用螺丝刀再用锤子。
Agent演变过程
两者对比

第一个智能体组装

v0.3版本的工作原理

在LangChain的Agents实际架构中,Agent的角色是接收输入并决定采取的操作,但它本身并不直接执行这些操作。这一任务是由AgentExecutor来完成的。将Agent(决策大脑)与AgentExecutor(执行操作的Runtime)结合使用,才构成了完整的Agents(智能体),其中AgentExecutor负责调用代理并执行指定的工具,以此来实现整个智能体的功能。
工作流程可以分为以下步骤:
输入解析:语言模型分析用户输入,理解任务目标。
推理规划:
- 使用推理框架(如 ReAct)生成操作计划。ReAct(Reasoning + Acting):结合推理和行动,模型在每次迭代中思考(生成推理) 并执行(调用工具)
- 决定是否调用工具、调用哪些工具以及调用顺序。
工具调用:
- 根据推理计划调用工具,传递输入并获取结果。
- 工具结果反馈给语言模型
迭代推理
- 语言模型根据工具结果更新推理,可能触发更多工具调用。
- 循环直到任务完成或达到终止条件。
语言模型综合所有信息,生成最终答案。
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版本的工作原理

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 调度最佳实践)
以下规范是模板能稳定运行的关键,无需修改,严格遵循即可:
- 子 Agent 规范
单一职责:一个子 Agent 只负责一个业务,只绑定一个专属工具;
统一接口:所有子 Agent 都封装为Prompt | 绑定工具的LLM | output_parser的 Runnable 链,对外仅暴露invoke()方法;
明确 Prompt:必须指定专属工具、参数值、强制返回结果,避免大模型歧义。
- 总协调 Agent 规范
统一调度:所有子 Agent 的调用都由总协调 Agent 控制,子 Agent 之间不直接交互;
稳定性保障:每个子 Agent 调用都加try-except,且对空结果做兜底(调用@tool的原始函数);
统一输入:所有 Agent 的调用参数均为{"input": 字符串},符合 LangChain 1.0 的 Runnable 规范。
- 工具封装规范
装饰器必用:用@tool(工具名, description=工具描述)封装业务函数,替代旧版Tool类;
原始函数提取:兜底时通过工具对象.func获取原始可调用函数,解决StructuredTool不可调用问题;
描述准确:工具的description必须明确参数名、参数说明,大模型通过描述识别参数。
