我把自己的 Agent 从 Wails 迁到了 MyGo 原生 GUI,全程踩坑和实践分享
最近把自己写的工作 agent [Ally agent]( Bronya0/ally-agent) 的界面重写了一遍,起因是无意中看到有人推荐 MyGo ,加上我对内存和性能一直有点追求,多年来一直在用 wails ,想换个试试,就想拿自己的项目试点,没想到结果意外的不错。
旧版本的技术栈是典型的 Go + Wails + Vue (wails 我用了很多年 最近终于 V3 发正式版了 等了四年,可惜我也老了),界面用 Naive UI ,样式美观,效果也相当不错。但做久了有几个地方比较麻烦( tarui 应该也是一样):
跨平台界面的细节不一致,构建链比较长,还有前端内存出了问题以后,我这个写后端的人不太容易排查。
折腾了一段时间,现在主要页面已经迁到 [MyGo]( egoist/mygo) 了,记录一下过程中比较有感触的地方。
先说一下技术路线:Wails 的界面通过系统 WebView 展示网页; MyGo 既支持这种方式,也支持用 Go 写 Native UI ,由框架自己绘制。WebView 也有自己的优势,MyGo 两种都支持,相当于有个兜底,下面会说。
Ally 现在的聊天、设置等常用页面用 Native UI ,插件页面和部分预览仍然按需使用 WebView 。
一、UI 跨平台一致性
这是我这次迁移最重视的一点。
之前用 Wails 做界面,Windows 上调好的页面,到了 macOS 上经常还要再看一遍。字体、行高、滚动条、输入框这些地方,偶尔就会有一点不一样。
比如我画的表格,不知道为啥线条在 macOS 上看着发虚,Windows 上就正常。单看问题都不大,但调整完一个平台还得顾着另一个平台,挺烦的。
Wails 使用系统 WebView ,Windows 和 macOS 背后的浏览器引擎不一样,前端样式最终要经过各自的渲染实现。
MyGo 的 Native UI 由框架自己负责布局和绘制。迁移后,我在 Windows 上调好的主体界面,拿到 macOS 上看到的效果接近得多,需要针对平台单独修的地方也少了。
当然,自绘也不等于所有像素都完全一致。MyGo 的文字排版仍然使用[各平台的文字系统]( mygo.egoist.dev):Windows 是 DirectWrite ,macOS 是 Core Text ,Linux 是 Pango 。字体回退、DPI 、输入法这些还是要分别检查。但至少不用再同时处理两套 WebView 引擎带来的样式差异了。
以前总觉得 Electron 重,碰到这些问题后倒是能理解为什么有些项目愿意用它:自带 Chromium ,渲染环境相对好统一。OpenCode 桌面端后来也[从 Tauri 换到了 Electron]( github.com/anomalyco/opencode/blob/dev/packages/d…),不过人家具体为什么换,我就不瞎猜了。
二、内存问题现在能用熟悉的 Go 工具查
以前 Wails 版本的 Go 后端一样可以用 pprof,但界面在 WebView 里,Go 堆、JavaScript 堆、DOM 、浏览器渲染进程是几块不同的东西。
前端也有 DevTools ,可以拍 JS heap snapshot 、查 DOM 引用。但我平时主要写 Go ,遇到界面内存持续上涨,分析起来没有 Go 那么顺手。
现在主要界面和业务都在 Go 进程里,我给 Ally 加了一个按需导出诊断的入口:在设置里点“导出内存诊断”,会导出 heap.pprof、allocs.pprof、goroutine.pprof 等文件。
比如在空闲时导出一次,复现内存增长后再导出一次,就可以对比:
go tool pprof -http=127.0.0.1:0 \
-base=before/heap.pprof after/heap.pprof
heap 主要看还活着的对象,allocs 主要看累计分配。分配量大不一定是泄漏,也可能是某段代码反复创建临时对象,GC 能回收,但会持续消耗 CPU 。
当然,pprof 主要看到的是 Go 堆。系统和 GPU 资源,以及 WebView 子进程的内存,还是得用其他工具看。
三、帧处理的坑
MyGo 的界面更新会重新构建控件树,写 UI 时要特别留意渲染函数里做了什么。
我的 Agent 有命令执行卡片,命令输出可能很长。之前即使卡片折叠着、界面上只展示最后几行,代码仍然会先把整段输出拆分、去空行,再取出末尾几行。
看起来只是一个字符串处理函数,但界面更新时会反复执行。
后来改了两处:
1. 只需要末尾几行时,从字符串尾部向前扫描,不再先拆分整个输出。
2. 需要重复使用的派生结果按原始内容缓存;内容没变就复用。折叠时不构造展开用的行数据,并释放对应缓存。
一次本地测试里,用两万行命令输出测试折叠卡片的相关处理,单次分配从约 1.9 MB 降到 1.9 KB ,耗时从约 0.72 ms 降到 0.02 ms。
这是这段处理在该样本下的局部结果,不是整个应用的性能对比。[对应修改在这里]( github.com/Bronya0/ally-agent/commit/a1e085b9397e…)。
类似的问题还有工具参数 JSON:同一条消息的卡片头、卡片体都可能读取参数,如果每次渲染都 Unmarshal 一遍,长对话里分配会越来越明显。现在把解析结果挂在控件局部状态上,原始参数变化时才重新解析。
我的感受是,性能问题要先分清楚是“每次请求执行一次”,还是“界面更新时每帧执行一次”。后者哪怕单次看着不重,乘上帧数和控件数量也很拖累性能。
四、构建链变短
旧项目的构建大致要经过前端依赖、绑定生成、Vite 构建,再编译 Go 。MyGo Native UI 的主要页面直接用 Go 写,主界面去掉了前端打包这一步,我自己的项目构建起来明显快了不少。
MyGo 不依赖 CGO ,交叉编译也省事。目前 Ally 的构建脚本可以在 macOS 上处理 Windows 、macOS 、Linux 的 x64 和 ARM64 六个目标,另外生成 macOS Universal 包。
例如编译一个 Windows x64 二进制,核心就是:
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o Ally.exe .
实际发布还要处理资源、压缩包、签名等事情;能交叉编译出来,也不代表已经在目标系统完成运行验证。Linux 的图形环境和使用 WebView 时需要的系统库,也仍然要考虑。
这里补充一下:我刚看了 [Wails 3.1 的发布说明]( wails.io),它也加入了无 CGO 构建能力。目前 macOS 和 Linux 的 purego 后端还是实验性质,CGO 仍是默认选项。
所以“无 CGO”以后不一定是两者之间的主要区别。对我这个项目来说,更明显的差别是主界面不用再经过 Vue 那套构建流程了。
五、MyGo 组件够用,但 Web 生态还是强很多
迁移前我担心原生 GUI 要从头写一堆控件,实际比想象中好一些。
MyGo 已经有常见的表单、列表、树、弹窗,也有代码编辑器、Diff 、Markdown 等组件。Ally 的文件浏览和代码编辑用到了它的编辑器,聊天内容也用到了 Markdown 相关能力。
但如果和成熟的 Web 组件库比,差距还是很明显。Naive UI 这类库积累了很长时间,一个表单、选择器或者弹窗拿来就能用,样式、焦点、键盘操作、动画都比较完整。
好在 MyGo 的定制能力不错,但要做出自己满意的效果,颜色、间距、状态和交互细节还是得花时间。我之前不太想碰这类原生 UI ,一个原因就是觉得手写样式太累。
现在有 AI 帮忙,这件事容易太多了。先用框架已有组件和 Base/Parts API ,让 AI 根据目标效果调整样式,再自己检查实际交互。
我的经验是,尽量复用组件本身的焦点、键盘导航、浮层和滚动行为,别为了改外观把这些重新实现一遍,非常容易踩坑。
六、WebView 的优势还在,可以混合使用
WebView 最大的好处之一就是生态。
视频播放、复杂图表、各种文件预览,Web 上已经有大量现成方案。很多东西如果坚持全部原生重写,维护成本很高。
MyGo 可以在 Native UI 窗口里嵌入 WebView ,所以我的做法是:常驻界面用原生 UI ,需要 Web 生态的地方再创建 WebView 。
Ally 目前的 HTML 和 Mermaid 预览就是这样处理的。预览可见时按需创建,数量做限制,离开后释放。否则一个长对话里出现很多图表时,每个预览都长期挂着 WebView ,前面省下来的资源很容易又花回去。
迁移后日常使用的内存占用和响应都有比较明显的改善。不过我还没有整理同一机器、相同会话和操作条件下的完整对照数据,暂时不拿具体数字比较两个框架。
七、插件化是 Go 里比较麻烦的东西,需要谨慎考虑
这是迁移前有点低估、做起来以后感受很深的一点。
看看 [DeepSeek Harness 的 Web Client 架构]( github.com/deepseek-ai/deepseek-harness/blob/mast…),它有插件加载、服务、事件和 UI Slots ,插件可以在已有界面里贡献功能。
Web 技术和 JavaScript 的动态加载能力,让这类扩展比较容易分发和迭代。当然,WebView 本身不会自动提供插件体系,生命周期、扩展点和权限边界都得应用自己设计。
纯 Go 桌面应用要做同样的事就麻烦得多。[Go 标准库的 plugin]( pkg.go.dev) 不支持 Windows ,加载后也不能卸载,对编译工具链和依赖版本还有要求,不适合直接拿来做一个跨 Windows 、macOS 、Linux 的热插拔 GUI 插件市场。
最后 Ally 还是用了混合方案:主程序是原生 UI ,插件页面按需跑在 WebView 里。插件可以打成 zip 安装,在侧栏增加页面,通过宿主提供的接口访问存储、HTTP 和工作区等能力;重新导入后可以更新,不用重启整个应用。
目前这更接近“可插拔页面”,和 DSH 那种能扩展更多核心能力的插件体系不太一样。WebView 也有自己的生命周期和资源成本,要做成完整生态,扩展点、兼容性和权限模型还有不少事要解决。
代码
大概就这些,迁移完之后我自己还是比较满意的,欢迎大家一起尝试原生界面
我的项目代码在 [Ally 仓库]( Bronya0/ally-agent): Bronya0/ally-agent 原来的 Wails 版本还在 main 分支,MyGo 版本在 [mygo 分支]( github.com/Bronya0/ally-agent/tree/mygo),感兴趣或想学习 MyGo 的可以对比着看,欢迎点个 Star 。
15 comments
> But if you get a Claude subscription, don't you have to use Claude Code?
Seems like the doors are (slightly?) open right now. OpenCode just announced that users can use their Claude subscription:
- x.com
- opencode#58782
Solving a complex multi-task with 262k vs 128 context dropped performance from 91% to 57%... Then I made two changes. Solves with 128k went up to 94%.
I've been benchmarking local models as coding agents in opencode on real bugs in a real codebase, graded by hidden tests. The hardest tasks bundle 6–12 independent bugs into one request. At a 128k window, Qwen3.8-Flash-Next (IQ3_XXS, 2× 7900 XTX) kept "finishing" early with half the bugs untouched.
What was happening (from the recorded requests):
- When the context fills up, opencode replaces the conversation with a model-written summary. The original request is gone.
- The first summary lists every bug. Later summaries are summaries of summaries and only keep "completed" and "in progress", so bugs not started yet quietly drop out.
- After the summary, opencode adds "Continue if you have next steps, or stop and ask for clarification". The agent reports what it did and stops. One run literally asked me to paste issues 3, 4, 5, 7, 8 and 9 again because it no longer had them.
Two fixes, as a small opencode plugin:
1. Re-insert: after every compaction, put the original request back word for word.
2. Checklist: tell the summarizer to start every summary with a checklist of every item in the original request, marked done / in progress / not started, and never drop one. Then have the agent sync its todo list to that checklist and check it before finishing.
Results (4 multi-bug tasks, 79 hidden checks, 128k window):
┌─────────────────────────────────────────────────────┬────────┐
│ Setup │ passed │
├─────────────────────────────────────────────────────┼────────┤
│ Stock opencode │ 57% │
├─────────────────────────────────────────────────────┼────────┤
│ Checklist only │ 82% │
├─────────────────────────────────────────────────────┼────────┤
│ Re-insert only │ 90% │
├─────────────────────────────────────────────────────┼────────┤
│ Both │ 94% │
├─────────────────────────────────────────────────────┼────────┤
│ Same model, no fixes, 262k window (rarely compacts) │ 91% │
├─────────────────────────────────────────────────────┼────────┤
│ Claude Opus 5.5 (19 runs, same tasks) │ 94% │
└─────────────────────────────────────────────────────┴────────┘
So at 128k the fixes undo effectively all the damage compaction does--and perhaps even help on top of that. On these long tasks, a quantized local model then matches Opus on sub-tests. (Opus still fully solves more of the tasks outright.)
Caveats:
- One run per setup on the local side, and only 4 tasks. An earlier pilot showed the same direction.
- Single-bug tasks didn't change; they rarely compact, so no difference between 128k and 262k on those tasks.
It seems painfully obvious and I'm kind of shocked no one else has done this.
There's an opencode issue about todos being cleared on compaction (#5934 (opencode#5934)) and an open PR that
carries todos over (#25982 (opencode#25982)). Nothing I've seen re-inserts the original request, and in my runs that was the bigger win.
Had claude push it out there as a plugin for anyone that wanted to test it themselves:
Plugin: kylebakerio/opencode-compaction-anchor
I'm doing more tests, but these early results were so direct that I decided to share this right away.
fwiw, my output is shared at:
lab.kylebaker.io
I don't sell anything or care if people visit. I've just spent the last month+ non-stop running local benchmarks and building out and upgrading my local benchmarks and had built my own dashboard to manage and view it all, so eventually decided I may as well throw it out there.
Finally, I've not really put this out there before, but for anyone that wants to replicate what I'm doing--I don't share MY benchmark of bugs, but the harness and framework that would allow you to build your OWN benchmark from your OWN git history is here:
kylebakerio/LL-Dash
(drafted with claude because of time, then hand-edited.)
44 points · 26 comments
OpenCode 2.2系へ上げる前に:import・TUI・CIの互換性を棚卸しする
OpenCodeを社内の開発環境に組み込んでいる場合、更新で確認したいのは新機能だけではありません。自作プラグインのimport先、画面部品の依存版、CIからの呼び出しが、それぞれ別の理由で見直し対象になります。
公式リリースの表示では、v2.2.0が10月27日、v2.2.3とv2.2.4が10月30日に公開されています。この記事は11月1日時点でこれらの変更をまとめた更新前レビューです。v2.2.0を今日の新発表として扱うものではありません。対象は、OpenCodeを使うだけでなく、SDKやプラグインを保守している開発者です。v2.2.0・v2.2.3・v2.2.4
本稿では、レビュー箇所を拾う小さなPythonスクリプトを用意します。実行したのは架空の入力に対するオフラインテストだけです。OpenCode本体のインストール、実AIモデルへの接続、移行後の性能測定は行っていません。
import先の変更は文字列置換だけで済ませない
v2.2.0では、@opencode/aiのルートがPromise APIを公開する形になり、Effect APIは@opencode/ai/effectへ移っています。従来の@opencode/ai/promiseは削除されました。公式例でいえば、AIをPromise側から使うimportはルートへ、LLMをEffect側から使うimportは/effectへ移す関係です。
重要なのは、旧ルートを参照するコードを全部同じ文字列へ置き換えないことです。どのAPIを期待しているかを、importするシンボルと呼び出し側で確認します。ラッパーライブラリが再exportしている場合は、その利用者側まで型検査の対象にします。公式リリースはワイルドカードのsubpath exportも明示的な一覧に置き換わったと説明しており、内部パスを直接参照していたコードは追加確認が必要です。
このため後述のスクリプトは、ルートimportを検出しても「壊れている」と断定しません。Promise APIとして正しく使っているコードも同じ文字列になるため、レビュー候補として出力します。
TUIは0.7で止めず、移行先の条件を見る
v2.2.0ではOpenTUI 0.7への対応が必要になりましたが、v2.2.3では@opentui/coreと@opentui/solidに0.8.0以降が必要とされています。2.2系へまとめて上げる際に、2.2.0の案内だけを読んで0.7へ合わせると、移行先の要件を満たしません。
0.7ではカスタム描画クラスのフック設定方法など、0.8ではScrollBoxの内部要素の扱いとRenderContextの要件が変わっています。独自のスクロールや描画処理があるチームは、インストールできたかだけでなく、スクロール、サイズ変更、画面復帰も確認したいところです。変更の細部は各リリースがリンクするOpenTUIの資料と照合してください。
package.jsonに書いた範囲と、lockfileで実際に解決した版も分けて残します。^0.8.0という宣言を見つけたことは、期待した依存一式で動作した証拠にはなりません。公式プラグイン資料も、対象OpenCode版と互換性のある版を使い、workspaceからリンクしたコピーだけでなくインストール後のパッケージをテストするよう案内しています。プラグイン概要
CIのActionと権限制御も別枠で確認する
v2.2.0ではOpenCodeのGitHub Actionが今後公開されないことと、ワークフローからの削除が案内されています。これはOpenCodeのCLIやSDK全体が廃止されたという意味ではありません。また、この案内だけで既存のタグ参照がすべて即時実行不能になったと断定することも避けます。まず、自社のワークフローがどの参照を使い、今後誰が保守するかを確認します。
移行時に「代わりのAction」を名前だけで選ぶのは危険です。付与する権限、実行条件、渡すコードや秘密情報、モデル利用料まで変わる可能性があります。既存の連携を止める判断と、新しい実装を動かす判断を分けてレビューします。本稿のサンプルはワークフローを変更せず、文字列から確認候補を列挙するだけです。
もう一つ、v2.2.3ではmanaged policyのintegration.useがintegration accountだけを扱う形になり、MCP、skill、pluginにはそれぞれのuse actionが追加されています。既存の接頭辞付きルールは自動変換される一方、単独のintegration.useはそれら全体へは適用されなくなります。設定を使用している組織では、許可ケースと拒否ケースの両方を移行検証に含めます。
APIを呼ばずにレビュー候補を集める
以下はaudit.pyの全文です。Python 3.12.14、標準ライブラリだけで検証しました。外部パッケージの追加、認証情報の読取り、ネットワーク送信はありません。サンプルと同じディレクトリの2つの架空ファイルだけを読みます。
"""Offline review hints only; not a parser or compatibility proof."""
import json
import re
from pathlib import Path
RULES = (
('removed_import', r'[\"\']@opencode/ai/promise[\"\']'),
('root_api_review', r'[\"\']@opencode/ai[\"\']'),
('retired_action', r'(?:anomalyco|sst)/opencode/github@'),
('policy_review', r'\bintegration.use\b'),
('tui_hook_review', r'\bdefineNativeIntegration\s*('),
)
def inspect_text(text):
return sorted(name for name, pattern in RULES
if re.search(pattern, text))
def inspect_dependencies(package):
hits = []
for section in ('dependencies', 'devDependencies', 'peerDependencies'):
for name, version in package.get(section, {}).items():
if name in {'@opentui/core', '@opentui/solid'}:
hits.append({'section': section, 'name': name,
'declared': version, 'check': 'review_for_0.8'})
return hits
if name == 'main':
base = Path(file).parent
text = (base / 'example.txt').read_text(encoding='utf-8')
package = json.loads((base / 'package.sample.json').read_text())
print(json.dumps({'text_hints': inspect_text(text),
'dependencies': inspect_dependencies(package)},
ensure_ascii=False, indent=2))
実行は、添付ZIPを展開したディレクトリで行います。example.txtには旧importとAction参照、package.sample.jsonにはOpenTUIの架空の依存宣言を入れています。
python3 --version
python3 audit.py
python3 -m unittest -v
出力のtext_hintsにはremoved_importとretired_actionが入り、依存関係にはOpenTUIの2件が出ます。14件のテストでは、引用符の違い、Effect用パス、ルートimport、旧オーナー名のAction参照、設定名、3種類の依存宣言欄などを確認しました。テストが通った範囲はこの関数と架空データであり、OpenCode 2.2.4の動作確認ではありません。
実ファイルへ適用するなら、まず必要な部分だけを複製し、秘密情報を除いて入力を作ります。この例はリポジトリを再帰走査しません。出力も「移行済み」の判定ではなく、人が読むための候補です。
このチェックが見落とすもの
正規表現なので、コメントに残った旧importも拾います。別名、動的に組み立てた文字列、生成コード、間接依存は十分に追えません。TypeScriptの構文木、YAMLの意味、semver範囲を解釈する実装でもありません。OpenTUIは0.8の宣言でも候補に残し、依存解決とUI検証を人に返しています。
そのため、検出が0件でも本番更新の可否は決まりません。次は対象版を固定した隔離環境で、型検査、プラグイン読込み、画面操作、必要なCI経路を順番に確認します。依存の更新と権限変更を同じ修正へ詰め込まないと、問題が出た際の原因を切り分けやすくなります。
ロールバックも、バイナリを戻すだけで十分とは限りません。設定や保存データの互換性を別途確認し、検証前の状態を保管してから試します。本稿では実環境のロールバック可否まで試していません。
コストと認証を確認してから採用する
このオフライン例はAI APIを使いませんが、実際のOpenCode運用では、選択するモデル提供者、認証経路、契約、利用上限の確認が別途必要です。プラグインの互換性が保てたからといって、新しい認証接続や追加課金まで承認されたことにはなりません。
v2.2.4にはClaude Agentのセットアップ案内や実験的なartifact機能なども含まれますが、本稿では有効化しません。移行の検証に関係しない新機能まで一度に試すと、送信先や権限、ログの扱いも変わり、比較条件が増えるためです。具体的なモデル単価や削減効果は本稿では評価していません。
採用判断は、使っている拡張の範囲で分けると進めやすくなります。標準CLI中心のチームは既存の主要操作から、自作TUIを持つチームは描画と依存版から、CIやmanaged policyを運用するチームは連携と許可・拒否条件から確認します。
未対応のプラグインに日常業務が依存している場合や、検証担当・復旧手順が決まっていない場合は、一括更新を急がない方がよいでしょう。今回の更新を評価する材料は、新機能の数だけではなく、自社が依存する部分を説明でき、検証結果を残せるかどうかです。
this might be api side? i wouldnt be surprised if its api side
but lol opencode#58782 anthropic is allowing opencode to kinda use claude subs
5 likes
OpenCode on X: "you can now use your claude subscription in OpenCode" / X
Wondering if they just use the SDK or they really got some special deal with anthropic?! Hope for us?
Update: opencode#58782 looks like it’s just the sdk way so same as Claude-pi-bridge which is pointless IMO.
59 points · 30 comments
@maria_rcks fwiw
> We got permission from Anthropic and everything here is fully tos compliant
opencode#58782
14 likes · 1,847 views
Operation Cheepseek phase 4? 🤩
Just found a PR with raised DeepSeek V4.1 Flash limits here:
opencode#60629
UPD: and the website shows the $100 limit now
73 points · 20 comments
i now use this as a "bad example" of a system prompt... github.com/anomalyco/opencode/blob/dev/packages/c…
it's just ... lacking everything you'd expect to be present... no guardrails, or structure
10 coding agents, one Qwen 3.8 27B, same llama-server: tokens and cache reuse differ more than pass rates
Part 2 of my agent test (part 1 was a 4B model)
Hardware I used: MacBook Pro M5 Max, 64 GB. Qwen 3.8 27B Q4 on llama.cpp, thinking off.
Setup
- same model and server for everyone
- 5 small coding tasks, each with a little trap
- each agent locked in a sandbox: it can only touch its own folder and only talk to the model
- auto-approve on, telemetry off, 5 min limit
Results
|#|Agent|Passed|Median time|Median tokens|Cache reuse|Tried to go online|
|:-|:-|:-|:-|:-|:-|:-|
|1|Pi|5/5|38 s|9.2k|72%|no|
|2|DeepSeek Harness|5/5|60 s|35.3k|83%|no|
|3|Codex CLI|5/5|94 s|36.3k|79%|every run|
|4|OpenClaw|5/5|106 s|39.0k|72%|unclear|
|5|Cline|5/5|152 s|29.6k|79%|every run|
|6|OpenCode|4/5|50 s|32.4k|75%|every run|
|7|Hermes|4/5|80 s|64.9k|93%|no|
|8|Atomic Agent|5/5|56 s|30.4k|86%|no|
|9|goose|4/5|98 s|20.3k|76%|every run|
|10|Qwen Code|4/5|183 s|107.9k|74%|unclear|
Tokens and cache
Pi is crazy lean: about 9k tokens per task, the lowest on every single task. Qwen Code was the hungriest at about 108k per task.
Atomic Agent is the odd one out in how it works. It was the only one talking to llama.cpp's native endpoint with a grammar that forces the tool calls into shape. The other nine all went through the OpenAI-style API. It's also built to keep its prompt the same from step to step, which is why it reused 86% of the cache (2rd of 10). It stayed offline the whole time and passed 5/5.
DeepSeek Harness was the surprise. It’s still an alpha, but it went 5/5, never tried to go online, and had the best cache reuse after Atomic Agent (83%).
The trap and the network
The log task got three agents. They grepped "ERROR" and got 244 instead of 64, because some normal lines just mention errors. OpenCode, goose and Atomic Agent fell for it.
Codex, Cline, goose and OpenCode tried to reach the internet in every run, even with telemetry off. The sandbox blocked it, so I don't know what they were calling. OpenClaw and Qwen Code are "unclear" because they run as plain node and I couldn't tell them apart.
llama.cpp notes
- Codex needs llama-server's /v1/responses now
- Hermes needs at least 64K context
- Cline and Qwen Code have usage stats on by default, DeepSeek Harness uploads session logs (per their docs). I turned it all off
Which column matters most to you when you pick an agent for a local model like Qwen 3.8 27B: passing, speed, tokens, or staying offline?
36 points · 36 comments
Mentions
我把自己的 Agent 从 Wails 迁到了 MyGo 原生 GUI,全程踩坑和实践分享 最近把自己写的工作 agent [Ally agent]( Bronya0/ally-agent) 的界面重写了一遍,起因是无意中看到有人推荐 MyGo ,加上我对内存和性能一直有点追求,多年来一直在用 wails ,想换个试试,就想拿自己的项目试点,没想到结果意外的不错。 旧版本的技术栈是典型的 Go + Wails + Vue (wails 我用了很多年 最近终于 V3 发正式版了 等了四年,可惜我也老了),界面用 Naive UI ,样式美观,效果也相当不错。但做久了有几个地方比较麻烦( tarui 应该也是一样): 跨平台界面的细节不一致,构建链比较长,还有前端内存出了问题以后,我这个写后端的人不太容易排查。 折腾了一段时间,现在主要页面已经迁到 [MyGo]( egoist/mygo) 了,记录一下过程中比较有感触的地方。 先说一下技术路线:Wails 的界面通过系统 WebView 展示网页; MyGo 既支持这种方式,也支持用 Go 写 Native UI ,由框架自己绘制。WebView 也有自己的优势,MyGo 两种都支持,相当于有个兜底,下面会说。 Ally 现在的聊天、设置等常用页面用 Native UI ,插件页面和部分预览仍然按需使用 WebView 。 一、UI 跨平台一致性 这是我这次迁移最重视的一点。 之前用 Wails 做界面,Windows 上调好的页面,到了 macOS 上经常还要再看一遍。字体、行高、滚动条、输入框这些地方,偶尔就会有一点不一样。 比如我画的表格,不知道为啥线条在 macOS 上看着发虚,Windows 上就正常。单看问题都不大,但调整完一个平台还得顾着另一个平台,挺烦的。 Wails 使用系统 WebView ,Windows 和 macOS 背后的浏览器引擎不一样,前端样式最终要经过各自的渲染实现。 MyGo 的 Native UI 由框架自己负责布局和绘制。迁移后,我在 Windows 上调好的主体界面,拿到 macOS 上看到的效果接近得多,需要针对平台单独修的地方也少了。 当然,自绘也不等于所有像素都完全一致。MyGo 的文字排版仍然使用[各平台的文字系统]( mygo.egoist.dev):Windows 是 DirectWrite ,macOS 是 Core Text ,Linux 是 Pango 。字体回退、DPI 、输入法这些还是要分别检查。但至少不用再同时处理两套 WebView 引擎带来的样式差异了。 以前总觉得 Electron 重,碰到这些问题后倒是能理解为什么有些项目愿意用它:自带 Chromium ,渲染环境相对好统一。OpenCode 桌面端后来也[从 Tauri 换到了 Electron]( github.com/anomalyco/opencode/blob/dev/packages/d…),不过人家具体为什么换,我就不瞎猜了。 二、内存问题现在能用熟悉的 Go 工具查 以前 Wails 版本的 Go 后端一样可以用 pprof,但界面在 WebView 里,Go 堆、JavaScript 堆、DOM 、浏览器渲染进程是几块不同的东西。 前端也有 DevTools ,可以拍 JS heap snapshot 、查 DOM 引用。但我平时主要写 Go ,遇到界面内存持续上涨,分析起来没有 Go 那么顺手。 现在主要界面和业务都在 Go 进程里,我给 Ally 加了一个按需导出诊断的入口:在设置里点“导出内存诊断”,会导出 heap.pprof、allocs.pprof、goroutine.pprof 等文件。 比如在空闲时导出一次,复现内存增长后再导出一次,就可以对比: go tool pprof -http=127.0.0.1:0 \ -base=before/heap.pprof after/heap.pprof heap 主要看还活着的对象,allocs 主要看累计分配。分配量大不一定是泄漏,也可能是某段代码反复创建临时对象,GC 能回收,但会持续消耗 CPU 。 当然,pprof 主要看到的是 Go 堆。系统和 GPU 资源,以及 WebView 子进程的内存,还是得用其他工具看。 三、帧处理的坑 MyGo 的界面更新会重新构建控件树,写 UI 时要特别留意渲染函数里做了什么。 我的 Agent 有命令执行卡片,命令输出可能很长。之前即使卡片折叠着、界面上只展示最后几行,代码仍然会先把整段输出拆分、去空行,再取出末尾几行。 看起来只是一个字符串处理函数,但界面更新时会反复执行。 后来改了两处: 1. 只需要末尾几行时,从字符串尾部向前扫描,不再先拆分整个输出。 2. 需要重复使用的派生结果按原始内容缓存;内容没变就复用。折叠时不构造展开用的行数据,并释放对应缓存。 一次本地测试里,用两万行命令输出测试折叠卡片的相关处理,单次分配从约 1.9 MB 降到 1.9 KB ,耗时从约 0.72 ms 降到 0.02 ms。 这是这段处理在该样本下的局部结果,不是整个应用的性能对比。[对应修改在这里]( github.com/Bronya0/ally-agent/commit/a1e085b9397e…)。 类似的问题还有工具参数 JSON:同一条消息的卡片头、卡片体都可能读取参数,如果每次渲染都 Unmarshal 一遍,长对话里分配会越来越明显。现在把解析结果挂在控件局部状态上,原始参数变化时才重新解析。 我的感受是,性能问题要先分清楚是“每次请求执行一次”,还是“界面更新时每帧执行一次”。后者哪怕单次看着不重,乘上帧数和控件数量也很拖累性能。 四、构建链变短 旧项目的构建大致要经过前端依赖、绑定生成、Vite 构建,再编译 Go 。MyGo Native UI 的主要页面直接用 Go 写,主界面去掉了前端打包这一步,我自己的项目构建起来明显快了不少。 MyGo 不依赖 CGO ,交叉编译也省事。目前 Ally 的构建脚本可以在 macOS 上处理 Windows 、macOS 、Linux 的 x64 和 ARM64 六个目标,另外生成 macOS Universal 包。 例如编译一个 Windows x64 二进制,核心就是: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o Ally.exe . 实际发布还要处理资源、压缩包、签名等事情;能交叉编译出来,也不代表已经在目标系统完成运行验证。Linux 的图形环境和使用 WebView 时需要的系统库,也仍然要考虑。 这里补充一下:我刚看了 [Wails 3.1 的发布说明]( wails.io),它也加入了无 CGO 构建能力。目前 macOS 和 Linux 的 purego 后端还是实验性质,CGO 仍是默认选项。 所以“无 CGO”以后不一定是两者之间的主要区别。对我这个项目来说,更明显的差别是主界面不用再经过 Vue 那套构建流程了。 五、MyGo 组件够用,但 Web 生态还是强很多 迁移前我担心原生 GUI 要从头写一堆控件,实际比想象中好一些。 MyGo 已经有常见的表单、列表、树、弹窗,也有代码编辑器、Diff 、Markdown 等组件。Ally 的文件浏览和代码编辑用到了它的编辑器,聊天内容也用到了 Markdown 相关能力。 但如果和成熟的 Web 组件库比,差距还是很明显。Naive UI 这类库积累了很长时间,一个表单、选择器或者弹窗拿来就能用,样式、焦点、键盘操作、动画都比较完整。 好在 MyGo 的定制能力不错,但要做出自己满意的效果,颜色、间距、状态和交互细节还是得花时间。我之前不太想碰这类原生 UI ,一个原因就是觉得手写样式太累。 现在有 AI 帮忙,这件事容易太多了。先用框架已有组件和 Base/Parts API ,让 AI 根据目标效果调整样式,再自己检查实际交互。 我的经验是,尽量复用组件本身的焦点、键盘导航、浮层和滚动行为,别为了改外观把这些重新实现一遍,非常容易踩坑。 六、WebView 的优势还在,可以混合使用 WebView 最大的好处之一就是生态。 视频播放、复杂图表、各种文件预览,Web 上已经有大量现成方案。很多东西如果坚持全部原生重写,维护成本很高。 MyGo 可以在 Native UI 窗口里嵌入 WebView ,所以我的做法是:常驻界面用原生 UI ,需要 Web 生态的地方再创建 WebView 。 Ally 目前的 HTML 和 Mermaid 预览就是这样处理的。预览可见时按需创建,数量做限制,离开后释放。否则一个长对话里出现很多图表时,每个预览都长期挂着 WebView ,前面省下来的资源很容易又花回去。 迁移后日常使用的内存占用和响应都有比较明显的改善。不过我还没有整理同一机器、相同会话和操作条件下的完整对照数据,暂时不拿具体数字比较两个框架。 七、插件化是 Go 里比较麻烦的东西,需要谨慎考虑 这是迁移前有点低估、做起来以后感受很深的一点。 看看 [DeepSeek Harness 的 Web Client 架构]( github.com/deepseek-ai/deepseek-harness/blob/mast…),它有插件加载、服务、事件和 UI Slots ,插件可以在已有界面里贡献功能。 Web 技术和 JavaScript 的动态加载能力,让这类扩展比较容易分发和迭代。当然,WebView 本身不会自动提供插件体系,生命周期、扩展点和权限边界都得应用自己设计。 纯 Go 桌面应用要做同样的事就麻烦得多。[Go 标准库的 plugin]( pkg.go.dev) 不支持 Windows ,加载后也不能卸载,对编译工具链和依赖版本还有要求,不适合直接拿来做一个跨 Windows 、macOS 、Linux 的热插拔 GUI 插件市场。 最后 Ally 还是用了混合方案:主程序是原生 UI ,插件页面按需跑在 WebView 里。插件可以打成 zip 安装,在侧栏增加页面,通过宿主提供的接口访问存储、HTTP 和工作区等能力;重新导入后可以更新,不用重启整个应用。 目前这更接近“可插拔页面”,和 DSH 那种能扩展更多核心能力的插件体系不太一样。WebView 也有自己的生命周期和资源成本,要做成完整生态,扩展点、兼容性和权限模型还有不少事要解决。 代码 大概就这些,迁移完之后我自己还是比较满意的,欢迎大家一起尝试原生界面 我的项目代码在 [Ally 仓库]( Bronya0/ally-agent): Bronya0/ally-agent 原来的 Wails 版本还在 main 分支,MyGo 版本在 [mygo 分支]( github.com/Bronya0/ally-agent/tree/mygo),感兴趣或想学习 MyGo 的可以对比着看,欢迎点个 Star 。
> But if you get a Claude subscription, don't you have to use Claude Code? Seems like the doors are (slightly?) open right now. OpenCode just announced that users can use their Claude subscription: - x.com - opencode#58782
Solving a complex multi-task with 262k vs 128 context dropped performance from 91% to 57%... Then I made two changes. Solves with 128k went up to 94%. I've been benchmarking local models as coding agents in opencode on real bugs in a real codebase, graded by hidden tests. The hardest tasks bundle 6–12 independent bugs into one request. At a 128k window, Qwen3.8-Flash-Next (IQ3_XXS, 2× 7900 XTX) kept "finishing" early with half the bugs untouched. What was happening (from the recorded requests): - When the context fills up, opencode replaces the conversation with a model-written summary. The original request is gone. - The first summary lists every bug. Later summaries are summaries of summaries and only keep "completed" and "in progress", so bugs not started yet quietly drop out. - After the summary, opencode adds "Continue if you have next steps, or stop and ask for clarification". The agent reports what it did and stops. One run literally asked me to paste issues 3, 4, 5, 7, 8 and 9 again because it no longer had them. Two fixes, as a small opencode plugin: 1. Re-insert: after every compaction, put the original request back word for word. 2. Checklist: tell the summarizer to start every summary with a checklist of every item in the original request, marked done / in progress / not started, and never drop one. Then have the agent sync its todo list to that checklist and check it before finishing. Results (4 multi-bug tasks, 79 hidden checks, 128k window): ┌─────────────────────────────────────────────────────┬────────┐ │ Setup │ passed │ ├─────────────────────────────────────────────────────┼────────┤ │ Stock opencode │ 57% │ ├─────────────────────────────────────────────────────┼────────┤ │ Checklist only │ 82% │ ├─────────────────────────────────────────────────────┼────────┤ │ Re-insert only │ 90% │ ├─────────────────────────────────────────────────────┼────────┤ │ Both │ 94% │ ├─────────────────────────────────────────────────────┼────────┤ │ Same model, no fixes, 262k window (rarely compacts) │ 91% │ ├─────────────────────────────────────────────────────┼────────┤ │ Claude Opus 5.5 (19 runs, same tasks) │ 94% │ └─────────────────────────────────────────────────────┴────────┘ So at 128k the fixes undo effectively all the damage compaction does--and perhaps even help on top of that. On these long tasks, a quantized local model then matches Opus on sub-tests. (Opus still fully solves more of the tasks outright.) Caveats: - One run per setup on the local side, and only 4 tasks. An earlier pilot showed the same direction. - Single-bug tasks didn't change; they rarely compact, so no difference between 128k and 262k on those tasks. It seems painfully obvious and I'm kind of shocked no one else has done this. There's an opencode issue about todos being cleared on compaction (#5934 (opencode#5934)) and an open PR that carries todos over (#25982 (opencode#25982)). Nothing I've seen re-inserts the original request, and in my runs that was the bigger win. Had claude push it out there as a plugin for anyone that wanted to test it themselves: Plugin: kylebakerio/opencode-compaction-anchor I'm doing more tests, but these early results were so direct that I decided to share this right away. fwiw, my output is shared at: lab.kylebaker.io I don't sell anything or care if people visit. I've just spent the last month+ non-stop running local benchmarks and building out and upgrading my local benchmarks and had built my own dashboard to manage and view it all, so eventually decided I may as well throw it out there. Finally, I've not really put this out there before, but for anyone that wants to replicate what I'm doing--I don't share MY benchmark of bugs, but the harness and framework that would allow you to build your OWN benchmark from your OWN git history is here: kylebakerio/LL-Dash (drafted with claude because of time, then hand-edited.)
OpenCode 2.2系へ上げる前に:import・TUI・CIの互換性を棚卸しする OpenCodeを社内の開発環境に組み込んでいる場合、更新で確認したいのは新機能だけではありません。自作プラグインのimport先、画面部品の依存版、CIからの呼び出しが、それぞれ別の理由で見直し対象になります。 公式リリースの表示では、v2.2.0が10月27日、v2.2.3とv2.2.4が10月30日に公開されています。この記事は11月1日時点でこれらの変更をまとめた更新前レビューです。v2.2.0を今日の新発表として扱うものではありません。対象は、OpenCodeを使うだけでなく、SDKやプラグインを保守している開発者です。v2.2.0・v2.2.3・v2.2.4 本稿では、レビュー箇所を拾う小さなPythonスクリプトを用意します。実行したのは架空の入力に対するオフラインテストだけです。OpenCode本体のインストール、実AIモデルへの接続、移行後の性能測定は行っていません。 import先の変更は文字列置換だけで済ませない v2.2.0では、@opencode/aiのルートがPromise APIを公開する形になり、Effect APIは@opencode/ai/effectへ移っています。従来の@opencode/ai/promiseは削除されました。公式例でいえば、AIをPromise側から使うimportはルートへ、LLMをEffect側から使うimportは/effectへ移す関係です。 重要なのは、旧ルートを参照するコードを全部同じ文字列へ置き換えないことです。どのAPIを期待しているかを、importするシンボルと呼び出し側で確認します。ラッパーライブラリが再exportしている場合は、その利用者側まで型検査の対象にします。公式リリースはワイルドカードのsubpath exportも明示的な一覧に置き換わったと説明しており、内部パスを直接参照していたコードは追加確認が必要です。 このため後述のスクリプトは、ルートimportを検出しても「壊れている」と断定しません。Promise APIとして正しく使っているコードも同じ文字列になるため、レビュー候補として出力します。 TUIは0.7で止めず、移行先の条件を見る v2.2.0ではOpenTUI 0.7への対応が必要になりましたが、v2.2.3では@opentui/coreと@opentui/solidに0.8.0以降が必要とされています。2.2系へまとめて上げる際に、2.2.0の案内だけを読んで0.7へ合わせると、移行先の要件を満たしません。 0.7ではカスタム描画クラスのフック設定方法など、0.8ではScrollBoxの内部要素の扱いとRenderContextの要件が変わっています。独自のスクロールや描画処理があるチームは、インストールできたかだけでなく、スクロール、サイズ変更、画面復帰も確認したいところです。変更の細部は各リリースがリンクするOpenTUIの資料と照合してください。 package.jsonに書いた範囲と、lockfileで実際に解決した版も分けて残します。^0.8.0という宣言を見つけたことは、期待した依存一式で動作した証拠にはなりません。公式プラグイン資料も、対象OpenCode版と互換性のある版を使い、workspaceからリンクしたコピーだけでなくインストール後のパッケージをテストするよう案内しています。プラグイン概要 CIのActionと権限制御も別枠で確認する v2.2.0ではOpenCodeのGitHub Actionが今後公開されないことと、ワークフローからの削除が案内されています。これはOpenCodeのCLIやSDK全体が廃止されたという意味ではありません。また、この案内だけで既存のタグ参照がすべて即時実行不能になったと断定することも避けます。まず、自社のワークフローがどの参照を使い、今後誰が保守するかを確認します。 移行時に「代わりのAction」を名前だけで選ぶのは危険です。付与する権限、実行条件、渡すコードや秘密情報、モデル利用料まで変わる可能性があります。既存の連携を止める判断と、新しい実装を動かす判断を分けてレビューします。本稿のサンプルはワークフローを変更せず、文字列から確認候補を列挙するだけです。 もう一つ、v2.2.3ではmanaged policyのintegration.useがintegration accountだけを扱う形になり、MCP、skill、pluginにはそれぞれのuse actionが追加されています。既存の接頭辞付きルールは自動変換される一方、単独のintegration.useはそれら全体へは適用されなくなります。設定を使用している組織では、許可ケースと拒否ケースの両方を移行検証に含めます。 APIを呼ばずにレビュー候補を集める 以下はaudit.pyの全文です。Python 3.12.14、標準ライブラリだけで検証しました。外部パッケージの追加、認証情報の読取り、ネットワーク送信はありません。サンプルと同じディレクトリの2つの架空ファイルだけを読みます。 """Offline review hints only; not a parser or compatibility proof.""" import json import re from pathlib import Path RULES = ( ('removed_import', r'[\"\']@opencode/ai/promise[\"\']'), ('root_api_review', r'[\"\']@opencode/ai[\"\']'), ('retired_action', r'(?:anomalyco|sst)/opencode/github@'), ('policy_review', r'\bintegration.use\b'), ('tui_hook_review', r'\bdefineNativeIntegration\s*('), ) def inspect_text(text): return sorted(name for name, pattern in RULES if re.search(pattern, text)) def inspect_dependencies(package): hits = [] for section in ('dependencies', 'devDependencies', 'peerDependencies'): for name, version in package.get(section, {}).items(): if name in {'@opentui/core', '@opentui/solid'}: hits.append({'section': section, 'name': name, 'declared': version, 'check': 'review_for_0.8'}) return hits if name == 'main': base = Path(file).parent text = (base / 'example.txt').read_text(encoding='utf-8') package = json.loads((base / 'package.sample.json').read_text()) print(json.dumps({'text_hints': inspect_text(text), 'dependencies': inspect_dependencies(package)}, ensure_ascii=False, indent=2)) 実行は、添付ZIPを展開したディレクトリで行います。example.txtには旧importとAction参照、package.sample.jsonにはOpenTUIの架空の依存宣言を入れています。 python3 --version python3 audit.py python3 -m unittest -v 出力のtext_hintsにはremoved_importとretired_actionが入り、依存関係にはOpenTUIの2件が出ます。14件のテストでは、引用符の違い、Effect用パス、ルートimport、旧オーナー名のAction参照、設定名、3種類の依存宣言欄などを確認しました。テストが通った範囲はこの関数と架空データであり、OpenCode 2.2.4の動作確認ではありません。 実ファイルへ適用するなら、まず必要な部分だけを複製し、秘密情報を除いて入力を作ります。この例はリポジトリを再帰走査しません。出力も「移行済み」の判定ではなく、人が読むための候補です。 このチェックが見落とすもの 正規表現なので、コメントに残った旧importも拾います。別名、動的に組み立てた文字列、生成コード、間接依存は十分に追えません。TypeScriptの構文木、YAMLの意味、semver範囲を解釈する実装でもありません。OpenTUIは0.8の宣言でも候補に残し、依存解決とUI検証を人に返しています。 そのため、検出が0件でも本番更新の可否は決まりません。次は対象版を固定した隔離環境で、型検査、プラグイン読込み、画面操作、必要なCI経路を順番に確認します。依存の更新と権限変更を同じ修正へ詰め込まないと、問題が出た際の原因を切り分けやすくなります。 ロールバックも、バイナリを戻すだけで十分とは限りません。設定や保存データの互換性を別途確認し、検証前の状態を保管してから試します。本稿では実環境のロールバック可否まで試していません。 コストと認証を確認してから採用する このオフライン例はAI APIを使いませんが、実際のOpenCode運用では、選択するモデル提供者、認証経路、契約、利用上限の確認が別途必要です。プラグインの互換性が保てたからといって、新しい認証接続や追加課金まで承認されたことにはなりません。 v2.2.4にはClaude Agentのセットアップ案内や実験的なartifact機能なども含まれますが、本稿では有効化しません。移行の検証に関係しない新機能まで一度に試すと、送信先や権限、ログの扱いも変わり、比較条件が増えるためです。具体的なモデル単価や削減効果は本稿では評価していません。 採用判断は、使っている拡張の範囲で分けると進めやすくなります。標準CLI中心のチームは既存の主要操作から、自作TUIを持つチームは描画と依存版から、CIやmanaged policyを運用するチームは連携と許可・拒否条件から確認します。 未対応のプラグインに日常業務が依存している場合や、検証担当・復旧手順が決まっていない場合は、一括更新を急がない方がよいでしょう。今回の更新を評価する材料は、新機能の数だけではなく、自社が依存する部分を説明でき、検証結果を残せるかどうかです。
this might be api side? i wouldnt be surprised if its api side but lol opencode#58782 anthropic is allowing opencode to kinda use claude subs
OpenCode on X: "you can now use your claude subscription in OpenCode" / X Wondering if they just use the SDK or they really got some special deal with anthropic?! Hope for us? Update: opencode#58782 looks like it’s just the sdk way so same as Claude-pi-bridge which is pointless IMO.
@maria_rcks fwiw > We got permission from Anthropic and everything here is fully tos compliant opencode#58782
Operation Cheepseek phase 4? 🤩 Just found a PR with raised DeepSeek V4.1 Flash limits here: opencode#60629 UPD: and the website shows the $100 limit now
i now use this as a "bad example" of a system prompt... github.com/anomalyco/opencode/blob/dev/packages/c… it's just ... lacking everything you'd expect to be present... no guardrails, or structure
10 coding agents, one Qwen 3.8 27B, same llama-server: tokens and cache reuse differ more than pass rates Part 2 of my agent test (part 1 was a 4B model) Hardware I used: MacBook Pro M5 Max, 64 GB. Qwen 3.8 27B Q4 on llama.cpp, thinking off. Setup - same model and server for everyone - 5 small coding tasks, each with a little trap - each agent locked in a sandbox: it can only touch its own folder and only talk to the model - auto-approve on, telemetry off, 5 min limit Results |#|Agent|Passed|Median time|Median tokens|Cache reuse|Tried to go online| |:-|:-|:-|:-|:-|:-|:-| |1|Pi|5/5|38 s|9.2k|72%|no| |2|DeepSeek Harness|5/5|60 s|35.3k|83%|no| |3|Codex CLI|5/5|94 s|36.3k|79%|every run| |4|OpenClaw|5/5|106 s|39.0k|72%|unclear| |5|Cline|5/5|152 s|29.6k|79%|every run| |6|OpenCode|4/5|50 s|32.4k|75%|every run| |7|Hermes|4/5|80 s|64.9k|93%|no| |8|Atomic Agent|5/5|56 s|30.4k|86%|no| |9|goose|4/5|98 s|20.3k|76%|every run| |10|Qwen Code|4/5|183 s|107.9k|74%|unclear| Tokens and cache Pi is crazy lean: about 9k tokens per task, the lowest on every single task. Qwen Code was the hungriest at about 108k per task. Atomic Agent is the odd one out in how it works. It was the only one talking to llama.cpp's native endpoint with a grammar that forces the tool calls into shape. The other nine all went through the OpenAI-style API. It's also built to keep its prompt the same from step to step, which is why it reused 86% of the cache (2rd of 10). It stayed offline the whole time and passed 5/5. DeepSeek Harness was the surprise. It’s still an alpha, but it went 5/5, never tried to go online, and had the best cache reuse after Atomic Agent (83%). The trap and the network The log task got three agents. They grepped "ERROR" and got 244 instead of 64, because some normal lines just mention errors. OpenCode, goose and Atomic Agent fell for it. Codex, Cline, goose and OpenCode tried to reach the internet in every run, even with telemetry off. The sandbox blocked it, so I don't know what they were calling. OpenClaw and Qwen Code are "unclear" because they run as plain node and I couldn't tell them apart. llama.cpp notes - Codex needs llama-server's /v1/responses now - Hermes needs at least 64K context - Cline and Qwen Code have usage stats on by default, DeepSeek Harness uploads session logs (per their docs). I turned it all off Which column matters most to you when you pick an agent for a local model like Qwen 3.8 27B: passing, speed, tokens, or staying offline?