最近在用 Trae 集成 MiMo 模型进行多轮工具调用时频繁遇到Invalid request (400)错误。经过排查发现是 MiMo API 新增了对reasoning_content字段的回传要求。本文记录完整排查过程并介绍社区大佬 Mintneko 提供的 Proxy 解决方案。一、问题现象在 Trae 中使用 MiMo 模型如mimo-v2.5-pro进行多轮对话当模型在历史消息中产生过工具调用tool_calls后后续轮次的请求会直接报错这个路径之前mimo官方有个trae的对接文档不知道为啥现在没了应该就是发那个问题公告的原因trae现在不支持之前官方给的trae的对接链接是在token plan 的基础url上添加chat/completions 因为trae不会自己添加所以我这里之前的url是之前好使最近不行了。。。。。https://token-plan-cn.xiaomimimo.com/v1/chat/completionsInvalid request (Model Provider Error Code: 400, HTTP Status: 400)单轮对话或者不涉及工具调用的对话一切正常问题只在多轮 工具调用的场景下复现。二、排查过程2.1 官方公告经过搜索发现 MiMo 官方平台近期发布了一则公告公告链接关于 Agent 类产品多轮会话中回传 reasoning_content 的说明关于 Agent 类产品多轮会话中回传 reasoning_content 的说明当在 Agent 类产品的多轮会话中开启 MiMo 思考模式且历史会话中存在工具调用时后续所有 user 交互轮次中回传的 assistant 如果包含了工具调用必须完整回传reasoning_content字段否则 API 将返回 400 错误。之所以作此要求是因为历史reasoning_content一旦缺失模型上下文将不完整可能表现出指令遵循下降、幻觉增多等现象。受影响的产品包括TRAE、Cursor、Roo Code、GitHub Copilot CLI、Zed、AutoGen、Goose 等。受影响的模型包括MiMo-V2.5-Pro、MiMo-V2.5、MiMo-V2-Pro、MiMo-V2-Omni、MiMo-V2-Flash。2.2 根因分析简单来说问题的链条是这样的用户发起多轮对话 ↓ Trae 构造请求时将历史 assistant 消息含 tool_calls写入 messages ↓ 但 Trae 没有回传这些 assistant 消息的 reasoning_content 字段 ↓ MiMo API 发现有 tool_calls 却没有 reasoning_content → 400 错误为什么 Trae 不传reasoning_content这其实是大多数 Agent 框架的通用行为。这些框架遵循 OpenAI 的标准协议只关注content和tool_calls字段对reasoning_content这种扩展字段默认会忽略。所以历史消息中的推理内容在转发过程中被丢弃了。2.3 正确的回传方式按照官方文档正确的做法是把 assistant 消息的完整字段都保留下来# 错误 ❌ — 缺少 reasoning_content messages.append({ role: assistant, content: , tool_calls: [...] }) # 正确 ✅ — 完整回传 messages.append({ role: assistant, content: , tool_calls: [...], reasoning_content: The user wants to know... I should call... })但在 Trae 这类客户端中我们无法手动控制它构造 messages 的方式。这就需要一个中间层来帮忙——也就是 Proxy。三、解决方案MiMo Reasoning Content Proxy在 GitHub 上找到了社区大佬 Mintneko 提供的代理方案 mimo-proxy原理非常巧妙。3.1 核心思路在 Trae 和 MiMo API 之间加一层本地代理代理负责1.拦截并缓存每次 MiMo 返回的 assistant 消息中如果包含reasoning_content就把它缓存起来2.自动注入后续请求经过代理时检查历史消息中是否有 assistant 消息缺少reasoning_content如果有就从缓存中取出注入回去3.兜底降级如果缓存中找不到比如代理刚启动、缓存过期等就把该 assistant 消息的tool_calls剥离掉降级为纯文本避免触发 400Trae ──→ Proxy (localhost:8899) ──→ MiMo API │ │ │ ① 注入 reasoning_content │ │ ② 缓存返回的 reasoning │ │←──────────────────────────←│3.2 脚本逐模块解析下面对脚本的各个核心模块做一个详细介绍。模块一配置区MIMO_API_BASE https://token-plan-cn.xiaomimimo.com/v1 # MiMo API 地址 LISTEN_HOST 0.0.0.0 # 监听地址 LISTEN_PORT 8899 # 监听端口 CACHE_MAX_SIZE 2000 # 缓存最大条目数 CACHE_TTL 7200 # 缓存过期时间秒代理监听在本地8899端口Trae 的 API Base URL 改为http://127.0.0.1:8899/v1即可。缓存采用 LRU 策略最多存 2000 条每条 2 小时过期。模块二缓存机制这是整个方案的核心。缓存有两套索引索引方式键用途哈希索引SHA256(content tool_calls)[:16]按消息内容精确匹配Tool Call ID 索引tool_call_id按工具调用 ID 反查def _msg_hash(msg: dict) - str: content msg.get(content) or tool_calls json.dumps(msg.get(tool_calls) or [], sort_keysTrue) raw f{content}||{tool_calls} return hashlib.sha256(raw.encode()).hexdigest()[:16]为什么要两套索引因为 Trae 在回传历史消息时消息内容可能被稍作修改比如拼接了工具结果但tool_call_id通常是不变的。所以优先用哈希匹配匹配不到再用tool_call_id反查提高命中率。缓存操作使用 Python 的OrderedDict实现标准 LRUdef _cache_get(key: str) - str | None: if key in _cache: val, ts _cache[key] if time.time() - ts CACHE_TTL: # 未过期 _cache.move_to_end(key) # 移到末尾最近使用 return val del _cache[key] # 过期则删除 return None def _cache_set(key: str, value: str): if key in _cache: del _cache[key] _cache[key] (value, time.time()) while len(_cache) CACHE_MAX_SIZE: # 超出容量则淘汰最久未用的 _cache.popitem(lastFalse)模块三注入与降级逻辑这是处理请求的核心函数。遍历messages中的每条 assistant 消息检查是否需要处理def inject_reasoning(messages: list[dict]) - tuple[int, int]: for i, msg in enumerate(messages): if msg.get(role) ! assistant: continue if not msg.get(tool_calls): # 没有 tool_calls → 不需要处理 continue if msg.get(reasoning_content): # 已经有 reasoning_content → 跳过 continue # 尝试从缓存中查找 cached _cache_get(_msg_hash(msg)) if not cached: cached _find_by_tool_call_ids(msg) if cached: # ✅ 命中缓存 → 注入 reasoning_content msg[reasoning_content] cached else: # ⚠️ 未命中 → 降级剥离 tool_calls转为纯文本 del msg[tool_calls] # 同时在 content 中保留工具调用的摘要 msg[content] [Called get_current_weather]降级策略的思路很聪明MiMo API 只对「有tool_calls但没有reasoning_content」的消息报 400。那我把tool_calls去掉模型自然就不会要求reasoning_content了。虽然丢失了结构化的工具调用信息但至少不会报错而且在 content 中保留了工具调用的文本摘要模型仍然能理解上下文。模块四流式响应处理代理需要在流式传输过程中同时做两件事逐块转发给客户端 逐步累积完整消息用于缓存。async def _stream_proxy(client, url, headers, body): acc_content acc_reasoning acc_tool_calls [] async with client.stream(POST, url, headersheaders, jsonbody) as resp: async for raw_chunk in resp.aiter_bytes(): # 解析 SSE 数据 # 累积 reasoning_content、content、tool_calls # 原样转发给客户端 yield _sse(payload) # 收到 [DONE] 时将完整消息缓存 if payload [DONE]: if acc_reasoning and (acc_content or acc_tool_calls): _cache_set_with_index(hash, acc_reasoning, tool_call_ids)这个设计确保了缓存的实时性模型的每一次响应只要有reasoning_content都会在流结束后立即缓存后续轮次立刻可用。模块五错误处理与重试v1.4 版本重点增强了错误处理5xx 错误自动重试最多 3 次间隔递增1s、2s4xx 错误直接透传客户端参数问题不重试超时处理流式模式和非流式模式都有独立的超时重试逻辑空内容兜底如果 MiMo 只返回了reasoning_content而content为空自动将reasoning_content作为content返回# 空 content 兜底 if not msg.get(content) and not msg.get(tool_calls) and msg.get(reasoning_content): msg[content] msg[reasoning_content]模块六API 端点代理暴露了与 MiMo API 完全兼容的端点端点说明POST /v1/chat/completions核心聊天补全带注入/降级逻辑GET /v1/models模型列表直接透传GET /代理状态页GET /health健康检查四、使用步骤4.1 拉取mimo-proxy项目git clone https://github.com/Mintneko/mimo-proxy.git如果你拉不下来可以参考我上一篇博客里面有github处理方案用 OpenCode 装 Skills 一直失败它说是网不好但浏览器明明能上 git Hub 啊或者你也可以将mimo_proxy.py下载到本地在本地直接启动就行4.2 安装依赖pip install starlette uvicorn httpx4.3 启动代理python mimo_proxy.py启动后会看到Microsoft Windows [版本 10.0.26200.8117] (c) Microsoft Corporation。保留所有权利。 D:\SoftWareConfig\mimo-proxypython mimo_proxy.py 10:10:53 [mimo-proxy] MiMo Proxy v1.4 on 0.0.0.0:8899 → https://token-plan-cn.xiaomimimo.com/v1 ✅ 代理已启动请在 Trae 中配置以下地址 本机访问: http://127.0.0.1:8899/v1/chat/completions 局域网: http://192.168.1.154:8899/v1/chat/completions ⚠️ 注意 1. 地址必须是完整路径包含 /v1/chat/completions 2. 不要用 0.0.0.0那是监听地址不是访问地址 3. API Key 填你的 MiMo API Key INFO: Started server process [41220] INFO: Waiting for application startup. 10:10:53 [mimo-proxy] httpx client initialized INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8899 (Press CTRLC to quit)4.4 配置 Trae在 Trae 的模型设置中API Base URLhttp://127.0.0.1:8899/v1API Key填你的 MiMo API Key代理会原样透传Modelmimo-v2.5-pro或其他受影响模型4.5 验证配置完成后正常在 Trae 中使用 MiMo 模型即可。多轮对话 工具调用场景下400 错误不会再出现。五、方案原理总结整个方案可以用一句话概括用空间换正确性——通过本地缓存reasoning_content在请求转发时自动补全缺失字段从而绕过 Agent 框架不回传扩展字段的问题。第一轮请求 用户 → Trae → Proxy → MiMo API │ ←←←←←←←←←←←←←←│ 返回 reasoning_content │ │ │ ① 缓存到本地 │ ↓ 本地缓存 第二轮请求Trae 丢失了 reasoning_content 用户 → Trae → Proxy ──→ MiMo API │ ↑ │ ② 从缓存 │ │ 注入回去 │ ↓ │ 注入后的完整 messages三层保障1.缓存命中 → 注入最佳情况完整保留模型推理上下文2.缓存未命中 → 降级剥离 tool_calls避免 400保留基本对话能力3.API 错误 → 重试5xx 自动重试 3 次提高稳定性六、写在最后这个问题本质上是 MiMo 为了保证推理质量而新增的协议要求与现有 Agent 框架的兼容性还在逐步推进中。在框架方完成适配之前Mintneko 的这个 Proxy 方案是一个非常实用的过渡方案——部署简单、侵入性低、对 Trae 的使用完全透明。感谢 Mintneko 大佬的开源贡献。参考链接MiMo 官方公告【重要公告】关于 Agent 类产品多轮会话中回传 reasoning_content 的说明mimo-proxy 项目mimo-proxy
Trae 调用 MiMo API 报错 400?一文搞懂原因并用 Proxy 完美解决
发布时间:2026/5/19 11:56:35
最近在用 Trae 集成 MiMo 模型进行多轮工具调用时频繁遇到Invalid request (400)错误。经过排查发现是 MiMo API 新增了对reasoning_content字段的回传要求。本文记录完整排查过程并介绍社区大佬 Mintneko 提供的 Proxy 解决方案。一、问题现象在 Trae 中使用 MiMo 模型如mimo-v2.5-pro进行多轮对话当模型在历史消息中产生过工具调用tool_calls后后续轮次的请求会直接报错这个路径之前mimo官方有个trae的对接文档不知道为啥现在没了应该就是发那个问题公告的原因trae现在不支持之前官方给的trae的对接链接是在token plan 的基础url上添加chat/completions 因为trae不会自己添加所以我这里之前的url是之前好使最近不行了。。。。。https://token-plan-cn.xiaomimimo.com/v1/chat/completionsInvalid request (Model Provider Error Code: 400, HTTP Status: 400)单轮对话或者不涉及工具调用的对话一切正常问题只在多轮 工具调用的场景下复现。二、排查过程2.1 官方公告经过搜索发现 MiMo 官方平台近期发布了一则公告公告链接关于 Agent 类产品多轮会话中回传 reasoning_content 的说明关于 Agent 类产品多轮会话中回传 reasoning_content 的说明当在 Agent 类产品的多轮会话中开启 MiMo 思考模式且历史会话中存在工具调用时后续所有 user 交互轮次中回传的 assistant 如果包含了工具调用必须完整回传reasoning_content字段否则 API 将返回 400 错误。之所以作此要求是因为历史reasoning_content一旦缺失模型上下文将不完整可能表现出指令遵循下降、幻觉增多等现象。受影响的产品包括TRAE、Cursor、Roo Code、GitHub Copilot CLI、Zed、AutoGen、Goose 等。受影响的模型包括MiMo-V2.5-Pro、MiMo-V2.5、MiMo-V2-Pro、MiMo-V2-Omni、MiMo-V2-Flash。2.2 根因分析简单来说问题的链条是这样的用户发起多轮对话 ↓ Trae 构造请求时将历史 assistant 消息含 tool_calls写入 messages ↓ 但 Trae 没有回传这些 assistant 消息的 reasoning_content 字段 ↓ MiMo API 发现有 tool_calls 却没有 reasoning_content → 400 错误为什么 Trae 不传reasoning_content这其实是大多数 Agent 框架的通用行为。这些框架遵循 OpenAI 的标准协议只关注content和tool_calls字段对reasoning_content这种扩展字段默认会忽略。所以历史消息中的推理内容在转发过程中被丢弃了。2.3 正确的回传方式按照官方文档正确的做法是把 assistant 消息的完整字段都保留下来# 错误 ❌ — 缺少 reasoning_content messages.append({ role: assistant, content: , tool_calls: [...] }) # 正确 ✅ — 完整回传 messages.append({ role: assistant, content: , tool_calls: [...], reasoning_content: The user wants to know... I should call... })但在 Trae 这类客户端中我们无法手动控制它构造 messages 的方式。这就需要一个中间层来帮忙——也就是 Proxy。三、解决方案MiMo Reasoning Content Proxy在 GitHub 上找到了社区大佬 Mintneko 提供的代理方案 mimo-proxy原理非常巧妙。3.1 核心思路在 Trae 和 MiMo API 之间加一层本地代理代理负责1.拦截并缓存每次 MiMo 返回的 assistant 消息中如果包含reasoning_content就把它缓存起来2.自动注入后续请求经过代理时检查历史消息中是否有 assistant 消息缺少reasoning_content如果有就从缓存中取出注入回去3.兜底降级如果缓存中找不到比如代理刚启动、缓存过期等就把该 assistant 消息的tool_calls剥离掉降级为纯文本避免触发 400Trae ──→ Proxy (localhost:8899) ──→ MiMo API │ │ │ ① 注入 reasoning_content │ │ ② 缓存返回的 reasoning │ │←──────────────────────────←│3.2 脚本逐模块解析下面对脚本的各个核心模块做一个详细介绍。模块一配置区MIMO_API_BASE https://token-plan-cn.xiaomimimo.com/v1 # MiMo API 地址 LISTEN_HOST 0.0.0.0 # 监听地址 LISTEN_PORT 8899 # 监听端口 CACHE_MAX_SIZE 2000 # 缓存最大条目数 CACHE_TTL 7200 # 缓存过期时间秒代理监听在本地8899端口Trae 的 API Base URL 改为http://127.0.0.1:8899/v1即可。缓存采用 LRU 策略最多存 2000 条每条 2 小时过期。模块二缓存机制这是整个方案的核心。缓存有两套索引索引方式键用途哈希索引SHA256(content tool_calls)[:16]按消息内容精确匹配Tool Call ID 索引tool_call_id按工具调用 ID 反查def _msg_hash(msg: dict) - str: content msg.get(content) or tool_calls json.dumps(msg.get(tool_calls) or [], sort_keysTrue) raw f{content}||{tool_calls} return hashlib.sha256(raw.encode()).hexdigest()[:16]为什么要两套索引因为 Trae 在回传历史消息时消息内容可能被稍作修改比如拼接了工具结果但tool_call_id通常是不变的。所以优先用哈希匹配匹配不到再用tool_call_id反查提高命中率。缓存操作使用 Python 的OrderedDict实现标准 LRUdef _cache_get(key: str) - str | None: if key in _cache: val, ts _cache[key] if time.time() - ts CACHE_TTL: # 未过期 _cache.move_to_end(key) # 移到末尾最近使用 return val del _cache[key] # 过期则删除 return None def _cache_set(key: str, value: str): if key in _cache: del _cache[key] _cache[key] (value, time.time()) while len(_cache) CACHE_MAX_SIZE: # 超出容量则淘汰最久未用的 _cache.popitem(lastFalse)模块三注入与降级逻辑这是处理请求的核心函数。遍历messages中的每条 assistant 消息检查是否需要处理def inject_reasoning(messages: list[dict]) - tuple[int, int]: for i, msg in enumerate(messages): if msg.get(role) ! assistant: continue if not msg.get(tool_calls): # 没有 tool_calls → 不需要处理 continue if msg.get(reasoning_content): # 已经有 reasoning_content → 跳过 continue # 尝试从缓存中查找 cached _cache_get(_msg_hash(msg)) if not cached: cached _find_by_tool_call_ids(msg) if cached: # ✅ 命中缓存 → 注入 reasoning_content msg[reasoning_content] cached else: # ⚠️ 未命中 → 降级剥离 tool_calls转为纯文本 del msg[tool_calls] # 同时在 content 中保留工具调用的摘要 msg[content] [Called get_current_weather]降级策略的思路很聪明MiMo API 只对「有tool_calls但没有reasoning_content」的消息报 400。那我把tool_calls去掉模型自然就不会要求reasoning_content了。虽然丢失了结构化的工具调用信息但至少不会报错而且在 content 中保留了工具调用的文本摘要模型仍然能理解上下文。模块四流式响应处理代理需要在流式传输过程中同时做两件事逐块转发给客户端 逐步累积完整消息用于缓存。async def _stream_proxy(client, url, headers, body): acc_content acc_reasoning acc_tool_calls [] async with client.stream(POST, url, headersheaders, jsonbody) as resp: async for raw_chunk in resp.aiter_bytes(): # 解析 SSE 数据 # 累积 reasoning_content、content、tool_calls # 原样转发给客户端 yield _sse(payload) # 收到 [DONE] 时将完整消息缓存 if payload [DONE]: if acc_reasoning and (acc_content or acc_tool_calls): _cache_set_with_index(hash, acc_reasoning, tool_call_ids)这个设计确保了缓存的实时性模型的每一次响应只要有reasoning_content都会在流结束后立即缓存后续轮次立刻可用。模块五错误处理与重试v1.4 版本重点增强了错误处理5xx 错误自动重试最多 3 次间隔递增1s、2s4xx 错误直接透传客户端参数问题不重试超时处理流式模式和非流式模式都有独立的超时重试逻辑空内容兜底如果 MiMo 只返回了reasoning_content而content为空自动将reasoning_content作为content返回# 空 content 兜底 if not msg.get(content) and not msg.get(tool_calls) and msg.get(reasoning_content): msg[content] msg[reasoning_content]模块六API 端点代理暴露了与 MiMo API 完全兼容的端点端点说明POST /v1/chat/completions核心聊天补全带注入/降级逻辑GET /v1/models模型列表直接透传GET /代理状态页GET /health健康检查四、使用步骤4.1 拉取mimo-proxy项目git clone https://github.com/Mintneko/mimo-proxy.git如果你拉不下来可以参考我上一篇博客里面有github处理方案用 OpenCode 装 Skills 一直失败它说是网不好但浏览器明明能上 git Hub 啊或者你也可以将mimo_proxy.py下载到本地在本地直接启动就行4.2 安装依赖pip install starlette uvicorn httpx4.3 启动代理python mimo_proxy.py启动后会看到Microsoft Windows [版本 10.0.26200.8117] (c) Microsoft Corporation。保留所有权利。 D:\SoftWareConfig\mimo-proxypython mimo_proxy.py 10:10:53 [mimo-proxy] MiMo Proxy v1.4 on 0.0.0.0:8899 → https://token-plan-cn.xiaomimimo.com/v1 ✅ 代理已启动请在 Trae 中配置以下地址 本机访问: http://127.0.0.1:8899/v1/chat/completions 局域网: http://192.168.1.154:8899/v1/chat/completions ⚠️ 注意 1. 地址必须是完整路径包含 /v1/chat/completions 2. 不要用 0.0.0.0那是监听地址不是访问地址 3. API Key 填你的 MiMo API Key INFO: Started server process [41220] INFO: Waiting for application startup. 10:10:53 [mimo-proxy] httpx client initialized INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8899 (Press CTRLC to quit)4.4 配置 Trae在 Trae 的模型设置中API Base URLhttp://127.0.0.1:8899/v1API Key填你的 MiMo API Key代理会原样透传Modelmimo-v2.5-pro或其他受影响模型4.5 验证配置完成后正常在 Trae 中使用 MiMo 模型即可。多轮对话 工具调用场景下400 错误不会再出现。五、方案原理总结整个方案可以用一句话概括用空间换正确性——通过本地缓存reasoning_content在请求转发时自动补全缺失字段从而绕过 Agent 框架不回传扩展字段的问题。第一轮请求 用户 → Trae → Proxy → MiMo API │ ←←←←←←←←←←←←←←│ 返回 reasoning_content │ │ │ ① 缓存到本地 │ ↓ 本地缓存 第二轮请求Trae 丢失了 reasoning_content 用户 → Trae → Proxy ──→ MiMo API │ ↑ │ ② 从缓存 │ │ 注入回去 │ ↓ │ 注入后的完整 messages三层保障1.缓存命中 → 注入最佳情况完整保留模型推理上下文2.缓存未命中 → 降级剥离 tool_calls避免 400保留基本对话能力3.API 错误 → 重试5xx 自动重试 3 次提高稳定性六、写在最后这个问题本质上是 MiMo 为了保证推理质量而新增的协议要求与现有 Agent 框架的兼容性还在逐步推进中。在框架方完成适配之前Mintneko 的这个 Proxy 方案是一个非常实用的过渡方案——部署简单、侵入性低、对 Trae 的使用完全透明。感谢 Mintneko 大佬的开源贡献。参考链接MiMo 官方公告【重要公告】关于 Agent 类产品多轮会话中回传 reasoning_content 的说明mimo-proxy 项目mimo-proxy