| 1 | # OpenCode comparison after live tests / 真实测试后的 OpenCode 对照 |
| 2 | |
| 3 | Date / 日期: 2026-09-05. Source baseline / 源码基线: |
| 4 | [`bbd72fb8b0bb6de580d2041a0150016227c63ac0`](https://github.com/anomalyco/opencode/tree/bbd72fb8b0bb6de580d2041a0150016227c63ac0). |
| 5 | This is a source comparison with Reasonix live experiments, not a same-account |
| 6 | end-to-end benchmark of the OpenCode executable. / 本文结合 OpenCode 固定源码与 |
| 7 | Reasonix 实测;没有宣称运行 OpenCode 完整客户端作同账户对照。 |
| 8 | |
| 9 | ## 1. Normal text completion without the requested tool / 正常结束却没有执行工具 |
| 10 | |
| 11 | Kimi K3 returned HTTP 200 and `stop` with no tool-call fields on the original |
| 12 | wire. It sometimes invented an echo result. Reasonix did not drop a received |
| 13 | call: the call was absent before parsing. Both low and max effort reproduced |
| 14 | this behavior with the minimal no-argument echo fixture. |
| 15 | |
| 16 | Kimi 的问题发生在模型输出层:原始响应没有工具调用,却输出了声称的结果; |
| 17 | low、max 两个档位均出现过。不能把调高思考档位当成已证实的修复。 |
| 18 | |
| 19 | OpenCode's [session loop](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/opencode/src/session/prompt.ts#L1096) |
| 20 | exits on a normal finish without tool parts. It explicitly continues when real |
| 21 | tool parts exist even if the provider says `stop`. It does not generally infer |
| 22 | an unperformed task from prose and automatically ask again. |
| 23 | Its [Kimi prompt selection](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/opencode/src/session/system.ts#L45) |
| 24 | and [action instructions](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/opencode/src/session/prompt/kimi.txt#L3) |
| 25 | make execution requirements explicit. Ordinary runs do not force tools: |
| 26 | [`required` is selected for JSON-schema structured output](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/opencode/src/session/prompt.ts#L1285). |
| 27 | |
| 28 | OpenCode 也不会因为用户的自然语言要求而普遍强制工具调用。应吸收它的明确 |
| 29 | 行动提示与真实工具状态判断,而不是把 `stop` 改为重试,或给所有轮次设置 |
| 30 | `tool_choice=required`,从而破坏问答、最终总结和权限边界。 |
| 31 | |
| 32 | Prompt-only experiment: adding a short execution-and-evidence instruction at |
| 33 | max effort completed 6/6 baseline/cut samples, versus 5/6 with the original |
| 34 | minimal prompt. Production prompts are unchanged. The samples were sequential, |
| 35 | small, and not statistically conclusive. A separate three-sample Kimi parameter |
| 36 | fixture passed twice; one run used a label that did not preserve the requested |
| 37 | Unicode/newline value. All three runs called each tool once. This semantic value |
| 38 | mismatch is not malformed JSON and is not solved by SDK tool-name repair. |
| 39 | |
| 40 | 短提示实验六例全完成,可作为优先验证的候选;不能直接宣称问题已经修复。 |
| 41 | 参数值不符应由工具返回具体校验错误,允许模型在授权范围内纠正;适配器不应 |
| 42 | 擅自替换业务参数。首个错误样本没有记录原始参数字节,故没有进一步断言其 |
| 43 | 精确错误值;三例工具各调用一次,也不能证明参数纠错循环已经验证。 |
| 44 | |
| 45 | Recommended change / 建议: |
| 46 | |
| 47 | - First validate a small, static instruction that action requests require real |
| 48 | tool execution and that completion claims require actual results. Evaluate |
| 49 | realistic read/edit/test tasks as well as the echo fixture. / 先验证短小、 |
| 50 | 稳定的行动提示,覆盖真实读取、编辑、测试任务;不能只优化一个 echo 用例。 |
| 51 | - If a host-owned workflow already has a verifiable unfinished required action, |
| 52 | allow at most one bounded reminder, sharing the existing turn budget. A |
| 53 | completed write, permission rejection, cancellation or unknown side effect |
| 54 | must prevent action replay. Ordinary conversation should retain normal stops. |
| 55 | / 仅在宿主已有确定的未完成动作状态时考虑一次提醒,共享预算;不得凭文字 |
| 56 | 猜任务,也不得重放已完成写入、拒绝权限或结果未知的操作。 |
| 57 | - This reminder would be a Reasonix extension, not behavior proven in OpenCode. |
| 58 | Any system-prompt change changes the cache prefix once; do not inject changing |
| 59 | reminders into healthy turns. / 自动提醒属于 Reasonix 的额外设计。固定提示 |
| 60 | 修改会使对应缓存前缀变化一次,不应向正常轮次持续注入动态提示。 |
| 61 | |
| 62 | ## 2. Opaque HTTP 400 on custom DeepSeek Messages / 自定义 Messages 的不透明 400 |
| 63 | |
| 64 | Six Go Flash/Pro replay-rejection probes deliberately damaged the second |
| 65 | outbound request while retaining the completed local tool result. Chat and |
| 66 | Responses returned an identifiable error and recovered: four probes each made |
| 67 | three requests and executed the tool once. Messages returned only |
| 68 | `{"model":"deepseek-v4-flash"}` or the Pro counterpart with HTTP 400. Both |
| 69 | Messages probes stopped after two requests, retaining the single completed tool. |
| 70 | |
| 71 | Go 的 Chat、Responses 四例能识别并修复;Messages 两例缺少具体错误原因。 |
| 72 | 我们知道测试故意破坏了回放,但真实客户端没有代理的内部知识,因此不能由 |
| 73 | HTTP 400 加模型名称推断 reasoning 缺失。 |
| 74 | |
| 75 | OpenCode's [retry classifier](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/opencode/src/session/retry.ts#L85) |
| 76 | also requires retryable status/metadata or recognized error text; this bare 400 |
| 77 | body does not satisfy those conditions. Its current gateway |
| 78 | [requires matching wire formats](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/console/app/src/routes/zen/util/handler.ts#L214), |
| 79 | and the [same-format converter is an identity](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/console/app/src/routes/zen/util/provider/provider.ts#L215). |
| 80 | This source snapshot does not establish which deployed upstream or intermediary |
| 81 | removed the details. / 当前公开源码不能证明是哪一层部署丢失了错误详情,不能 |
| 82 | 直接把现场现象归因于某个转换函数。 |
| 83 | |
| 84 | Recommended change / 建议: |
| 85 | |
| 86 | - Continue automatic repair for an explicit replay error or locally provable |
| 87 | invalid history, using the existing one-time repair budget. / 明确回放错误、 |
| 88 | 本地可证明无效的历史继续走现有一次修复路径。 |
| 89 | - Preserve safe status, error code and available request identifiers; surface |
| 90 | an explicit “upstream did not provide the cause” diagnostic for opaque errors. |
| 91 | Do not retry all 400s or fabricate missing thinking/signatures. / 不透明错误 |
| 92 | 清楚说明上游未提供原因;不把全部 400 改成自动重生成,不伪造证明。 |
| 93 | - The service owner should preserve structured errors and retry headers on |
| 94 | failure paths. The audited gateway keeps only content-type/cache-control from |
| 95 | upstream headers on this path; locally generated quota errors have a separate |
| 96 | path. No upstream issue/comment was posted. / 应由服务端保留错误和重试提示; |
| 97 | 本次没有代用户向上游提交 issue 或评论。 |
| 98 | - Keep the recommended DeepSeek Chat route, while respecting explicit custom |
| 99 | Messages/Responses selections. Provide a deliberate recovery-from-valid-history |
| 100 | action if automatic classification is impossible, preserving completed results. |
| 101 | / 保留推荐的 Chat 默认路由,自定义协议不静默切换;无法自动判断时,可提供 |
| 102 | 明确的有效历史恢复入口,保留已完成结果,不重放未知写入。 |
| 103 | |
| 104 | ## 3. Search succeeded but structured sources were absent / 搜索成功但来源缺失 |
| 105 | |
| 106 | Two Go Responses samples completed native search and the following echo call. |
| 107 | The raw search action contained queries but no sources; the final response's |
| 108 | message annotations were empty. This is a source-availability gap, not a failed |
| 109 | search request or demonstrated parser loss. Go's DeepSeek Messages search |
| 110 | sample supplied ten structured sources. |
| 111 | |
| 112 | OpenCode's [independent websearch tool](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/opencode/src/tool/websearch.ts#L65) |
| 113 | uses Exa or Parallel through a separate MCP-style call, independent of the main |
| 114 | model's inference protocol. It returns actual tool output and asks for the |
| 115 | search permission. It does not establish that native Responses must supply |
| 116 | `action.sources`. / OpenCode 通过独立搜索服务提供工具结果,主模型用哪种协议 |
| 117 | 与搜索后端解耦;不能据此声称原生 Responses 一定能返回来源。 |
| 118 | |
| 119 | Recommended change / 建议: |
| 120 | |
| 121 | - Reuse Reasonix's existing independent `web_search` owner. Keep the main model, |
| 122 | protocol and thinking settings unchanged. / 复用已实现的独立搜索,不再建平行系统。 |
| 123 | - Distinguish search execution, raw item replay, and structured source availability |
| 124 | in tests and presentation. Missing sources are not a transport retry signal. |
| 125 | / 分开验证搜索执行、原始 item 回放与来源可用性;缺来源不触发网络重试。 |
| 126 | - Use the configured source-capable search route. Any fallback requires an |
| 127 | already configured/authorized route and a finite auxiliary budget; do not send |
| 128 | a vendor credential to another service. Never infer verified sources from |
| 129 | generated prose. / 使用已配置且能提供来源的后端;后备搜索共享辅助预算, |
| 130 | 不跨供应商发送密钥,也不把模型生成的 URL 当作已验证来源。 |
| 131 | |
| 132 | ## 4. Protocol-specific compatibility and retry ownership / 分协议兼容与重试归属 |
| 133 | |
| 134 | OpenCode [normalizes interleaved Chat reasoning, including empty values](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/opencode/src/provider/transform.ts#L322). |
| 135 | Its [Anthropic normalization preserves signed empty blocks](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/opencode/src/provider/transform.ts#L168). |
| 136 | These transformations should not be copied as a universal empty-reasoning rule. |
| 137 | Reasonix should retain its protocol contracts: Chat empty-field compatibility; |
| 138 | actual unsigned thinking for DeepSeek Messages; original proofs for native |
| 139 | Claude; complete raw items for Responses. / 不应跨协议照搬补空值。此前实现的 |
| 140 | 能力判定与原始回放资料仍应保留。 |
| 141 | |
| 142 | OpenCode [disables SDK retries by default and repairs malformed tool calls at the tool boundary](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/opencode/src/session/llm.ts#L296). |
| 143 | The pinned [session retry policy](https://github.com/anomalyco/opencode/blob/bbd72fb8b0bb6de580d2041a0150016227c63ac0/packages/opencode/src/session/retry.ts#L26) |
| 144 | uses five retries, exponential delay and jitter. Reasonix already has centralized |
| 145 | request ownership; retain its agreed Pi-style three fast retries and explicitly |
| 146 | selected main-session waiting semantics. Do not silently replace them with this |
| 147 | OpenCode version's constants. / 借鉴重试归属与错误分类,不照抄次数,也不声称 |
| 148 | Reasonix 的主会话持续等待是 OpenCode 的默认行为。 |
| 149 | |
| 150 | ## Delivery and next priority / 交付与下一步优先级 |
| 151 | |
| 152 | Already implemented during testing: correct Go client/session headers across |
| 153 | all three adapters and quota-aware errors that distinguish allowance exhaustion |
| 154 | from invalid credentials. Root/desktop suites, lint and focused race tests passed. |
| 155 | This analysis introduces no silent model/protocol switch or new global retry. |
| 156 | |
| 157 | 本轮已经落地三协议请求头与额度分类修复。剩余优先级:验证行动提示的真实任务 |
| 158 | 收益;补齐不透明上游错误的诊断/明确恢复入口;完善搜索来源可用性状态。 |
| 159 | 所有动作继续复用当前恢复预算、工具持久化和独立搜索实现。 |
| 160 | |
| 161 | See [live results and limitations](MULTIPROVIDER_VALIDATION.md). / 具体样本、 |
| 162 | 未通过场景及验证边界见实测报告,不能宣布 #9808 的任意端点场景已完全解决。 |
| 163 |