「AIエージェントを作ったが、品質が70-80%で止まり本番投入できない」——この壁は$FRAMEWORK系を触った人なら一度は通る。CrewAIで動くデモまでは1時間、しかし顧客に渡せる品質に上げる段になると、フレームワークの内部を逆コンパイルし、最終的にゼロから書き直すという地獄が待っている。

HumanLayer創業者のDex Horthy(@dexhorthy)は2025年3月、その地獄を100社以上のSaaS事業者と話して整理し、humanlayer/12-factor-agentsとして公開した。GitHubで★19,949、フォーク1,509という規模に達したこのドキュメント集は、Herokuの12-Factor Appsにインスパイアされた『LLMエージェントを本番品質に持っていくための12個のモジュラー設計則』である。

AIエージェントフレームワーク全体の比較・選定基準は AIエージェントフレームワーク比較ガイド2026 をご覧ください。

この記事のポイント
  • ・12-Factor AgentsはHumanLayer Dex Horthyが提唱したLLMエージェント設計12原則。
  • ・フレームワークの否定ではなく、フレームワークが内部で行う設計判断を既存プロダクトに段階的に取り込む方向性。
  • ・最重要原則はFactor 3(コンテキストウィンドウを所有する)。次点はFactor 2・5・8。
  • ・ステートレスReducerモデルで、エージェント本体を純粋関数化し再開・分岐・観測を容易にする。
  • ・MIT・LangGraph・OpenAI Assistants APIなどとは『どこを自分で持つか』の境界線が決定的に異なる。

1. 12-Factor Agentsとは何か:Dex Horthyが提示したLLMエージェント設計原則

12-Factor AgentsはHumanLayer社CEO/創業者のDex Horthy(GitHub: @dexhorthy)が2025年3月に公開したドキュメント集で、リポジトリはhumanlayer/12-factor-agentsにある。最終pushは2025年9月21日、Open Issuesは20件、Discordコミュニティとaidotengineerでの17分カンファレンストークが付随する。

タイトルはHerokuエンジニアのAdam Wigginsが2012年に提示した『The Twelve-Factor App』(12factor.net)への明示的なオマージュで、SaaSの設計原則をLLMエージェント時代に拡張した形になっている。

12-Factor Agentsの基本情報

項目 内容
リポジトリ humanlayer/12-factor-agents
著者 Dex Horthy(HumanLayer創業者)
スター数 19,949(2026年5月時点)
フォーク数 1,509
初版公開 2025年3月30日
最終push 2025年9月21日
主言語 TypeScript(解説・サンプルコード)
コンテンツライセンス CC BY-SA 4.0
コードライセンス Apache 2.0
関連実装 got-agents/agentshumanlayer/kubechain

Dexは前提として「すべてのフレームワークを試した結果、本番顧客向けエージェントの多くはフレームワークを使っていない」という観察を提示している。CrewAI・LangChain・smolagents・LangGraph・Griptapeを全部触った上での結論として書かれているため、フレームワーク否定論ではなくフレームワーク疲れに対する処方箋として読むべきだ。

1.1 「エージェント=loop until done」というアンチパターン

Dexの最初の問題提起は明快で、AnthropicがBuilding Effective Agentsで定義した「ここにプロンプトとツール一式があるからゴールに達するまでループしろ」というパターンは、本番運用には耐えないというものだ。

実際に出回っている『AIエージェント』を名乗るプロダクトの大半は、決定的コードの中にLLMステップを要所要所で差し込んでいるだけで、純粋なエージェントループはほとんど使われていない。エージェントは大部分が普通のソフトウェアで構成されている、というのが現場の現実である。

1.2 70-80%の壁と『フレームワーク逆コンパイル地獄』

READMEに掲載されたSaaSビルダーの典型的なジャーニーは7ステップで描かれている。

  1. エージェントを作りたいと決める
  2. プロダクト設計とUXマッピングを行う
  3. 早く動かしたいから$FRAMEWORKを採用する
  4. 品質70-80%まで到達する
  5. 顧客向け機能には80%では足りないと気づく
  6. 80%を超えるためにフレームワーク内部・プロンプト・フローを逆コンパイルし始める
  7. ゼロから書き直す

