MiniMax-M2.7 与 LangChain ToolStrategy 兼容性问题解决实录
摘要:本文记录了在使用 LangChain 构建 Agent 时,MiniMax-M2.7 模型与 ToolStrategy 结构化输出功能的兼容性问题的完整排查过程和解决方案。代码完全照搬官方文档却无法工作?问题不在代码,而在模型理解差异。
一、问题背景
最近在用 LangChain 构建一个天气查询 Agent 时,遇到了一个令人困惑的问题:
- 代码完全照搬 LangChain 官方文档
- 使用 Claude 时工作正常
- 切换到 MiniMax-M2.7 后,结构化输出失效
1.1 技术栈
| 组件 | 版本/型号 |
|---|---|
| LangChain | 1.2.13 |
| 模型 | MiniMax-M2.7 |
| API 兼容 | Anthropic |
| Python | 3.11+ |
1.2 预期功能
我们想实现一个简单的天气查询 Agent:
用户询问天气
↓
Agent 调用工具获取用户位置
↓
查询该位置天气
↓
以结构化格式返回结果(包含 punny 回复和天气状况)
二、问题现象
2.1 核心代码
python
from langchain.agents.structured_output import ToolStrategy
from langchain.agents import create_agent
from dataclasses import dataclass
@dataclass
class ResponseFormat:
punny_response: str
weather_conditions: str | None = None
agent = create_agent(
model=llm,
tools=[get_weather_for_location, get_user_location],
system_prompt=WEATHER_FORECASTER,
response_format=ToolStrategy(ResponseFormat),
)
response = agent.invoke(
{"messages": [{"role": "user", "content": "What is the weather outside?"}]}
)
print(response['structured_response']) # 期望输出 ResponseFormat 对象
2.2 错误信息
=== Structured Response ===
Traceback (most recent call last):
File "main.py", line 77, in <module>
print(f"Punny Response: {response['structured_response'].punny_response}")
~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 'structured_response'
2.3 调试输出
python
print(f"Response keys: {response.keys()}")
# 输出:dict_keys(['messages'])
# 注意:没有 'structured_response' 键!
完整响应内容:
{
'messages': [
HumanMessage(content='What is the weather outside?'),
AIMessage(content="I'd be happy to check the weather..."),
ToolMessage(content='Florida', name='get_user_location'),
AIMessage(content='...checking weather for Florida...'),
ToolMessage(content='hot and humid, 85°F', name='get_weather_for_location'),
AIMessage(
content='🌴 Florida is having a "sun-derful" day!...', # ← 普通文本回复
)
]
}
2.4 问题分析
| 检查项 | 状态 |
|---|---|
| 代码符合官方示例 | ✅ |
| 模型调用成功 | ✅ |
| 业务工具调用正常 | ✅ |
返回 structured_response |
❌ |
结论 :模型能正常调用业务工具,但最后用普通文本回复,没有调用 ResponseFormat 工具。
三、排查过程
3.1 检查代码是否正确
对比 LangChain 官方文档:
python
# 官方示例
agent = create_agent(
model=model,
system_prompt=SYSTEM_PROMPT,
tools=[get_user_location, get_weather_for_location],
context_schema=Context,
response_format=ToolStrategy(ResponseFormat),
)
# 我们的代码(完全一样)
agent = create_agent(
model=llm,
tools=[get_weather_for_location, get_user_location],
system_prompt=WEATHER_FORECASTER,
context_schema=WeatherContext,
response_format=ToolStrategy(ResponseFormat),
)
结论:代码完全一致,没有问题。
3.2 检查模型配置
python
from app.agent import create_llm
llm = create_llm(
model="MiniMax-M2.7",
model_provider="anthropic"
)
print(f'LLM type: {type(llm)}')
# <class 'langchain_anthropic.chat_models.ChatAnthropic'>
结论:LLM 创建成功,配置正确。
3.3 检查 ToolStrategy 行为
python
from langchain.agents.structured_output import ToolStrategy
strategy = ToolStrategy(ResponseFormat)
print(f'schema: {strategy.schema}')
print(f'schema_specs: {strategy.schema_specs}')
输出:
schema: <class '__main__.ResponseFormat'>
schema_specs: [_SchemaSpec(
schema=<class '__main__.ResponseFormat'>,
name='ResponseFormat',
description='ResponseFormat(punny_response: str, weather_conditions: str | None = None)',
json_schema={
'properties': {
'punny_response': {'type': 'string'},
'weather_conditions': {'type': 'string', 'nullable': True}
},
'required': ['punny_response']
}
)]
结论 :ToolStrategy 正常工作,它创建了一个名为 ResponseFormat 的虚拟工具。
四、问题根因分析
4.1 ToolStrategy 工作原理
当我们传递 response_format=ToolStrategy(ResponseFormat) 时,LangChain 会:
- 将
ResponseFormat转换为一个工具定义 - 将这个工具和其他工具一起发送给模型
- 期望模型在需要返回结构化数据时调用这个工具
模型实际看到的工具列表:
json
[
{
"name": "get_user_location",
"description": "Retrieve user's location",
"input_schema": {"type": "object", ...}
},
{
"name": "get_weather_for_location",
"description": "Get weather for a city",
"input_schema": {"type": "object", ...}
},
{
"name": "ResponseFormat", // ← ToolStrategy 创建的虚拟工具
"description": "ResponseFormat(punny_response: str, ...)",
"input_schema": {
"properties": {
"punny_response": {"type": "string"},
"weather_conditions": {"type": "string"}
},
"required": ["punny_response"]
}
}
]
4.2 模型理解差异对比
| 模型 | 对 ToolStrategy 的理解 | 实际行为 |
|---|---|---|
| Claude | 经过训练,理解 ResponseFormat 是用来返回结构化答案的特殊工具 |
调用业务工具获取信息 → 调用 ResponseFormat 工具返回结构化结果 ✅ |
| MiniMax-M2.7 | 只看到 3 个普通工具,不知道哪个是特殊的、必须使用的 | 调用业务工具 → 直接用文本回复 ❌ |
4.3 原始 Prompt 的缺失
python
PROMPT = """You are an expert weather forecaster, who speaks in puns.
You have access to two tools:
- get_weather_for_location: use this to get the weather
- get_user_location: use this to get the user's location
If a user asks you for the weather, make sure you know the location..."""
问题点:
- Prompt 说"two tools ",但实际发送了 3 个工具
- 没有说明
ResponseFormat工具的用途 - 没有指示模型必须使用
ResponseFormat工具返回最终答案
4.4 核心结论
MiniMax-M2.7 没有在类似 ToolStrategy 的模式上训练过,它不理解
ResponseFormat工具的特殊用途。ToolStrategy 主要适配 Claude、GPT 等经过专门训练的模型,其他模型需要更明确的 prompt 指示。
五、解决方案
5.1 修改 Prompt
我们更新了 prompt,明确告诉模型有三个工具,并且必须使用 ResponseFormat:
python
PROMPT = """You are an expert weather forecaster, who speaks in puns.
You have access to these tools:
- get_weather_for_location: use this to get the weather for a specific location
- get_user_location: use this to get the user's location
- ResponseFormat: use this to return your final response in structured format
When a user asks you for the weather:
1. First, determine their location (use get_user_location if they mean their current location)
2. Then, get the weather using get_weather_for_location
3. Finally, use the ResponseFormat tool to return your answer with:
- punny_response: A weather forecast with puns
- weather_conditions: The actual weather conditions (optional)
IMPORTANT: Always use the ResponseFormat tool to provide your final answer.
Do not just respond with regular text - you MUST call the ResponseFormat tool."""
5.2 关键改动对比
| 改动点 | 修改前 | 修改后 |
|---|---|---|
| 工具数量描述 | "two tools" | "these tools"(列出三个) |
| ResponseFormat 工具 | 未提及 | 明确说明用途 |
| 执行步骤 | 无 | 1-2-3 步骤说明 |
| 强制要求 | 无 | "MUST call the ResponseFormat tool" |
六、验证结果
6.1 运行测试
bash
uv run python main.py
6.2 成功输出
=== Structured Response ===
Punny Response: 🌴 Weather in Florida: It's a scorching 85°F with humidity so high, even the palm trees are sweating! Conditions are *muggy*-tastic outside, so you'll want to stay as cool as a penguin in a snowstorm. Just a friendly heads up---keep an eye on the horizon, because hurricane season is in full swing...
Weather: hot and humid, 85°F - watch out for hurricanes!
6.3 调试验证
python
print(f"Response keys: {response.keys()}")
# 输出:dict_keys(['messages', 'structured_response']) ← 现在有了!
print(f"structured_response: {response['structured_response']}")
# 输出:
# ResponseFormat(
# punny_response="🌴 Weather in Florida...",
# weather_conditions="hot and humid, 85°F..."
# )
🎉 成功!
七、经验总结
7.1 核心发现
| 发现 | 说明 |
|---|---|
| ToolStrategy 不是万能的 | 它主要适配 Claude、GPT 等经过训练的模型 |
| Prompt 是模型的"操作手册" | 模型不知道工具的"潜规则",需要明确告知 |
| 官方示例 ≠ 适用于所有模型 | 使用非标准模型时需要额外适配 |
7.2 通用解决方案模板
如果你也遇到类似问题,可以直接套用这个 prompt 模板:
python
PROMPT = """
You are [角色描述].
You have access to these tools:
- tool_a: [用途说明]
- tool_b: [用途说明]
- ResponseFormat: use this to return your final response in structured format
When a user asks you to [任务描述]:
1. First, [步骤 1]
2. Then, [步骤 2]
3. Finally, use the ResponseFormat tool to return your answer
IMPORTANT: Always use the ResponseFormat tool to provide your final answer.
Do not just respond with regular text - you MUST call the ResponseFormat tool.
"""
7.3 替代方案
如果 prompt 修改后仍不工作,可以考虑:
- 使用模型原生集成 (如
langchain-minimax,如果可用) - 不使用 ToolStrategy,直接从消息中提取结构化数据
- 换回 Claude/GPT 等完全兼容的模型
八、完整代码
8.1 main.py
python
"""Main entry point for the LangChain Practice project."""
import os
from dataclasses import dataclass
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
from app.agent import create_llm
from app.tools import get_weather_for_location, get_user_location, WeatherContext
from prompts import WEATHER_FORECASTER
load_dotenv()
@dataclass
class ResponseFormat:
"""Response schema for the weather forecaster agent."""
punny_response: str
weather_conditions: str | None = None
def main():
"""Run the weather forecaster agent."""
model = os.getenv("MODEL", "claude-sonnet-4-6")
model_provider = os.getenv("MODEL_PROVIDER")
llm = create_llm(model=model, model_provider=model_provider)
agent = create_agent(
model=llm,
tools=[get_weather_for_location, get_user_location],
system_prompt=WEATHER_FORECASTER,
context_schema=WeatherContext,
response_format=ToolStrategy(ResponseFormat),
checkpointer=None,
)
context = WeatherContext(user_id="1")
config = {"configurable": {"thread_id": "1"}}
response = agent.invoke(
{"messages": [{"role": "user", "content": "What is the weather outside?"}]},
config=config,
context=context,
)
print("\n=== Structured Response ===")
print(f"Punny Response: {response['structured_response'].punny_response}")
if response['structured_response'].weather_conditions:
print(f"Weather: {response['structured_response'].weather_conditions}")
if __name__ == "__main__":
main()
8.2 prompts/weather/init.py
python
"""Weather forecaster prompt."""
PROMPT = """You are an expert weather forecaster, who speaks in puns.
You have access to these tools:
- get_weather_for_location: use this to get the weather for a specific location
- get_user_location: use this to get the user's location
- ResponseFormat: use this to return your final response in structured format
When a user asks you for the weather:
1. First, determine their location (use get_user_location if they mean their current location)
2. Then, get the weather using get_weather_for_location
3. Finally, use the ResponseFormat tool to return your answer with:
- punny_response: A weather forecast with puns
- weather_conditions: The actual weather conditions (optional)
IMPORTANT: Always use the ResponseFormat tool to provide your final answer.
Do not just respond with regular text - you MUST call the ResponseFormat tool."""
九、参考资料
- LangChain ToolStrategy 官方文档
- LangChain create_agent 文档
- MiniMax-M2.7 官方文档 - Tool Use
- LangChain ChatMiniMax 集成
十、关于作者
本文基于真实项目问题排查过程编写。如果你也遇到了类似问题,欢迎交流讨论!
版权声明:本文可自由转载,请注明出处。