はじめに
AIにコードを書かせていると、あるタイミングでうまくいかなくなります。
最初はそれっぽいものが出てくるのに、少し規模が大きくなると急に破綻する。
命名が揃わなかったり、前に直したはずのところがまた壊れたり、そもそも仕様を無視し始めたりする。
モデルが賢くないのかというと、そういう感じでもない。
どちらかというと、「どう動かしているか」の問題に見えてきます。
何が起きているのか
例えば、AIにこう頼むとします。
ユーザー一覧APIを作って
それっぽいコードは出てきます。
ただ、そのあとで
- 別のエンドポイントを追加する
- レスポンス形式を変える
- テストを書く
みたいなことを続けていくと、だんだんズレてきます。
前提が揃っていないまま進んでいる感じです。
人間であれば、
「このプロジェクトではこういう書き方をする」
「この命名は避ける」
みたいな暗黙のルールがありますが、AIはそれを自然には持ちません。
ハーネスエンジニアリング
このあたりを整理するために出てきたのが、ハーネスエンジニアリングという考え方です。
言っていること自体はそこまで難しくなくて、
AIそのものを調整するのではなく、
AIが動く環境を整える、という話です。
どういう情報を渡すか、どこまでを前提として扱うか、どこで間違いを止めるか。
そういう部分をちゃんと設計する。
プロンプトだけでは足りない
少し前までは「いいプロンプトを書く」が主戦場でした。
ただ、エージェントとして長く動かすと、それだけでは持たない。
途中でコンテキストが抜けたり、前の状態と矛盾したまま進んだりする。
一回の応答としては正しくても、全体として見ると崩れていく。
このあたりは、プロンプト単体ではどうにもならないです。
役割が変わる
ここで面白いのが、人間の役割です。
以前はコードを書く側でしたが、少しずつ変わってきています。
何を書くかよりも、
どういうルールで動かすか
どこでチェックを入れるか
どういう状態を持たせるか
を決める方に寄っていく。
実際に、コード自体はほとんどAIに任せて、人間は周りだけを作る、という形も出てきています。
どう作るか
やっていることはそこまで特別ではなくて、
例えば
- 参照させるドキュメントを決める
- 出力をチェックする仕組みを入れる
- 状態をどこかに書き出させる
といったことを組み合わせていきます。
ここが曖昧だと、AIの出力も安定しません。
逆に、このあたりが揃っていると、かなり素直に動くようになります。
もう少し具体的に
例えば、APIを作らせるようなケースだと、こんな形になります。
まず、前提となるルールをまとめておきます。
- レスポンスはすべてJSON
- エラーレスポンスの形式は統一する
- 命名はcamelCase
これを毎回プロンプトに書くのではなく、ファイルとして持たせておきます。
次に、参照する情報を決めます。
/docs/api-spec.md
/docs/error-format.md
AIには「このあたりを見てから実装する」という前提を持たせます。
さらに、出力をそのまま使わないようにします。
例えば、
- 生成されたコードに対してテストを実行する
- Linterを通す
- フォーマットをかける
ここで問題があれば、その結果をもう一度AIに渡して修正させます。
もう一つ重要なのが状態の扱いです。
例えば、
/progress.md
のようなファイルを用意して、
- 何を実装したか
- 何が未完了か
を書き出させるようにします。
こうしておくと、次のタスクでも文脈を引き継ぎやすくなります。
ここまでやると、
「とりあえず生成する」から
「ルールの中で継続的に作る」
という動きに変わります。
まとめ
AIに何かをさせるときに、モデルそのものに目が行きがちですが、
実際にはその周りの作りの方が効いてきます。
ハーネスエンジニアリングは、その「周り」を扱うための考え方です。
ちゃんと動くようにするための土台、という位置づけに近いです。