🛳️

アルカノイド風ブロック崩し / コーディングエージェント / サイバネティクス

に公開

コーディングエージェントの動き方とサイバネティクス

コーディングエージェントは、こんなふうに動くようです。

  • 人間が描いたプロンプト(とシステムプロンプト)がモデルに渡される。
  • モデルが推論した結果に対して「何かする」。
  • そしてその結果が、また、モデルの推論の入力になる。

というのを繰り返して、最終的なレスポンスが生み出されます。
「何かする」というのは、典型的にはbashを操作して何らかのコマンドを実行することですが、他にもmcpの呼び出しとか色々ある。大事なのはその何かした結果が、またモデルの入力になる、ということです。

われわれが、与えられた一つの型通りに、あるものに運動を行わせようとするとき、その運動の原型と、実際に行われた運動との差を、また新たな入力として使い、このような制御によってその運動を原型にさらに近づけるということである。
――ノーバート・ウィーナー『サイバネティックス――動物と機械における制御と通信』池原止戈夫・彌永昌吉・室賀三郎・戸田巌 訳、岩波文庫、2011年、pp.36

ウィーナーの「『サイバネティックス』では、フィードバックについて上のように説明しています。
この定義を使って、ちょっと強引にコーディングエージェントの動きに説明してみると、以下のようになります。

  • 運動を行わせようとする ... これがプロンプト
  • 運動の原型 ... プロンプトによって実現したいこと
  • 実際に行われた運動 ... モデルによる推論の出力に基づいて「何かした」結果
  • 差を、また新たな入力として使い、このような制御によってその運動を原型にさらに近づける ... 出力をまたモデルの入力にして「差」がなくなったと判断したら、レスポンスを返す。

つまり、コーディングエージェントはフィードバックループというかたちで駆動されている。

では、サイバネティクスとは何でしょうか。以下も引用です。

われわれの状況に関する二つの変量があるものとして、その一方はわれわれには制御できないもの、他の一方はわれわれに調整できるものであるとしましょう。そのとき制御できない変量の過去から現在にいたるまでの値にもとづいて、調節できる変量の値を適当を定め、われわれに最もつごうのよい状況をもたらせたいという望みがもたれます。それを達成する方法がCyberneticsにほかならないのです。
――ノーバート・ウィーナー『サイバネティックス――動物と機械における制御と通信』池原止戈夫・彌永昌吉・室賀三郎・戸田巌 訳、岩波文庫、2011年、pp.4-5

  • 調節できる変量 ... 広い意味でのプロンプト
  • 制御できない変量 ... モデルによる推論の出力
  • 最もつごうのよい状況 ... いわゆるDefinition of Doneが満たされること

「最もつごうのよい状況をもたらせたいという望み」を実現するための方法がサイバネティクスである。とすれば、私たちが、ここ数年、探ってきたCoding Agentのためのエンジニアリング手法とはサイバネティクスの具象である、と言えそうです。

脱線 ... サイバネティクスとkubenetesは語源が一緒

この記事を書くまで知らなかったんですが、サイバネティクスとkubenetesは語源が一緒なんですね。どちらもギリシャ語の κυβερνήτης(kybernētēs)、つまり「舵取り」「操舵手」に由来するそうです。

kubenetesは、以下のように整理できそう。

  • 制御できない変量 ... トラフィックの急増、ノードの障害、リソースの枯渇
  • 制御できる変量 ... Podのレプリカ数、リソース割り当て、スケジューリングなど。マニフェストに定義される。
  • 最もつごうのよい状況 ... いわゆるdesired state

そして「desired state」を維持するために、reconciliation loopが働くわけですが、これは、先に挙げたフィードバックループそのものです。

サイバネティクスの具象としてのPrompt / Context / Harness Engineering

Prompt / Context / Harness Engineeringは「われわれに最もつごうのよい状況をもたらせたいという望み」を実現するための手法と捉えることができる。

Prompt Engineering