12-Factor Agentsの存在意義は、この7ステップ目に到達せずに済むよう、ステップ3の段階で『フレームワークではなく原則を取り込む』選択肢を提示することにある。

1.3 12原則の全体マップ

公式の12原則は以下のとおりだ。順番は依存関係を厳密に表すものではなく、各原則は独立適用可能になっている。

# 原則 1行要約
1 Natural Language to Tool Calls 自然言語を構造化ツール呼び出しに変換する
2 Own your prompts プロンプトをフレームワークに任せず自分で持つ
3 Own your context window コンテキストウィンドウの構築を自分で制御する
4 Tools are just structured outputs ツール≒構造化出力+決定的コードの結合
5 Unify execution state and business state 実行状態とビジネス状態を一本化する
6 Launch/Pause/Resume with simple APIs 起動・一時停止・再開をシンプルAPIで提供する
7 Contact humans with tool calls 人間への質問もツール呼び出しとして扱う
8 Own your control flow エージェント外側の制御フローを自分で書く
9 Compact errors into context window エラーを圧縮してコンテキストに戻す
10 Small, focused agents 大きい1エージェントより小さい多数を並べる
11 Trigger from anywhere Slack・メール・Webhookどこからでも起動可能にする
12 Make your agent a stateless reducer エージェント本体をステートレスReducer化する

Appendix扱いの『Factor 13: Pre-fetch all the context you might need』も付随しており、これは『LLMが必要としそうなコンテキストを先回りで取得しておく』最適化指針だ。

2. 設計の全体像:エージェントループとステートレスReducerの関係

12原則の背骨を理解するうえで欠かせないのが、Dexが提示するエージェントループのミニマル疑似コードだ。

initial_event = {"message": "..."}
context = [initial_event]
while True:
    next_step = await llm.determine_next_step(context)
    context.append(next_step)

    if next_step.intent == "done":
        return next_step.final_answer

    result = await execute_step(next_step)
    context.append(result)

ループは4ステップに分解される。

  1. LLMが構造化JSONで次ステップ(ツール呼び出し)を出力する
  2. 決定的コードがツールを実行する
  3. 結果をコンテキストウィンドウに追記する
  4. 「done」が返るまで繰り返す

この素朴ループはFactor 9のエラー圧縮を加えれば自己回復もできる。ただしDex本人が「実際にはこれだけではうまく動かない」と明言しているとおり、ここに残り11原則を積層しないと本番品質には届かない。

