Open16

12-Factor Agents - Principles for building reliable LLM applications

じゅっじゅおーじゅっじゅおー

ターゲット

https://github.com/humanlayer/12-factor-agents

とりあえずざっと要約させていきながら学ぶスタイルでのスクラップ

全体の要約

「12-Factor Agents」は、現代のソフトウェア開発で広く受け入れられているベストプラクティス集「The Twelve-Factor App」の原則を、LLM(大規模言語モデル)を搭載したAIエージェントの開発に応用するための方法論です。デモやプロトタイプの段階では魔法のように見えるAIエージェントも、実世界の複雑な要求に応える本番環境で、信頼性、拡張性、保守性を確保しながら運用するには、堅牢なソフトウェアエンジニアリングの原則が必要不可欠であるという思想に基づいています。

この方法論の核心は、LLMの持つ不確実性や曖昧さを可能な限り排除し、システムの振る舞いを決定論的かつ予測可能にすることにあります。具体的には、ユーザーからの曖昧な自然言語入力を、まず明確に定義された「ツール呼び出し」という構造化データに変換する(Factor 1, 4)ことを徹底します。これにより、LLMの役割を自由な思考から、より限定的で制御しやすいタスクへと絞り込みます。

また、エージェントの振る舞いを決定づけるプロンプト(Factor 2)や、LLMの思考の土台となるコンテキストウィンドウ(Factor 3)、そしてタスクの実行順序を定義する制御フロー(Factor 8)を、外部のフレームワーク任せにせず、開発者が完全に所有し、コードとしてバージョン管理することを強く推奨します。これは、AIアプリケーションの心臓部をブラックボックス化させず、テスト可能で再現性の高い状態に保つためです。

さらに、エージェントの実行状態をアプリケーションのビジネス状態と統一的に管理し(Factor 5)、長時間のタスクでも安全に起動・一時停止・再開できる仕組み(Factor 6)を設けることで、運用の柔軟性を高めます。判断が困難な場合やエラー発生時には、エージェントが自律的に人間に助けを求めたり(Factor 7)、エラー情報を要約して自己修正を試みたりする(Factor 9)ことで、人間とAIが協調して問題を解決する堅牢なループを構築します。

アーキテクチャとしては、一つの万能エージェントではなく、特定の責務に特化した小さく焦点を絞ったエージェント(Factor 10)を複数組み合わせることを推奨し、各エージェントが状態を持たない純粋な関数(Stateless Reducer, Factor 12)として設計されるべきだと説きます。これにより、システム全体の複雑さが低減され、テストと再利用が容易になります。

総じて、「12-Factor Agents」は、LLMという強力だが予測困難なコンポーネントを、従来のソフトウェア開発の原則と統合し、エンタープライズレベルの要求に応えうる、真にプロダクションレディなAIアプリケーションを構築するための羅針盤となるものです。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/brief-history-of-software.md

ソフトウェアの簡単な歴史 (A Brief History of Software)

本章では、現代のAIエージェント開発が直面する課題を、ソフトウェア開発全体の歴史的文脈から捉え直します。かつてソフトウェアが物理的なハードウェアや特定のOSに強く依存していた時代から、コンテナ化や「Infrastructure as Code」のような宣言的なアプローチへと進化し、環境間の差異を吸収してポータビリティとスケーラビリティを獲得してきた歴史を振り返ります。この進化のアナロジーとして、現在のLLMアプリケーション開発もまた、特定のモデルや曖昧なプロンプトへの依存から脱却し、より構造化され、宣言的で、信頼性の高いアーキテクチャへと移行する必要があると論じます。「12-Factor Agents」の各原則は、この歴史的必然とも言える進化のステップを、AIエージェント開発において具体化するための指針として提示されています。

じゅっじゅおーじゅっじゅおー

結局はDAG

エージェントパターン
決定論的なDAGに入れ込む

よく言われるけど、AIの正確性が90%だとしてもそれは顧客に渡せるほどの精度とは程遠い
精度落としたサービスでも、10回に1回クラッシュするwebサイトは使えないという例が挙げられてたが、その感覚はわかる
それをより高い精度にするために、決定論的なコードの中にエージェントを押し込んだりHITLで制御する

じゅっじゅおーじゅっじゅおー

AIエージェントを精度高く顧客に提供するためには、以下に不確実性を極小化して閉じ込めるか
外部からクラッシュしたり終わらない時のフローを安全に制御できるか

これまでの大規模サービスを設計、構築してきたソフトウェアエンジニアリングの力はまだまだ適用できる
それをプロダクトにどんどん組み込んでいくために実験をしていく必要がある
AIを社内だけのツールで利用する、開発の生産性を高めるために利用するというのは使っていけば良いため比較的誰でもできるようになるが、プロダクトとして大々的にAIエージェントですっていってちゃんとしたものを出しているところはまだ少ないから、先を見据えてそこに投資する必要はある

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-01-natural-language-to-tool-calls.md

Factor 1: 自然言語をツール呼び出しへ (Natural Language to Tool Calls)

この原則は、AIエージェントの信頼性を確保するための最も基本的な出発点です。ユーザーからの入力は曖昧さを含む自然言語ですが、これを直接LLMに解釈させて複雑なタスクを実行させようとすると、結果が不安定になりがちです。そこで本原則では、ユーザーの意図をまず最初に、明確に定義された構造化データである「ツール呼び出し(Tool Call)」に変換することを提唱します。例えば、「今日の東京の天気はどう?」という自然言語は、get_weather(city="Tokyo", date="today")のような、プログラムで確実に処理できる形式に変換します。この変換ステップにLLMの能力を集中させることで、その後の処理は決定論的なコードで実行できるようになり、エージェント全体の動作が予測可能かつデバッグしやすくなるという大きな利点があります。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-02-own-your-prompts.md

Factor 2: プロンプトを所有する (Own Your Prompts)

プロンプトは、LLMの振る舞いを指示する「ソースコード」そのものであると本原則は主張します。多くのフレームワークはプロンプト生成を内部で隠蔽しがちですが、これに頼ると、モデルのアップデートや予期せぬ入力への対応が困難になります。そこで、プロンプトをアプリケーションの第一級の構成要素として扱い、開発者が完全に制御下に置くことを要求します。具体的には、プロンプトをテキストファイルや設定ファイルとしてコードベースに含め、バージョン管理システム(Gitなど)で変更を追跡し、単体テストや回帰テストの対象とします。これにより、プロンプトの品質を継続的に改善し、アプリケーションの振る舞いを安定させることが可能になります。プロンプトエンジニアリングを、場当たり的な調整作業から、体系的なソフトウェア開発プロセスへと昇華させるための重要な原則です。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-03-own-your-context-window.md

Factor 3: コンテキストウィンドウを所有する (Own Your Context Window)

LLMが思考するための短期記憶に相当するのがコンテキストウィンドウです。この限られたスペースにどのような情報を、どのような順序で、どの程度の詳細さで含めるかは、エージェントの性能に決定的な影響を与えます。本原則は、このコンテキストウィンドウの構築をフレームワーク任せにせず、開発者が戦略的に管理・所有することの重要性を説きます。過去の対話履歴、ツール実行の結果、発生したエラーの要約、関連ドキュメントの抜粋など、タスクの遂行に本当に必要な情報だけを厳選し、圧縮してLLMに提供する必要があります。不適切な情報(長すぎるエラーログや無関係な履歴)は、LLMを混乱させ、性能を低下させる「コンテキスト中毒」を引き起こしかねません。コンテキストの管理は、単なる技術的な作業ではなく、エージェントの知性を設計する中核的な活動であると位置づけられます。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-04-tools-are-structured-outputs.md

Factor 4: ツールは構造化された出力 (Tools are Structured Outputs)

エージェントが現実世界とやりとりするための手足となるのが「ツール」です。本原則は、このツールが返す結果は、必ずJSONなどの機械可読な構造化データでなければならないと定義します。LLMが自然言語で書かれたツールの実行結果を解釈しようとすると、誤解や情報の欠落が生じるリスクがあります。例えば、API呼び出しの結果が成功したのか失敗したのか、失敗した場合はその理由は何なのかを、明確なフィールド(例:{"status": "error", "reason": "API_KEY_INVALID"})で返すようにツールを設計します。これにより、エージェントはツールの実行結果を確実に理解し、エラーからの回復や次のアクションの決定を、より信頼性の高い方法で行うことができるようになります。ツールからの出力を構造化することは、エージェントのワークフロー全体を堅牢にするための基礎となります。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-05-unify-execution-state.md

Factor 5: 実行状態とビジネス状態を統一する (Unify Execution State)

AIエージェントは、複数のステップからなるタスクを実行する過程で、様々な「状態」を持ちます。これには、「次にどのツールを呼び出すべきか」といった実行状態と、「ユーザーの注文がどの段階にあるか」といったアプリケーション固有のビジネス状態の両方が含まれます。本原則は、これら二種類の状態を別々に管理するのではなく、単一の、統一されたデータ構造で表現することを要求します。これにより、状態の不整合が起こるのを防ぎ、現在のエージェントの状態をスナップショットとして永続化(保存)しやすくなります。状態が統一されていれば、タスクの途中で問題が発生した際に、その状態を正確に記録・再現してデバッグすることが格段に容易になります。また、監査やロギングの観点からも、一貫した状態管理は不可欠です。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-06-launch-pause-resume.md

Factor 6: 起動・一時停止・再開 (Launch, Pause, Resume)

AIエージェントが処理するタスクは、単純な質疑応答と異なり、数分から数時間、あるいは数日に及ぶことがあります。このような長時間タスクを、単一のリクエスト/レスポンスサイクルで処理するのは非現実的です。本原則は、エージェントの実行を、単純なAPI(例:start(), pause(), resume())を通じて外部から制御できるように設計することを要求します。これにより、例えばユーザーからの追加情報が必要になった時点でタスクを一時停止させたり、外部システムの準備が整うのを待ってから再開させたりといった、柔軟な運用が可能になります。このアーキテクチャは、エージェントの実行プロセスをステートフルなものとして捉え、そのライフサイクルを明確に管理することで、システムの信頼性と対話性を向上させるための重要な鍵となります。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-07-contact-humans-with-tools.md

Factor 7: ツールで人間にコンタクトする (Contact Humans with Tools)

完全に自律的なエージェントは理想ですが、現実には、倫理的な判断、最終承認、あるいは予期せぬ事態への対応など、人間の介入が必要な場面が必ず存在します。本原則は、人間への問い合わせを、他のAPIやデータベースを呼び出すのと同じ「ツール」としてモデル化することを提唱します。例えば、ask_human_for_approval(question="...")というツールを定義し、エージェントは必要に応じてこのツールを呼び出します。すると、システムは人間に通知を送り、その応答をツールの返り値としてエージェントに渡します。この「Human-in-the-Loop」のアプローチをツールとして抽象化することで、エージェントは人間との協力を自身のワークフローに自然に組み込むことができ、より安全で信頼性の高いシステムを構築することが可能になります。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-08-own-your-control-flow.md

Factor 8: 制御フローを所有する (Own Your Control Flow)

多くのエージェントフレームワークは、「次に何をすべきか」という判断(制御フロー)をLLMに委ねるアプローチを取りますが、これは予測不能な振る舞いや無限ループのリスクを伴います。本原則は、これとは対照的に、タスクの主要なワークフローやロジックは、開発者が書いた決定論的なコードによって制御されるべきだと主張します。LLMの役割は、この制御フローの特定の分岐点で判断を下したり、必要な情報を生成したりすることに限定します。例えば、一連のステップをグラフ構造(ステートマシン)としてコードで定義し、各ステップ間の遷移条件の判断をLLMに任せる、といった形です。このように、アプリケーションの骨格をコードで固め、LLMを知的なコンポーネントとしてその中に組み込むことで、エージェントの振る舞いを予測可能でデバッグしやすいものに保つことができます。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-09-compact-errors.md