Prompt Engineeringは、この記事では、包括的なコンセプトである、と考えておきます。抽象としては、Context / Harness Engineeringと排他的な関係にあるのではなく、包括する概念だということです。
一方、具体で考えた場合は、Coding Agentの入力になる事前に定義していない文言をいかに記述することで効果的に望ましい結果を得るか、をめぐる方法論ということではないか。

Context Engineering

Context Engineeringは、プロンプトと同時に、定義された文書を渡すことでCoding Agentにより望ましい振る舞いをさせるための方法論としましょう。
典型的には、ClAUDE.mdやAGENTS.mdとそこから参照される規約や要件に関する文書をどう整備するのか、ということをめぐる方法論です。

Harness Engineering

Harness Engineeringについての記事を読むと「エージェントが自己修正する仕組み」や「エージェントが二度と同じ失敗をしないための仕組み」というようなコンセプトが盛り込まれています。
おそらく、このあたりに、ハーネスという言葉が言わんとしていることがありそうです。

この記事では、サイバネティクスの言葉に寄せて定義をしてみたい。つまり、Harness Engineeringは、Context Engineeringの一種ではあるが、「負のフィードバックが発生するように明示的にコンテキストを構造化している」手法である、と考えてみます。

プランを作成しtodoを作っていくフェーズや、コードをどんどん出力していくフェーズはコンテキストが膨張していきます。間違った方向に進んでいる可能性もある。発散していくフェーズと言えるかもしれません。

「負のフィードバック」は、反対に収束として考えられます。誤差信号を捉えて、その信号がなくなることで目的に近づけていくフェーズです。誤差信号とは、例えば、「プラン」に対する「考慮もれ」であったり、「生成したコード」の「単体テストエラー」や「lintの失敗」です。

「負のフィードバック」が適切に動作することで生成のフェーズの行き過ぎや失敗をおさえるようにコンテキストが構造化されているのがHarness Engineeringである。こう考えると、Harness Engineeringが最も色濃くサイバネティクスを具体化している、という感じがしますが、どう思いますか。

ブロック崩しを作る

突然ですが、ブロック崩しゲームを作ろうと思うんです。

渡すプロンプトは、「ebitengine を使って楽しく遊べるブロック崩しゲームを作ってください」
ebitengineは、golang向けのシンプルなゲームエンジン。

ただし、つぎの4つのパターンでやっています。

  1. Prompt Engineering
    • プロンプトしか渡さない
  2. Context Engineering その1
    • golangのコーディングスタイルの規約とebitengineのベストプラクティスをコンテキストにふくめる
  3. Context Engineering その2
  4. Harness Engineering
    • Context Engineering その2のコンテキストでskill chainを使って作るように指示

使うのは、Claude Code。そして、セッションログを取り出して、https://claude.ai/ に貼り付けていろいろ質問してみます。
※実際に出来上がったもののplay動画を.gifにして貼り付けてあります。

1. Prompt Engineering

項目
入力トークン 272,960
出力トークン 8,041
ループ回数 8
thinking回数 2
backtracking回数 0
作成ファイル数 1

※thinking ... モデルが出力を生成する前の内部推論
※backtracking ... thinkingの中で一度決めたことを覆すこと

とてもシンプルな出来上がりです。コードもmain.goひとつに収まっています。8回のループは以下の通り。

  1. ls — ディレクトリ確認
  2. go version && go mod init — Go確認&モジュール初期化
  3. go get ebiten — 依存取得
  4. Write main.go — 1ファイルにゲーム全体を一気に書き出し
  5. go build — ビルド確認
  6. Edit main.go — テキスト表示の改善
  7. go build — 再ビルド確認
  8. go run — 実行テスト

main.goは、一回の出力で作られているようです。面白いのは、6. でちょっと表示の調整をしている。つまり、特に明示的に指示しなくても負のフィードバックが働いている。コーディングエージェント単体のサイバネティクス機構が垣間見えますね。

ゲームとしては単調ですね。

2. Context Engineering その1

以下のCLAUDE.mdを作成します。 CLAUDE.mdから参照しているのは、両方ともhttps://claude.ai/ で作成しました。

