MiniMax-M2.7 与 LangChain ToolStrategy 兼容性问题解决

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 会:

  1. ResponseFormat 转换为一个工具定义
  2. 将这个工具和其他工具一起发送给模型
  3. 期望模型在需要返回结构化数据时调用这个工具

模型实际看到的工具列表

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..."""

问题点

  1. Prompt 说"two tools ",但实际发送了 3 个工具
  2. 没有说明 ResponseFormat 工具的用途
  3. 没有指示模型必须使用 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 修改后仍不工作,可以考虑:

  1. 使用模型原生集成 (如 langchain-minimax,如果可用)
  2. 不使用 ToolStrategy,直接从消息中提取结构化数据
  3. 换回 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."""

九、参考资料


十、关于作者

本文基于真实项目问题排查过程编写。如果你也遇到了类似问题,欢迎交流讨论!


版权声明:本文可自由转载,请注明出处。

相关推荐
用户8356290780516 小时前
Python 实现 PDF 文件加密与解密方法
后端·python
用户8356290780516 小时前
使用 Python 冻结与拆分 Excel 窗格教程
后端·python
大流星14 小时前
LangChainJs之基础模型(一)
javascript·langchain
你好潘先生14 小时前
别再记命令了,用 yeero do 说句人话就能跑脚本,而且不烧 token
服务器·python·命令行
AIOps打工人14 小时前
我以为 LangChain 就是调用大模型,直到我写出第一条 Chain
langchain
Agent_大师14 小时前
WebSocket 行情重连成功,K线缺口不会自动消失
python
荣码14 小时前
LLM结构化输出:让AI返回JSON而不是废话,我踩了4个坑
java·python
copyer_xyf15 小时前
FastAPI 如何连接 MySQL
后端·python
apocelipes1 天前
常用编程语言和库的正则表达式性能对比
c语言·c++·python·性能优化·golang·开发工具和环境
用户8356290780511 天前
使用 Python 在 PDF 中创建与管理书签
后端·python