Skip to content

fix: 修复 thinking 模式 reasoning_content 未回传#1696

Open
QQHKX wants to merge 5 commits into
Mai-with-u:mainfrom
QQHKX:fix/reasoning-content-passback
Open

fix: 修复 thinking 模式 reasoning_content 未回传#1696
QQHKX wants to merge 5 commits into
Mai-with-u:mainfrom
QQHKX:fix/reasoning-content-passback

Conversation

@QQHKX
Copy link
Copy Markdown
Contributor

@QQHKX QQHKX commented May 15, 2026

摘要

  • 保留 Maisaka assistant 历史消息中的 reasoning_content
  • 在 OpenAI 兼容请求中按 provider 的推理字段名回传 assistant reasoning。
  • 补齐请求快照、Hook 消息载荷、无工具请求清洗链路中的 reasoning_content 传递。
  • 增加回归测试,覆盖 reasoning 保存、清洗与回传场景。

背景

部分 OpenAI 兼容服务商在 thinking mode 下要求多轮请求必须把上一轮 assistant 返回的 reasoning_content 原样传回 API。

目前 MaiBot 已经能从模型响应中读取 reasoning_content,但该字段没有被保存到 assistant 历史消息中,也不会在下一轮请求中回传。因此在使用 mimoAPI、DeepSeek 兼容 thinking mode 等接口时,可能触发 400 错误:

The reasoning_content in the thinking mode must be passed back to the API.

Refs #1608

修改内容

  • 为统一 Message 和 Maisaka AssistantMessage 增加 reasoning_content 字段。
  • 模型响应后将 generation_result.reasoning 写入 assistant 历史消息。
  • _convert_messages() 在 assistant 历史存在 reasoning 时按 provider 字段名写入 OpenAI-compatible payload。
  • 无工具请求清洗 assistant tool_calls 时保留 reasoning,避免清洗链路丢字段。
  • 请求快照、Hook 消息载荷和 LLM service 字典恢复链路保留 reasoning_content
  • Planner 内部 last reasoning 优先使用 response.reasoning_content,避免把正式输出误当成推理内容。

临时规避方案

在该修复合入前,受影响用户可以对相关模型配置额外参数:

{
  enable_thinking: false
}

这样可以临时关闭 thinking mode,绕过上游对 reasoning_content 回传的强制要求。

测试

已运行:

uv run pytest pytests/test_openai_client_toolless_request.py

结果:

12 passed

已运行:

uv run pytest pytests/test_maisaka_timing_gate.py::test_assistant_message_keeps_reasoning_content_for_llm_history pytests/test_maisaka_timing_gate.py::test_timing_gate_filter_preserves_reasoning_content_when_trimming_tool_calls

结果:

2 passed

已运行:

uv run ruff check src/llm_models/payload_content/message.py src/llm_models/model_client/openai_client.py src/llm_models/request_snapshot.py src/maisaka/context_messages.py src/maisaka/chat_loop_service.py src/maisaka/reasoning_engine.py src/plugin_runtime/hook_payloads.py src/services/llm_service.py pytests/test_openai_client_toolless_request.py pytests/test_maisaka_timing_gate.py

结果:

All checks passed!

已知情况

uv run pytest pytests/test_maisaka_timing_gate.py 全量运行时仍有 3 个既有测试失败,原因是当前代码中的 timing 工具名已经是 no_action,但部分旧测试仍断言 no_reply。该失败与本次 reasoning_content 修复无关。

影响范围

该修改只在 assistant 历史消息存在 reasoning_content 时额外回传字段。普通非 thinking 模式、没有 reasoning 的模型请求不会额外增加该字段。

Summary by CodeRabbit

  • New Features

    • 在对话流程和消息交换中保留并传递“推理内容”,使生成的响应与历史记录中包含更完整的思考文本。
  • Tests

    • 新增多项测试,验证在消息转换、历史过滤和无工具请求场景下推理内容不会丢失。

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7e655baf-38f4-4493-80f9-fef894e46a50

📥 Commits

Reviewing files that changed from the base of the PR and between 14ff8f4 and 77d0e7f.

📒 Files selected for processing (5)
  • pytests/test_openai_client_toolless_request.py
  • src/llm_models/model_client/openai_client.py
  • src/llm_models/payload_content/message.py
  • src/webui/routers/expression.py
  • src/webui/routers/memory.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/llm_models/payload_content/message.py
  • pytests/test_openai_client_toolless_request.py
  • src/llm_models/model_client/openai_client.py

Walkthrough

本 PR 在消息模型、序列化、Maisaka 上下文、OpenAI 客户端、对话循环与 WebUI 路由处新增或传播 reasoning_content 字段,并补充对应单元测试以验证该字段在消息构建、序列化、toolless 清理与历史过滤中的保留与映射行为。

变更详情

推理内容端到端支持链路

