「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回目8885888292
2回目9590100100100
3回目1001009595100

ラウンドを追うごとに指摘が本質→些末に移っていく。「行数表記が実物と違う」(初回)から「インライン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倍食うが、審査には思考を付けろ、が結論。コマンド一つで賢くなるのだから安い。

ハマりどころ(自作する人へ)

  1. mlx-lm ≥0.31 は <tool_call> をサーバー側でパースする。プロンプトだけのテキストプロトコルは空応答になる。tools パラメータを送って message.tool_calls を読むこと
  2. UTF-8の途中で切るな。ツール出力を64KBで切るとき、日本語の途中で切ると不正バイト列になり、mlx サーバーのリクエストハンドラごと落ちる(実際に落とした)
  3. finish_reason: "length" はリカバリ必須。max_tokens で切れた巨大 write_file は何も書いていないのに、モデルは書いたつもりになる
  4. ローカルLLMはフロンティアモデルと違う失敗をする。検証コマンド無限ループ、API スキーマの捏造、JSON-RPC の書式忘れ。ハーネス側で「スキーマを渡す」「書き込みを100行以下に分割させる」「ループを検出して止める」と全部補える

成果物

エージェントは魔法じゃなくて while ループ。それが今日いちばん伝えたいことです。