Factor 9: エラーを圧縮してコンテキストに含める (Compact Errors)

ツール実行時などにエラーが発生した際、その詳細なスタックトレース全体をそのままLLMのコンテキストウィンドウに含めるのは非効率的です。長大なエラーメッセージは貴重なコンテキストスペースを消費し、LLMを混乱させる可能性があります。本原則が提唱するのは、エラー情報をLLMが理解し、対処しやすいように、簡潔で要点を押さえた形式に「圧縮」することです。例えば、「APIキーが無効です」や「ファイルが見つかりません」といった、根本原因を示す短いメッセージに要約します。この圧縮されたエラー情報をコンテキストに含めることで、LLMは問題の原因を素早く特定し、「別のAPIキーを試す」「ファイルパスを修正する」といった具体的な自己修正アクションを生成する可能性が高まります。これは、エージェントに効果的な自己回復能力を持たせるための重要なテクニックです。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-10-small-focused-agents.md

Factor 10: 小さく、焦点を絞ったエージェント (Small, Focused Agents)

あらゆるタスクをこなせる単一の巨大なモノリシック・エージェントを構築しようとする試みは、多くの場合、複雑性の増大と信頼性の低下につながります。本原則は、UNIX哲学の「一つのことをうまくやれ」という思想に基づき、各エージェントが単一の、明確に定義された責務を持つように設計することを推奨します。例えば、「顧客からの問い合わせ内容を分類するエージェント」「データベースから顧客情報を検索するエージェント」「回答メールを作成するエージェント」のように、タスクを分解し、それぞれを専門とする小さなエージェントに担当させます。これらの小さなエージェントを連携させることで、より複雑なタスクを達成します。このアプローチにより、各エージェントは独立して開発、テスト、デプロイが可能になり、システム全体の保守性と再利用性が大幅に向上します。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-11-trigger-from-anywhere.md

Factor 11: どこからでもトリガーする (Trigger from Anywhere)

AIエージェントの用途は、チャットインターフェースを通じたユーザーとの対話に限定されません。本番システムでは、様々なイベント駆動でエージェントを起動する必要があります。本原則は、エージェントが、多様なトリガーに対応できるように設計されるべきだと要求します。具体的には、従来のHTTPリクエスト(APIコール)やユーザーからのメッセージだけでなく、データベースの更新を検知するWebhook、定期実行されるcronジョブ、メッセージキューからのイベントなど、様々なソースから起動できる必要があります。このようにエージェントを疎結合なコンポーネントとして設計することで、既存のシステムやバックエンドプロセスにシームレスに組み込むことができ、AIの能力をより広範なビジネスプロセスに適用することが可能になります。

じゅっじゅおーじゅっじゅおー

https://github.com/humanlayer/12-factor-agents/blob/main/content/factor-12-stateless-reducer.md

Factor 12: ステートレスなReducerであること (Stateless Reducer)

システムの予測可能性とテスト容易性を最大化するため、本原則は関数型プログラミングの概念である「Reducer」をエージェントの設計に導入します。これは、エージェントの各実行ステップを、「現在の状態」と「新しい入力」の2つだけを受け取り、「新しい状態」を返す、副作用のない純粋な関数としてモデル化するという考え方です。この関数は内部に状態を持たない(ステートレスである)ため、同じ入力に対しては常に同じ出力を返します。エージェントの実行全体は、このReducer関数を繰り返し呼び出すことで進行します。この設計に従うことで、特定時点の状態を再現することが非常に容易になり、デバッグやテストが劇的に簡素化されます。また、状態を外部で管理するため、システムのスケーリングも容易になるという利点があります。