# AI reasoning_content 回传与“继续思考”实验研究报告 日期:2026-08-23 范围:OpenCodeGo / `deepseek-v4-flash`,OpenAI 兼容 `/chat/completions` 状态:实验性研究,未集成到主项目 ## 摘要 本报告记录了为验证“将模型上一轮 `reasoning_content` 截断/修改后回传,模型是否能够继续合理思考”而执行的一系列实验。 结论是:**仅发送 `reasoning_content` 或“上一轮思维链”并不稳定;成功率最高的方案是伪造一段 tool call 历史,并把续写要求、输出格式和需要引用的内容放入 tool 结果中。** 与本研究相关的代码在 `AiV2.Tests/Program.cs` 中,默认不会执行。 ## 目标 验证以下问题: 1. 模型能否在不追加新的 user 消息时,基于 assistant 的 `reasoning_content` 继续生成最终回答? 2. 截断后的 `reasoning_content` 是否仍然能被模型读取和引用? 3. 伪造 `tool_calls` + `tool` 结果历史是否比普通多轮对话更有效? 4. 工具结果中的措辞是否会显著影响模型对“目标思维链”的定位? 5. 标记在思维链中的位置是否影响模型的可召回性? ## 实验方法与基础设施 所有实验使用: - 当前应用配置文件中的 OpenCodeGo / `deepseek-v4-flash` - `stream=false` 的一次性 OpenAI 兼容请求 - 初始问题: `A 比 B 高 20%,B 比 C 高 25%,那么 A 比 C 高多少?` - 确定性记忆标记: `TOKEN_MARK=731942` 专用测试位于 `AiV2.Tests/Program.cs`,默认跳过。 执行方式: ```powershell dotnet build .\AiV2.Tests\AiV2.Tests.csproj /p:AiV2TestsBuilding=true $env:ARR_AI_E2E = "1" $env:ARR_AI_E2E_TOOL = "1" $env:ARR_AI_TOOL_REPORT_PATH = "AI_reasoning_continuation_position_sweep_report.md" & .\AiV2.Tests\bin\Debug\net461\AiV2.Tests.exe ``` ## 尝试过程 ### 1. 普通多轮:assistant 推理 + 正文再回传 消息结构: ```text system → user → assistant(reasoning_content + content) → user ``` 结果:协议层接受 `reasoning_content`,模型通常会重新推导,但无法确认它真正延续了旧思维链。 ### 2. 只发 assistant 推理,不追加 user 消息结构: ```text system → user → assistant(reasoning_content,content 不发送) ``` 结果:OpenCodeGo 接受最后一条 assistant 消息并生成完成,但新 reasoning 往往从题目重新开始。 ### 3. 伪造 tool call 历史 消息结构: ```text system → user → assistant(content 不发送, reasoning_content, tool_calls) → tool(tool_call_id, 工具结果) ``` 请求同时声明 `tools`,并将 `tool_choice` 设置为 `"none"`。 工具结果中可以注入: - “继续当前推理”指令 - 输出格式要求 - 需要引用的旧思维链内容 - “如果没有该内容,明确回答不存在”的边界条件 这一步开始观察到:**模型可能在部分运行中读取并引用 `reasoning_content`。** ### 4. 确定性记忆标记 为避免模型从工具结果中照抄标记,标记值只注入到 `reasoning_content`,`tool` 结果中不出现标记值。 ### 5. 措辞 A/B 对比三种措辞: ```text A:上一轮思维链 B:当前推理 C:调用工具之前的思维链 D:本次 tool_calls 消息中携带的 reasoning_content ``` 去掉“上一轮”“截断点”等歧义词后,B/D 的命中率明显更高。 ### 6. 标记位置扫描 使用成功率最高的两种措辞: - B:当前推理 - D:本次 tool_calls 消息中携带的 reasoning_content 标记插入完整思维链的 25%、50%、75%、末尾四个位置,每个组合重复 5 次。 ## 结果 ### 措辞 A/B(每个组合 3 次) | 工具提示措辞 | 正确写出标记 | 明确说标记不存在 | |---|---:|---:| | A:上一轮思维链 | 2 / 3 | 1 / 3 | | B:当前推理 | 3 / 3 | 0 / 3 | | C:调用工具之前的思维链 | 2 / 3 | 1 / 3 | | D:tool_calls 携带的 reasoning_content | 3 / 3 | 0 / 3 | ### 位置扫描(每个组合 5 次) | 措辞 | 标记位置 | 正确写出标记 | 明确说没有 | 请求失败 | |---|---|---:|---:|---:| | B | 25% | 4 / 5 | 1 / 5 | 0 | | B | 50% | 5 / 5 | 1 / 5 | 0 | | B | 75% | 5 / 5 | 1 / 5 | 0 | | B | 末尾 | 3 / 5 | 3 / 5 | 0 | | D | 25% | 4 / 5 | 0 / 5 | 1(HTTP 503) | | D | 50% | 5 / 5 | 0 / 5 | 0 | | D | 75% | 5 / 5 | 2 / 5 | 0 | | D | 末尾 | 4 / 5 | 2 / 5 | 0 | ## 关键发现 1. **tool call 是当前最有效的载体。** 把“继续推理、输出格式、引用要求”放进 tool 结果,模型会把这些内容当作任务输入,而不是普通 user 消息。 2. **措辞决定模型能否正确定位思维链。** “当前推理”和“本次 tool_calls 消息中携带的 reasoning_content”比“上一轮思维链”更稳定。不要使用“上一轮”“截断点”这些容易引起歧义的词。 3. **标记在思维链开头/中部时最容易召回。** 50% 和 75% 位置均为 5/5;末尾位置明显下降。这说明不要把关键事实放在 `reasoning_content` 末尾。 4. **“正确写出标记”和“明确说没有”不是互斥的。** 模型有时先否定、后在同一输出中写出标记。统计时两者可能同时为真,人工审阅必须以完整 reasoning 和正文为准。 5. **服务端不稳定。** D/25% 有一次 HTTP 503,属于服务端错误,不是模型失败。OpenCodeGo 未返回 `usage`,因此无法评估成本/token。 6. **主项目不应直接启用该机制。** 当前实验只能证明“部分情况下有效”,不足以作为生产依赖。 ## 推荐方案 ### 推荐消息结构 ```text system → user(原始任务) → assistant( content 不发送, reasoning_content, tool_calls: [{ id, type: "function", function: { name, arguments } }] ) → tool( tool_call_id, content: "工具结果:请继续你本次 tool_calls 消息中携带的 reasoning_content。 请原样写出其中的内部记忆标记;如果没有,请明确回答“标记不存在”。 请使用 Markdown 输出。" ) ``` 请求级配置: ```json { "tools": [ { "type": "function", "function": { "name": "analysis_hint", "description": "提供继续上一个内部推理的提示", "parameters": { "type": "object", "properties": { "instruction": { "type": "string" } }, "required": ["instruction"] } } } ], "tool_choice": "none" } ``` ### 推荐提示词措辞 推荐: ```text 请继续你当前正在进行的内部推理。 请继续你本次 tool_calls 消息中携带的 reasoning_content。 ``` 不推荐: ```text 上一轮思维链在这里被截断,请继续。 ``` ## 对主项目的建议 - 保留现有 UI 对 `reasoning_content` 的展示。 - 不要在生产管线中自动发送/截断/修改 `reasoning_content`。 - 如未来需要接入,优先使用 tool call 载体,并将关键指令、格式和引用要求放入 tool 结果。 - 不要假设 `reasoning_content` 的末尾内容一定能被模型召回。 - 增加原始响应/请求日志,以便区分“模型未看到”和“模型看到了但没引用”。 ## 专用测试状态 - 测试代码:`AiV2.Tests/Program.cs` 中的 `OpenCodeGoFakeToolCallTests`。 - 默认行为:不执行。 - 启用条件:`ARR_AI_E2E=1` 且 `ARR_AI_E2E_TOOL=1`。 - 输出:Markdown 报告,路径通过 `ARR_AI_TOOL_REPORT_PATH` 指定。 主项目中的实验性 `ChatMessage` 扩展、`AiReasoningContinuationSettings`、UI 开关、回传/降级逻辑和 tool call 历史构造均已移除。