AIエージェントを使って、情報収集やポッドキャスト制作などを自動化しています。
最初は、仕事に必要な能力を作り、それらを順番につなげればよいと考えていました。ところが、安定して動かすために条件や検査を足していくうちに、エージェントとワークフローが混じってしまいました。
たとえば、ある能力を別の仕事から呼び出そうとすると、元のワークフローの前工程や資料一式まで必要になる。一方で、実行を管理するはずの仕組みが、何を比較し、何を採用するかまで決めている。
動かすための修正はしているのに、直す場所が増えていく。今回の改造では、個別の不具合を直す前に、この責務の分け方を見直しました。
この記事では、その過程で苦労したことと、ほかのエージェント開発にも使えそうな知見をまとめます。特定のフレームワークの紹介ではなく、自作の仕組みを運用しながら境界を引き直した話です。
安定させようとして用途が能力に入り込んだ
情報収集、資料選定、台本作成といった能力は、本来いろいろな仕事で使いたいものです。
ただ、固定のワークフローを確実に通すには、その仕事特有の条件も必要です。どの資料を使うか、どんな制約を守るか、何がそろえば完了なのか。そこを曖昧にしたままでは、毎回違う結果になってしまいます。
問題は、その条件を能力そのものの必須条件へ寄せすぎたことでした。
説明のために単純化すると、「台本を作る能力」に「この番組の前回資料」「このワークフローの資料選定結果」まで固定してしまうような状態です。その番組では便利でも、別のテーマで一度だけ台本を作りたいときには使いにくくなります。
そこで、能力に普遍的な条件と、今回の仕事で必要な条件を分けました。
- 能力側は、正規の成果物を作るために最低限必要な入力と、返す成果物を定義する
- ワークフロー側は、今回使う資料、その役割、制約、前後の依存関係、完了条件を定義する
これは条件を緩くする変更ではありません。条件の置き場所を変える変更です。固定ワークフローでは今までどおり厳密な条件を渡し、別の用途ではその用途に必要な条件を渡せるようにします。
何を判断するかで境界を引く
整理するうえでは、主に次の三つを分けて考えると分かりやすくなりました。
| 部分 | 主な役割 |
|---|---|
| 能力を実行するエージェント | 内容を読み、比較し、採否や理由を判断し、成果物を作る |
| ワークフロー | 入力の供給元や制約、工程の依存関係、仕事全体の完了条件を決める |
| 実行基盤 | 権限、予算、入力の配送、契約検査、正式な保存、実行状態を管理する |
実装では、能力をCapability、実行基盤をRunnerなどと呼んでいます。ただし、名前やフォルダを分けるだけでは不十分でした。
特に苦労したのは、実行基盤に入り込んだ業務判断を取り除くことです。情報収集では、基盤側に比較の組合せや段階的な処理計画、採否や理由の書換えまで入り込んでいました。これではエージェントに判断を任せているつもりでも、実際には基盤が仕事の進め方を強く決めています。
そこで、探索や比較の計画、採否の判断は能力側へ戻し、基盤には実際に渡した入力、消費した予算、保存した結果などを確実に扱う責任を残しました。
能力の内部には、決定的な処理や再利用できるツールがあって構いません。すべての処理をLLMに判断させる必要はありません。重要なのは、その能力の仕事に属する判断と、実行全体を守る管理が混ざらないことです。
検査を増やせば安全になるとは限らなかった
境界の難しさが表れた例に、取得した本文の判定がありました。
実行基盤の正規表現が、記事中に引用されているHTMLを確認画面だと判定し、読める資料を拒否してしまうことがありました。確認画面そのものなのか、それを解説した記事なのかは、文字列があるだけでは判断できません。
この判定を見直し、基盤ではHTTPのステータスや応答が空かどうかなど、観測できる事実を扱うようにしました。本文の意味を読んで利用できるか判断する役割は、能力側へ戻しています。
ここで学んだのは、検査と判断の境界も設計する必要があるということです。
形式や配送の検査を厳しくすることには意味があります。ただ、その中へ内容の解釈を紛れ込ませると、同じ資料を基盤とエージェントが別々に判断する構造になってしまいます。
入力を渡したことと使えたことを分ける
責務を分けると、次に「必要な入力を本当に使ったのか」が気になります。
ここでも、一つの成功判定にまとめるとうまくいきませんでした。分けて確認したいのは、次の三つです。
- 必要な内容が、実際の呼出しへ欠けずに渡ったか
- エージェントが、何を使い、何を確認したうえで使わなかったと報告したか
- その内容や制約が、成果物へ適切に反映されているか
配送記録やハッシュは、一つ目を確認する材料になります。しかし、それだけで理解を証明できるわけではありません。エージェントの「読んだ」という報告も、成果物の品質そのものを保証しません。
実際の再監査では、必要な索引が配送・報告されていなくても正常完了できる経路が見つかりました。途中の呼出しで選んだ入力と、仕事全体で供給された元の入力を混同していたことが原因の一つです。
そのため、途中の応答はその呼出しの入力に対して確認し、仕事の完了時には元の入力全体に対して確認するようにしました。
また、入力が任意であることと、渡された入力を確認しなくてよいことも分けています。私の仕組みでは、参考資料も確認したうえで採否を判断する契約にしました。「取得できなかった」と「確認して不採用にした」は、別の結果として残します。
失敗を直す場所を間違えない
失敗すると、とにかく最後まで通るようにしたくなります。ただ、その修正を実行基盤へ集めると、また責務が混ざり始めます。
今回、特に意識したのは、足りない判断や報告を基盤が補って、つじつまを合わせないことでした。成果物を作れなかった場合には、能力側が理由を返す。形式が不正なら、基盤が拒否する。その不正な応答も、確認できた範囲では診断用に残しますが、正式な成果物としては採用しません。
予算上限にも、同じような切り分けが必要でした。
情報収集で20件を上限にしたとき、19件まで進んだ状態から2件まとめて取得しようとして、全体が停止する例がありました。制限したかったのは新しく取得する候補数なのに、取得済みの情報を評価する仕事まで止めてしまっていたわけです。
そこで、新規取得の残り枠を確認し、上限に達したら新しい取得だけを閉じ、取得済み候補の評価へ戻れるようにしました。
上限を設けること自体より、何の上限なのかを明確にすることが大切でした。一件の取得失敗、仕事全体の予算超過、権限の問題では、止める範囲も、扱い方も違います。
完了にもいくつかの段階がある
「成功」の意味も分け直しました。
応答が返ってきたこと、成果物が契約を満たしたこと、保存が確定したこと、ワークフロー全体が完了したことは、それぞれ別です。公開を伴う仕事なら、公開先で実物を確認できたかまで必要になります。
たとえばポッドキャストでは、音声ファイルができたことやアップロード処理が終わったことだけでなく、公開結果を確認してから後続の整理へ進むようにしています。
また、実行記録の整合性を検証する仕組みがPASSしても、仕事そのものが成功したとは限りません。失敗した仕事でも、その失敗を正しく記録して終了できていれば、記録の検証には通ります。
テストにも似た落とし穴がありました。実装が作った入力一覧を、そのままテストの期待値にも使うと、両方が同じ入力を見落とします。期待値は元のテストデータから独立に決め、実際の配送や保存と照合する必要がありました。
今回の一区切りと残っていること
今回の改造後、情報収集、日次のWiki新聞、日記分析、ポッドキャスト制作の代表的なワークフローを通すことができました。
ただ、これですべての異常系や成果物の品質まで保証できたわけではありません。大きな構造変更はいったん区切り、実際に動かしながら修正を続ける段階だと考えています。
今回、特に次の開発へ持ち越したいのは、次の点です。
- 能力の最小条件と用途の条件を分ける。 固定ワークフローの厳密さを、すべての呼出しへ背負わせない
- 判断の所有者を決める。 内容の採否や理由を、実行基盤が代わりに作らない
- 証拠の種類を分ける。 渡した事実、利用したという報告、成果物の品質を、それぞれ確認する
- 失敗の範囲を保つ。 一件の失敗と全体の異常を混同せず、確認できなかったことを成功に含めない
- 検証を独立させる。 実装自身の出力だけを、正しさの根拠にしない
エージェントとワークフローを組み合わせると、便利にするための処理をどこへ置くか迷う場面が増えます。そのとき、単にコードを分けるのではなく、「誰が判断し、誰が事実を記録し、誰が結果を確定するのか」を確認する。
今回の改造では、この問いに立ち返ることが、場当たり的な修正を減らすために役立ちました。