# breakout
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## docs
@docs/ebitengine-best-practices.md 
@docs/go-coding-conventions.md
  • docs/ebitengine-best-practices.md ... ebitengineのベストプラクティスのmd
  • docs/go-coding-conventions.md ... effective goを参照して作ってもらったgoのコーディング規約のmd

この時点では、ブロック崩しゲームの要件はひとつもプロンプトに含まれていないです。

セッションログから見える各種項目の値は以下の通り。

項目
入力トークン 1,269,234
出力トークン 46,185
ループ回数 14
thinking回数 3
backtracking回数 21
作成ファイル数 6

ループの仕事内容は以下

  • ループ1から3まで ... CLAUDE.mdと参照先のコンテキストを読み込んで設計を考える。実は、出力のトークンの3/4はここで消費されていた。
  • ループ4から11 ... 開発環境構築とebiファイル作成とコーディング。6ファイルを1つずつ連続作成。
  • ループ12から14 ... go buildで確認。go runで起動確認。

面白いのは、一切ゲームの仕様に言及していないのに、じょじょにボールが速くなるという仕様が勝手に盛り込まれている、という点です。
どうやら、CLAUDE.mdを渡したのに起因して、思考が深くなった → その過程でたまたまゲームデザインについても考える時間が生まれた → 速度上昇の仕様が出てきたの仕様が出てきた。という流れのようです。セッションログをみると以下が出力されています。

"Now I'm thinking about what makes this fun—satisfying physics when the ball bounces, some visual feedback like a flash when blocks break, maybe a combo system for hitting multiple blocks in succession, speed ramping as you clear blocks"

"I could add a speed indicator or even a combo multiplier for breaking consecutive bricks, but I think keeping it classic and clean is the better approach here."

どうやら、**「楽しく遊べるブロック崩しゲーム」**というプロンプトの一部が深くなった思考と合わさって、ある種の創造性を発揮したということのようです。

この「良かれと思って」系追加仕様の業務アプリケーション開発だと、往々にして「なんで勝手にそんなことを、、、」となるわけですが、ゲームだと、素直に興味深いと思えます。

一方、backtracking回数が多い。どうやら作るのにだいぶ迷っているみたいですね。明確にどんなものが欲しいかを伝えていないので、創造性は発揮したがゆえに迷いも多かった、という感じでしょうか。

3. Context Engineering その2

さて、アルカノイドです。まさか、たった数文字タイピングしただけでアルカノイドが作れる時代になるなんて。

CLAUDE.mdには、@docs/arkanoid.mdを追加しています。これは、https://claude.ai/アルカノイドのWikipediaを.mdに変換したものです。

# breakout
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## docs
@docs/ebitengine-best-practices.md 
@docs/go-coding-conventions.md
@docs/arkanoid.md

アルカノイド風の仕様になっています。

  • 全部で5ステージ。
    • ステージごとにブロックの配置が違う
  • アイテムが降ってくる
    • ボールの分裂
    • バーのワイド化
    • ボールのスローダウン
    • レーザービーム

セッションログから見える各種項目の値は以下の通り。

項目
入力トークン 1,698,348
出力トークン 20,161
ループ回数 25
thinking回数 2
backtracking回数 0
作成ファイル数 12

興味深いのは、前述のContext Engineering その1よりも、入力のトークンは増えているのに出力のトークンが半分以下になっている、ということです。これはなぜなのか。
その1の方は、「何を作るか」を自分で考える必要があったのです。それゆえ、thinking回数も増え、backtrackingも多数発生してしまった。
その2は何を作るかが決まっているので、それをTodoWriteでtodoに落とし込んでそのまま実装すればよかった。出力のトークンが小さく済んだのでした。
コンテキストを整えて、AIの考える負荷を下げてあげることの効果がよくわかりますね。
コンテキスト(や次にみるハーネス)はAIに何を考えさせて、何を考えさせないか、の境界として働く、と理解することもできそうです。

さて、先のgifを全部見ればわかるんですが、バグってます。ボールをキャッチするアイテムを手に入れて、ボールをキャッチすると、キャッチしたボールがリリースできません。
そして、なんとレーザービームのアイテムを手に入れてもレーザーが一発しか打てない、、、という。

これは、「ランタイムの振る舞いに対する負のフィードバック」が全く働いていない、ということです。複雑になったコンテキストに対応できる、品質管理の仕組みがない。

4. Harness Engineering

CLUADE.mdは、前述と同じです。
違うのは、以下のようにSkill Chainを組みこのchainによって開発を行ったという点です。

writing-plan => check-plan => builder => qa

計画を作る=> 作った計画を検証 => 開発を行う => 開発の結果を検証 という流れです。
二つの検証スキルを入れることで、「評価と修正のフィードバックが発生するように明示的にコンテキストを構造化している」というこの記事におけるHarness Engineeringの定義を満たすようにしています。Skill Chain以外の方法で実現する方法もあると思います。が、説明のために一番直感的にわかりやすそうなSkill Chainを選びました。

キャッチしたらリリースできないバグは修正されました。
レーザーのバグも修正されました。あのレーザーの連射がアルカノイドの醍醐味でしたので、これである程度楽しく遊べそう。

セッションログから見える各種項目の値は以下の通り。

項目
入力トークン 7,654,652
出力トークン 50,677
ループ回数 59
thinking回数 14
backtracking回数 2
作成ファイル数 16

skillがどう動いたのか確認してみると以下の通り。

qaからbuilderへ差し戻しが発生しています。これは、どうやら、builderがシナリオテストのコードを書きそびれていたことを指摘したようです。
このような差し戻しは、正常に「負のフィードバック」が動作した、という意味では良いのです。が、同じ失敗を繰り返さないためには、builderが一回でパスできるようにハーネスを強化するのが、大事です。

この場合、なぜ、このミスをしたのか、どんなスキル/ハーネスがあれば、このミスを回避できたのか、という対話を繰り返して、コーディングエージェント自身に追加のスキル/ハーネスを修正してもらう、というやり方が想定されます。

これをもっと構造的にやるなら、「スキル/ハーネス/セッションログ読み込んで、ある程度複雑な機能実装を効果的、効率的に実現したかを評価する」スキルを使って、評価し、そのフィードバックをスキル/ハーネスに反映する、というある意味ではメタ的なフィードバックループを組んでみる、ということになるのでしょうか。これも自動化できると面白いかもしれません。

一方で、1. Prompt Engineeringで見たように、本来モデル自体にも負のフィードバックをトリガーする能力は含まれています。
したがって、いたずらにスキル/ハーネスを強化し続けた結果、本来モデルが担える部分もスキル/ハーネスに盛り込んでしまい、単にエージェントの動きがもっさりするだけで益なし、ということになりかねない。
この「モデル任せる or スキル/ハーネスに書く」の境界面を探る上手な方法を考えてみたいです。

まとめ

  • コーディングエージェントはフィードバックループとして動作している。
  • Prompt / Context / Harness Engineeringはサイバネティクスの具体的な方法論と捉えることができる。
  • とりわけ、Harness Engineeringは、もっとも色濃くサイバネティクスを体現しているんじゃないか。
  • コンテキストとは、「コーディングエージェントに思考してほしいこと」と「コーディングエージェントの思考の前提となること」の境界として働く。
    • この境界が曖昧だとコーディングエージェントの思考過負荷が発生し、それはセッションの長時間化と出力トークンの増大をもたらす。
  • 良いコーディングエージェントは「生成のフェーズ」と「負のフィードバック」のバランスしているのではないか。
    • 負のフィードバックが強すぎると過度に保守的になるが、生成のフェーズに手綱がなければ暴走するかもしれない。
  • コーディングエージェントのセッションログをみると、いろいろなことがわかって楽しい。
  • スキル/ハーネスを自動的に強化するメタ的なフィードバックループの可能性。
  • スキル/ハーネスの過剰をどう防ぐ?

Discussion