Layer / File(s) Summary
消息核心数据结构与推理字段
src/llm_models/payload_content/message.py
Message 类新增 reasoning_content: str | None 字段,MessageBuilder 新增 set_reasoning_content()(仅限 Assistant 角色)与归一化逻辑,并在 build() 时传入该字段。
Maisaka 上下文消息集成
src/maisaka/context_messages.py
AssistantMessage 新增 reasoning_content 字段,_build_message_from_sequence() 支持可选推理参数,to_llm_message() 将推理内容传入消息构建流程。
序列化与反序列化支持
src/llm_models/request_snapshot.py, src/plugin_runtime/hook_payloads.py, src/services/llm_service.py
在快照、Hook 载荷和 LLM 服务中添加 reasoning_content 的条件序列化与反序列化,确保推理内容的持久化和恢复。
OpenAI 客户端推理字段映射
src/llm_models/model_client/openai_client.py
_extract_reasoning_content 支持可空的 reasoning_key_convert_messages 扩展为接收 reasoning_key 并根据其值映射推理字段;无工具请求清洗时保留推理内容;_execute_response_request 传入 self.reasoning_key 以激活字段映射。
对话循环与推理引擎集成
src/maisaka/chat_loop_service.py, src/maisaka/reasoning_engine.py
ChatResponse 新增 reasoning_content 字段;chat_loop_step 从生成结果捕获推理内容并赋值给 AssistantMessage 和响应;_filter_history_for_request_kind 在过滤工具时保留推理内容;reasoning_engine 优先使用 response.reasoning_content 并在历史中保存。
WebUI 路由默认参数常量
src/webui/routers/memory.py, src/webui/routers/expression.py
引入 EMPTY_DICT_BODYUPLOAD_FILES_FIELDCHAT_IDS_QUERY 等 FastAPI 参数常量,并将多个路由的默认值替换为这些常量以复用声明。
测试验证推理内容保留
pytests/test_maisaka_timing_gate.py, pytests/test_openai_client_toolless_request.py
新增多条测试用例,验证:AssistantMessage.to_llm_message() 保留 reasoning_content、历史过滤在修剪 tool_calls 时保留 reasoning_content、toolless 清理保留 assistant 推理且将 tool_calls 置空、_convert_messages 根据 reasoning_key 回填对应字段。

Sequence Diagram

sequenceDiagram
  participant LLM
  participant chat_loop_step
  participant OpenaiClient
  participant AssistantMessage
  participant chat_history
  participant ChatResponse
  LLM->>chat_loop_step: 返回 response + reasoning_content
  chat_loop_step->>AssistantMessage: 构造并 set reasoning_content
  chat_loop_step->>OpenaiClient: 调用 _convert_messages(reasoning_key)
  AssistantMessage->>chat_history: 写入历史(含 reasoning_content)
  chat_loop_step->>ChatResponse: 返回 reasoning_content
Loading

预估代码审查工作量

🎯 3 (Moderate) | ⏱️ ~20 分钟

已更新总体变更说明(供合并请求描述使用)

本 PR 在系统各层添加了模型推理内容(reasoning_content)的端到端支持,从消息模型定义、序列化机制、Maisaka 框架集成、OpenAI 客户端配置,到对话循环和推理引擎的完整流程中确保推理内容的捕获、保留与正确传递。

可能相关的问题

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR标题准确指出了本次修复的核心问题:thinking模式下reasoning_content未被回传,与changeset主要改动内容一致。
Description check ✅ Passed PR描述包含摘要、背景、修改内容、临时规避、测试结果和影响范围等完整信息,符合模板核心要求。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14ff8f4e5f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

tool_call_id=message.tool_call_id,
tool_name=message.tool_name,
tool_calls=None,
reasoning_content=message.reasoning_content,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve reasoning when sanitizing tool-call-only assistant turns

在无工具请求清洗链路里,reasoning_content 只会在重建 assistant 消息时被拷贝,但这段逻辑仍受前面的 if not message.parts: continue 约束。对于“仅有 tool_calls、正文为空”的 assistant 历史(这是模型函数调用常见返回形态),该消息会被直接丢弃,导致上一轮推理内容无法回传到下一轮请求,thinking 模式下仍可能触发要求回传 reasoning 的 400 错误。

Useful? React with 👍 / 👎.

@QQHKX
Copy link
Copy Markdown
Contributor Author

QQHKX commented May 15, 2026

补充说明一下当前 PR 的状态,方便维护者判断后续处理方式:

  1. 本 PR 的核心修复已经完成,范围目前已重新收敛到 reasoning_content 回传链路本身,包括:

    • src/llm_models/**
    • src/maisaka/**
    • src/plugin_runtime/hook_payloads.py
    • src/services/llm_service.py
    • 对应回归测试
  2. 之前为排查工作流临时加入过 src/webui/routers/memory.pysrc/webui/routers/expression.py 的 B008 lint 修复,但这两部分已经通过后续提交从 PR diff 中剥离,避免超出 issue 本身的修复边界。

  3. 当前 PR 仍然被 ruff 阻塞,但从本地和工作流日志看,剩余失败主要来自仓库现存的全局 lint 问题,并不集中在本 PR 最终改动文件。当前可见的失败来源主要包括:

    • src/A_memorix/**
    • src/A_memorix/scripts/**
    • scripts/run_a_memorix_webui_backend.py
    • pytests/test_gemini_thought_signatures.py
    • pytests/webui/test_memory_routes_integration.py
    • src/maisaka/monitor_events.py
    • 以及少量其他历史文件
  4. 也就是说,现在的情况更像是:

    • 本 PR 的主修复逻辑已经完成
    • review comment 已处理
    • PR 范围已尽量保持最小
    • 但仓库当前的全量 ruff 基线本身还有较多历史问题,导致这个 PR 无法直接变绿

如果维护上希望继续推进到绿灯,我可以继续补修这些全局 lint 问题;但从 issue 边界和最小 bugfix 原则看,它们已经明显超出本 PR 原始修复范围,所以想先同步给维护者确认一下处理策略。

@QQHKX
Copy link
Copy Markdown
Contributor Author

QQHKX commented May 15, 2026

image 现在只能这样关闭thinking来解决awa

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant