🎯 课程主题
演示多工具场景下的调用流程:当一个用户问题需要同时调用多个工具时,如何使用 while True 循环持续检查 tool_calls 直到所有工具执行完毕。
📝 核心知识点
1. 多工具调用场景
- 概念说明:当模型绑定了多个工具,且用户问题涉及多项任务(如"查询苹果股价和最新新闻"),模型的一次返回中
tool_calls可能包含多个工具调用。 - 关键细节:
- 即使函数体内没有匹配的公司数据,模型仍会尝试调用(因为它在函数描述中看到了相关能力)。
- 实际开发中,工具通常对接真实 API(如 Google Search、Tavily Search),确保能返回有效结果。
2. while True 循环处理模式
- 概念说明:使用
while True包裹整个调用流程,每次模型返回后检查tool_calls是否为空,有则执行工具并继续循环,无则break退出。 - 关键细节:
- 相比前两个案例的"单次判断"方式,
while循环能处理多次迭代的工具调用(如图一工具结果又触发了新的工具调用)。 - 每轮循环中:将
response(AIMessage)追加到消息列表 → 提取tool_calls列表 → 遍历执行各工具 → 追加ToolMessage→ 再次invoke模型。 - 当
response.tool_calls为空列表时break,此时response即为最终回答。
- 相比前两个案例的"单次判断"方式,
3. 案例四:天气+新闻多工具调用
- 概念说明:定义
get_weather和search_news两个独立工具,演示同时查询天气和新闻的完整流程。 - 关键细节:与案例三逻辑一致,结构更简洁(未使用 while 循环,因假设一次调用即可完成)。
🏗️ 架构与工作流
┌─────────────────────────────────────────────────┐
│ while True: │
│ 1. response = model.invoke(messages) │
│ 2. messages.append(response) │
│ 3. if not response.tool_calls: break │
│ 4. for tc in response.tool_calls: │
│ tool_msg = tool.invoke(tc) │
│ messages.append(tool_msg) │
│ 5. 继续循环(回到步骤1) │
└─────────────────────────────────────────────────┘
💻 代码实战
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from langchain_deepseek import ChatDeepSeek
# ========== 工具定义 ==========
@tool(parse_docstring=True)
def get_stock_price(company: str = "苹果", period: str = "今天") -> str:
"""获取指定公司的股票价格。
Args:
company: 公司名称(苹果、谷歌、微软)
period: 时间范围(今天、本周、本月)
Returns:
str: 股票价格信息
"""
stocks = {
"苹果": 175.5, "谷歌": 140.2, "微软": 330.8
}
price = stocks.get(company, "未找到")
return f"{company} {period} 股价:{price}美元" if isinstance(price, float) else f"未找到{company}的股价信息"
@tool(parse_docstring=True)
def search_news(company: str = "苹果") -> str:
"""搜索指定公司的最新新闻。
Args:
company: 公司名称(苹果、谷歌、微软)
Returns:
str: 新闻摘要
"""
news = {
"苹果": "苹果发布新一代M4芯片,性能提升显著",
"谷歌": "谷歌推出Gemini 2.0大模型",
"微软": "微软宣布加大对OpenAI的投资"
}
return news.get(company, f"暂未找到{company}的相关新闻")
# ========== 模型初始化与工具绑定 ==========
model = ChatDeepSeek(model="deepseek-chat")
tools = [get_stock_price, search_news]
model_with_tools = model.bind_tools(tools)
# ========== 多工具调用流程 ==========
messages = [HumanMessage(content="苹果今天股价是多少?最近有什么新闻?")]
while True:
# 调用模型
response = model_with_tools.invoke(messages)
messages.append(response)
# 无工具调用则退出
if not response.tool_calls:
break
# 执行所有工具调用
for tool_call in response.tool_calls:
tool_name = tool_call["name"]
if tool_name == "get_stock_price":
tool_msg = get_stock_price.invoke(tool_call)
elif tool_name == "search_news":
tool_msg = search_news.invoke(tool_call)
else:
continue
messages.append(tool_msg)
# 输出最终结果
print(response.content)
# 消息流专家分析
for msg in messages:
print(msg.pretty_repr())
⚠️ 常见问题与避坑指南
- 案例一/二中只做了一次工具调用判断,实际应使用
while True循环来覆盖可能的多轮工具调用。 - 多个
tool_calls共享同一个AIMessage,追加到消息列表时只追加一次。 - 每次进入 while 循环需要重新调用模型,否则消息列表未更新,模型无法利用工具返回的结果。
- 如果工具返回的结果又触发了新的工具调用需求,while 循环会自动处理(如 Agent 多步推理场景)。
💡 个人总结与延伸
多工具调用是 Agent 自动化工具编排的基础模式。while True + 检查 tool_calls 的循环模式本质上就是简化版的 Agent 执行循环(AgentExecutor 内部也是类似逻辑)。理解这个模式后,后续学习 Agent 和 LangGraph 的状态图编排会更加顺畅。