🎯 课程主题
LLMToolSelectorMiddleware 使用子模型对工具列表进行智能筛选,当 Agent 工具数量过多时仅保留最相关的几个工具,减少 Token 消耗并提升选择准确率。
📝 核心知识点
1. LLMToolSelectorMiddleware 概念
- 概念说明:在 Agent 调用主模型之前,先由一个轻量子模型根据用户问题从所有工具中筛选出最相关的子集,再交给主模型进行工具选择与调用。
- 关键细节:
model:用于筛选工具的子模型(建议使用更小、更便宜的模型)。max_tools(int):最多保留的工具数量上限;如果实际工具数少于此值则全部保留。always_include(list):始终包含的工具列表,不计入max_tools配额。- 子模型与外层 Agent 的主模型是两个独立的模型实例。
2. 参数联动机制
- 概念说明:
max_tools+always_include的组合决定了最终可用工具数量。 - 关键细节:
- 若提供 100 个工具,
max_tools=5,always_include=["get_weather"],则最终可用 6 个工具(5 个筛选 + 1 个强制保留)。 - 若
max_tools=0且always_include=["get_weather"],则仅get_weather可用,其他工具全部被过滤。
- 若提供 100 个工具,
🏗️ 架构与工作流
用户问题 → Agent
↓
子模型(model_in)根据问题筛选工具
↓
保留: always_include 工具 + 最多 max_tools 个相关工具
↓
主模型(model_out)在精简工具集中选择并调用
↓
返回结果
💻 代码实战
from langchain.agents import create_agent
from langchain.agents.middleware import LLMToolSelectorMiddleware
from langchain.chat_models import init_chat_model
# 两个模型:外层主模型 + 内层筛选子模型
model_out = init_chat_model("deepseek-v4-mini") # Agent 主模型
model_in = init_chat_model("deepseek-v4-mini") # 工具筛选子模型
# 工具定义
def get_weather(city: str) -> str:
"""查询指定城市的天气信息"""
return f"{city}天气:晴,25°C"
def get_news() -> str:
"""获取今日新闻概要"""
return "今日新闻:AI 领域取得新突破..."
def get_stock(code: str) -> str:
"""查询指定股票代码的行情"""
return f"股票 {code} 当前价格:100.5"
# 示例 1:max_tools=0,仅 always_include 的工具可用
middleware1 = LLMToolSelectorMiddleware(
model=model_in,
max_tools=0,
always_include=["get_weather"]
)
agent1 = create_agent(
model=model_out,
tools=[get_weather, get_news, get_stock],
middleware=[middleware1]
)
result1 = agent1.invoke({"messages": [{"role": "user", "content": "北京天气如何?今日新闻概要?"}]})
# get_weather 可调用,get_news 被过滤不可用
# 示例 2:max_tools=0,always_include=["get_news"]
middleware2 = LLMToolSelectorMiddleware(
model=model_in,
max_tools=0,
always_include=["get_news"]
)
agent2 = create_agent(
model=model_out,
tools=[get_weather, get_news, get_stock],
middleware=[middleware2]
)
result2 = agent2.invoke({"messages": [{"role": "user", "content": "北京天气如何?今日新闻概要?"}]})
# get_news 可调用,get_weather 被过滤不可用
# 示例 3:always_include 包含全部所需工具
middleware3 = LLMToolSelectorMiddleware(
model=model_in,
max_tools=0,
always_include=["get_weather", "get_news"]
)
agent3 = create_agent(
model=model_out,
tools=[get_weather, get_news, get_stock],
middleware=[middleware3]
)
# 两个工具都可调用,正常输出天气和新闻
# 示例 4:max_tools=1,always_include=["get_weather"]
middleware4 = LLMToolSelectorMiddleware(
model=model_in,
max_tools=1,
always_include=["get_weather"]
)
agent4 = create_agent(
model=model_out,
tools=[get_weather, get_news, get_stock],
middleware=[middleware4]
)
# get_weather(强制)+ get_news(子模型根据问题筛选)均可用
# 示例 5:max_tools=1,always_include=["get_news"]
middleware5 = LLMToolSelectorMiddleware(
model=model_in,
max_tools=1,
always_include=["get_news"]
)
agent5 = create_agent(
model=model_out,
tools=[get_weather, get_news, get_stock],
middleware=[middleware5]
)
# get_news(强制)+ get_weather(子模型根据问题筛选)均可用
⚠️ 常见问题与避坑指南
- 子模型建议使用更小、更快、更便宜的模型(如 mini 版本),因为筛选任务本身只需语义匹配,无需强推理能力。
always_include中的工具名必须与注册时的函数名完全一致,否则无效。- 如果
max_tools+always_include数量仍不足,主模型可能因缺少必要工具而回答质量下降。
💡 个人总结与延伸
LLMToolSelectorMiddleware 本质是一种两阶段工具检索策略,类似 RAG 中的检索-重排范式。当 Agent 挂载几十上百个工具时,直接全量传入会导致上下文爆炸且选择精度下降,先用子模型粗筛再用主模型精调是高效的工程实践。