<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Opencode on Svtter's Blog</title><link>https://svtter.cn/tags/opencode/</link><description>Recent content in Opencode on Svtter's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 02 Jun 2026 09:46:55 +0800</lastBuildDate><atom:link href="https://svtter.cn/tags/opencode/index.xml" rel="self" type="application/rss+xml"/><item><title>从 claude code 转向 omp/opencode</title><link>https://svtter.cn/p/%E4%BB%8E-claude-code-%E8%BD%AC%E5%90%91-omp/opencode/</link><pubDate>Tue, 02 Jun 2026 09:46:55 +0800</pubDate><guid>https://svtter.cn/p/%E4%BB%8E-claude-code-%E8%BD%AC%E5%90%91-omp/opencode/</guid><description>&lt;img src="https://svtter.cn/p/%E4%BB%8E-claude-code-%E8%BD%AC%E5%90%91-omp/opencode/cover.jpg" alt="Featured image of post 从 claude code 转向 omp/opencode" /&gt;&lt;p&gt;如果你不是在使用 claude 订阅，那么我建议你转向 omp（oh-my-pi）。&lt;/p&gt;
&lt;h2 id="为什么"&gt;为什么
&lt;/h2&gt;&lt;p&gt;原因不复杂。目前按照 claude code 发展趋势和策略，claude 会朝向专用 agent 发展。每次 claude 更新都会遇到新的问题。Anthropic 接口虽然现在几个国内的厂商是兼容的，但是总归是有一定的时效。需要一定时间去 catch up 上游 Anthropic 的更改。&lt;/p&gt;
&lt;p&gt;现状是 Anthropic 兼容 API 是厂商提供 coding agent/coding plan 的重要标准。但是实际上厂商也在转向。作为重要的组成部分，agent harness 一定要掌握在自己的手里。&lt;/p&gt;
&lt;h2 id="opencode"&gt;opencode
&lt;/h2&gt;&lt;p&gt;如果喜欢 opencode，可以考虑使用 opencode。目前来说比较稳定，但是新功能开发不够快。&lt;/p&gt;
&lt;p&gt;我之前写过很多 opencode 相关的文章，可以作为参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/%E9%AB%98%E6%95%88%E7%9C%81%E9%92%B1%E6%88%91%E7%9A%84-ai-agent-%E5%B7%A5%E4%BD%9C%E6%B5%81%E9%80%89%E6%8B%A9/" &gt;高效省钱：我的 AI Agent 工作流选择&lt;/a&gt; — OpenCode + Gemini 的高性价比工作流&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/%E6%9C%80%E8%BF%91%E5%8F%91%E7%8E%B0%E5%A5%BD%E7%94%A8%E7%9A%84-mcp-%E5%B7%A5%E5%85%B7/" &gt;最近发现好用的 MCP 工具&lt;/a&gt; — OpenCode/Claude Code 配套工具推荐&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%BC%98%E5%8C%96%E8%AE%B0%E5%BD%95/" &gt;OpenCode 配置优化&lt;/a&gt; — Token 和配置优化技巧&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/opencode-three-months-reflection/" &gt;探索 opencode 之后，我的一些思考&lt;/a&gt; — 三个月使用回顾与思考&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/opencode-actions-%E4%B8%80%E4%B8%AA-coding-review-agent/" &gt;opencode-actions - 一个 coding review agent&lt;/a&gt; — 基于 OpenCode 的 GitHub Action&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="omp"&gt;omp
&lt;/h2&gt;&lt;p&gt;但是我现在更加推荐 &lt;a class="link" href="https://github.com/can1357/oh-my-pi" target="_blank" rel="noopener"
&gt;omp&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;omp 对于 &lt;a class="link" href="https://github.com/can1357/oh-my-pi/commit/933058a24156e219ce2dc9916ebaeeb7a8d2e665" target="_blank" rel="noopener"
&gt;/goal&lt;/a&gt;、&lt;a class="link" href="https://github.com/can1357/oh-my-pi/commit/4f45cf1fb4a9ab41481421fa8acb7162a90cac09" target="_blank" rel="noopener"
&gt;/loop&lt;/a&gt; 以及 &lt;a class="link" href="https://github.com/can1357/oh-my-pi/commit/5fae08d2b734c00359f129728ddd61a248d25102" target="_blank" rel="noopener"
&gt;workflow&lt;/a&gt; 的支持都非常快速。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/can1357/oh-my-pi/commits/main" target="_blank" rel="noopener"
&gt;omp commit&lt;/a&gt; 的迭代频率非常高，基本每天都在更新。&lt;/p&gt;</description></item><item><title>Multi-Review：多 Agent 并发代码审查的工程实践</title><link>https://svtter.cn/p/multi-review%E5%A4%9A-agent-%E5%B9%B6%E5%8F%91%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5%E7%9A%84%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/</link><pubDate>Fri, 29 May 2026 10:00:00 +0800</pubDate><guid>https://svtter.cn/p/multi-review%E5%A4%9A-agent-%E5%B9%B6%E5%8F%91%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5%E7%9A%84%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/</guid><description>&lt;img src="https://svtter.cn/p/multi-review%E5%A4%9A-agent-%E5%B9%B6%E5%8F%91%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5%E7%9A%84%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/cover.png" alt="Featured image of post Multi-Review：多 Agent 并发代码审查的工程实践" /&gt;&lt;p&gt;在&lt;a class="link" href="https://svtter.cn/p/opencode-actions-%e4%b8%80%e4%b8%aa-coding-review-agent/" &gt;上一篇文章&lt;/a&gt;中，我介绍了 opencode-actions——一个基于 OpenCode 构建的 GitHub Action，用来做自动化的 PR 代码审查。那个版本的 review action 用的是一个模型、一个 prompt，针对整个 PR diff 做一次综合审查。&lt;/p&gt;
&lt;p&gt;这个方案能用，但有个明显的问题：&lt;strong&gt;一个 prompt 要同时关注代码质量、安全漏洞、性能问题和架构设计，每一项都不够深入。&lt;/strong&gt; 就像让一个工程师同时扮演 QA、安全审计、DBA 和架构师——每个角色都能说两句，但都不专业。&lt;/p&gt;
&lt;p&gt;Cloudflare 在今年四月发了一篇&lt;a class="link" href="https://blog.cloudflare.com/ai-code-review/" target="_blank" rel="noopener"
&gt;博客&lt;/a&gt;，详细介绍了他们用 OpenCode 构建的多 Agent 并发代码审查系统。他们用了 7 个专业 reviewer 加 1 个 coordinator，在 5169 个仓库上跑了 13 万次审查。核心思路是：&lt;strong&gt;不要让一个模型什么都看，而是让多个专业模型各看各的，最后用一个 coordinator 统一汇总。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;看完这篇文章，我决定在 opencode-actions 中实现类似的能力。这篇文章就是 multi-review action 的设计记录。&lt;/p&gt;
&lt;h2 id="为什么需要多-agent-并发审查"&gt;为什么需要多 Agent 并发审查？
&lt;/h2&gt;&lt;p&gt;单模型审查的问题不只是&amp;quot;不够深入&amp;quot;。实际使用中有几个具体的痛点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;维度冲突&lt;/strong&gt;：安全审查要求严格（宁可误报不能漏报），性能审查要求务实（关注可测量的指标而非理论风险）。一个 prompt 很难同时表达两种截然不同的审查哲学。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;上下文稀释&lt;/strong&gt;：当 prompt 中塞了太多&amp;quot;你要检查 A、B、C、D、E&amp;quot;时，模型的注意力被分散，每个维度的审查质量都打折扣。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结果模糊&lt;/strong&gt;：一个模型输出的&amp;quot;有条件合并&amp;quot;到底是因为安全问题还是性能问题？很难区分。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Cloudflare 的做法是把审查拆成独立的维度，每个 reviewer 只关注自己擅长的领域。他们的 security reviewer 有明确的 &amp;ldquo;What to Flag&amp;rdquo; 和 &amp;ldquo;What NOT to Flag&amp;rdquo; 指令——告诉模型什么该忽略，比告诉它什么该检查更重要。&lt;/p&gt;
&lt;h2 id="架构并发-reviewer--coordinator"&gt;架构：并发 Reviewer + Coordinator
&lt;/h2&gt;&lt;p&gt;multi-review 的核心架构用一句话概括：&lt;strong&gt;N 个专业 reviewer 并发审查，1 个 coordinator 汇总去重。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;整个流程是这样的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PR 触发 → 获取 diff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 启动 OpenCode Server（单实例）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────┼───────────┐───────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ ↓ ↓ ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Quality Security Performance Architecture
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(reviewer) (reviewer) (reviewer) (reviewer)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────┴───────────┴───────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ (Promise.all)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Coordinator
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (去重 + 交叉验证)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; PR Comment
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;关键设计选择：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;单 Server 多 Session&lt;/strong&gt;：只启动一个 OpenCode Server 实例，所有 reviewer 和 coordinator 共享。这样避免了 N 个 reviewer 各自冷启动 MCP server 的开销。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Promise.all 并发&lt;/strong&gt;：所有 reviewer 通过 &lt;code&gt;Promise.all&lt;/code&gt; 同时启动，不排队。实际运行时间取决于最慢的那个 reviewer，而不是所有 reviewer 的总和。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全局超时 + 自适应余量&lt;/strong&gt;：一个全局 deadline，每个 reviewer 的剩余时间 = &lt;code&gt;max(30s, deadline - now)&lt;/code&gt;。先完成的 reviewer 不影响后面的，后面的 reviewer 自然获得更少的时间。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="四个专业-reviewer"&gt;四个专业 Reviewer
&lt;/h2&gt;&lt;p&gt;默认启动四个 reviewer，每个有独立的 YAML 定义：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reviewer&lt;/th&gt;
&lt;th&gt;关注领域&lt;/th&gt;
&lt;th&gt;决策标签&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Quality&lt;/td&gt;
&lt;td&gt;代码质量、bug、逻辑错误、风格一致性、错误处理&lt;/td&gt;
&lt;td&gt;可合并 / 有条件合并 / 不可合并&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;输入验证、认证授权、注入漏洞、数据暴露、OWASP Top 10&lt;/td&gt;
&lt;td&gt;安全无虞 / 存在风险 / 高危漏洞&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;算法复杂度、内存模式、N+1 查询、缓存、并发&lt;/td&gt;
&lt;td&gt;性能良好 / 性能有疑虑 / 性能问题严重&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;耦合度、模块划分、分层、接口设计、霰弹式修改&lt;/td&gt;
&lt;td&gt;架构合理 / 架构有疑虑 / 架构有问题&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;每个 reviewer 的 prompt 都遵循同一个约束：&lt;strong&gt;第一行必须是决策标签&lt;/strong&gt;，然后是总结、阻塞项和建议项。这让 coordinator 能结构化地解析每个 reviewer 的输出。&lt;/p&gt;
&lt;p&gt;和 Cloudflare 的做法一样，每个 reviewer 的 prompt 都明确告诉它&lt;strong&gt;只看当前代码中存在的问题&lt;/strong&gt;，不要重复早期版本的问题，不要建议修改未变更的代码。&lt;/p&gt;
&lt;h3 id="team-配置"&gt;Team 配置
&lt;/h3&gt;&lt;p&gt;reviewer 的组合通过 &lt;code&gt;default-team&lt;/code&gt; 参数配置，格式是 &lt;code&gt;persona:count&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 默认：每种角色各一个&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;default-team&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;quality:1,security:1,performance:1,architecture:1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c"&gt;# 自定义：加强安全审查&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;default-team&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;quality:1,security:2,performance:1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c"&gt;# 极简：只看质量和安全&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;default-team&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;quality:1,security:1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;当 count &amp;gt; 1 时，同角色的 reviewer 会带上编号（如 &lt;code&gt;security-1&lt;/code&gt;、&lt;code&gt;security-2&lt;/code&gt;），用同一个 prompt 但独立运行。相同 prompt 的多次运行可以起到交叉验证的效果——如果两个 security reviewer 都发现了同一个漏洞，可信度自然更高。&lt;/p&gt;
&lt;p&gt;Cloudflare 有个类似的&amp;quot;风险分层&amp;quot;机制：trivial 改动（≤10 行）只跑 2 个 agent，full 改动（&amp;gt;100 行）跑全部 7 个。multi-review 目前靠用户手动配置 team，未来可以加入自动分层。&lt;/p&gt;
&lt;h2 id="coordinator去重和交叉验证"&gt;Coordinator：去重和交叉验证
&lt;/h2&gt;&lt;p&gt;并发审查带来的直接问题是&lt;strong&gt;多个 reviewer 可能发现同一个问题&lt;/strong&gt;。比如一个 SQL 注入，quality reviewer 可能从 bug 角度报告，security reviewer 从安全角度报告。如果不处理，最终报告会有重复。&lt;/p&gt;
&lt;p&gt;coordinator 的工作就是整合：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;跨 reviewer 去重&lt;/strong&gt;：同一问题只报告一次&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;交叉验证&lt;/strong&gt;：至少 2 个 reviewer 同意的问题标记为&amp;quot;已确认&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;冲突处理&lt;/strong&gt;：意见不一致时取多数意见&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保留领域见解&lt;/strong&gt;：安全发现即使只有一个 reviewer 提到，也要保留&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最严重决策&lt;/strong&gt;：如果任何一个 reviewer 认为不可合并，最终决策就是不可合并&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;coordinator 的 prompt 模板支持自定义，通过 &lt;code&gt;{{REVIEWS}}&lt;/code&gt; 占位符注入各 reviewer 的输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;你是一个代码审查协调员。以下审查由独立的专家 reviewer 生成。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;你的任务是整合为一个去重后的综合报告。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;规则：
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1. 跨 reviewer 去重（同一问题只提一次）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2. 交叉验证：至少 2 个 reviewer 同意的问题标记为&amp;#34;已确认&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3. 冲突时取多数意见
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;最终生成的 PR comment 包含两部分：coordinator 的综合报告在上，各 reviewer 的详细审查结果折叠在 &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; 标签中。&lt;/p&gt;
&lt;h2 id="和-cloudflare-方案的对比"&gt;和 Cloudflare 方案的对比
&lt;/h2&gt;&lt;p&gt;两者使用了相同的思路——多 Agent 并发 + Coordinator 汇总——但在规模和复杂度上有明显差异：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;Cloudflare&lt;/th&gt;
&lt;th&gt;multi-review&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agent 数量&lt;/td&gt;
&lt;td&gt;7 个专业 agent + coordinator&lt;/td&gt;
&lt;td&gt;4 个（可配置） + coordinator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;并发模型&lt;/td&gt;
&lt;td&gt;SDK 多 session，circuit breaker + failback&lt;/td&gt;
&lt;td&gt;SDK 多 session，Promise.all + 全局超时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模型路由&lt;/td&gt;
&lt;td&gt;按任务复杂度分配不同模型（Opus/Sonnet/Kimi）&lt;/td&gt;
&lt;td&gt;单一模型，通过 &lt;code&gt;model&lt;/code&gt; 参数指定&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;弹性&lt;/td&gt;
&lt;td&gt;三级 circuit breaker，provider 级别的 failback chain&lt;/td&gt;
&lt;td&gt;超时 + fallback comment（coordinator 失败时直接展示原始结果）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;风险分层&lt;/td&gt;
&lt;td&gt;自动（trivial/lite/full）&lt;/td&gt;
&lt;td&gt;手动配置 team&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;扩展性&lt;/td&gt;
&lt;td&gt;插件架构，7 个 plugin 组合&lt;/td&gt;
&lt;td&gt;YAML persona 定义，内置 4 种&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token 优化&lt;/td&gt;
&lt;td&gt;共享 context file，7x 的 MR 上下文不重复&lt;/td&gt;
&lt;td&gt;每个 reviewer 独立接收完整 diff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;适用场景&lt;/td&gt;
&lt;td&gt;内部 GitLab，5169 仓库，企业级&lt;/td&gt;
&lt;td&gt;GitHub/Gitea，开源项目，开箱即用&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最核心的差异在&lt;strong&gt;弹性&lt;/strong&gt;上。Cloudflare 的 circuit breaker 和 failback chain 是为生产环境设计的——7 个并发 AI 调用，provider 限频和宕机是必然事件。multi-review 的规模小得多，4 个并发的失败概率可控，用超时 + fallback comment 就够了。&lt;/p&gt;
&lt;p&gt;另一个差异是 &lt;strong&gt;token 优化&lt;/strong&gt;。Cloudflare 把 MR diff 写到一个共享文件，7 个 reviewer 读同一个文件而不是各拷一份。注意这里共享的是&lt;strong&gt;原始 diff（输入数据）&lt;/strong&gt;，而不是 reviewer 之间的审查结论。每个 reviewer 的审查过程仍然是完全独立的。这个优化在 7 个 reviewer 的情况下能省 85% 的重复 token。multi-review 目前每个 reviewer 都接收完整 diff，在 4 个 reviewer 的规模下重复还能接受，但如果 team 配置到 7+ 就需要考虑类似优化了。&lt;/p&gt;
&lt;h2 id="reasoning-effort-和-thinking"&gt;Reasoning Effort 和 Thinking
&lt;/h2&gt;&lt;p&gt;multi-review 支持 &lt;code&gt;reasoning-effort&lt;/code&gt; 和 &lt;code&gt;enable-thinking&lt;/code&gt; 两个参数，直接传递给 OpenCode SDK 的 agent 配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Run Multi-Review&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sun-praise/opencode-actions/multi-review@v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;reasoning-effort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;max &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# low / medium / high / max&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enable-thinking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 启用思维链&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;默认 &lt;code&gt;reasoning-effort: max&lt;/code&gt; 和 &lt;code&gt;enable-thinking: true&lt;/code&gt;，这意味着每个 reviewer 和 coordinator 都会用最大推理力度并输出思维链。对于代码审查这种需要深度分析的任务，这个默认值是合理的。&lt;/p&gt;
&lt;p&gt;Cloudflare 也有类似的分层：coordinator 用 Opus（最强推理），code quality 和 security 用 Sonnet（平衡速度和能力），文档审查用 Kimi（便宜）。multi-review 暂时没有按角色分配不同模型的能力，但这是一个明确的优化方向。&lt;/p&gt;
&lt;h2 id="使用方式"&gt;使用方式
&lt;/h2&gt;&lt;p&gt;在 &lt;code&gt;.github/workflows/&lt;/code&gt; 中添加：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Multi Review&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;issue_comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;created]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;multi-review&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; github.event.issue.pull_request &amp;amp;&amp;amp;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; contains(fromJSON(&amp;#39;[&amp;#34;/multi-review&amp;#34;, &amp;#34;/mr&amp;#34;]&amp;#39;), github.event.comment.body)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sun-praise/opencode-actions/multi-review@v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;github-token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;zhipu-api-key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.ZHIPU_API_KEY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 可选配置&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default-team&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;quality:1,security:1,performance:1,architecture:1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;reasoning-effort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;max&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enable-thinking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这段配置的效果是：在 PR 中评论 &lt;code&gt;/multi-review&lt;/code&gt; 或 &lt;code&gt;/mr&lt;/code&gt;，就会触发多 Agent 并发审查。&lt;/p&gt;
&lt;h2 id="代码实现"&gt;代码实现
&lt;/h2&gt;&lt;p&gt;核心实现在 &lt;code&gt;orchestrator.ts&lt;/code&gt; 中的两个函数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;并发审查&lt;/strong&gt;——&lt;code&gt;runParallelReviewers&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reviewers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reviewer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deadline&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 1. 创建独立 session
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sessionResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;withTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;throwOnError&lt;/span&gt;: &lt;span class="kt"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;reviewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sessionResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 2. 发送 persona + diff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;withTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;: &lt;span class="kt"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;: &lt;span class="kt"&gt;reviewer.prompt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;\n\nPR Diff:\n```\n&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;prDiff&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;\n```&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;reviewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 3. 提取结果
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;withTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;: &lt;span class="kt"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;reviewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;reviewer&lt;/span&gt;: &lt;span class="kt"&gt;reviewer.name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;: &lt;span class="kt"&gt;extractText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt;: &lt;span class="kt"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;reviewer&lt;/span&gt;: &lt;span class="kt"&gt;reviewer.name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt;: &lt;span class="kt"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;: &lt;span class="kt"&gt;err.message&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 4. 清理 session
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;: &lt;span class="kt"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Coordinator 汇总&lt;/strong&gt;——&lt;code&gt;runCoordinator&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 把各 reviewer 的结果拼接成 {{REVIEWS}}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reviewsText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reviews&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sb"&gt;`## &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reviewer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="sb"&gt;n&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`（失败: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;）`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;\n\n---\n\n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coordinatorPrompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{REVIEWS}}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reviewsText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 创建 session、发送 prompt、提取结果...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如果 coordinator 本身失败了，还有个 fallback：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;buildFallbackComment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reviews&lt;/span&gt;: &lt;span class="kt"&gt;ReviewResult&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// coordinator 挂了，直接把各 reviewer 的原始输出拼起来
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;**Multi-Review (fallback — coordinator failed)**\n\n&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;\n\n---\n\n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这确保了即使汇总环节出问题，审查结果也不会丢失。&lt;/p&gt;
&lt;h2 id="和单模型-review-的关系"&gt;和单模型 Review 的关系
&lt;/h2&gt;&lt;p&gt;multi-review 和之前&lt;a class="link" href="https://svtter.cn/p/opencode-actions-%e4%b8%80%e4%b8%aa-coding-review-agent/" &gt;单模型 review action&lt;/a&gt; 不是替代关系，而是互补：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;review&lt;/th&gt;
&lt;th&gt;multi-review&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;定位&lt;/td&gt;
&lt;td&gt;快速审查，适合每次 PR 都跑&lt;/td&gt;
&lt;td&gt;深度审查，适合关键 PR 或手动触发&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模型数&lt;/td&gt;
&lt;td&gt;1 个&lt;/td&gt;
&lt;td&gt;4+ 个（可配置）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;耗时&lt;/td&gt;
&lt;td&gt;1-3 分钟&lt;/td&gt;
&lt;td&gt;3-8 分钟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token 消耗&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;td&gt;中（N 倍于单模型）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;触发方式&lt;/td&gt;
&lt;td&gt;PR 创建/更新自动触发&lt;/td&gt;
&lt;td&gt;手动评论触发&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;适合场景&lt;/td&gt;
&lt;td&gt;日常迭代，快速反馈&lt;/td&gt;
&lt;td&gt;重要功能、安全相关、大型重构&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;日常开发用 review 做快速检查，关键 PR 用 multi-review 做深度审查。两个 action 可以共存于同一个仓库。&lt;/p&gt;
&lt;h2 id="未来优化方向"&gt;未来优化方向
&lt;/h2&gt;&lt;p&gt;参考 Cloudflare 的经验和当前实现的局限，有几个明确的优化方向：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;自动风险分层&lt;/strong&gt;：根据 diff 行数和文件类型自动决定跑几个 reviewer。typo 修改变动不需要 4 个 agent 并发审查。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共享上下文&lt;/strong&gt;：把 diff 写到文件而不是每个 reviewer 都拷贝一份，减少 token 消耗。但需要注意一个微妙的问题：&lt;strong&gt;共享的边界在哪里？&lt;/strong&gt; 共享原始 diff（输入数据）是安全的，但如果过早共享 reviewer 之间的中间结论，会导致严重的偏差问题——确认偏差、锚定效应、同质化审查。正确的设计是：reviewer 阶段完全隔离，只共享输入；coordinator 阶段才汇总结论。这不仅是 token 优化的问题，更是一个审查独立性的架构决策。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型路由&lt;/strong&gt;：coordinator 用强模型，文档审查用轻量模型，按角色分配不同 provider。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;增量审查&lt;/strong&gt;：push 新 commit 后，只审查变更的部分，不从头开始。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;multi-review 的核心思路来自 Cloudflare 的文章：&lt;strong&gt;把一个复杂任务拆成多个专业子任务，并发执行后统一汇总&lt;/strong&gt;。这比一个全能模型覆盖所有维度更有效，因为每个子任务的 prompt 更聚焦，模型的注意力更集中。&lt;/p&gt;
&lt;p&gt;实现上，multi-review 用 OpenCode SDK 的多 session 能力做并发，用 Promise.all 做编排，用全局超时做兜底，用 coordinator 做去重和交叉验证。这是一个在开源项目规模下足够实用的架构——没有 circuit breaker 和 failback chain 的复杂度，但覆盖了并发审查的核心需求。&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/sun-praise/opencode-actions" target="_blank" rel="noopener"
&gt;opencode-actions&lt;/a&gt; — GitHub Action 仓库&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.cloudflare.com/ai-code-review/" target="_blank" rel="noopener"
&gt;Cloudflare: Orchestrating AI Code Review at Scale&lt;/a&gt; — 设计灵感的来源&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/opencode-actions-%e4%b8%80%e4%b8%aa-coding-review-agent/" &gt;opencode-actions - 一个 coding review agent&lt;/a&gt; — 单模型 review action 的介绍&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/opencode-%e6%8f%92%e4%bb%b6%e4%bc%98%e5%8c%96/" &gt;OpenCode 配置之外的优化&lt;/a&gt; — opencode-review 插件的设计，session 内的代码审查&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Kimi Code 对 kimi-k2.6 的专用处理：与 OpenCode 的对比</title><link>https://svtter.cn/p/kimi-code-%E5%AF%B9-kimi-k2.6-%E7%9A%84%E4%B8%93%E7%94%A8%E5%A4%84%E7%90%86%E4%B8%8E-opencode-%E7%9A%84%E5%AF%B9%E6%AF%94/</link><pubDate>Wed, 27 May 2026 10:30:00 +0800</pubDate><guid>https://svtter.cn/p/kimi-code-%E5%AF%B9-kimi-k2.6-%E7%9A%84%E4%B8%93%E7%94%A8%E5%A4%84%E7%90%86%E4%B8%8E-opencode-%E7%9A%84%E5%AF%B9%E6%AF%94/</guid><description>&lt;img src="https://svtter.cn/p/kimi-code-%E5%AF%B9-kimi-k2.6-%E7%9A%84%E4%B8%93%E7%94%A8%E5%A4%84%E7%90%86%E4%B8%8E-opencode-%E7%9A%84%E5%AF%B9%E6%AF%94/featured-image.png" alt="Featured image of post Kimi Code 对 kimi-k2.6 的专用处理：与 OpenCode 的对比" /&gt;&lt;p&gt;最近 kimi code 从 python 迁移到了 ts 版本。于是做了个简单的分析。&lt;/p&gt;
&lt;p&gt;根据对 &lt;strong&gt;kimi-code&lt;/strong&gt; 源代码（特别是 &lt;code&gt;packages/kosong/src/providers/kimi.ts&lt;/code&gt;、&lt;code&gt;kimi-schema.ts&lt;/code&gt;、&lt;code&gt;kimi-files.ts&lt;/code&gt; 等）以及 &lt;strong&gt;OpenCode&lt;/strong&gt; 相关兼容性 Issue 的分析，以下是 kimi-code 针对 kimi-k2.6（及 Kimi 系列模型）做的专用处理，以及相对于 OpenCode 的关键差异。&lt;/p&gt;
&lt;h2 id="1-原生-kimi-provider非通用-openai-兼容层"&gt;1. 原生 Kimi Provider（非通用 OpenAI 兼容层）
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;kimi-code&lt;/strong&gt; 没有直接把 Kimi 当&amp;quot;又一个 OpenAI 兼容接口&amp;quot;处理，而是实现了专门的 &lt;code&gt;kimi&lt;/code&gt; provider 类型：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;kimi-code&lt;/th&gt;
&lt;th&gt;OpenCode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Provider 类型&lt;/td&gt;
&lt;td&gt;专门的 &lt;code&gt;'kimi'&lt;/code&gt; 类型，独立适配&lt;/td&gt;
&lt;td&gt;通过 OpenAI/Anthropic 通用 bridge 访问&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;专有字段&lt;/td&gt;
&lt;td&gt;原生处理 &lt;code&gt;reasoning_content&lt;/code&gt;、&lt;code&gt;thinking&lt;/code&gt;、&lt;code&gt;generationKwargs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bridge 层常丢失 &lt;code&gt;reasoning_content&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;认证头&lt;/td&gt;
&lt;td&gt;支持 &lt;code&gt;kimiRequestHeaders&lt;/code&gt; 和 &lt;code&gt;X-Msh-Tool-Call-Id&lt;/code&gt; 等 Moonshot 专有 header&lt;/td&gt;
&lt;td&gt;通用 header 转发&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="2-reasoning_content-的全生命周期处理"&gt;2. &lt;code&gt;reasoning_content&lt;/code&gt; 的全生命周期处理
&lt;/h2&gt;&lt;p&gt;kimi-k2.6 默认开启 thinking，且&lt;strong&gt;要求多轮对话历史中保留 &lt;code&gt;reasoning_content&lt;/code&gt;&lt;/strong&gt;，否则工具调用后会报 400 错误。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;kimi-code 的处理：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;convertMessage&lt;/code&gt;&lt;/strong&gt;：将内部 &lt;code&gt;think&lt;/code&gt; 类型的 content part 提取并序列化为 &lt;code&gt;reasoning_content&lt;/code&gt; 字段，确保历史消息中 thinking 内容不丢失&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流式解析&lt;/strong&gt;：在 &lt;code&gt;_convertStreamResponse&lt;/code&gt; 和 &lt;code&gt;_convertNonStreamResponse&lt;/code&gt; 中都专门提取 &lt;code&gt;delta.reasoning_content&lt;/code&gt; / &lt;code&gt;message.reasoning_content&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TUI 渲染&lt;/strong&gt;：有专门的 &lt;code&gt;ThinkingComponent&lt;/code&gt; 组件实时显示 thinking 内容，支持展开/收起和 spinner 动画&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;OpenCode 的问题：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OpenCode Go 的 bridge 在第二轮对话时&lt;strong&gt;丢失 &lt;code&gt;reasoning_content&lt;/code&gt;&lt;/strong&gt;，导致 Moonshot API 返回：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;thinking&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="n"&gt;but&lt;/span&gt; &lt;span class="n"&gt;reasoning_content&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;assistant&lt;/span&gt; &lt;span class="k"&gt;tool&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="3-json-schema-专用规范化kimi-schemats"&gt;3. JSON Schema 专用规范化（&lt;code&gt;kimi-schema.ts&lt;/code&gt;）
&lt;/h2&gt;&lt;p&gt;Moonshot 的工具参数验证器对 JSON Schema 有严格且特殊的要求，这是 OpenCode 与 kimi-k2.6 不兼容的主要来源之一。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;kimi-code 的 &lt;code&gt;normalizeKimiToolSchema&lt;/code&gt; 做了：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解引用 &lt;code&gt;$ref&lt;/code&gt;&lt;/strong&gt;：将 &lt;code&gt;$defs&lt;/code&gt; / &lt;code&gt;definitions&lt;/code&gt; 中的定义内联展开，消除外部引用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;补全缺失的 &lt;code&gt;type&lt;/code&gt;&lt;/strong&gt;：Kimi 验证器会拒绝省略 &lt;code&gt;type&lt;/code&gt; 的嵌套属性（比如 MCP 产生的 enum-only schema），kimi-code 会推断并补全 &lt;code&gt;type: string/object/array&lt;/code&gt; 等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;循环引用检测&lt;/strong&gt;：遇到循环引用时保留原始 &lt;code&gt;$ref&lt;/code&gt;，避免无限递归&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;OpenCode 的问题：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;生成 schema 使用 &lt;code&gt;#/definitions/&lt;/code&gt; 而非 Moonshot 要求的 &lt;code&gt;#/$defs/&lt;/code&gt;，且没有为 Kimi 做 schema 类型推断和补全，导致复杂工具调用直接 400。&lt;/p&gt;
&lt;h2 id="4-thinking-模式的原生配置体系"&gt;4. Thinking 模式的原生配置体系
&lt;/h2&gt;&lt;p&gt;kimi-code 从配置层到 UI 层都内置了对 Kimi thinking 模式的支持：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;配置解析&lt;/strong&gt;：&lt;code&gt;ThinkingConfigSchema&lt;/code&gt; 支持 &lt;code&gt;mode: auto/on/off&lt;/code&gt; 和 &lt;code&gt;effort: low/medium/high/xhigh/max&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模型能力标注&lt;/strong&gt;：&lt;code&gt;ModelAlias&lt;/code&gt; 支持 &lt;code&gt;capabilities: ['thinking', 'always_thinking']&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模型选择器 UI&lt;/strong&gt;：按 &lt;code&gt;←→&lt;/code&gt; 键可切换 thinking on/off，&lt;code&gt;always-on&lt;/code&gt; 模型不可关闭&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Provider 方法&lt;/strong&gt;：&lt;code&gt;withThinking(effort)&lt;/code&gt; 会正确生成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;reasoning_effort&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;high&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;extra_body&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;thinking&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;token 预算&lt;/strong&gt;：自动将 legacy &lt;code&gt;max_tokens&lt;/code&gt; 规范化为 Kimi 偏好的 &lt;code&gt;max_completion_tokens&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;OpenCode 的问题：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通过 Anthropic bridge 时硬编码发送 &lt;code&gt;thinking&lt;/code&gt; content blocks，但 Kimi API 只支持 &lt;code&gt;text/image_url/video_url/video&lt;/code&gt;，导致：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Invalid value: thinking. Supported values are: &amp;#39;text&amp;#39;,&amp;#39;image_url&amp;#39;,&amp;#39;video_url&amp;#39; and &amp;#39;video&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="5-moonshot-原生服务集成"&gt;5. Moonshot 原生服务集成
&lt;/h2&gt;&lt;p&gt;kimi-code 内置了 Moonshot 专属服务，而非使用通用本地实现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;MoonshotFetchURLProvider&lt;/code&gt;&lt;/strong&gt;：优先调用 Moonshot 的 &lt;code&gt;coding-fetch&lt;/code&gt; 服务（已做页面文本提取），失败才 fallback 到本地&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;MoonshotWebSearchProvider&lt;/code&gt;&lt;/strong&gt;：直接调用 Moonshot 搜索 API，支持 &lt;code&gt;enable_page_crawling&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;KimiFiles&lt;/code&gt;&lt;/strong&gt;：视频上传到 Moonshot 文件服务，生成 &lt;code&gt;ms://&amp;lt;file-id&amp;gt;&lt;/code&gt; 格式的 &lt;code&gt;video_url&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="6-工具调用层细节"&gt;6. 工具调用层细节
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内置函数&lt;/strong&gt;：以 &lt;code&gt;$&lt;/code&gt; 开头的工具名会被识别为 Kimi builtin function，序列化为 &lt;code&gt;type: 'builtin_function'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Usage 提取&lt;/strong&gt;：支持 Moonshot 特有的 &lt;code&gt;choices[0].usage&lt;/code&gt; 位置，以及 &lt;code&gt;cached_tokens&lt;/code&gt; 等字段&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Finish Reason 映射&lt;/strong&gt;：将 OpenAI 格式的 &lt;code&gt;stop&lt;/code&gt;/&lt;code&gt;tool_calls&lt;/code&gt;/&lt;code&gt;length&lt;/code&gt; 等映射到内部统一枚举&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="7-cli-核心与-llm-sdk-的架构隔离"&gt;7. CLI 核心与 LLM SDK 的架构隔离
&lt;/h2&gt;&lt;p&gt;这是一个容易被忽视但很重要的架构差异。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;kimi-code 的核心 CLI（&lt;code&gt;apps/kimi-code&lt;/code&gt;）本身不直接依赖任何 OpenAI 或 Anthropic 的 TypeScript SDK。&lt;/strong&gt; 查看其 &lt;code&gt;package.json&lt;/code&gt;，核心依赖只有 TUI 渲染（&lt;code&gt;pi-tui&lt;/code&gt;）、命令行解析（&lt;code&gt;commander&lt;/code&gt;）、语法高亮（&lt;code&gt;cli-highlight&lt;/code&gt;）等通用库。所有与 LLM provider 的交互都被隔离在自研的 &lt;code&gt;kosong&lt;/code&gt; 包中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;packages/kosong&lt;/code&gt; 虽然内部使用了 &lt;code&gt;openai&lt;/code&gt; 和 &lt;code&gt;@anthropic-ai/sdk&lt;/code&gt; 作为实现细节（因为 Kimi API 是 OpenAI-compatible 的），但对外提供的是统一的 LLM 抽象接口。CLI 核心只依赖 &lt;code&gt;kosong&lt;/code&gt;，不感知底层 vendor SDK。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OpenCode 则不同。&lt;/strong&gt; 其 &lt;code&gt;packages/opencode&lt;/code&gt; 核心包直接依赖了大量 vendor SDK：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@ai-sdk/openai&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@ai-sdk/anthropic&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@ai-sdk/google&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@ai-sdk/azure&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@openrouter/ai-sdk-provider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;……（共计十余个 provider-specific 包）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这意味着 OpenCode 的核心代码与各个 vendor 的 SDK 深度耦合，而 kimi-code 的核心 CLI 保持干净，模型交互完全通过自研抽象层隔离。&lt;/p&gt;
&lt;h2 id="8-commit-历史揭示的演进路径"&gt;8. Commit 历史揭示的演进路径
&lt;/h2&gt;&lt;p&gt;以上的代码结构差异只是静态结果。更有意思的是看两边的 commit 历史——&lt;strong&gt;动态演进方向完全不同&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="kimi-code原生设计持续消除配置负担"&gt;kimi-code：原生设计，持续消除配置负担
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;842e699&lt;/code&gt; — &amp;ldquo;Kimi For Coding&amp;rdquo;（初始提交）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是整个项目的起点。初始代码就包含了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;packages/kosong/src/providers/kimi.ts&lt;/code&gt;：专用 Kimi provider&lt;/li&gt;
&lt;li&gt;&lt;code&gt;packages/kosong/src/providers/kimi-schema.ts&lt;/code&gt;：专用 JSON Schema 规范化器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;packages/kosong/src/providers/kimi-files.ts&lt;/code&gt;：专用文件上传服务&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;结论：kimi-code 从一开始就把 Kimi API 当作一等公民设计，不是后期打补丁适配。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;d95b013&lt;/code&gt; fix(catalog): preserve reasoning fields in custom model (&lt;a class="link" href="https://github.com/MoonshotAI/kimi-code/pull/70" target="_blank" rel="noopener"
&gt;#70&lt;/a&gt;)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个 commit 修复了一个很隐蔽的问题。models.dev 用 &lt;code&gt;interleaved&lt;/code&gt; 字段标记 reasoning 支持，但早期代码把 &lt;code&gt;interleaved=true&lt;/code&gt; 当作 undefined 处理，导致通过 &lt;code&gt;/connect&lt;/code&gt; 选择的模型静默丢失 reasoning 能力。&lt;/p&gt;
&lt;p&gt;修复内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;interleaved=true&lt;/code&gt; 映射为默认的 &lt;code&gt;reasoning_content&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update-catalog.mjs&lt;/code&gt; 的 allowlist 加入 &lt;code&gt;interleaved&lt;/code&gt;，否则 release build 的离线 catalog 会再次丢失该字段&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;61f7d0e&lt;/code&gt; fix(kosong): make openai-compatible thinking work without reasoning_key (&lt;a class="link" href="https://github.com/MoonshotAI/kimi-code/pull/78" target="_blank" rel="noopener"
&gt;#78&lt;/a&gt;)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这是 reasoning 处理的核心 commit&lt;/strong&gt;，展示了 kimi-code 对兼容性的深层思考。diff 显示了三层关键设计：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inbound 自动扫描&lt;/strong&gt;（响应解析）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;KNOWN_REASONING_KEYS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;reasoning_content&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;reasoning_details&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;reasoning&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 自动扫描三个字段，第一个字符串值获胜
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Outbound 默认回写&lt;/strong&gt;（请求序列化）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DEFAULT_OUTBOUND_REASONING_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;KNOWN_REASONING_KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// &amp;#39;reasoning_content&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 默认以 reasoning_content 回传，无需用户配置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;自动注入 &lt;code&gt;reasoning_effort&lt;/code&gt;&lt;/strong&gt;（历史连续性）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 当历史中有 ThinkPart 但 caller 未显式设置 reasoning_effort 时，
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 自动注入 &amp;#39;medium&amp;#39;，避免 One API / DeepSeek 等严格网关返回 400
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;边界情况也处理得很细：blank &lt;code&gt;reasoning_key&lt;/code&gt;（&lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;）被 normalize 为 &lt;code&gt;undefined&lt;/code&gt;；caller 通过 &lt;code&gt;withGenerationKwargs&lt;/code&gt; 显式设置的值&lt;strong&gt;不会被自动注入覆盖&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;验证目标明确写的是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Manually verified end-to-end against the real DeepSeek API with a hand-written config.toml that does not set reasoning_key: thinking content renders, no 400, multi-turn conversations work.&lt;/p&gt;&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="opencode通用层设计openai-centric"&gt;OpenCode：通用层设计，OpenAI-centric
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;eb84f46&lt;/code&gt; fix(llm): split OpenAI reasoning summary blocks (#29000)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个 commit 展示了 OpenCode 处理 reasoning 的完全不同思路——围绕 &lt;strong&gt;OpenAI Responses API&lt;/strong&gt; 设计：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;维护 &lt;code&gt;encrypted_content&lt;/code&gt; 和 &lt;code&gt;item_reference&lt;/code&gt; 的状态机&lt;/li&gt;
&lt;li&gt;按 &lt;code&gt;item_id&lt;/code&gt; + &lt;code&gt;summary_index&lt;/code&gt; 折叠多个 summary parts&lt;/li&gt;
&lt;li&gt;&lt;code&gt;store:false&lt;/code&gt; 时过滤缺少 &lt;code&gt;encrypted_content&lt;/code&gt; 的 reasoning items&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;这与 Kimi 的 &lt;code&gt;reasoning_content&lt;/code&gt; 机制完全不同。&lt;/strong&gt; Kimi 不需要 &lt;code&gt;encrypted_content&lt;/code&gt; 或 &lt;code&gt;item_reference&lt;/code&gt;，而是直接在 message 上附加 &lt;code&gt;reasoning_content&lt;/code&gt; 字段。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="一个残酷的事实"&gt;一个残酷的事实
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a class="link" href="https://github.com/anomalyco/opencode/issues/26331" target="_blank" rel="noopener"
&gt;OpenCode Issue #26331&lt;/a&gt;&lt;/strong&gt; &amp;ldquo;Bug: OpenCode Go bridge layer incompatible with kimi-k2.6 tool calls&amp;rdquo; — &lt;strong&gt;状态：至今仍是 open&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a class="link" href="https://github.com/anomalyco/opencode/issues/27054" target="_blank" rel="noopener"
&gt;OpenCode Issue #27054&lt;/a&gt;&lt;/strong&gt; &amp;ldquo;KIMI K2.6 showing error in Opencode GO&amp;rdquo; — &lt;strong&gt;状态：closed，但关闭方式是禁用 MCP（workaround）&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;#27054 的最后一个 comment：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The workaround is to disable your MCP and then initiate the session&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;这不是修复，这是回避问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="commit-历史对比总结"&gt;Commit 历史对比总结
&lt;/h3&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;kimi-code&lt;/th&gt;
&lt;th&gt;OpenCode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;初始设计&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;初始提交即包含完整 Kimi provider + schema 规范化 + 文件服务&lt;/td&gt;
&lt;td&gt;通用多模型架构，后期通过 bridge 适配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;reasoning 机制&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;围绕 &lt;code&gt;reasoning_content&lt;/code&gt; 字段设计，自动扫描/回写/注入 effort&lt;/td&gt;
&lt;td&gt;围绕 OpenAI Responses 的 &lt;code&gt;encrypted_content&lt;/code&gt; + &lt;code&gt;item_reference&lt;/code&gt; 设计&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;schema 处理&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;专用 &lt;code&gt;normalizeKimiToolSchema&lt;/code&gt;，解引用 &lt;code&gt;$ref&lt;/code&gt; + 补全 &lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;通用 schema 验证，友好错误提示为主&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;配置哲学&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;让 OpenAI-compatible 网关&amp;quot;零配置工作&amp;quot;，自动推断所有字段&lt;/td&gt;
&lt;td&gt;依赖用户通过 bridge/config 手动适配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;issue 状态&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;持续发布 reasoning 相关 patch（&lt;a class="link" href="https://github.com/MoonshotAI/kimi-code/pull/70" target="_blank" rel="noopener"
&gt;#70&lt;/a&gt;, &lt;a class="link" href="https://github.com/MoonshotAI/kimi-code/pull/78" target="_blank" rel="noopener"
&gt;#78&lt;/a&gt;）&lt;/td&gt;
&lt;td&gt;kimi-k2.6 兼容性问题 &lt;a class="link" href="https://github.com/anomalyco/opencode/issues/26331" target="_blank" rel="noopener"
&gt;#26331&lt;/a&gt; 至今 open&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="总结核心差异"&gt;总结：核心差异
&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;kimi-code&lt;/th&gt;
&lt;th&gt;OpenCode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;架构定位&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;原生为 Kimi/Moonshot 设计，专用 provider&lt;/td&gt;
&lt;td&gt;通用多模型代理，通过 bridge 适配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thinking/Reasoning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;原生支持，全链路保留 &lt;code&gt;reasoning_content&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bridge 层易丢失，导致 400&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JSON Schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;专用 &lt;code&gt;normalizeKimiToolSchema&lt;/code&gt; 做解引用和类型补全&lt;/td&gt;
&lt;td&gt;通用 schema 生成，不符合 Kimi 验证器要求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API 格式&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;直接生成 Moonshot 原生格式（含 &lt;code&gt;thinking&lt;/code&gt; 配置、&lt;code&gt;$defs&lt;/code&gt; 规范等）&lt;/td&gt;
&lt;td&gt;经 OpenAI/Anthropic 协议转换，产生格式不匹配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;服务集成&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;内置 Moonshot fetch/search/file 服务&lt;/td&gt;
&lt;td&gt;使用通用本地工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;核心依赖&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CLI 核心不直接依赖 vendor SDK，通过自研 kosong 包隔离&lt;/td&gt;
&lt;td&gt;核心包直接耦合 &lt;code&gt;@ai-sdk/openai&lt;/code&gt; 等十余个 vendor SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;从 commit 历史看，kimi-code 的演进方向是&lt;strong&gt;持续消除用户配置负担&lt;/strong&gt;（&lt;code&gt;reasoning_key&lt;/code&gt; 从必填 → 可选覆盖 → 自动推断；&lt;code&gt;interleaved&lt;/code&gt; 从被过滤 → 正确映射），而 OpenCode 的演进方向是&lt;strong&gt;持续深化 OpenAI 生态集成&lt;/strong&gt;（Responses API、encrypted reasoning、item reference），对 Kimi 的适配停留在通用 bridge 层。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这就是 commit 层面的真相：一个是原生演进，一个是 bridge 间隙。&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>OpenCode LLM Provider 层的三连修：从图片序列化到错误信息丢失</title><link>https://svtter.cn/p/opencode-llm-provider-%E5%B1%82%E7%9A%84%E4%B8%89%E8%BF%9E%E4%BF%AE%E4%BB%8E%E5%9B%BE%E7%89%87%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%B0%E9%94%99%E8%AF%AF%E4%BF%A1%E6%81%AF%E4%B8%A2%E5%A4%B1/</link><pubDate>Sat, 23 May 2026 10:00:00 +0800</pubDate><guid>https://svtter.cn/p/opencode-llm-provider-%E5%B1%82%E7%9A%84%E4%B8%89%E8%BF%9E%E4%BF%AE%E4%BB%8E%E5%9B%BE%E7%89%87%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%B0%E9%94%99%E8%AF%AF%E4%BF%A1%E6%81%AF%E4%B8%A2%E5%A4%B1/</guid><description>&lt;img src="https://svtter.cn/p/opencode-llm-provider-%E5%B1%82%E7%9A%84%E4%B8%89%E8%BF%9E%E4%BF%AE%E4%BB%8E%E5%9B%BE%E7%89%87%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%B0%E9%94%99%E8%AF%AF%E4%BF%A1%E6%81%AF%E4%B8%A2%E5%A4%B1/cover.jpg" alt="Featured image of post OpenCode LLM Provider 层的三连修：从图片序列化到错误信息丢失" /&gt;&lt;p&gt;16 万 star 的 opencode 项目最近连续合并了三个 LLM provider 层的 bug 修复 PR，都是核心维护者 kitlangton 提交的。这三个 bug 不是某次重构引入的回归问题，而是从协议层初始设计就存在的缺陷——只是在最近使用量增长后才暴露出来。&lt;/p&gt;
&lt;p&gt;这篇文章分析这三个 bug 的根因、修复方式，以及对开发 AI 工具的启示。&lt;/p&gt;
&lt;h2 id="背景opencode-的-llm-协议层"&gt;背景：opencode 的 LLM 协议层
&lt;/h2&gt;&lt;p&gt;opencode 支持多个 LLM provider（OpenAI、Anthropic 等），每个 provider 有自己的协议实现文件，负责把统一的内部格式转换成各 provider 的 API 格式。比如 &lt;code&gt;openai-responses.ts&lt;/code&gt; 处理 OpenAI Responses API，&lt;code&gt;anthropic-messages.ts&lt;/code&gt; 处理 Anthropic Messages API。&lt;/p&gt;
&lt;p&gt;这些协议层有两个核心工作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;下行（lowering）&lt;/strong&gt;：把 opencode 内部的消息格式转成 provider 的请求格式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;上行（parsing）&lt;/strong&gt;：把 provider 返回的流式响应解析成 opencode 内部的消息格式&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;三个 bug 分别出在这两个方向上。&lt;/p&gt;
&lt;h2 id="bug-1tool-result-中的图片被字符串化"&gt;Bug 1：Tool Result 中的图片被字符串化
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;相关 PR&lt;/strong&gt;：&lt;a class="link" href="https://github.com/anomalyco/opencode/pull/28754" target="_blank" rel="noopener"
&gt;#28754&lt;/a&gt;（OpenAI，关闭 &lt;a class="link" href="https://github.com/anomalyco/opencode/issues/28859" target="_blank" rel="noopener"
&gt;#28859&lt;/a&gt;）、&lt;a class="link" href="https://github.com/anomalyco/opencode/pull/28755" target="_blank" rel="noopener"
&gt;#28755&lt;/a&gt;（Anthropic，关闭 &lt;a class="link" href="https://github.com/anomalyco/opencode/issues/28861" target="_blank" rel="noopener"
&gt;#28861&lt;/a&gt;）&lt;/p&gt;
&lt;h3 id="问题"&gt;问题
&lt;/h3&gt;&lt;p&gt;当 LLM 调用一个工具（比如截图工具），工具返回的结果可能包含图片（base64 编码）。opencode 的协议层在处理这种 tool result 时，统一调用了 &lt;code&gt;ProviderShared.toolResultText(part)&lt;/code&gt;，这个函数把整个 tool result——包括图片——JSON.stringify 成一个字符串。&lt;/p&gt;
&lt;p&gt;对于 OpenAI Responses API，这意味着：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 修复前：所有 tool result 都变成字符串
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;function_call_output&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;call_id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{&amp;#34;type&amp;#34;:&amp;#34;image&amp;#34;,&amp;#34;data&amp;#34;:&amp;#34;base64...&amp;#34;}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;一个包含 base64 图片的 tool result 被序列化成字符串塞进了 &lt;code&gt;function_call_output.output&lt;/code&gt;。对于 Anthropic Messages API 同样：图片被 JSON.stringify 后塞进了 &lt;code&gt;tool_result.content&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="修复"&gt;修复
&lt;/h3&gt;&lt;p&gt;OpenAI 端：新增 &lt;code&gt;lowerToolResultOutput&lt;/code&gt; 函数，判断 tool result 的类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文本/json/error → 保持原来的字符串行为（向后兼容）&lt;/li&gt;
&lt;li&gt;图片 → 以 &lt;code&gt;input_image&lt;/code&gt; 结构化块发送&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 修复后：图片以结构化格式发送
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;function_call_output&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;call_id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;input_image&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image_url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;data:image/png;base64,...&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Anthropic 端做了类似的处理，图片以 Anthropic 原生的 &lt;code&gt;image&lt;/code&gt; 块发送。同时 &lt;code&gt;function_call_output.output&lt;/code&gt; 的 schema 从 &lt;code&gt;Schema.String&lt;/code&gt; 改成了 &lt;code&gt;Schema.Union([Schema.String, Schema.Array(...)])&lt;/code&gt;，既支持旧的字符串格式，也支持新的结构化数组。&lt;/p&gt;
&lt;h3 id="根因"&gt;根因
&lt;/h3&gt;&lt;p&gt;这不是某次重构搞坏的，而是初始设计就没考虑到 tool result 会返回非文本内容。&lt;code&gt;toolResultText()&lt;/code&gt; 作为一个通用函数，把所有内容都当文本处理——在只有文本 tool result 的世界里这是对的，但世界变了。&lt;/p&gt;
&lt;h2 id="bug-2stream-error-信息被吞掉"&gt;Bug 2：Stream Error 信息被吞掉
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;相关 PR&lt;/strong&gt;：&lt;a class="link" href="https://github.com/anomalyco/opencode/pull/28757" target="_blank" rel="noopener"
&gt;#28757&lt;/a&gt;（关闭 &lt;a class="link" href="https://github.com/anomalyco/opencode/issues/28860" target="_blank" rel="noopener"
&gt;#28860&lt;/a&gt;）&lt;/p&gt;
&lt;h3 id="问题-1"&gt;问题
&lt;/h3&gt;&lt;p&gt;LLM provider 的流式响应可能在中途出错（rate limit、context overflow、model overload 等）。opencode 的错误处理代码把这些错误全部压成了通用字符串：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 修复前
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OpenAI Responses stream error&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;所有错误都变成了 &lt;code&gt;&amp;quot;OpenAI Responses stream error&amp;quot;&lt;/code&gt; 这一句话。维护者在 PR 描述里提到，这使得某个 session 的诊断变得极其痛苦——底层原因（base64 图片过大）完全不可见。&lt;/p&gt;
&lt;p&gt;OpenAI 的 &lt;code&gt;response.failed&lt;/code&gt; 事件更惨：错误信息在 &lt;code&gt;response.error&lt;/code&gt; 下面，但代码读的是 &lt;code&gt;event.message&lt;/code&gt; 和 &lt;code&gt;event.code&lt;/code&gt;（顶层字段），永远是 undefined。&lt;/p&gt;
&lt;h3 id="修复-1"&gt;修复
&lt;/h3&gt;&lt;p&gt;OpenAI 端：先读顶层 &lt;code&gt;event.{code, message, param}&lt;/code&gt;，再回退到嵌套的 &lt;code&gt;event.response.error.{code, message, param}&lt;/code&gt;。当 code 和 message 同时存在时，用 code 做前缀：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rate_limit_exceeded: Slow down
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;server_error: Upstream model unavailable
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Anthropic 端：用 &lt;code&gt;error.type&lt;/code&gt; 做前缀：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;overloaded_error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Overloaded&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="根因-1"&gt;根因
&lt;/h3&gt;&lt;p&gt;错误处理的代码写得太&amp;quot;乐观&amp;quot;了——假设错误信息总在预期的位置。但 OpenAI 的 API 在不同错误场景下把信息放在不同的嵌套层级，代码没有覆盖所有情况。&lt;/p&gt;
&lt;h2 id="bug-3anthropic-tool-result-类型检查不稳定"&gt;Bug 3：Anthropic Tool Result 类型检查不稳定
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;相关 PR&lt;/strong&gt;：&lt;a class="link" href="https://github.com/anomalyco/opencode/pull/28909" target="_blank" rel="noopener"
&gt;#28909&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="问题-2"&gt;问题
&lt;/h3&gt;&lt;p&gt;这个 PR 在前两个修复合并后才出现。修复了图片 tool result 的结构化发送后，Anthropic 端的类型检查变得不稳定——某些边缘情况下 tool result 的类型推断会失败。&lt;/p&gt;
&lt;p&gt;具体来说，&lt;code&gt;tool_result.content&lt;/code&gt; 的类型从 &lt;code&gt;string&lt;/code&gt; 扩展成了 &lt;code&gt;string | ContentItem[]&lt;/code&gt;，但下游代码没有完全适配这个联合类型。&lt;/p&gt;
&lt;h3 id="修复-2"&gt;修复
&lt;/h3&gt;&lt;p&gt;稳定了 Anthropic tool result 的类型检查逻辑，确保联合类型的所有分支都被正确处理。&lt;/p&gt;
&lt;h3 id="根因-2"&gt;根因
&lt;/h3&gt;&lt;p&gt;这是 Bug 1 修复的连锁反应。把 schema 从 &lt;code&gt;Schema.String&lt;/code&gt; 改成 &lt;code&gt;Schema.Union&lt;/code&gt; 后，类型系统变复杂了，之前不需要处理的分支现在必须处理。&lt;/p&gt;
&lt;h2 id="三个-bug-的关系"&gt;三个 Bug 的关系
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;初始设计缺陷&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="k"&gt;tool&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="err"&gt;只考虑文本场景&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;Bug&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt; &lt;span class="err"&gt;图片被字符串化&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="c1"&gt;#28754)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;Bug&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Anthropic&lt;/span&gt; &lt;span class="err"&gt;图片被字符串化&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="c1"&gt;#28755)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="err"&gt;只覆盖理想情况&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;Bug&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="err"&gt;信息丢失&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="c1"&gt;#28757)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;Bug&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;修复后类型变复杂&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;Bug&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;类型检查不稳定&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="c1"&gt;#28909)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Bug 1 和 Bug 2 是独立的初始设计缺陷。Bug 3 是 Bug 1 修复的副作用。&lt;/p&gt;
&lt;h2 id="对-ai-工具开发的启示"&gt;对 AI 工具开发的启示
&lt;/h2&gt;&lt;h3 id="1-协议层的够用就行是最危险的"&gt;1. 协议层的&amp;quot;够用就行&amp;quot;是最危险的
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;toolResultText()&lt;/code&gt; 在只有文本 tool result 的时候完全够用。但协议层的抽象一旦固定下来，后续扩展就很难——因为所有调用方都依赖当前行为。opencode 的修复保持了向后兼容（文本场景仍然用字符串），但代价是类型变复杂了（Bug 3）。&lt;/p&gt;
&lt;p&gt;如果初始设计就把 tool result 分成文本/结构化两条路径，就不会有后面的问题。当然，这是事后诸葛亮——没有人能在第一天就预见到 tool result 会包含图片。&lt;/p&gt;
&lt;h3 id="2-错误处理要假设最坏情况"&gt;2. 错误处理要假设最坏情况
&lt;/h3&gt;&lt;p&gt;&amp;ldquo;错误信息总在预期位置&amp;quot;这个假设在 LLM API 上尤其不成立。各家 provider 的错误格式不一致，同一家 provider 的不同错误类型格式也不一致。写错误处理代码时，应该假设错误信息可能在任何嵌套层级，甚至完全缺失。&lt;/p&gt;
&lt;h3 id="3-修一个-bug-可能暴露下一个"&gt;3. 修一个 bug 可能暴露下一个
&lt;/h3&gt;&lt;p&gt;opencode 的三个 PR 形成了一条修复链。修了图片序列化后，类型变复杂了，暴露了类型检查的漏洞。在提交修复时应该考虑到类型变更的下游影响。&lt;/p&gt;
&lt;h3 id="4-观察-bug-的方式决定了修复速度"&gt;4. 观察 bug 的方式决定了修复速度
&lt;/h3&gt;&lt;p&gt;kitlangton 在 #28757 的 PR 描述里提到，stream error 信息丢失使得某个 session 极其难诊断。如果错误信息是可见的，可能早就发现了。&lt;strong&gt;让错误可见&lt;/strong&gt;是基础设施类代码的重要原则——宁可多输出一点，也不要吞掉信息。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;opencode 的这三个 LLM provider bug 都源自初始设计对复杂场景的简化处理。随着 AI 工具能力边界的扩展（从纯文本到多模态），早期&amp;quot;够用就行&amp;quot;的抽象开始出现裂缝。修复的方式是务实的：保持向后兼容的同时扩展新能力。对 AI 工具开发者来说，这是一个提醒——协议层的设计要为未知的变化留余地，错误处理要假设最坏的情况。&lt;/p&gt;</description></item><item><title>OpenCode 的 GitHub Actions 自动化体系：27 个 Workflow 背后的工程实践</title><link>https://svtter.cn/p/opencode-%E7%9A%84-github-actions-%E8%87%AA%E5%8A%A8%E5%8C%96%E4%BD%93%E7%B3%BB27-%E4%B8%AA-workflow-%E8%83%8C%E5%90%8E%E7%9A%84%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/</link><pubDate>Fri, 22 May 2026 10:00:00 +0800</pubDate><guid>https://svtter.cn/p/opencode-%E7%9A%84-github-actions-%E8%87%AA%E5%8A%A8%E5%8C%96%E4%BD%93%E7%B3%BB27-%E4%B8%AA-workflow-%E8%83%8C%E5%90%8E%E7%9A%84%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/</guid><description>&lt;img src="https://svtter.cn/p/opencode-%E7%9A%84-github-actions-%E8%87%AA%E5%8A%A8%E5%8C%96%E4%BD%93%E7%B3%BB27-%E4%B8%AA-workflow-%E8%83%8C%E5%90%8E%E7%9A%84%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/cover.jpg" alt="Featured image of post OpenCode 的 GitHub Actions 自动化体系：27 个 Workflow 背后的工程实践" /&gt;&lt;p&gt;opencode 是一个 16 万 star 的 AI 编码工具，它的 &lt;code&gt;.github/workflows/&lt;/code&gt; 目录下有 27 个 workflow 文件。这个数量在开源项目里不算少，但真正有意思的不是数量，而是这些 workflow 覆盖的范围：从常规的 CI/CD 到 AI 驱动的社区治理，几乎把 GitHub Actions 能做的事情都做了一遍。&lt;/p&gt;
&lt;p&gt;这篇文章逐类分析这些 workflow 的设计，讨论这种自动化程度的利弊，以及对我们自己项目的启示。&lt;/p&gt;
&lt;h2 id="总览"&gt;总览
&lt;/h2&gt;&lt;p&gt;27 个 workflow 可以分成四类：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类别&lt;/th&gt;
&lt;th&gt;数量&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CI/测试&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;typecheck、单元测试、e2e、Nix 构建&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;发布/交付&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;CLI 发布、容器构建、VS Code 插件、GitHub Action 发布&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;自动化/Bot&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;issue 治理、PR 合规、AI code review、文档更新&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;文档/其他&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;统计、Discord 通知&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;16 个自动化 workflow 占了总数的 60%。opencode 不只是用 Actions 来跑测试和发布，而是把社区治理和代码质量审查也交给了自动化系统。&lt;/p&gt;
&lt;h2 id="ci测试扎实但克制"&gt;CI/测试：扎实但克制
&lt;/h2&gt;&lt;p&gt;四个测试相关的 workflow：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;typecheck.yml&lt;/strong&gt; — PR 和 push 到 dev 时跑 &lt;code&gt;bun typecheck&lt;/code&gt;。简单直接，没有多余动作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;test.yml&lt;/strong&gt; — 跨平台测试矩阵（Linux + Windows），跑单元测试和 Playwright e2e。有 concurrency 控制，同一个 PR 的新 commit 会取消旧运行。测试结果生成 JUnit 报告上传为 artifact。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;nix-eval.yml&lt;/strong&gt; — 验证 Nix flake 在四种架构（x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin）下的构建。必选包失败会阻断，可选包失败只是警告。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;storybook.yml&lt;/strong&gt; — UI 组件的 Storybook 构建，只在 storybook/ui 相关文件变更时触发。路径触发避免了不必要的运行。&lt;/p&gt;
&lt;p&gt;几个值得注意的设计选择：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;concurrency group + cancel-in-progress&lt;/strong&gt;：多个 workflow 都用了这个模式，同一个 PR 不会堆叠多次运行。对于一个接受大量社区 PR 的项目，这能省下不少 CI 资源。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路径触发&lt;/strong&gt;：containers.yml 只在容器文件变更时运行，storybook.yml 只在 UI 变更时运行。不是所有东西都全量跑。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;混合 Runner 策略&lt;/strong&gt;：大部分 workflow 使用 &lt;a class="link" href="https://blacksmith.sh/" target="_blank" rel="noopener"
&gt;Blacksmith&lt;/a&gt; 提供的第三方托管 runner（&lt;code&gt;blacksmith-4vcpu-ubuntu-2404&lt;/code&gt;、&lt;code&gt;blacksmith-4vcpu-windows-2025&lt;/code&gt;）。Blacksmith 是一个兼容 GitHub Actions API 的加速 runner 服务，底层用自建基础设施，比 GitHub 免费 runner 快不少。只有轻量级 bot 任务（close-issues、close-prs、compliance-close、pr-standards、deploy）留在 GitHub 原生的 &lt;code&gt;ubuntu-latest&lt;/code&gt; 上。计算密集的编译、测试、发布全走 Blacksmith，简单的脚本任务用 GitHub 原生 runner，按任务负载分配资源。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="发布交付全平台覆盖"&gt;发布/交付：全平台覆盖
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;publish.yml&lt;/strong&gt; 是最复杂的 workflow，一个文件处理了完整的发布流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;版本号计算&lt;/li&gt;
&lt;li&gt;CLI 构建矩阵（多平台多架构）&lt;/li&gt;
&lt;li&gt;Windows 代码签名（Azure Signing）&lt;/li&gt;
&lt;li&gt;macOS 代码签名（Apple Developer）&lt;/li&gt;
&lt;li&gt;Electron 应用构建&lt;/li&gt;
&lt;li&gt;npm 发布&lt;/li&gt;
&lt;li&gt;GitHub Release 创建&lt;/li&gt;
&lt;li&gt;AUR（Arch Linux）发布&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;一个 workflow 覆盖了 CLI、桌面应用、npm 包、Linux 包的分发。这种&amp;quot;全平台一次发布&amp;quot;的模式对用户友好——无论什么平台都能在同一天拿到新版本。&lt;/p&gt;
&lt;p&gt;其他发布 workflow 按产物类型拆分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;publish-github-action.yml&lt;/strong&gt; — 监听 &lt;code&gt;github-v*&lt;/code&gt; tag，发布 GitHub Action 到 Marketplace&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;publish-vscode.yml&lt;/strong&gt; — 监听 &lt;code&gt;vscode-v*&lt;/code&gt; tag，同时发布到 VS Code Marketplace 和 Open VSX&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;containers.yml&lt;/strong&gt; — 多架构容器镜像构建，推送到 GHCR&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;release-github-action.yml&lt;/strong&gt; — dev 分支有 github 目录变更时创建预发布&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;tag 触发是一个好的实践：发布是显式动作，不会因为误推代码就触发。&lt;code&gt;publish.yml&lt;/code&gt; 会在 push 到 &lt;code&gt;ci/dev/beta/fix&lt;/code&gt; 分支时自动构建 snapshot，但正式发布需要手动 dispatch 或 tag。&lt;/p&gt;
&lt;h2 id="自动化botai-驱动的社区治理"&gt;自动化/Bot：AI 驱动的社区治理
&lt;/h2&gt;&lt;p&gt;这是 opencode 最有特色的部分。16 个自动化 workflow 中，多个直接调用了 opencode 自己的 AI 能力来处理社区事务。&lt;/p&gt;
&lt;h3 id="issue-管理"&gt;Issue 管理
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;triage.yml&lt;/strong&gt; — 新 issue 创建时，用 opencode AI 自动分流，添加标签和分类。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;duplicate-issues.yml&lt;/strong&gt; — 新 issue 创建/编辑时，用 opencode AI 分析是否与已有 issue 重复。同时检查是否遵循了三种 issue 模板之一，是否包含 AI 生成内容。不合规的会被加上 &lt;code&gt;needs:compliance&lt;/code&gt; 标签。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;compliance-close.yml&lt;/strong&gt; — 每 30 分钟检查一次，带有 &lt;code&gt;needs:compliance&lt;/code&gt; 标签的 issue/PR 如果 2 小时内没有修正，自动关闭。关闭时会根据是 issue 还是 PR 给出不同的提示信息。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;close-issues.yml&lt;/strong&gt; — 每天凌晨 2 点 UTC 关闭过期的 stale issue。&lt;/p&gt;
&lt;p&gt;这四层形成了完整的 issue 生命周期管理：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;新 issue → AI 分流(triage) → 重复/合规检查(duplicate) → 合规宽限期(compliance) → 过期清理(close)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="pr-管理"&gt;PR 管理
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;pr-standards.yml&lt;/strong&gt; 是最长的 workflow 之一，做了两件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;标题格式检查&lt;/strong&gt;：强制 conventional commits 格式（feat/fix/refactor/&amp;hellip;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模板合规检查&lt;/strong&gt;：PR 描述必须包含 issue 引用、变更类型、验证方法等必要章节&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不合规的 PR 会被加上 &lt;code&gt;needs:compliance&lt;/code&gt; 标签，2 小时后自动关闭。team 成员和 bot 豁免。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pr-management.yml&lt;/strong&gt; — PR 创建时查重，并为社区贡献者添加标签。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;close-prs.yml&lt;/strong&gt; — 每天晚上 10 点 UTC 关闭超过 1 个月且点赞数不够的 PR。默认阈值是 2 个 reaction，可配置。&lt;/p&gt;
&lt;h3 id="ai-code-review"&gt;AI Code Review
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;review.yml&lt;/strong&gt; — 在 PR 评论中输入 &lt;code&gt;/review&lt;/code&gt;，opencode AI 会分析代码并在具体的行上留下 review 评论。只对 repo owner/member 开放。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;opencode.yml&lt;/strong&gt; — 在 issue 或 PR 评论中输入 &lt;code&gt;/oc&lt;/code&gt; 或 &lt;code&gt;/opencode&lt;/code&gt;，触发 opencode AI 进行更通用的交互。&lt;/p&gt;
&lt;p&gt;这两个 workflow 展示了&amp;quot;AI 作为协作者&amp;quot;的思路：不是全自动的 code review，而是按需触发，人在回路中做最终决定。&lt;/p&gt;
&lt;h3 id="文档与维护"&gt;文档与维护
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;docs-update.yml&lt;/strong&gt; — 每 12 小时检查最近的 commit，用 opencode AI 判断是否需要更新文档。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;generate.yml&lt;/strong&gt; — push 到 dev 时运行代码生成脚本，自动提交变更。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;beta.yml&lt;/strong&gt; — 每小时同步 beta 分支。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;stats.yml&lt;/strong&gt; — 每天更新下载统计到 STATS.md。&lt;/p&gt;
&lt;h2 id="值得借鉴的设计模式"&gt;值得借鉴的设计模式
&lt;/h2&gt;&lt;h3 id="1-分层治理"&gt;1. 分层治理
&lt;/h3&gt;&lt;p&gt;opencode 没有把所有自动化塞进一个 workflow，而是按职责拆分。issue 从创建到关闭经过了四个 workflow 的接力处理。每个 workflow 只做一件事，组合起来形成完整的治理链。&lt;/p&gt;
&lt;p&gt;这种设计的好处是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单个 workflow 可以独立修改、禁用，不影响其他环节&lt;/li&gt;
&lt;li&gt;每个 workflow 的触发条件、权限范围都是最小化的&lt;/li&gt;
&lt;li&gt;出问题时容易定位是哪个环节&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-合规宽限期"&gt;2. 合规宽限期
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;compliance-close.yml&lt;/code&gt; 不是检测到不合规就立即关闭，而是给 2 小时的宽限期。这对全球不同时区的贡献者来说是合理的——你可能正好在睡觉的时候提交了一个 issue，醒来发现有时间修正。&lt;/p&gt;
&lt;h3 id="3-ai-用在决策点而不是执行点"&gt;3. AI 用在决策点而不是执行点
&lt;/h3&gt;&lt;p&gt;triage、duplicate 检测、code review 这些都是 AI 做初步判断，人做最终决定。而代码构建、发布这些执行层面的事情完全不用 AI。这是一个务实的分工：AI 擅长模式识别和初步分类，但不擅长精确执行。&lt;/p&gt;
&lt;h3 id="4-显式触发-vs-自动触发"&gt;4. 显式触发 vs 自动触发
&lt;/h3&gt;&lt;p&gt;发布用 tag 触发，日常维护用 schedule 触发，社区治理用事件触发。三种触发方式对应三种不同的自动化信任等级：发布需要人工确认，维护可以定时自动，治理需要即时响应。&lt;/p&gt;
&lt;h2 id="过度自动化的风险"&gt;过度自动化的风险
&lt;/h2&gt;&lt;p&gt;opencode 的自动化体系很完善，但也有需要注意的地方：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;社区门槛&lt;/strong&gt;：新贡献者提交 issue 需要遵循特定模板，PR 需要符合 conventional commits，否则 2 小时后自动关闭。对于一个 16 万 star 的项目，这种严格是合理的——它能过滤掉大量低质量贡献。但对于小项目，这种程度的自动化会吓跑潜在贡献者。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;维护成本&lt;/strong&gt;：27 个 workflow 意味着 27 个需要维护的自动化脚本。opencode 有专门的自定义 runner 和专用脚本。如果某个 workflow 的逻辑需要调整，维护者需要在 GitHub Actions 的 YAML + 自定义脚本之间来回切换。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI 的不确定性&lt;/strong&gt;：duplicate-issues 和 triage 用 AI 做判断，但 AI 可能误判。一个合理的 issue 被标记为 duplicate 并关闭，对贡献者的体验是负面的。opencode 用宽限期和人工复核来缓解这个问题，但风险依然存在。&lt;/p&gt;
&lt;h2 id="对我们项目的启示"&gt;对我们项目的启示
&lt;/h2&gt;&lt;p&gt;不是每个项目都需要 27 个 workflow。但 opencode 的分层治理和&amp;quot;AI 在决策点&amp;quot;的思路值得参考：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;从 issue 模板开始&lt;/strong&gt;：如果项目开始收到大量重复或低质量 issue，先加模板和 duplicate 检查，而不是手动处理每一个。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;合规检查用宽限期&lt;/strong&gt;：自动关闭不合规贡献时，始终给一个宽限期。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 用于分类不用于执行&lt;/strong&gt;：让 AI 帮你分流 issue、检查 PR 格式，但不要让 AI 自动合并代码或发布版本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;发布用 tag 触发&lt;/strong&gt;：这是最安全的做法。自动发布的 snapshot 可以接受，正式版本需要人工确认。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;按需添加&lt;/strong&gt;：先有痛点再加自动化。opencode 的 27 个 workflow 不是一天建成的，是随着社区规模增长逐步增加的。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;opencode 的 GitHub Actions 体系展示了大型开源项目的自动化实践：CI/CD 覆盖全平台发布，社区治理用多层 workflow 接力处理，AI 被用在分流和审查等决策环节。这套系统的核心不是技术复杂度，而是&amp;quot;分层、宽限、显式触发&amp;quot;这三个原则。对于自己的项目，不需要照搬 27 个 workflow，但这些原则可以直接用上。&lt;/p&gt;</description></item><item><title>OpenCode 配置之外的优化 — 基于插件的优化</title><link>https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/</link><pubDate>Tue, 19 May 2026 10:00:00 +0800</pubDate><guid>https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/</guid><description>&lt;img src="https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/cover.png" alt="Featured image of post OpenCode 配置之外的优化 — 基于插件的优化" /&gt;&lt;p&gt;之前我写过一篇 &lt;a class="link" href="https://svtter.cn/p/opencode-%e9%85%8d%e7%bd%ae%e4%bc%98%e5%8c%96%e8%ae%b0%e5%bd%95/" &gt;OpenCode 配置优化记录&lt;/a&gt;，解决的是 token 消耗和上下文管理的问题。但配置优化管的是&amp;quot;模型怎么跑&amp;quot;，而&amp;quot;代码写到一半质量怎么样&amp;quot;——这件事配置管不了。这篇文章从我开发 opencode-review 插件的过程出发，聊聊 opencode-review 如何在一个 session 内帮助 agent 审查并改进自己的代码，让最终进入 PR 的代码质量更高。&lt;/p&gt;
&lt;h2 id="问题session-内的代码质量谁来把关"&gt;问题：session 内的代码质量谁来把关？
&lt;/h2&gt;&lt;p&gt;用 OpenCode 写代码时，一个典型的工作流是：agent 在一个 session 内完成编码，然后我 review diff、创建 PR。但我发现一个反复出现的问题：&lt;strong&gt;agent 写完的代码经常带着&amp;quot;第一次草稿&amp;quot;的质量问题就进入 PR 了&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这些问题包括：缺少错误处理、安全漏洞、性能不佳的查询、缺失的测试。如果能在 session 内——也就是代码还没提交到 PR 之前——让 agent 自行审查一轮，很多问题在 PR 阶段就不存在了。&lt;/p&gt;
&lt;p&gt;这和 CI 阶段的 code review 是不同层次的事情。CI 审查我已经通过 &lt;a class="link" href="https://github.com/sun-praise/opencode-actions" target="_blank" rel="noopener"
&gt;opencode-actions&lt;/a&gt;（之前写过一篇&lt;a class="link" href="https://svtter.cn/p/opencode-actions-%e4%b8%80%e4%b8%aa-coding-review-agent/" &gt;介绍文章&lt;/a&gt;）实现了——它发生在 PR 创建之后，由 GitHub Actions 触发。后来 Cloudflare 也在&lt;a class="link" href="https://blog.cloudflare.com/ai-code-review/" target="_blank" rel="noopener"
&gt;工程博客&lt;/a&gt;中分享了类似思路：用 OpenCode 构建大规模 AI code review。而 opencode-review 要解决的是更早的阶段：&lt;strong&gt;在 session 内、在 PR 之前，让 agent 在写完代码后主动审查并修复问题&lt;/strong&gt;。两者互补：opencode-review 提升进入 PR 的代码质量基线，opencode-actions 则作为最后一道关卡。&lt;/p&gt;
&lt;p&gt;具体来说，有三个需要解决的子问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;审查覆盖不全&lt;/strong&gt;：agent 生成的代码可能引入安全漏洞、性能问题，但它自己不会主动检查这些&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺乏系统性的审查框架&lt;/strong&gt;：没有结构化的维度来评估代码，容易只关注功能正确性而忽略安全和性能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;发现问题和修复之间缺乏闭环&lt;/strong&gt;：即使 agent 发现了问题，也需要一个机制来自动修复，而不是等人来指出&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="opencode-review-的设计"&gt;opencode-review 的设计
&lt;/h2&gt;&lt;p&gt;基于这三个问题，我设计了 opencode-review：一个结构化的代码审查插件。&lt;/p&gt;
&lt;h3 id="多维度分析"&gt;多维度分析
&lt;/h3&gt;&lt;p&gt;第一个设计决策是&lt;strong&gt;为什么分五个维度&lt;/strong&gt;，而不是一个笼统的&amp;quot;好不好&amp;quot;的评价。&lt;/p&gt;
&lt;p&gt;代码质量不是一个单一维度。一段代码可能功能正确、性能优秀，但存在 SQL 注入漏洞；也可能安全无害，但缺少测试覆盖。把它们混在一起评估，结果必然模糊。&lt;/p&gt;
&lt;p&gt;学术上，&lt;a class="link" href="https://github.com/watreyoung/MCR-Survey" target="_blank" rel="noopener"
&gt;Modern Code Review (MCR) Survey&lt;/a&gt; 收集了 2013-2025 年间的代码审查研究，提出了一个分类体系，涵盖缺陷检测、安全审查、性能分析、可维护性评估等多个任务维度。Ericsson 的研究团队在 &lt;a class="link" href="https://arxiv.org/html/2507.19115v2" target="_blank" rel="noopener"
&gt;Automated Code Review Using Large Language Models at Ericsson&lt;/a&gt; 中也验证了：按维度拆分的审查比笼统审查在工业场景中更有效。&lt;/p&gt;
&lt;p&gt;opencode-review 的五个维度——code-quality、security、performance、testing、documentation——对应的就是这些研究中识别出的核心审查维度。每个维度可以独立开关，因为不同项目关注的重点不同：一个内部工具可能不需要文档审查，但一个安全敏感的服务不能跳过 security 维度。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/dimensions.png"
width="1376"
height="768"
srcset="https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/dimensions_hu_7330a01d3840e4a9.png 480w, https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/dimensions_hu_fceacab38c67aa8b.png 1024w"
loading="lazy"
alt="五个审查维度"
class="gallery-image"
data-flex-grow="179"
data-flex-basis="430px"
&gt;&lt;/p&gt;
&lt;h3 id="严重性分级"&gt;严重性分级
&lt;/h3&gt;&lt;p&gt;第二个设计决策是&lt;strong&gt;为什么分三级严重性&lt;/strong&gt;（critical / suggestion / highlight）。&lt;/p&gt;
&lt;p&gt;这来自静态分析工具领域的经验教训。安全工具和 linter 长期面临一个问题：&lt;strong&gt;alert fatigue（告警疲劳）&lt;/strong&gt;。当所有问题都被标记为同等重要时，开发者会开始忽略它们。&lt;a class="link" href="https://www.veracode.com/blog/breaking-the-cycle-of-alert-fatigue/" target="_blank" rel="noopener"
&gt;Veracode 的研究&lt;/a&gt;指出，告警疲劳导致的直接后果是真正的严重问题被淹没在噪音中。&lt;/p&gt;
&lt;p&gt;分三级的逻辑是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;critical&lt;/strong&gt;：必须修复（安全漏洞、逻辑错误、资源泄漏）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;suggestion&lt;/strong&gt;：建议改进（代码可读性、性能优化、更好的实践）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;highlight&lt;/strong&gt;：值得注意（风格一致性、潜在的改进空间）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样开发者可以优先处理 critical，而不会在一堆 &amp;ldquo;consider refactoring&amp;rdquo; 中错过一个 SQL 注入。&lt;/p&gt;
&lt;h3 id="自动修复链"&gt;自动修复链
&lt;/h3&gt;&lt;p&gt;第三个设计决策是&lt;strong&gt;为什么 critical 问题要自动触发修复&lt;/strong&gt;，而不是仅仅报告。&lt;/p&gt;
&lt;p&gt;这是一个有争议的设计。传统的审查工具通常是&amp;quot;只报告不修复&amp;quot;，把修复留给开发者。但 opencode-review 的场景不同——它审查的代码本身就是 AI agent 刚写完的，让另一个 agent 去修复合情合理。&lt;/p&gt;
&lt;p&gt;学术上这属于 &lt;strong&gt;Automated Program Repair (APR)&lt;/strong&gt; 的范畴。&lt;a class="link" href="https://arxiv.org/html/2506.23749v1" target="_blank" rel="noopener"
&gt;A Survey of LLM-based Automated Program Repair (arXiv 2506.23749)&lt;/a&gt; 综述了 2022-2025 年间的 63 个 LLM-based APR 系统，分为四种范式。其中&amp;quot;分析增强&amp;quot;（analysis-augmented）范式——先用静态分析定位问题，再用 LLM 生成修复——被证明是最有效的。opencode-review 的 auto-fix chain 本质上就是这个范式：reviewer 发现 critical issue → 定位问题位置 → spawn fixer sub-agent → 生成最小化修复。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/auto-fix-chain.png"
width="1376"
height="768"
srcset="https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/auto-fix-chain_hu_67450c93ac3d843a.png 480w, https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/auto-fix-chain_hu_261b6e10779b8b33.png 1024w"
loading="lazy"
alt="自动修复链"
class="gallery-image"
data-flex-grow="179"
data-flex-basis="430px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://dl.acm.org/doi/10.1109/ICSE55347.2025.00169" target="_blank" rel="noopener"
&gt;ICSE 2025 的一篇论文&lt;/a&gt; 也指出，LLM 在 APR 中的关键挑战是目标对齐（objective alignment）——修复的目标不是&amp;quot;生成看起来合理的代码&amp;quot;，而是&amp;quot;精确解决报告的问题&amp;quot;。这也是为什么 opencode-review 的 fixer 被设计为 &lt;strong&gt;minimal fix&lt;/strong&gt;——只做最小的修改来解决问题，不重写、不重构、不&amp;quot;顺手&amp;quot;做其他改动。&lt;/p&gt;
&lt;h3 id="自动审查的隐性收益代码质量基线的持续提升"&gt;自动审查的隐性收益：代码质量基线的持续提升
&lt;/h3&gt;&lt;p&gt;上面三个设计解决的是&amp;quot;发现问题&amp;quot;和&amp;quot;修复问题&amp;quot;。但自动审查还有一个容易被忽略的好处：&lt;strong&gt;它在不经意间持续提升了代码质量的基线&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个效果来自两个机制：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一，审查反馈对写代码者的塑造。&lt;/strong&gt; &lt;a class="link" href="https://dl.acm.org/doi/10.1145/3540250.3558950" target="_blank" rel="noopener"
&gt;FSE 2022 的研究&lt;/a&gt; 在两年的工业实践中发现，当开发者知道自己的代码会被自动审查时，他们会在写代码阶段就更有意识地遵循规范——因为事后被指出来的成本变低了，提前写好的收益变高了。这是一种 &lt;strong&gt;nudge effect（助推效应）&lt;/strong&gt;。在 AI agent 的场景下，这个效应更强：agent 在一个 session 中写了代码、被 review 指出问题、修复、再次被审查——这个循环在同一 session 内就能完成多轮。每一轮反馈都在修正 agent 的输出倾向，相当于一个隐式的 fine-tuning 过程。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二，自动修复的直接质量累积。&lt;/strong&gt; critical issue 被自动修复意味着每一轮提交的代码质量都比没有审查时更高。这不是一次性的改进，而是持续的。就像代码库中的 lint 规则一样——一开始只是禁止明显错误，但随着规则积累，代码库的整体风格和质量在不知不觉中被拉高了。auto-fix chain 做的事情类似：安全漏洞被自动堵上、资源泄漏被自动修复、缺失的测试被自动补充。时间一长，代码库的质量基线自然高于没有自动审查的情况。&lt;/p&gt;
&lt;p&gt;简单说：&lt;strong&gt;审查不是目的，质量提升才是。自动审查把&amp;quot;事后检查&amp;quot;变成了&amp;quot;过程中提升&amp;quot;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/quality-baseline.jpg"
width="1376"
height="768"
srcset="https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/quality-baseline_hu_45c08f85532cd10c.jpg 480w, https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%B9%8B%E5%A4%96%E7%9A%84%E4%BC%98%E5%8C%96-%E5%9F%BA%E4%BA%8E%E6%8F%92%E4%BB%B6%E7%9A%84%E4%BC%98%E5%8C%96/quality-baseline_hu_f97e789531211c0b.jpg 1024w"
loading="lazy"
alt="代码质量基线提升"
class="gallery-image"
data-flex-grow="179"
data-flex-basis="430px"
&gt;&lt;/p&gt;
&lt;h3 id="cooldown-机制"&gt;Cooldown 机制
&lt;/h3&gt;&lt;p&gt;还有一个小的设计细节：&lt;strong&gt;cooldown_seconds&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;auto-review 在 session idle 时触发，但 idle 事件可能频繁触发（比如 agent 在等待用户确认时也会 idle）。没有 cooldown 的话，同一份代码可能被审查好几次，浪费 token。120 秒的默认冷却期是一个经验值——足够让一轮修改完成，又不会等太久。&lt;/p&gt;
&lt;h2 id="opencode-froggy另一种思路"&gt;opencode-froggy：另一种思路
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/smartfrog/opencode-froggy" target="_blank" rel="noopener"
&gt;opencode-froggy&lt;/a&gt;（85 Stars，昨天刚发 0.12.0）提供了另一种思路。它不做结构化的多维度审查，而是提供 6 个专用 agent（architect、code-reviewer、code-simplifier、doc-writer、partner、rubber-duck）和一套灵活的 hooks 系统。&lt;/p&gt;
&lt;p&gt;Froggy 的 code-reviewer 是一个通用的只读审查 agent，不区分维度和严重性。但它的 hooks 系统很强——你可以配置 &lt;code&gt;session.idle&lt;/code&gt; 事件自动跑 lint、自动格式化、甚至在写入敏感文件时拦截：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;session.idle&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;hasCodeChange, isMainSession]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;bash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;npm run lint --fix&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;simplify-changes&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这是一种&amp;quot;开发者自己编排流程&amp;quot;的思路，和 opencode-review 的&amp;quot;开箱即用的结构化审查&amp;quot;形成互补。&lt;/p&gt;
&lt;h3 id="对比"&gt;对比
&lt;/h3&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;opencode-review&lt;/th&gt;
&lt;th&gt;opencode-froggy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;审查方式&lt;/td&gt;
&lt;td&gt;结构化多维度分析&lt;/td&gt;
&lt;td&gt;通用 code-reviewer agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;严重性分级&lt;/td&gt;
&lt;td&gt;critical / suggestion / highlight&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;自动修复&lt;/td&gt;
&lt;td&gt;critical issue → fixer sub-agent&lt;/td&gt;
&lt;td&gt;code-simplifier，需手动触发&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;触发方式&lt;/td&gt;
&lt;td&gt;session idle + cooldown&lt;/td&gt;
&lt;td&gt;hooks 配置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;自定义规则&lt;/td&gt;
&lt;td&gt;custom_rules 支持项目规范&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;其他功能&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;6 agent + hooks + gitingest + 区块链&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;两个不冲突，可以一起装。我的建议是：&lt;strong&gt;opencode-review 做日常自动审查，froggy 的 hooks 做流程编排&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="插件安装"&gt;插件安装
&lt;/h2&gt;&lt;p&gt;两个插件的安装方式不同。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;opencode-froggy&lt;/strong&gt; 支持通过 npm 直接安装，在 &lt;code&gt;opencode.json&lt;/code&gt; 中添加即可：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;plugin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;opencode-froggy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;opencode-review&lt;/strong&gt; 目前 npm 安装尚未上线，需要 clone 后本地链接：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# clone 到任意位置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/sun-praise/opencode-review.git /path/to/opencode-review
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 项目级安装（推荐）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p .opencode/plugins
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ln -s /path/to/opencode-review/src/index.ts .opencode/plugins/opencode-review.ts
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 或全局安装&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ln -s /path/to/opencode-review/src/index.ts ~/.config/opencode/plugins/opencode-review.ts
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;opencode-review 还需要创建 &lt;code&gt;.opencode/review.json&lt;/code&gt; 来配置审查行为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;language&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;zh&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;dimensions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;code-quality&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;security&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;performance&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;testing&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;documentation&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;trigger&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;auto_on_idle&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cooldown_seconds&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;custom_rules&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;All API endpoints must have error handling&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Database queries must use parameterized statements&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="其他值得关注的插件"&gt;其他值得关注的插件
&lt;/h2&gt;&lt;p&gt;生态已经超过 70 个插件了，再推荐几个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;opencode-worktree&lt;/strong&gt;：零摩擦的 git worktree 管理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;opencode-notify&lt;/strong&gt;：任务完成时发送系统通知&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dynamic-context-pruning&lt;/strong&gt;：自动裁剪过时的工具输出，优化 token 使用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;envsitter-guard&lt;/strong&gt;：阻止 agent 读取 &lt;code&gt;.env&lt;/code&gt; 敏感文件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;完整列表见 &lt;a class="link" href="https://github.com/awesome-opencode/awesome-opencode" target="_blank" rel="noopener"
&gt;awesome-opencode&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="参考文献"&gt;参考文献
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/watreyoung/MCR-Survey" target="_blank" rel="noopener"
&gt;Modern Code Review (MCR) Survey&lt;/a&gt; — 2013-2025 代码审查研究综述&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/html/2507.19115v2" target="_blank" rel="noopener"
&gt;Automated Code Review Using LLMs at Ericsson&lt;/a&gt; — LLM 辅助代码审查的工业实践&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/html/2506.23749v1" target="_blank" rel="noopener"
&gt;A Survey of LLM-based Automated Program Repair&lt;/a&gt; — LLM 自动修复综述，覆盖 63 个系统&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://dl.acm.org/doi/10.1109/ICSE55347.2025.00169" target="_blank" rel="noopener"
&gt;Aligning the Objective of LLM-Based Program Repair (ICSE 2025)&lt;/a&gt; — LLM 修复的目标对齐问题&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://dl.acm.org/doi/10.1145/3540250.3558950" target="_blank" rel="noopener"
&gt;Understanding Automated Code Review Process (FSE 2022)&lt;/a&gt; — 两年工业环境自动审查的经验总结&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://homes.cs.washington.edu/~rjust/publ/code_review_automation_aiware_2024.pdf" target="_blank" rel="noopener"
&gt;AI-Assisted Assessment in Modern Code Review (AIware 2024)&lt;/a&gt; — AutoCommenter 的部署与评估&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/html/2603.23448v2" target="_blank" rel="noopener"
&gt;Code Review Agent Benchmark (c-CRAB)&lt;/a&gt; — AI agent 代码审查基准测试&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/opencode-actions-%e4%b8%80%e4%b8%aa-coding-review-agent/" &gt;opencode-actions - 一个 coding review agent&lt;/a&gt; — 基于 OpenCode 构建的 GitHub Action，CI 阶段的 code review&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.cloudflare.com/ai-code-review/" target="_blank" rel="noopener"
&gt;Cloudflare: Orchestrating AI Code Review at Scale&lt;/a&gt; — Cloudflare 用 OpenCode 构建大规模 AI 审查&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>如何解决 opencode 中 deepseek 模型的 reasoning 问题</title><link>https://svtter.cn/p/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3-opencode-%E4%B8%AD-deepseek-%E6%A8%A1%E5%9E%8B%E7%9A%84-reasoning-%E9%97%AE%E9%A2%98/</link><pubDate>Fri, 24 Apr 2026 12:23:58 +0800</pubDate><guid>https://svtter.cn/p/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3-opencode-%E4%B8%AD-deepseek-%E6%A8%A1%E5%9E%8B%E7%9A%84-reasoning-%E9%97%AE%E9%A2%98/</guid><description>&lt;img src="https://svtter.cn/p/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3-opencode-%E4%B8%AD-deepseek-%E6%A8%A1%E5%9E%8B%E7%9A%84-reasoning-%E9%97%AE%E9%A2%98/cover.png" alt="Featured image of post 如何解决 opencode 中 deepseek 模型的 reasoning 问题" /&gt;&lt;p&gt;当我们使用 deepseek-reasoner 的时候，往往会遇到这个问题：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;The reasoning_content&amp;#39; in the thinking mode must be passed back to the API.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="更新"&gt;更新
&lt;/h2&gt;&lt;p&gt;现在两个问题都已经被 opencode 官方解决。用户只需要安装最新的 opencode，通过 deepseek provider 即可使用，无需额外配置。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;问题1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;The reasoning_content&amp;#39; in the thinking mode must be passed back to the API.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;问题2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Bad Request: {&amp;#34;error&amp;#34;:{&amp;#34;message&amp;#34;:&amp;#34;The content[].thinking in the thinking mode must be passed back to the
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;API.&amp;#34;,&amp;#34;type&amp;#34;:&amp;#34;invalid_request_error&amp;#34;,&amp;#34;param&amp;#34;:null,&amp;#34;code&amp;#34;:&amp;#34;invalid_request_error&amp;#34;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这两个问题已经被官方解决。安装 1.14.29 以及以上版本即可。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;以下旧的解决方法：&lt;/p&gt;
&lt;p&gt;怎么解决呢？也容易。&lt;/p&gt;
&lt;h2 id="如何配置"&gt;如何配置
&lt;/h2&gt;&lt;p&gt;添加 provider 信息到配置中：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.config/opencode/opencode.json&lt;/code&gt; 或者 &lt;code&gt;.config/opencode/opencode.jsonc&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;修改 provider 部分为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;provider&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;deepseek&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;npm&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@ai-sdk/anthropic&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;DeepSeek&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;options&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;baseURL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://api.deepseek.com/anthropic&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;apiKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;apikey&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;models&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;deepseek-v4-pro&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;DeepSeek-V4-Pro&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;context&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1048576&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;output&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;262144&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;options&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;thinking&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;budgetTokens&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8192&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;deepseek-v4-flash&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;DeepSeek-V4-Flash&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;context&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1048576&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;output&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;262144&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;options&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;thinking&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;budgetTokens&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8192&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="如何使用"&gt;如何使用
&lt;/h2&gt;&lt;p&gt;选择 deepseek 模型。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3-opencode-%E4%B8%AD-deepseek-%E6%A8%A1%E5%9E%8B%E7%9A%84-reasoning-%E9%97%AE%E9%A2%98/pics/clipboard-1777007449883.png"
width="1152"
height="441"
srcset="https://svtter.cn/p/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3-opencode-%E4%B8%AD-deepseek-%E6%A8%A1%E5%9E%8B%E7%9A%84-reasoning-%E9%97%AE%E9%A2%98/pics/clipboard-1777007449883_hu_90da77582546fc32.png 480w, https://svtter.cn/p/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3-opencode-%E4%B8%AD-deepseek-%E6%A8%A1%E5%9E%8B%E7%9A%84-reasoning-%E9%97%AE%E9%A2%98/pics/clipboard-1777007449883_hu_7b7f08ffd58455a8.png 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="261"
data-flex-basis="626px"
&gt;&lt;/p&gt;
&lt;p&gt;效果。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3-opencode-%E4%B8%AD-deepseek-%E6%A8%A1%E5%9E%8B%E7%9A%84-reasoning-%E9%97%AE%E9%A2%98/pics/clipboard-1777007433107.png"
width="1361"
height="510"
srcset="https://svtter.cn/p/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3-opencode-%E4%B8%AD-deepseek-%E6%A8%A1%E5%9E%8B%E7%9A%84-reasoning-%E9%97%AE%E9%A2%98/pics/clipboard-1777007433107_hu_b83fabfded18efdc.png 480w, https://svtter.cn/p/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3-opencode-%E4%B8%AD-deepseek-%E6%A8%A1%E5%9E%8B%E7%9A%84-reasoning-%E9%97%AE%E9%A2%98/pics/clipboard-1777007433107_hu_c24f8389856c64c.png 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="266"
data-flex-basis="640px"
&gt;&lt;/p&gt;
&lt;h2 id="补充"&gt;补充
&lt;/h2&gt;&lt;p&gt;这个方式无法解决问题&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Bad Request: {&amp;quot;error&amp;quot;:{&amp;quot;message&amp;quot;:&amp;quot;The content[].thinking in the thinking mode must be passed back to the API.&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;invalid_request_error&amp;quot;,&amp;quot;param&amp;quot;:null,&amp;quot;code&amp;quot;:&amp;quot;invalid_request_error&amp;quot;}}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;如果你遇到这个问题，还需要等待 opencode 去修复。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;相关文章&lt;/strong&gt;：&lt;a class="link" href="../../deepseek-cc-thinking-block-issue/" &gt;DeepSeek + Claude Code: Thinking Block 兼容性问题分析&lt;/a&gt; — 分析了 Claude Code 使用 DeepSeek 时 extended thinking 模式下多轮对话触发 400 错误的根因，以及社区解决方案。&lt;/p&gt;</description></item><item><title>opencode-actions - 一个 coding review agent</title><link>https://svtter.cn/p/opencode-actions-%E4%B8%80%E4%B8%AA-coding-review-agent/</link><pubDate>Thu, 23 Apr 2026 11:36:34 +0800</pubDate><guid>https://svtter.cn/p/opencode-actions-%E4%B8%80%E4%B8%AA-coding-review-agent/</guid><description>&lt;img src="https://svtter.cn/p/opencode-actions-%E4%B8%80%E4%B8%AA-coding-review-agent/cover.jpg" alt="Featured image of post opencode-actions - 一个 coding review agent" /&gt;&lt;p&gt;为了方便引入 opencode 来做 code review，我构建了一个 github action repo。和 opencode 一起编程来做这个事情不难。&lt;/p&gt;
&lt;p&gt;目前主要提供两个功能，一个是 review，另一个是借助 runner 来执行 opencode （也就是直接用 runner 跑 opencode 的 prompt），来跑一下其他的功能。例如改代码，创建新的 issue，根据 issue 创建 PR 等。&lt;/p&gt;
&lt;h2 id="稳定性怎么样"&gt;稳定性怎么样？
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;这个 repo 已经经过多个项目的验证，release 的版本是可靠的。&lt;/li&gt;
&lt;li&gt;但是要注意 main 分支的版本是快速迭代版本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="如何引入"&gt;如何引入？
&lt;/h2&gt;&lt;p&gt;在你的 &lt;code&gt;.github/workflows/opencode-review.yml&lt;/code&gt; 下添加：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Run OpenCode review&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sun-praise/opencode-actions/review@v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;github-token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# only one is enough.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;zhipu-api-key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.ZHIPU_API_KEY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;opencode-go-api-key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.OPENCODE_GO_API_KEY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;现在，这个 actions 主要支持的是 Z.AI，ZHIPU，以及 OPENCODE GO 订阅。因此，如果用 ZHIPU，那么在项目中的 secrets 加上自己的 ZHIPU_API_KEY 即可。如果用的是 opencode go 订阅，则需要加上 OPENCODE_GO_API_KEY。&lt;/p&gt;
&lt;p&gt;其他的都可以根据默认配置来。默认模型是 &lt;code&gt;zhipuai-coding-plan/glm-5-turbo&lt;/code&gt;。如果有更多的配置要求，建议去看原仓库的 &lt;a class="link" href="https://github.com/sun-praise/opencode-actions" target="_blank" rel="noopener"
&gt;README&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;我之前在 &lt;a class="link" href="https://svtter.cn/p/%E4%BD%BF%E7%94%A8-opencode--glm-5-%E5%AE%9E%E7%8E%B0-github-pr-%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5/" target="_blank" rel="noopener"
&gt;代码审查文章中也有讲过&lt;/a&gt;，这个快速地 review 脚本。&lt;/p&gt;
&lt;h2 id="差异性"&gt;差异性
&lt;/h2&gt;&lt;p&gt;实际上 opencode 自己也有 actions，那么为什么我要再做一个呢？&lt;/p&gt;
&lt;p&gt;与官方的差异主要体现在几个方面：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;th&gt;上游状态&lt;/th&gt;
&lt;th&gt;本仓库&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model 默认值 fallback&lt;/td&gt;
&lt;td&gt;仅 required input&lt;/td&gt;
&lt;td&gt;三级 fallback（input → MODEL_NAME → 硬编码默认）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider 便捷字段&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zhipu-api-key&lt;/code&gt;、&lt;code&gt;opencode-go-api-key&lt;/code&gt; 等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review prompt 模板&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;中文格式化 review（可合并/有条件合并/不可合并）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry 逻辑&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;attempts / retry-profile / retry-on-regex / retry-delay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;执行超时&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;timeout-seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;版本检查&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;OPENCODE_MIN_VERSION&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;安装重试&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;install-attempts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XDG cache 缓存&lt;/td&gt;
&lt;td&gt;仅缓存 bin&lt;/td&gt;
&lt;td&gt;同时缓存 bin + XDG cache&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;在 &lt;a class="link" href="https://github.com/sun-praise/opencode-actions/issues/29" target="_blank" rel="noopener"
&gt;sun-praise/opencode-actions#29&lt;/a&gt; 中有详细的说明。&lt;/p&gt;
&lt;h2 id="review-效果"&gt;review 效果
&lt;/h2&gt;&lt;p&gt;可以从 repo 本身的 PR 看到 &lt;a class="link" href="https://github.com/sun-praise/opencode-actions/pull/30" target="_blank" rel="noopener"
&gt;opencode-actions#30&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;大概是这个效果：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/opencode-actions-%E4%B8%80%E4%B8%AA-coding-review-agent/pics/clipboard-1776916692946.png"
width="1407"
height="1208"
srcset="https://svtter.cn/p/opencode-actions-%E4%B8%80%E4%B8%AA-coding-review-agent/pics/clipboard-1776916692946_hu_2bf58faf91a7a9d.png 480w, https://svtter.cn/p/opencode-actions-%E4%B8%80%E4%B8%AA-coding-review-agent/pics/clipboard-1776916692946_hu_6740bc1877d53b60.png 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="116"
data-flex-basis="279px"
&gt;&lt;/p&gt;
&lt;h2 id="未来"&gt;未来
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;尝试集成 gemini cli。现在 google 的 GEM 3.1 PRO 模型性价比很高，单位智能是最高的。&lt;/li&gt;
&lt;li&gt;集成 MCP 插件功能。opencode review 的时候 MCP 如果有，可能会带来更好的 review 效果。&lt;/li&gt;
&lt;li&gt;商业化的插件功能集成&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>探索 opencode 之后，我的一些思考</title><link>https://svtter.cn/p/opencode-three-months-reflection/</link><pubDate>Tue, 31 Mar 2026 11:30:00 +0800</pubDate><guid>https://svtter.cn/p/opencode-three-months-reflection/</guid><description>&lt;img src="https://svtter.cn/p/opencode-three-months-reflection/cover.jpg" alt="Featured image of post 探索 opencode 之后，我的一些思考" /&gt;&lt;blockquote&gt;
&lt;p&gt;一个开源工具，让我看清了&amp;quot;不被绑定&amp;quot;的价值，也让我重新理解了《大教堂与集市》&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id="缘起选择-opencode-作为编程工具"&gt;缘起：选择 opencode 作为编程工具
&lt;/h2&gt;&lt;p&gt;之前我写了一篇博客——《高效省钱：我的 AI Agent 工作流选择》，分享了自己用 opencode + Gemini 3 Flash + 智谱免费 GLM 4.7 构建&amp;quot;省钱又不妥协&amp;quot;的 AI 编程助手的经验。&lt;/p&gt;
&lt;p&gt;当时我正想找一个性价比高的方案。Claude Code 的 Pro 计划一个月 20 美元，Max 计划则要 100 美元，对个人开发者来说确实不算便宜。而智谱的 Coding Plan Lite 套餐 20 元/月，加上 opencode 这个开源终端工具，正好组合成了一套&amp;quot;省钱&amp;quot;方案。&lt;/p&gt;
&lt;p&gt;真正打动我的，是 opencode 的一个核心特性：它支持 75 种以上的模型供应商——OpenAI、Anthropic、Google Gemini、智谱，甚至本地 Ollama。这意味着我随时可以换模型：用智谱的套餐日常开发，想尝鲜某家新模型时也能快速接入，不会被任何一家绑定。&lt;/p&gt;
&lt;p&gt;这正是我想要的：没有 vendor lock-in，不被&amp;quot;花钱的工具&amp;quot;绑架。我用谁家的模型，应该取决于模型能力，而不是因为工具只支持那一家。&lt;/p&gt;
&lt;p&gt;从 1 月开始，我就这样用起了 opencode + 智谱 Coding Plan。三个月下来，跑得很熟，体验也一直很稳定。&lt;/p&gt;
&lt;h2 id="转折github-上的积压让我心里一沉"&gt;转折：GitHub 上的&amp;quot;积压&amp;quot;让我心里一沉
&lt;/h2&gt;&lt;p&gt;直到最近，我随手翻了一下 opencode 的 GitHub 仓库。我看到了大量无人处理的 issue，还有堆了几周甚至几个月的 pull request，没人合并，没人回复。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/opencode-three-months-reflection/pics/inline-01.jpg"
width="1200"
height="896"
srcset="https://svtter.cn/p/opencode-three-months-reflection/pics/inline-01_hu_bd038bed779bd657.jpg 480w, https://svtter.cn/p/opencode-three-months-reflection/pics/inline-01_hu_f26788b5e1da7bc3.jpg 1024w"
loading="lazy"
alt="大量无人处理的 PR 和 issue 堆积在 GitHub 上"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="321px"
&gt;&lt;/p&gt;
&lt;p&gt;我心里&amp;quot;嘶&amp;quot;了一下：&amp;ldquo;这项目是不是没人维护了？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;后来我才知道，2026 年 3 月，Anthropic 向 opencode 发来了正式的法律要求，要求移除所有 Claude 相关的集成。项目方在 1.3.0 版本中执行了这件事，创始人 Dax Raad 公开回应：&amp;ldquo;我们尽力说服 Anthropic 支持开发者的选择权，但他们派了律师来。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;但说实话，这个法律事件本身我并不太担心。我只是一个用户，用 opencode 跑智谱的套餐，不涉及法律风险。真正让我在意的，是另一个问题——&lt;strong&gt;项目的工程治理能力&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;opencode 发展很快，功能越做越多，社区贡献者也越来越多。但从 GitHub 上的积压来看，核心团队对 issue 的分类、triage、PR review 的流程，明显没跟上项目膨胀的速度。PR 堆在那里，有人愿意贡献，但没人有精力、有流程去接住这些贡献。说白了就是：项目膨胀了，治理能力没跟上，积压是早晚的事。&lt;/p&gt;
&lt;p&gt;一个开源项目，长期没人处理贡献，社区慢慢就凉了。这跟&amp;quot;厂商锁定&amp;quot;是另一种层面的风险：开源工具也会因为没人维护而变得没法用。&lt;/p&gt;
&lt;h2 id="开源的安全垫历史版本可以继续用"&gt;开源的安全垫：历史版本可以继续用
&lt;/h2&gt;&lt;p&gt;但转念一想，我又释然了。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;因为是开源软件，所以 opencode 实际上就算是暂时不维护，我也可以使用历史版本。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;这个想法一下子点醒了我。开源软件和商业 SaaS 的区别就在这：商业工具一旦公司倒闭或停服，你就真的没了；开源项目，哪怕 GitHub 上再也没人合并 PR，我手上那个跑得很顺的版本，照样能用。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/opencode-three-months-reflection/pics/inline-02.jpg"
width="1200"
height="896"
srcset="https://svtter.cn/p/opencode-three-months-reflection/pics/inline-02_hu_9c6b2d411a75c6aa.jpg 480w, https://svtter.cn/p/opencode-three-months-reflection/pics/inline-02_hu_b7f15e1f630cb77c.jpg 1024w"
loading="lazy"
alt="开源的安全垫：哪怕项目停滞，历史版本依然可以继续使用"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="321px"
&gt;&lt;/p&gt;
&lt;p&gt;模型 API 可能会升级，但那通常有缓冲期，而且 opencode 的 provider 抽象层是独立的，即使官方不更新，社区或我自己也可以 fork 改一下适配层。安全漏洞也许是个问题，但 coding agent 这类工具的风险主要在于代码执行权限，自己注意控制就好。&lt;/p&gt;
&lt;p&gt;所以，最坏情况我也能继续用现在的版本，不用急着迁移。但我会保持观察——如果几个月后积压依然严重，可能就要考虑备选方案了。&lt;/p&gt;
&lt;h2 id="大教堂与集市linus-为什么是大师"&gt;大教堂与集市：Linus 为什么是大师
&lt;/h2&gt;&lt;p&gt;积压这个问题，让我想到了《大教堂与集市》。&lt;/p&gt;
&lt;p&gt;Eric S. Raymond 把开源开发模式分成两种：大教堂模式（少数人精心设计、封闭开发）和集市模式（开放、迭代快、靠&amp;quot;足够多的眼睛&amp;quot;来捉 bug）。Linux 内核是集市模式的代表。Linus Torvalds 写的代码其实不算多，但他建立了一套让人放心的流程和权威——这才是重点。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/opencode-three-months-reflection/pics/inline-03.jpg"
width="1200"
height="896"
srcset="https://svtter.cn/p/opencode-three-months-reflection/pics/inline-03_hu_27e02f9e72a13ff3.jpg 480w, https://svtter.cn/p/opencode-three-months-reflection/pics/inline-03_hu_44a4ec2ba45cf506.jpg 1024w"
loading="lazy"
alt="大教堂模式（左）：封闭、有序；集市模式（右）：开放、混沌但高效"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="321px"
&gt;&lt;/p&gt;
&lt;p&gt;集市模式能成功，关键在于有一个清晰的、被信任的维护者来合并代码、建立规范、保持方向。即便 Linus 某段时间回复慢，大家也知道规范在那里，最终会被处理。Linux 活到现在，靠的是大家相信&amp;quot;好代码最终会被合进去&amp;quot;，不是靠 PR 响应速度。&lt;/p&gt;
&lt;p&gt;用这个框架看 opencode，它一开始确实带着集市的气质：多模型支持、插件化、社区驱动、快速迭代。但最近积压的 issue 和 PR 暴露了一个问题：&lt;strong&gt;集市模式要运转，前提是有一只&amp;quot;看似松散但始终在运作的手&amp;quot;在合并代码&lt;/strong&gt;。这只手忙不过来了，集市也就乱了。&lt;/p&gt;
&lt;p&gt;opencode 缺的是机制，不缺热心人。&lt;/p&gt;
&lt;h2 id="我现在的态度继续用但保持关注"&gt;我现在的态度：继续用，但保持关注
&lt;/h2&gt;&lt;p&gt;我现在依然用着 opencode + 智谱 Coding Plan，日常开发没有任何问题。对于项目的未来，我的打算很简单：继续用，但不指望它一定会变好。现有版本够用，哪怕不再更新，我也能撑一段时间。我会关注积压的处理情况——如果两三个月后 PR 开始被清理、核心团队恢复沟通，说明扛过去了；如果还是没人管，就开始看别的方案。好在我选的就是开源方案，代码和数据都不会被锁死，这正是当初选它的原因。&lt;/p&gt;
&lt;h2 id="一点最后的想法"&gt;一点最后的想法
&lt;/h2&gt;&lt;p&gt;这件事让我重新理解了&amp;quot;开源工具 vs 商业 SaaS&amp;quot;。商业 SaaS 省心，但你被绑着。开源工具自由，但项目健康不健康得自己盯着。我还是倾向于开源——我不喜欢把命脉交给一家公司。不过这次也提醒了我：自由不是免费的，得自己盯生态、评风险、想好退路。&lt;/p&gt;
&lt;p&gt;Linus 早就说明白了，一个好的开源项目，光靠热情撑不住，得有一套让人信任的规则，还有源源不断愿意维护的人。我希望 opencode 能走过这段波折，继续做那个&amp;quot;不被任何一家模型绑架&amp;quot;的工具。&lt;/p&gt;
&lt;p&gt;好工具值得多一点耐心。但耐心也不是无限的。&lt;/p&gt;</description></item><item><title>Codex 和 OpenCode 的上下文压缩</title><link>https://svtter.cn/p/codex-%E5%92%8C-opencode-%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E5%8E%8B%E7%BC%A9/</link><pubDate>Thu, 19 Mar 2026 11:30:00 +0800</pubDate><guid>https://svtter.cn/p/codex-%E5%92%8C-opencode-%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E5%8E%8B%E7%BC%A9/</guid><description>&lt;img src="https://svtter.cn/p/codex-%E5%92%8C-opencode-%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E5%8E%8B%E7%BC%A9/cover.webp" alt="Featured image of post Codex 和 OpenCode 的上下文压缩" /&gt;&lt;p&gt;上下文压缩是 coding agent 必备的特性。&lt;/p&gt;
&lt;p&gt;最近翻 Codex 文档，看到一个点：它明确提到了 &lt;code&gt;server-side compaction&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我觉得这个点挺有意思。不是说&amp;quot;压缩上下文&amp;quot;这件事本身有多新鲜，而是它把这件事放在服务端做，并且写进了文档。也就是说，上下文管理不是用户自己在提示词层面想办法，而是产品本身就内建了一套机制。&lt;/p&gt;
&lt;p&gt;顺手我又去看了一下 OpenCode 的 compaction。两边都叫 &lt;code&gt;compaction&lt;/code&gt;，但不是一回事。&lt;/p&gt;
&lt;p&gt;Codex 这边强调的是：服务端在上下文达到阈值时，会把前面的交互压缩成一个更短的表示，后续请求继续带着这个压缩后的状态跑。文档里写得很清楚，&lt;code&gt;server-side compaction&lt;/code&gt; 会用更少的 tokens 携带之前的关键状态。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/codex-%E5%92%8C-opencode-%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E5%8E%8B%E7%BC%A9/compaction-flow.webp"
width="2752"
height="1536"
srcset="https://svtter.cn/p/codex-%E5%92%8C-opencode-%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E5%8E%8B%E7%BC%A9/compaction-flow_hu_f03edb827bd032dc.webp 480w, https://svtter.cn/p/codex-%E5%92%8C-opencode-%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E5%8E%8B%E7%BC%A9/compaction-flow_hu_556f88c4a4052352.webp 1024w"
loading="lazy"
alt="Codex server-side compaction 示意图"
class="gallery-image"
data-flex-grow="179"
data-flex-basis="430px"
&gt;&lt;/p&gt;
&lt;p&gt;OpenCode 这边，我看了一下源码，它也有 compaction，但更像应用层的会话压缩：上下文快满时，调用一个隐藏的 &lt;code&gt;compaction&lt;/code&gt; agent 生成 summary，后续请求不再带完整旧历史，而是保留这段 summary 继续。还有 &lt;code&gt;prune&lt;/code&gt; 会把旧的工具输出清掉，只保留占位文本。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/codex-%E5%92%8C-opencode-%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E5%8E%8B%E7%BC%A9/agent-compaction.webp"
width="2752"
height="1536"
srcset="https://svtter.cn/p/codex-%E5%92%8C-opencode-%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E5%8E%8B%E7%BC%A9/agent-compaction_hu_ce8a676aa70786e6.webp 480w, https://svtter.cn/p/codex-%E5%92%8C-opencode-%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E5%8E%8B%E7%BC%A9/agent-compaction_hu_3701a87c6e58bf7c.webp 1024w"
loading="lazy"
alt="OpenCode 应用层 compaction 示意图"
class="gallery-image"
data-flex-grow="179"
data-flex-basis="430px"
&gt;&lt;/p&gt;
&lt;p&gt;所以两边虽然名字一样，但关注点不一样：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Codex：服务端的上下文管理能力&lt;/li&gt;
&lt;li&gt;OpenCode：应用层的会话状态续写&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个区分我觉得值得记一下。很多人看到同一个词，就默认是同一种机制，其实不是。与其急着比较谁更省、谁更好，不如先把机制边界看清楚。&lt;/p&gt;
&lt;h2 id="参考"&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://developers.openai.com/api/docs/guides/compaction" target="_blank" rel="noopener"
&gt;OpenAI Compaction 文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://opencode.ai/docs/config/" target="_blank" rel="noopener"
&gt;OpenCode Config 文档&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>OpenCode 配置优化记录</title><link>https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%BC%98%E5%8C%96%E8%AE%B0%E5%BD%95/</link><pubDate>Wed, 28 Jan 2026 00:00:00 +0000</pubDate><guid>https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%BC%98%E5%8C%96%E8%AE%B0%E5%BD%95/</guid><description>&lt;img src="https://svtter.cn/p/opencode-%E9%85%8D%E7%BD%AE%E4%BC%98%E5%8C%96%E8%AE%B0%E5%BD%95/cover.png" alt="Featured image of post OpenCode 配置优化记录" /&gt;&lt;p&gt;不知道大家是否意识到，实际上使用 MCP 等服务的时候，会大量消耗 token。这是由于一次提交的上下文数量过多，导致 token 消耗较大。&lt;/p&gt;
&lt;p&gt;那么在使用 opencode 的时候，可以进行哪些优化呢？&lt;/p&gt;
&lt;h2 id="我已经进行的优化"&gt;我已经进行的优化
&lt;/h2&gt;&lt;h3 id="1-启用-compaction-自动压缩"&gt;1. 启用 Compaction (自动压缩)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;compaction&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;auto&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 上下文满时自动压缩
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;prune&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// 移除旧的工具输出以节省空间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;: 预估节省 20-30% token&lt;/p&gt;
&lt;h3 id="2-配置-watcher-忽略模式"&gt;2. 配置 Watcher 忽略模式
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;watcher&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;ignore&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;node_modules/**&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;dist/**&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;.git/**&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*.lock&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;**/*.log&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;.cache/**&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;build/**&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;__pycache__/**&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;.venv/**&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;venv/**&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;: 减少不必要的文件监控，预估节省 5-10% token&lt;/p&gt;
&lt;h3 id="3-配置-small-model"&gt;3. 配置 Small Model
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;small_model&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;anthropic/claude-haiku-4-5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;: 轻量任务自动使用便宜模型，预估节省 10-20% token&lt;/p&gt;
&lt;h2 id="我没有采用的优化项"&gt;我没有采用的优化项
&lt;/h2&gt;&lt;p&gt;这里的优化项是我认为暂时没有必要采用的。&lt;/p&gt;
&lt;h3 id="5-减少-mcp-服务-高优先级"&gt;5. 减少 MCP 服务 (高优先级)
&lt;/h3&gt;&lt;p&gt;MCP 工具描述可消耗 6000+ tokens。建议禁用不常用的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;playwright_headless&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;zai-mcp-server&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;tmux-mcp&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;sentry&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;预估节省&lt;/strong&gt;: 30-50% token&lt;/p&gt;
&lt;h3 id="6-创建项目级-ignore-文件"&gt;6. 创建项目级 .ignore 文件
&lt;/h3&gt;&lt;p&gt;在项目根目录创建 &lt;code&gt;.ignore&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node_modules/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dist/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;build/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;*.lock
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;*.log
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;coverage/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.next/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;__pycache__/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;*.pyc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.venv/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="7-使用技巧"&gt;7. 使用技巧
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;/compact&lt;/code&gt; 手动压缩长对话&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;/clear&lt;/code&gt; 清理历史&lt;/li&gt;
&lt;li&gt;完成任务后开始新会话，避免&amp;quot;上下文腐烂&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="不能采用的优化项"&gt;不能采用的优化项
&lt;/h2&gt;&lt;p&gt;这些优化项会导致风控&lt;/p&gt;
&lt;h3 id="启用-anthropic-缓存"&gt;启用 Anthropic 缓存
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;provider&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;anthropic&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;options&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;setCacheKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;timeout&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="配置文件位置"&gt;配置文件位置
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;主配置: &lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Oh-My-OpenCode: &lt;code&gt;~/.config/opencode/oh-my-opencode.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Memory 插件: &lt;code&gt;~/.config/opencode/opencode-mem.jsonc&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参考资料"&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://opencode.ai/docs/config/" target="_blank" rel="noopener"
&gt;OpenCode Config 文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.truefoundry.com/blog/opencode-token-usage-how-it-works-and-how-to-optimize-it" target="_blank" rel="noopener"
&gt;Token 优化指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/sst/opencode/issues/5813" target="_blank" rel="noopener"
&gt;GitHub Issue #5813 - Raw Chat Agent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>使用 OpenCode + GLM-5 实现 GitHub PR 自动代码审查</title><link>https://svtter.cn/p/%E4%BD%BF%E7%94%A8-opencode--glm-5-%E5%AE%9E%E7%8E%B0-github-pr-%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5/</link><pubDate>Mon, 26 Jan 2026 00:00:00 +0000</pubDate><guid>https://svtter.cn/p/%E4%BD%BF%E7%94%A8-opencode--glm-5-%E5%AE%9E%E7%8E%B0-github-pr-%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5/</guid><description>&lt;img src="https://svtter.cn/p/%E4%BD%BF%E7%94%A8-opencode--glm-5-%E5%AE%9E%E7%8E%B0-github-pr-%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5/cover.png" alt="Featured image of post 使用 OpenCode + GLM-5 实现 GitHub PR 自动代码审查" /&gt;&lt;blockquote&gt;
&lt;p&gt;大模型会磨洋工，需要另一个 agent 或 AI 来监督。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;最近在项目中集成了 OpenCode 的 AI 代码审查功能，使用智谱 AI 的 GLM-5 模型自动 review PR 代码。本文记录完整的集成过程和踩过的坑。&lt;/p&gt;
&lt;h2 id="背景"&gt;背景
&lt;/h2&gt;&lt;p&gt;项目需要一个自动化的代码审查机制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当 PR 创建或更新时，自动触发 AI 审查&lt;/li&gt;
&lt;li&gt;AI 只提供 review 建议，不修改代码&lt;/li&gt;
&lt;li&gt;将审查结果以 comment 形式添加到 PR 中&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="技术选型"&gt;技术选型
&lt;/h2&gt;&lt;h3 id="opencode"&gt;OpenCode
&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://opencode.ai" target="_blank" rel="noopener"
&gt;OpenCode&lt;/a&gt; 是一个开源的 AI coding agent，支持：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多种 LLM 提供商（OpenAI、Anthropic、智谱等）&lt;/li&gt;
&lt;li&gt;GitHub/GitLab 集成&lt;/li&gt;
&lt;li&gt;在 CI 环境中运行&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="glm-5-coding-plan"&gt;GLM-5 Coding Plan
&lt;/h3&gt;&lt;p&gt;选择智谱 AI 的 &lt;a class="link" href="https://docs.z.ai/guides/llm/glm-5" target="_blank" rel="noopener"
&gt;GLM Coding Plan&lt;/a&gt; 原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;专为 AI 编程优化，支持 OpenCode&lt;/li&gt;
&lt;li&gt;价格实惠（$3/月起）&lt;/li&gt;
&lt;li&gt;国内访问稳定&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tldr"&gt;TL;DR
&lt;/h2&gt;&lt;p&gt;如果只是想快速引入 code review，不需要处理权限、凭据等配置，可以直接使用封装好的 action。这是最快引入 code review 的方法，review 结果是中文：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Run OpenCode review&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Svtter/opencode-actions/review@v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;github-token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# only one is enough.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;zhipu-api-key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.ZHIPU_API_KEY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;opencode-go-api-key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.OPENCODE_GO_API_KEY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;只需要配置对应的 secret 即可使用。&lt;/p&gt;
&lt;h2 id="配置详解"&gt;配置详解
&lt;/h2&gt;&lt;h3 id="1-确定模型格式"&gt;1. 确定模型格式
&lt;/h3&gt;&lt;p&gt;OpenCode 的模型格式为 &lt;code&gt;provider_id/model_id&lt;/code&gt;。对于智谱 GLM-5：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;配置项&lt;/th&gt;
&lt;th&gt;值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;环境变量&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZHIPU_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model 格式&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zhipuai-coding-plan/glm-5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="2-创建-workflow-文件"&gt;2. 创建 Workflow 文件
&lt;/h3&gt;&lt;p&gt;创建 &lt;code&gt;.github/workflows/opencode-review.yml&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;OpenCode PR Review&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;opened, synchronize, reopened, ready_for_review]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;review&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;AI Code Review&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 仅在 PR 准备就绪时运行（跳过 draft PR）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;github.event.pull_request.draft == false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;self-hosted&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;id-token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;read&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pull-requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Checkout repository&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v6&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;persist-credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Run OpenCode Review&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;anomalyco/opencode/github@latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ZHIPU_API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.ZHIPU_API_KEY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;zhipuai-coding-plan/glm-5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;use_github_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Review this pull request (read-only mode, DO NOT modify any code):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Please check:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Code quality issues
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Potential bugs or logic errors
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Code style consistency
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Security concerns
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Performance issues
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Suggest improvements
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Please respond in Chinese. DO NOT modify any code, only provide review comments.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="3-配置-github-secrets"&gt;3. 配置 GitHub Secrets
&lt;/h3&gt;&lt;p&gt;在仓库 Settings → Secrets and variables → Actions 中添加：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ZHIPU_API_KEY&lt;/code&gt;: 从 &lt;a class="link" href="https://z.ai/manage-apikey/apikey-list" target="_blank" rel="noopener"
&gt;Z.AI Console&lt;/a&gt; 获取&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="踩坑记录"&gt;踩坑记录
&lt;/h2&gt;&lt;h3 id="坑-1-权限不足-403-forbidden"&gt;坑 1: 权限不足 (403 Forbidden)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;错误信息&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Resource not accessible by integration - https://docs.github.com/rest/issues/comments#create-an-issue-comment
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：permissions 配置为 &lt;code&gt;read&lt;/code&gt;，无法写入 PR comment。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：将权限改为 &lt;code&gt;write&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pull-requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 不是 read&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 不是 read&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="坑-2-git-凭据问题"&gt;坑 2: Git 凭据问题
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;错误信息&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fatal: could not read Username for &amp;#39;https://github.com&amp;#39;: No such device or address
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：checkout 时 &lt;code&gt;persist-credentials: false&lt;/code&gt; 导致后续 git 操作无法认证。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v6&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;persist-credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 不是 false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="坑-3-draft-pr-不应触发审查"&gt;坑 3: Draft PR 不应触发审查
&lt;/h3&gt;&lt;p&gt;Draft PR 还在开发中，不需要 AI 审查，浪费资源。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：添加条件判断：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;review&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;github.event.pull_request.draft == false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="工作流程"&gt;工作流程
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PR opened/updated
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;检查是否为 Draft PR
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ (非 Draft)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitHub Actions 触发
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;OpenCode 下载并分析 diff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GLM-5 生成审查意见
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;自动添加 PR comment
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;开发者查看建议，手动决定是否采纳
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="效果展示"&gt;效果展示
&lt;/h2&gt;&lt;p&gt;配置完成后，每次 PR 更新都会自动触发 AI 审查，审查结果会以 comment 形式出现在 PR 页面。&lt;/p&gt;
&lt;p&gt;审查内容包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代码质量问题&lt;/li&gt;
&lt;li&gt;潜在 bug&lt;/li&gt;
&lt;li&gt;风格一致性&lt;/li&gt;
&lt;li&gt;安全隐患&lt;/li&gt;
&lt;li&gt;性能问题&lt;/li&gt;
&lt;li&gt;改进建议&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="成本估算"&gt;成本估算
&lt;/h2&gt;&lt;p&gt;使用 GLM Coding Plan：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基础套餐 $3/月&lt;/li&gt;
&lt;li&gt;中等项目每月约 10-20 次 PR，成本可控&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;通过 OpenCode + GLM-5 实现自动代码审查：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;配置简单&lt;/strong&gt;：一个 workflow 文件即可&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;成本低&lt;/strong&gt;：GLM Coding Plan 价格友好&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;效果好&lt;/strong&gt;：GLM-5 在编程任务上表现优秀&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全&lt;/strong&gt;：只读模式，不会修改代码&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="参考链接"&gt;参考链接
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://opencode.ai/docs/" target="_blank" rel="noopener"
&gt;OpenCode 官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://opencode.ai/docs/github/" target="_blank" rel="noopener"
&gt;OpenCode GitHub 集成&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.z.ai/guides/llm/glm-5" target="_blank" rel="noopener"
&gt;GLM-5 文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.z.ai/coding-plan/overview" target="_blank" rel="noopener"
&gt;GLM Coding Plan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="相关文章"&gt;相关文章
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/%E6%9C%80%E8%BF%91%E5%8F%91%E7%8E%B0%E5%A5%BD%E7%94%A8%E7%9A%84-mcp-%E5%B7%A5%E5%85%B7/" &gt;最近发现好用的 MCP 工具&lt;/a&gt; - OpenCode/Claude Code 配套工具推荐&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/%E9%AB%98%E6%95%88%E7%9C%81%E9%92%B1%E6%88%91%E7%9A%84-ai-agent-%E5%B7%A5%E4%BD%9C%E6%B5%81%E9%80%89%E6%8B%A9/" &gt;高效省钱：我的 AI Agent 工作流选择&lt;/a&gt; - OpenCode + Gemini 的高性价比工作流&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/%E5%9C%A8%E8%99%9A%E6%8B%9F%E6%9C%BA%E4%B8%8A%E8%BF%90%E8%A1%8C-claude-code-%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E4%B8%8E%E5%BC%80%E5%8F%91/" &gt;在虚拟机上运行 Claude Code 实现自动化测试与开发&lt;/a&gt; - 使用 VM + Playwright MCP 实现自动化测试&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svtter.cn/p/git-mcp-tools.md/" &gt;Git MCP Tools&lt;/a&gt; - 让大模型操作 Git 的 MCP 服务&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>最近发现好用的 MCP 工具</title><link>https://svtter.cn/p/%E6%9C%80%E8%BF%91%E5%8F%91%E7%8E%B0%E5%A5%BD%E7%94%A8%E7%9A%84-mcp-%E5%B7%A5%E5%85%B7/</link><pubDate>Sat, 17 Jan 2026 22:18:33 +0800</pubDate><guid>https://svtter.cn/p/%E6%9C%80%E8%BF%91%E5%8F%91%E7%8E%B0%E5%A5%BD%E7%94%A8%E7%9A%84-mcp-%E5%B7%A5%E5%85%B7/</guid><description>&lt;img src="https://svtter.cn/p/%E6%9C%80%E8%BF%91%E5%8F%91%E7%8E%B0%E5%A5%BD%E7%94%A8%E7%9A%84-mcp-%E5%B7%A5%E5%85%B7/bg.png" alt="Featured image of post 最近发现好用的 MCP 工具" /&gt;&lt;p&gt;近期大量使用 opencode/claude code 组合来开发，探索了三个比较有用的工具。&lt;/p&gt;
&lt;p&gt;他们分别解决了几个问题:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在单服务器上并行开发；控制 tmux： &lt;code&gt;tmux&lt;/code&gt; 和 &lt;code&gt;tmux-mcp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;让 claude code 不在无意义的位置上停止 &lt;code&gt;ralph-loop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;端到端自动化测试 &lt;code&gt;playwright mcp&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="工具列表"&gt;工具列表
&lt;/h2&gt;&lt;h3 id="tmux-mcp"&gt;tmux mcp
&lt;/h3&gt;&lt;p&gt;先让 opencode 配置 Linux 环境的 tmux，然后让 opencode 安装 &lt;a class="link" href="https://github.com/rinadelph/tmux-mcp.git" target="_blank" rel="noopener"
&gt;https://github.com/rinadelph/tmux-mcp.git&lt;/a&gt; ；安装完成后即可使用 oc 来控制 tmux 的内容。&lt;/p&gt;
&lt;p&gt;可以通过这种方式来重新激活的停止的 opencode session。例如，可以开多个 tmux，然后让其中一个 opencode 通过 tmux 工具来监控，启停任务。&lt;/p&gt;
&lt;h3 id="ralph-loop"&gt;ralph-loop
&lt;/h3&gt;&lt;p&gt;Ralph 是一个自主 AI 代理循环，它会反复运行 Amp，直到所有 PRD 项都完成。每次迭代都会创建一个全新的 Amp 实例，并具有干净的上下文。&lt;/p&gt;
&lt;p&gt;Ralph 最早可能来自这里 &lt;a class="link" href="https://github.com/snarktank/ralph" target="_blank" rel="noopener"
&gt;https://github.com/snarktank/ralph&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;因为其卓有成效（实际上是在模型性能进一步提升之后发生的），也被引入了 claude code。&lt;/p&gt;
&lt;script src="https://svtter.cn/js/repo-card.js"&gt;&lt;/script&gt;
&lt;div class="repo-card" data-repo="frankbria/ralph-claude-code"&gt;&lt;/div&gt;
&lt;p&gt;Ralph-loop 是一个 Claude Code 插件，可以让 Claude Code 在完成任务时自动重新启动，形成一个循环执行任务的机制。这对于需要持续改进或迭代的任务特别有用。&lt;/p&gt;
&lt;h4 id="安装方法"&gt;安装方法
&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;通过 Claude Code 官方插件市场安装&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/plugin install ralph-wiggum@claude-plugins-official&lt;/code&gt; 或者 &lt;code&gt;cc '/plugin install ralph-wiggum@claude-plugins-official'&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;配置使用&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;安装完成后，在 Claude Code 中可以通过 &lt;code&gt;/ralph-loop&lt;/code&gt; 命令来启动&lt;/li&gt;
&lt;li&gt;设置任务和终止条件，Claude Code 会在每次停止时自动重新启动&lt;/li&gt;
&lt;li&gt;这对于需要多次迭代改进代码、调试或测试的场景特别有用&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="使用场景"&gt;使用场景
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;代码重构&lt;/strong&gt;：让 Claude Code 持续改进代码质量&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;测试驱动开发&lt;/strong&gt;：编写测试，然后让 Claude Code 不断改进实现&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;调试循环&lt;/strong&gt;：自动重新启动调试会话&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;持续集成&lt;/strong&gt;：在本地模拟 CI/CD 流程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个插件的缺点是非常烧 token；如果没有 max20 订阅，还是不要订阅比较好。但对于需要高质量输出的任务，这个工具可以显著提高工作效率。&lt;/p&gt;
&lt;h3 id="playwright-mcp"&gt;playwright mcp
&lt;/h3&gt;&lt;p&gt;这个插件可以启动浏览器来完成端到端测试，或者编写端到端测试代码。可以更好的形成 loop，来让 cc 或者 oc 改进代码。&lt;/p&gt;
&lt;p&gt;安装方法 &lt;code&gt;claude '帮我安装 playwright mcp'&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="改写成-agents"&gt;改写成 agents
&lt;/h2&gt;&lt;p&gt;我建议直接通过 opencode 或者 claude code 将这些工具和 mcp 改写成 agent。&lt;/p&gt;
&lt;p&gt;相比于 skills command，这些工具，更适合通过 agent 来调用。&lt;a class="link" href="https://svtter.cn/p/why-agent/#llm-%E7%9A%84%E9%97%AE%E9%A2%98" target="_blank" rel="noopener"
&gt;agent 上下文很干净&lt;/a&gt;，对工具调用几乎是必然的事情。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;随着 LLM 越来越强，大量的 mcp，依赖于 LLM 能力的 MCP 自然而然的获得了提升。原本不怎么好用的工具变得更好用了。这也正是迎合了那句话：不要做大模型能力增强之后没有意义的东西。
大模型的能力仍然在增强，并且价格在不断下降。&lt;/p&gt;
&lt;p&gt;我认为下一步打通不同模态和工具之间的交互，以及给工具赋予大模型的能力，是 agent engineer 必然的发展方向之一。&lt;/p&gt;</description></item><item><title>高效省钱：我的 AI Agent 工作流选择</title><link>https://svtter.cn/p/%E9%AB%98%E6%95%88%E7%9C%81%E9%92%B1%E6%88%91%E7%9A%84-ai-agent-%E5%B7%A5%E4%BD%9C%E6%B5%81%E9%80%89%E6%8B%A9/</link><pubDate>Mon, 05 Jan 2026 16:00:00 +0800</pubDate><guid>https://svtter.cn/p/%E9%AB%98%E6%95%88%E7%9C%81%E9%92%B1%E6%88%91%E7%9A%84-ai-agent-%E5%B7%A5%E4%BD%9C%E6%B5%81%E9%80%89%E6%8B%A9/</guid><description>&lt;img src="https://svtter.cn/p/%E9%AB%98%E6%95%88%E7%9C%81%E9%92%B1%E6%88%91%E7%9A%84-ai-agent-%E5%B7%A5%E4%BD%9C%E6%B5%81%E9%80%89%E6%8B%A9/featured-image.jpg" alt="Featured image of post 高效省钱：我的 AI Agent 工作流选择" /&gt;&lt;p&gt;Claude Code 一个月 $100 费用有些高，很多朋友都有点扛不住。为了解决问题，我实践了一套工作流。&lt;/p&gt;
&lt;p&gt;模型方面，我的建议是，选择 &lt;code&gt;Gemini 3 Flash&lt;/code&gt; 的按需用量作为替代。&lt;/p&gt;
&lt;p&gt;原因：Gemini 3 Flash 性价比极高；响应速度快、处理效率高，价格仅为 Opus 和 Sonnet 的几分之一。对于大多数任务，Flash 版本已经足够使用。&lt;/p&gt;
&lt;h2 id="省钱的-workflow"&gt;省钱的 Workflow
&lt;/h2&gt;&lt;p&gt;一个经济实惠的工作流：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;制定计划&lt;/strong&gt;：使用 Gemini 3 Flash&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行构建&lt;/strong&gt;：使用 OpenCode 提供的免费 GLM 4.7（或 MiniMax M2.1）。亦或者你已经购买了 &lt;a class="link" href="https://svtter.cn/p/%E6%99%BA%E8%B0%B1-glm-4.5-%E5%9C%A8%E7%BC%96%E7%A8%8B%E6%96%B9%E9%9D%A2%E7%9A%84%E8%8B%A5%E5%B9%B2%E9%97%AE%E9%A2%98/" target="_blank" rel="noopener"
&gt;Zhipu Coding Plan&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;提到 Gemini 3 就不能不提 GPT-5.2。&lt;/p&gt;
&lt;p&gt;首先，部分工程师不使用 coding agent，而是使用直接使用 ChatGPT.com。这种使用方式且不论是否高效，可靠性就令人担忧。从实际体验来看，GPT-5.2 的回复语气做了拟人化微调，过于迎合用户。虽然可以调整语气，但对于专业开发者来说可能不是最佳选择。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://svtter.cn/p/%E9%AB%98%E6%95%88%E7%9C%81%E9%92%B1%E6%88%91%E7%9A%84-ai-agent-%E5%B7%A5%E4%BD%9C%E6%B5%81%E9%80%89%E6%8B%A9/pics/image_1767597061665_0.png"
width="1023"
height="930"
srcset="https://svtter.cn/p/%E9%AB%98%E6%95%88%E7%9C%81%E9%92%B1%E6%88%91%E7%9A%84-ai-agent-%E5%B7%A5%E4%BD%9C%E6%B5%81%E9%80%89%E6%8B%A9/pics/image_1767597061665_0_hu_175ada8cb4120ce2.png 480w, https://svtter.cn/p/%E9%AB%98%E6%95%88%E7%9C%81%E9%92%B1%E6%88%91%E7%9A%84-ai-agent-%E5%B7%A5%E4%BD%9C%E6%B5%81%E9%80%89%E6%8B%A9/pics/image_1767597061665_0_hu_c7107e2757a481d7.png 1024w"
loading="lazy"
alt="GPT-5.2 回复语气截图"
class="gallery-image"
data-flex-grow="110"
data-flex-basis="264px"
&gt;&lt;/p&gt;
&lt;p&gt;此外，尽管 GPT-5.2 在 SWE-bench veried 上得到了较好的性能，但是我实际体验不好。这就不得不说一下 SWE-bench 的历史：&lt;/p&gt;
&lt;p&gt;SWE-bench 最初由&lt;strong&gt;普林斯顿大学&lt;/strong&gt;团队提出（ICLR 2024），用于评估语言模型解决真实 GitHub 问题的能力。&lt;/p&gt;
&lt;p&gt;但问题在于：2024年8月，OpenAI 的 Preparedness 团队与原作者合作，推出了 &lt;strong&gt;SWE-bench Verified&lt;/strong&gt;（500个经过人工验证的问题子集）。由于 OpenAI 参与了这个新版本 benchmark 的&lt;strong&gt;设计&lt;/strong&gt;，因此在这个 benchmark 下测试的 OpenAI 模型性能值得怀疑。这并不一定是主观引入性能优化，但是 Bias 在这种情况下，极有可能存在。&lt;/p&gt;
&lt;p&gt;还是那句话：从实际使用上来看，codex 总不能带来特别理想的结果。&lt;/p&gt;
&lt;h2 id="一些-opencode-技巧"&gt;一些 opencode 技巧
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;通过 OpenCode 使用 Agent&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;OpenCode 支持启动 SubAgent。调试前后端代码时，可以让 OpenCode 在不同目录启动 Agent，有效避免权限问题。&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;OpenSpec：跨 Agent 共享规范&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1. OpenCode + Gemini 3 Flash → 生成 proposal
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2. Codex → 代码审查
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3. Claude Code → 再次审查
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4. OpenSpec Apply → 最终执行
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;OpenSpec 生成 spec 是靠谱的，但是有时候便宜模型代码质量不行。这个时候可以多生成几次，使用 spec 生成多次，从中挑选最好的结果。&lt;/p&gt;
&lt;p&gt;最后，如果从 PR 看到确实不行，仍可以继续采用 sonnet 4.5 来实现代码。&lt;/p&gt;
&lt;h2 id="思考"&gt;思考
&lt;/h2&gt;&lt;p&gt;作为 Agent 工程师，我们需要基于以下趋势做决策：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;模型会越来越聪明&lt;/li&gt;
&lt;li&gt;执行速度会越来越快&lt;/li&gt;
&lt;li&gt;价格会越来越便宜&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;虽然这是大趋势，但在实际任务中仍需平衡计算速度、成本和最终效果。也许很快就会出现能够自动平衡这些因素的 Agent 系统，但是现阶段考虑这些问题，没错。&lt;/p&gt;</description></item></channel></rss>