flowchart LR User["ユーザー/Webhook/Cron"] -->|Factor 11: trigger from anywhere| Launch["Launch API
Factor 6"] Launch --> Thread["Thread / context window
Factor 5: unified state"] Thread -->|Factor 3: own context| LLM["LLM
Factor 1: NL to tool calls
Factor 2: own prompts"] LLM -->|Factor 4: structured output| Router["Switch / Control Flow
Factor 8"] Router -->|deterministic code| Tools["Tools / APIs"] Tools -->|Factor 9: compact errors| Thread Router -->|Factor 7: ask human| Human["Human via Slack/email"] Human -->|webhook resume| Thread Router -->|Factor 10: handoff| SubAgent["Small focused sub-agent"] Router -->|done| Reducer["Factor 12: stateless reducer
state = fold thread"]

このフロー図は12原則をすべて1枚に並べたものだ。中心にあるのがThread(コンテキストウィンドウそのもの)で、すべてのツール呼び出し・人間応答・エラーがイベントとして追記される。これを実現するのがFactor 5の「実行状態とビジネス状態の統一」である。

2.1 「状態 = thread.foldl」というモデル

Factor 12『Make your agent a stateless reducer』はDex自身が「これは半分楽しみで書いた」と前置きしている原則だが、本質はエージェントを純粋関数に閉じ込めることにある。

つまり、エージェントの現在状態は「これまでに発生したイベント列をfoldlした結果」として常に再構築可能であり、エージェント本体は次のステップを決定するだけのステートレス関数になる、というモデルだ。

これはReduxのreducer、Event Sourcing、関数型のFold/Scanと同じ発想で、再開・分岐・リプレイ・タイムトラベルデバッグといった嬉しさがそのままLLMエージェントに持ち込める。

3. Factor 1-4:プロンプトとコンテキストを自分のコードとして持つ

12原則の前半4つは「LLMとアプリケーションコードの境界」を設計するための原則群だ。フレームワークが隠蔽しがちな部分を、明示的に開発者の手元に取り戻す方向で設計されている。

3.1 Factor 1: Natural Language to Tool Calls

最も基本的な原則で、自然言語のリクエストを構造化されたツール呼び出しに変換するという考え方だ。Dexは例として「2月のAI tinkerers meetupのスポンサー代として$750のpayment linkをTerri宛てに作って」という自然言語を、Stripe APIを叩く構造化JSONに変換するパターンを示している。

# LLMが自然言語を受け取り、構造化オブジェクトを返す
next_step = await llm.determine_next_step(
    "create a payment link for $750 to Jeff "
    "for sponsoring the february AI tinkerers meetup"
)

# 構造化出力に応じて決定的コードが分岐する
if next_step.function == "create_payment_link":
    stripe.payment_links.create(next_step.parameters)
elif next_step.function == "something_else":
    ...

ポイントは、LLMの仕事は「次に何をするかをJSONで返すこと」だけに絞り、実行は決定的コードに任せる役割分担だ。

3.2 Factor 2: Own your prompts

フレームワークが提供する「rolegoalpersonalitytoolsを渡せばあとはやっておく」タイプのブラックボックス抽象を捨て、プロンプトをファーストクラスのコードとして所有しろという原則だ。

Dexのサンプルでは、HumanLayerが推進する型付きプロンプトDSLであるBAMLを使った例が掲載されている。

function DetermineNextStep(thread: string) -> DoneForNow | ListGitTags | DeployBackend | DeployFrontend | RequestMoreInformation {
    prompt #"
        {{ _.role("system") }}

        You are a helpful assistant that manages deployments for frontend and backend systems.
        You work diligently to ensure safe and successful deployments by following best practices
        and proper deployment procedures.

        Before deploying any system, you should check:
        - The deployment environment (staging vs production)
        - The correct tag/version to deploy
        - The current system status
    "#
}

プロンプトを型と一緒に書ける、Gitで差分が取れる、レビューできる、テストできる——この4つを満たさないプロンプト管理は本番運用に耐えない、というのがFactor 2の主張だ。フレームワーク内部に隠蔽されると、最後の20%を詰める段階でブラックボックスを開かないと進めなくなる。

3.3 Factor 3: Own your context window(最重要原則)

READMEのトップに『Looking for Context Engineering? Jump straight to factor 3』と書かれているとおり、Dexが12原則の中で最も力を入れているのがこれだ。

LLMはステートレス関数で、入力(コンテキストウィンドウ)の質がそのまま出力の質を決める。そして「コンテキストウィンドウに何を入れるか」は標準のmessage形式(system/user/assistant/tool)に縛られる必要はない、というのが核心だ。

例えば、message形式ではなく独自XMLでgit操作履歴を表現すれば、トークン効率と注意効率を大幅に高められる、とDexは具体例を提示している。

Factor 3が指す『コンテキスト』に含まれるもの
  • ・モデルへの指示プロンプト
  • ・RAGなどで取得した外部ドキュメント
  • ・過去の状態・ツール呼び出し・結果・履歴
  • ・関連する別スレッドのMemory
  • ・期待する構造化出力のスキーマ

RAG・エンベディング・検索を含めた『コンテキスト供給の総合設計』は RAG完全ガイド2026:仕組みから運用まで と合わせて読むと、より立体的に理解できます。

3.4 Factor 4: Tools are just structured outputs

『ツール』を特別な概念にせず、「LLMが出すJSON+決定的コードによるディスパッチ」として捉え直す原則だ。

class Issue:
    title: str
    description: str
    team_id: str
    assignee_id: str

class CreateIssue:
    intent: "create_issue"
    issue: Issue

class SearchIssues:
    intent: "search_issues"
    query: str
    what_youre_looking_for: str

この設計を採ると、「LLMがツールを呼んだら必ず特定の関数を実行する」という1対1マッピングから解放され、同じintentでも文脈に応じて異なる挙動を取れる柔軟性が得られる。Factor 8の「制御フローを所有する」と組み合わせることで真価を発揮する。

4. Factor 5-8:状態統一・人間連携・制御フローを自分のコードに引き戻す

ここから先は「フレームワークが隠蔽するエージェント外側の挙動」を、開発者の手元に取り戻すための原則群だ。

4.1 Factor 5: Unify execution state and business state

AIフレームワークの多くは「実行状態(現在ステップ・次ステップ・待機状態・リトライ回数)」と「ビジネス状態(OpenAIメッセージ列・ツール呼び出しと結果)」を別々に管理する。

Dexの主張はこれを1本化せよ、というものだ。理想は「実行状態はビジネス状態(≒threadイベント列)から導出可能」にすること。これによって以下7つの利点が一気に手に入る。

  • Simplicity: 真実の源(source of truth)が1つになる
  • Serialization: threadが自明にシリアライズ・デシリアライズ可能
  • Debugging: 全履歴が1箇所に集約される
  • Flexibility: 新しい状態はイベント型を追加するだけで足る
  • Recovery: threadを読み込めば任意の地点から再開できる
  • Forking: threadの一部をコピーして別IDで分岐できる
  • Human Interfaces: threadをそのままMarkdown・Web UIに変換できる

これがそのままFactor 12のステートレスReducerにつながる伏線になっている。

4.2 Factor 6: Launch/Pause/Resume with simple APIs

エージェントは結局のところプログラムなので、起動・一時停止・再開のAPIを普通のプログラムと同じ作法で提供せよ、という原則だ。

ユーザー・他アプリ・パイプライン・他エージェントから簡単なAPIで起動できること、長時間処理の前で一時停止できること、Webhookなど外部トリガーで再開できることが要件になる。これはFactor 5・Factor 8と密接に絡む。

特に注意すべき点として、多くのAIオーケストレータは「ツール選択とツール実行の間で一時停止できない」という制約を持つ、とDexは指摘している。Factor 7の人間承認フローを実装するうえで決定的な制約になる。

4.3 Factor 7: Contact humans with tool calls

LLM APIにはデフォルトで「プレーンテキストを返すか、構造化データを返すか」という最初の1トークンに大きな重みが乗る、という性質がある。

天気の問い合わせなら最初のトークンは"the"fetch_weatherを呼ぶなら|JSON>のような特殊トークン。Dexはここで「常にJSONを返させて、その中でrequest_human_inputdone_for_nowのような意図を宣言させる」設計を提案する。

class Options:
    urgency: Literal["low", "medium", "high"]
    format: Literal["free_text", "yes_no", "multiple_choice"]
    choices: list[str]

class RequestHumanInput:
    intent: "request_human_input"
    question: str
    context: str
    options: Options

if next_step.intent == "request_human_input":
    thread.events.append({
        "type": "human_input_requested",
        "data": next_step,
    })
    thread_id = await save_state(thread)
    await notify_human(next_step, thread_id)
    return  # ループを抜けて応答待ち

人間への質問もツール呼び出しの一種として統一的に扱うことで、Slack・メール・SMSなど媒体を選ばずに人間-エージェント間のやりとりを設計できる。HumanLayer社のプロダクト自体がこのパターンを実装したマネージドサービスである。

4.4 Factor 8: Own your control flow

LangGraphやLangChainが提供する「制御フロー」を、自分のアプリケーションコードとして書け、という強い主張の原則だ。

def handle_next_step(thread: Thread):
    while True:
        next_step = await determine_next_step(thread_to_prompt(thread))

        if next_step.intent == "request_clarification":
            thread.events.append({"type": "request_clarification", "data": next_step})
            await send_message_to_human(next_step)
            await db.save_thread(thread)
            break  # 非同期ステップ:Webhookで再開
        elif next_step.intent == "fetch_open_issues":
            thread.events.append({"type": "fetch_open_issues", "data": next_step})
            issues = await linear.fetch_open_issues()
            thread.events.append({"type": "fetch_open_issues_result", "data": issues})
            # ループ継続
        elif next_step.intent == "deploy_backend":
            # 高リスク操作:人間承認待ち
            await request_approval(next_step)
            break

request_clarificationfetch_git_tagsdeploy_backendのように、ツール呼び出しの種類によってループを抜けるか継続するかを開発者が決められる。さらに以下のような実装が自分のコードに収まる。

  • ツール結果の要約・キャッシュ
  • 構造化出力に対するLLM-as-judge
  • コンテキストウィンドウのコンパクション
  • ロギング・トレーシング・メトリクス
  • クライアントサイドのレートリミット
  • 永続的なsleep/pause/イベント待ち

ハーネス(実行ループ)そのものの設計判断と実装パターンは エージェントハーネス基礎:自律エージェント実装の最小構成と落とし穴 で、さらに踏み込んだ実装則を扱っています。

5. Factor 9-12:エラー圧縮・小型化・トリガー多様化・ステートレスReducer

12原則の後半は『エージェントを本番のソフトウェアシステムに組み込むときの境界条件』を扱う。1個でも欠けると運用フェーズで負債になる。

5.1 Factor 9: Compact Errors into Context Window

LLMエージェントの『自己回復』はエラー・スタックトレースをそのままコンテキストに戻すだけで成立することが多い。Dexはこれを最小実装として示している。

thread = {"events": [initial_message]}
consecutive_errors = 0

while True:
    next_step = await determine_next_step(thread_to_prompt(thread))
    thread["events"].append({"type": next_step.intent, "data": next_step})

    try:
        result = await handle_next_step(thread, next_step)
        thread["events"].append({"type": next_step.intent + "_result", "data": result})
        consecutive_errors = 0
    except Exception as e:
        consecutive_errors += 1
        if consecutive_errors < 3:
            thread["events"].append({"type": "error", "data": format_error(e)})
            continue
        else:
            raise

ポイントは「連続エラーカウンタを設けて無限ループを防ぐ」「format_errorで長大なスタックトレースを圧縮する」の2点。生のスタックトレースを丸ごとコンテキストに戻すとトークンを浪費するので、要約・圧縮が前提だ。

5.2 Factor 10: Small, Focused Agents

『なんでもできる巨大エージェント1個』より『3-10、最大20ステップで完結する小さいエージェントを多数並べる』方が本番品質が安定する、という原則だ。

理由はシンプルで、コンテキストが大きくなるほどLLMは集中力を失う。20ステップに収まる小さなエージェントを複数組み合わせる方が、デバッグも責任分割も明確になる。

As context grows, LLMs are more likely to get lost or lose focus

『LLMがもっと賢くなれば不要では?』という反論に対し、Dexは「賢くなれば1エージェントあたりのステップ数を増やせるが、それでも分割の利点(テスト容易性・観測可能性・並列実行)は残る」と答えている。

5.3 Factor 11: Trigger from anywhere, meet users where they are

エージェントはSlack・メール・SMS・Discord・Webhook・cron・他エージェントなど、ユーザーがいる場所すべてから起動できるべきだ、という原則だ。

これはFactor 6(Launch/Pause/Resume API)とセットで考える必要があり、HTTPだけでなくWebSocket・キュー・メッセージブローカーすべてをトリガー源として扱えるアーキテクチャを要求する。

5.4 Factor 12: Make your agent a stateless reducer

最後の原則は半分ジョーク半分本気のまとめで、「エージェントを純粋関数 = ステートレスReducerにせよ」というものだ。

type AgentEvent =
  | { type: "user_message"; data: { content: string } }
  | { type: "tool_call"; data: { intent: string; params: unknown } }
  | { type: "tool_result"; data: { intent: string; result: unknown } }
  | { type: "human_input"; data: { content: string } }
  | { type: "error"; data: { message: string } };

type Thread = { id: string; events: AgentEvent[] };

// エージェント本体はステートレス:thread → 次のAgentEvent
const determineNextStep = async (thread: Thread): Promise<AgentEvent> => {
  const prompt = threadToPrompt(thread);
  return await llm.next(prompt);
};

// 状態は常にイベント列をfold(reduce)した結果
const currentState = (thread: Thread) =>
  thread.events.reduce(applyEvent, initialState);

このモデルが綺麗に乗ると、エージェントの動作はReduxのreducerやイベントソーシングと同型になり、リプレイ・分岐・タイムトラベルデバッグ・並列実行・分散化がすべて『普通のソフトウェアと同じ作法』で扱えるようになる。

エージェント時代のエンジニア像とキャリア設計は Agentic AIエンジニア・ロードマップ2026 を参照すると、12-Factor Agentsの位置付けがより明確になります。

6. 12-Factor Agentsを実装に落とすチェックリストと類似アプローチ比較

ここまで読んで「結局どの原則から実装するか」を判断するための、実装チェックリストと類似アプローチの比較を示す。

6.1 実装チェックリスト(推奨優先度順)

導入時に最初に実装すべき4原則
  1. Factor 3: コンテキストウィンドウを自分で構築(最重要)
  2. Factor 2: プロンプトをコードとして所有(BAML等の型付きDSL推奨)
  3. Factor 5: 実行状態とビジネス状態をthread単一に統合
  4. Factor 8: 制御フローをアプリケーションコードに書く
本番投入直前に追加すべき4原則
  1. Factor 6: Launch/Pause/Resume APIを公開
  2. Factor 7: 人間質問もツール呼び出し化(HumanLayer等を活用)
  3. Factor 9: エラー圧縮とリトライカウンタ
  4. Factor 10: エージェントを3-20ステップに分割
スケール時に必要になる4原則
  1. Factor 1: 自然言語→ツール呼び出し変換の品質計測
  2. Factor 4: ツール=構造化出力+ディスパッチへの統一
  3. Factor 11: トリガー源の多様化(Slack・メール・Webhook)
  4. Factor 12: ステートレスReducer化(リプレイ・分岐基盤)

6.2 類似アプローチとの比較

12-Factor Agentsを位置付けるため、代表的な5つのアプローチと並べて比較する。

アプローチ 提供形態 プロンプト所有 状態モデル 人間連携 適性レンジ
12-Factor Agents 設計原則集 完全に開発者 thread単一・Reducer tool callで明示 70-100%の本番品質を狙う
LangChain / LangGraph フレームワーク 一部隠蔽 graphベース callback/breakpoint 0-80%の高速立ち上げ
CrewAI フレームワーク role/goal/backstory抽象 エージェント間共有 限定的 デモ・PoC・社内ツール
OpenAI Assistants API マネージドAPI 限定的(system指示のみ) thread API(不可視) 限定的 OpenAI依存で完結する用途
AutoGen フレームワーク 部分所有 会話ベース proxy agent マルチエージェント研究
Building Effective Agents (Anthropic) ガイド 完全に開発者 パターン提示のみ パターン提示のみ 設計思想の基礎

12-Factor Agentsの立ち位置は「フレームワークではなく原則集」「マネージドではなく開発者所有」「設計思想ではなく実装可能な12個の具体則」という3点で他とは交わらない。

6.3 関連OSS・関連ドキュメント

Dex本人と関係者が公開している関連リソースをまとめておく。これらを順に読むと12-Factor Agentsの理解が深まる。

エージェント時代のトークン経済を含めた最適化観点は、別記事の AIエージェントのトークン最適化ガイド2026 で、コンテキストウィンドウ圧縮の具体手法と合わせて扱っている。

6.4 12-Factor Agentsを採用しない方が良いケース

最後に、Dex本人もREADMEで触れている「フレームワークを選んだ方が良いケース」を明確化しておく。

  • 社内ツール・PoC・ハッカソンプロジェクト:70-80%品質で十分なら、CrewAIやLangChainの方が立ち上がりが圧倒的に速い
  • MLエンジニアが主導するR&D:パターン研究には事前パッケージ済みのAutoGen・LangGraphの方が適している
  • OpenAI/Anthropicに完全依存できる用途:Assistants APIで足りるなら、自前で12原則を実装する価値は薄い

12-Factor Agentsの主戦場は「顧客向け本番プロダクトで品質80-100%を狙う」段階だ。この段階に至っていないチームが先回りで12原則を実装すると、過剰設計になりやすい点には注意したい。

参照ソース