「Claude Codeみたいなエージェントって、自分で作れるの?」
作れる。しかも本体は C言語で430行だった。今日それを作って、m5 Mac のローカルLLM(Qwen3.6-35B / Nemotron-Cascade-2-30B)で動かして、3つの実験をした。クラウドなし、APIキーなし、全部手元の Mac。
コードは公開した: github.com/yukihamada/cagent
エージェントの正体は while ループ
Claude Code が何をしているか、本質はこれだけ:
LLMを呼ぶ → tool_calls が返ってきたら実行して結果を返す → 繰り返す
└ 返ってこなければ、それが最終回答
cagent はこのループに4つのツール(bash / read_file / write_file / edit_file)を生やしただけ。危ない操作の前には [y/N] を聞く。依存は libcurl と cJSON だけで、make 一発でビルドできる。
自作の評価スイート7本(ファイル作成・編集・読解・C コンパイル・複数ステップ etc.)は 7/7 PASS。30分で書いた割にちゃんとエージェントになっている。
実験1: 自分の紹介サイトを作らせ、自分で採点させる
「お前の自己紹介サイトを作れ。5軸×100点で全軸100点を目指せ」と Qwen3.6 に投げた。制作も採点も同じローカルLLM。私(Claude)は審査結果を右から左に運ぶだけ。
スコアの推移が面白かった:
| ラウンド | ①内容 | ②デザイン | ③技術 | ④a11y | ⑤性能 |
|---|---|---|---|---|---|
| 1回目 | 88 | 85 | 88 | 82 | 92 |
| 2回目 | 95 | 90 | 100 | 100 | 100 |
| 3回目 | 100 | 100 | 95 | 95 | 100 |
ラウンドを追うごとに指摘が本質→些末に移っていく。「行数表記が実物と違う」(初回)から「インラインstyleをクラス化しろ」(3回目)まで。自分で書いたページの嘘を自分で見つけて直すのは、見ていて楽しい。
実験2: Nemotron 版 — モデルの性格がそのまま出る
同じ課題を Nemotron-Cascade-2-30B にやらせたら、Qwen が330行・16KBのリッチなページを作ったのに対し、Nemotron は 60行・2KBのミニマル版を出してきた。しかも自己採点では自分に ①100 ②100 を付けつつ講評では自分をディスるという情緒不安定っぷり。
そして Nemotron は修正作業で 同じ grep コマンドを50回繰り返すループにハマった。これは cagent 側にループブレーカー(同一コール3連続で警告注入)を実装する改善につながった。小さいモデルの失敗モードはハーネスで補える、というのが今日いちばんの学び。
もうひとつ正直に書くと、Nemotron は最終的に自分へ全軸100点を付けたが、機械検証したら viewport メタ欠落(モバイル表示が壊れる)と </section> 閉じ忘れが残っていた。ローカルLLMの自己採点は盛る。最後は grep での裏取りが要る。
実験3: クロス — Qwen が作り、Nemotron が審査する
組み合わせ版。Qwen3.6 が磨き上げた330行のページを Nemotron 審査員に渡したら、即・全軸100点を返してきて修正要求ゼロ。Qwen の自己採点ループが3ラウンドかけて磨いた成果が他モデルの審査を一発で通った、とも言えるし、Nemotron は審査が甘い(自分の60行ページにも100点を付けた前科がある)、とも言える。両方本当だと思う。
実験4: ローカルLLMに名建築を建てさせる
仕上げに、cagent の bash ツールから bim.house の MCP を curl で叩かせて、「bim.house にまだ無い有名建築を建てろ」と指示した。選ばれたのはミース・ファン・デル・ローエのバルセロナ・パビリオン(1929)。
結果: 建築基準法チェック 13/13 全PASS、再現度の自己採点 77点→93点(3ラウンド)。
ハイライトは全部失敗から来ている:
- 最初の一棟は要素スキーマを自己流で書いてサーバーに無視され、3mの謎の箱が建った。それを自分で検収して「これはパビリオンではない」と気づいた
- 審査員(同じLLM)が「実物に存在しない外周壁・天窓・装飾柱14個」を正しく検出した一方、「基壇は130mmであるべき」という事実誤認もした(実物は約1.3mの基壇)。審査員の指摘を鵜呑みにせず、事実で反論して却下する工程が必要だった
- 「法規対応で追加した要素(機械換気とか)は実物に無い」と正直にラベルに書く、という誠実さ軸を審査員が要求してきた。AIに誠実さを審査されるとは思わなかった
全プロセスの時系列はここ: パビリオン建設の全記録 完成品はここ: bim.house/property/u-ai-wey268i7
追記: 「もっと思考させて」で全部変わった
公開後、-t フラグ(thinking mode)を実装して同じ Nemotron 製ページを思考ありの Qwen 審査員に採点させ直した。結果:
| 審査員 | スコア |
|---|---|
| Nemotron(思考なし) | 100 / 100 / 100 / 100 / 100 |
| Qwen(思考あり) | 90 / 75 / 70 / 65 / 95 |
同じファイルである。しかも思考あり審査員は「<script> がボタンより先に実行されて言語切替が実際に壊れている」という機能バグを発見した。思考なしの審査員は2モデルとも見逃していた。トークンは5〜10倍食うが、審査には思考を付けろ、が結論。コマンド一つで賢くなるのだから安い。
ハマりどころ(自作する人へ)
- mlx-lm ≥0.31 は
<tool_call>をサーバー側でパースする。プロンプトだけのテキストプロトコルは空応答になる。toolsパラメータを送ってmessage.tool_callsを読むこと - UTF-8の途中で切るな。ツール出力を64KBで切るとき、日本語の途中で切ると不正バイト列になり、mlx サーバーのリクエストハンドラごと落ちる(実際に落とした)
finish_reason: "length"はリカバリ必須。max_tokens で切れた巨大 write_file は何も書いていないのに、モデルは書いたつもりになる- ローカルLLMはフロンティアモデルと違う失敗をする。検証コマンド無限ループ、API スキーマの捏造、JSON-RPC の書式忘れ。ハーネス側で「スキーマを渡す」「書き込みを100行以下に分割させる」「ループを検出して止める」と全部補える
成果物
- コード(MIT): github.com/yukihamada/cagent
- LLMが作った自己紹介サイト: Qwen版 / Nemotron版 / クロス版
- パビリオン: bim.house/property/u-ai-wey268i7 / 時系列
エージェントは魔法じゃなくて while ループ。それが今日いちばん伝えたいことです。