(この記事はClaude Codeで大半を書いたものです)

前提: この記事の想定読者
この記事は、ChatGPTにアドホックに質問するようなユースケースを想定していない。
想定しているのは、生成AIを業務フローに組み込もうとしている人だ。問い合わせ対応の自動化、ドキュメントからの情報抽出、社内ナレッジの検索と要約。こうした用途でAIをプロダクションに載せようとすると、すぐに「ワークフローをどう設計するか」という問題にぶつかる。
この問題に対する一つの視座として、フロントエンド開発とインフラ運用の世界で起きたパラダイムシフトを振り返りたい。
まえおき: UIは「どう作るか」から「どうあるべきか」へ変わった
jQueryの時代、開発者は「この要素を取得し、この値を書き換え、このクラスを付与する」と手順を書いていた。React以降は「stateがAならUIはこう、Bならこう」と状態と結果の対応を書く。手順の記述から、あるべき姿の宣言へ。これは単なる流行ではなく、複雑さを扱うための必然的な抽象化だった。
宣言型UIの本質 ― Reactは何を変えたのか
Reactの宣言型アプローチを支えるのは、いくつかの設計原則だ。
仮想DOMと差分検出(Reconciliation)
開発者はUIの「あるべき姿」を宣言する。Reactは仮想DOMを使って「今」と「次」を比較し、変更が必要な部分だけを実際のDOMに反映する。厳密には汎用的なツリー差分ではなく、キーや要素型などのヒューリスティクスで同一性を判定する仕組みだが、開発者から見れば「どの要素をどう書き換えるか」という手順を自分で書かなくてよくなった、という点が重要だ。
純粋性を前提とした設計
Reactは、コンポーネントが同じpropsとstateから同じJSXを返すことを期待している。副作用はuseEffect等に寄せる。この原則に従うことで、挙動が予測可能になり、再レンダリングの最適化も効くようになる。もちろん、Math.random()やグローバル変数を参照すれば簡単に非純粋になる。Reactが純粋性を保証してくれるわけではない。あくまで開発者が守るべき規約だ。
データフローの一貫性
Reactはデータが親から子へ一方向に流れることを基本とする。Flux/Reduxのような設計では「単一の情報源(Single Source of Truth)」が強調されるが、これはReact自体の必須要件ではない。現実のアプリでは状態がコンポーネントローカル、URL、サーバーキャッシュ、フォームなど複数箇所に分散することも多い。それでも、状態の出どころを明確にし、一貫したデータフローを保つことで複雑さを抑える、という設計思想がReactの背景にはある。
これらが組み合わさることで、開発者は「How(どうやるか)」ではなく「What(何であるべきか)」を書けるようになった。
インフラでも同じことが起きた ― Kubernetesの事例
このパラダイムシフトは、フロントエンドに限った話ではない。インフラの世界でも同じ転換が起きている。
かつてサーバー管理者は、詳細なデプロイ手順書を片手に、一歩ずつコマンドを打ち込んでいた。Kubernetes(K8s)の登場以降、開発者は「あるべき状態」をYAMLに記述するだけでよくなった。「このDeploymentは3つのレプリカで動いていること」「このServiceはポート80で公開されていること」。
K8sの設計を支えるのは「Reconciliation Loop(制御ループ)」だ。現在の状態を常に監視し、あるべき状態との差分を検出し、差分を埋める操作を自動で実行する。このループが回り続けることで、システムは自己修復する。コンテナが落ちても、ノードが死んでも、K8sは黙々とあるべき状態に戻そうとする。
フロントエンドとインフラ。領域は異なるが、起きたことは同じだ。複雑さが一定を超えたとき、手順を書く命令型は限界を迎え、あるべき姿を書く宣言型へ移行した。
現在の生成AI ― 命令型の世界
翻って、現在の生成AIをプロダクションに組み込もうとするとどうなるか。
「問い合わせを受けたら、まずカテゴリを分類するプロンプトを実行し、その結果に応じてナレッジベースを検索し、検索結果を元に回答を生成する」。
これは本質的に、jQueryでDOMを操作していた時代、あるいはデプロイ手順書でサーバーを構築していた時代と同じだ。人間が司令塔となり、一歩ずつ処理を定義する。一箇所の出力が想定と異なれば、後続のタスクはすべて崩壊する。
今のAI活用は、極めて命令型の世界に留まっている。
命令型の限界 ― ワークフローを組んだことがある人なら知っている
命令型の限界は、AIに限った話ではない。ワークフローシステムを触ったことがある人なら、この面倒さには覚えがあるはずだ。
例1:ソフトウェア開発のイテレーション
「概念設計→DB設計→実装→テスト→失敗したら戻る」という流れをタスクの連鎖として定義するのは難しい。仮に定義できたとして、「テスト設計の担当者を増やしたい」となった瞬間、フロー全体を作り直す羽目になる。
例2:技術サポート対応
「問い合わせを受け、技術担当に調査を依頼し、結果をレポートにまとめる」。人間がこの役割分担を想像するのは容易だが、これをタスクレベルで分解してフローに落とし込むのは骨が折れる。
共通する問題
命令型のフローは「事前にすべての分岐と手順を定義する」ことを要求する。現実の業務は例外だらけで、変更も頻繁に起きる。フローが複雑になるほど、修正コストが指数的に増える。これはjQueryでDOMを直接操作していたときの問題、手順書でサーバーを構築していたときの問題と、構造的に同じだ。
宣言型AIという解 ― ReactとKubernetesの設計思想はAIにも適用できる
ReactとKubernetesに共通する設計思想は、比喩としてAIの世界にも対応させることができる。
| 共通する設計思想 | AIでの対応(比喩) |
|---|---|
| 差分検出と反映 | 状態監視と差分実行 |
| あるべき姿を宣言し、フレームワークが現状との差分を計算して反映する | 人間はあるべき状態を宣言し、AIが現状との差分を見て必要なアクションを実行する |
| 純粋性・冪等性 | 条件と振る舞いの対応 |
| 同じ入力なら同じ出力を期待。副作用は明示的に分離 | 同じ条件なら同じ振る舞いを期待。外部操作は明示的に定義 |
| データフローの一貫性 | コンテキストの一元管理 |
| 状態の出どころを明確にし、一貫したフローを保つ | ルール・履歴・状態の出どころを明確にし、AIの判断はそこから導出 |
具体的には、人間は以下のような宣言をAIに渡すことになる:
- 「テストが失敗したら、原因を特定して修正案を提示する」(条件→振る舞い)
- 「未読メールが10件を超えたら、優先度の低いものをアーカイブする」(状態監視→差分実行)
- 「技術的な問い合わせには、まずログを確認し、必要なら担当者にエスカレーションする」(ルールからの導出)
人間は「ゴール」と「制約」と「判断基準」を宣言する。AIはその宣言に基づいて、状況に応じた手順を自律的に組み立てる。Reactが仮想DOMで「How」を自動計算したように、K8sがReconciliation Loopで「How」を自動実行したように、AIが「How」を自動で導出する。
ただし、この対応には本質的な非対称性がある。ReactのDOM更新はブラウザ内で閉じている。K8sのController操作もクラスタ内で閉じている。失敗しても再レンダリングやリトライで回復しやすい。一方、AIの「差分実行」は外部世界に副作用を起こす。メールを送る、データを書き換える、顧客に通知する。一度実行すれば取り消せないことも多い。だからこそ、宣言型に寄せるほど、ガードレール ― 権限管理、人間による確認、取り消し手段、監査ログ ― の設計が重要になる。
技術的な現在地 ― 何があり、何が足りないか
すでにある要素技術
エージェント技術は実用段階にある。ツール呼び出し、マルチステップ推論、自己修正。AIが複数の手順を自律的に実行する能力はすでに動いている。コンテキストウィンドウの拡大により、状態や履歴を保持したまま長期タスクを遂行できるようになった。MCPのような外部接続の標準化も進み、AIが外部システムを操作する手段は整いつつある。
まだ標準化されていないもの
一方で、ReactやK8sのReconciliationに相当する抽象化 ― AIが「世界の状態」を継続的に観測し、あるべき状態との差分を検出して必要なアクションを決定する仕組み ― は、一般解としてはまだ確立していない。
「状態監視→差分検出→実行」というパターン自体は、LLM以前から監視ツールやジョブスケジューラ、イベント駆動アーキテクチャとして存在する。LLMを組み込んだ実装も増えている。だが、ReactやK8sのように「開発者が宣言を書けば、あとはフレームワークが面倒を見てくれる」という開発者体験には至っていない。
非決定性という本質的な違い
ReactやKubernetesとの対比で見落としてはならない点がある。
ReactのレンダリングもK8sのControllerも決定論的だ。同じ仮想DOMからは常に同じDOM操作が導出される。「レプリカ数が3であるべきところ2しかない」なら、「1つ起動する」という判断は一意に決まる。
一方、AIの判断は確率的であり、同じ入力から異なる出力が生まれうる。
たとえば「問い合わせの未対応件数は常に5件以内でなければならない」「技術サポートはログとドキュメントを材料として調査を行う」といった宣言を並べたとする。しかし、実際のタスクをこなすにはこれだけでは足りない。ログに該当する記述がなかったらどうするか。ドキュメントが古かったら。顧客が急いでいたら。宣言でカバーされていない状況に直面したとき、AIはその隙間を自分で埋めて判断するしかない。
古典的なAIでは、評価関数を人間が設計し、最も評価値の高い遷移先を選んでいた。判断基準は明示的だった。LLMベースのAIでは、この評価基準がモデル内部に暗黙的に学習されている。宣言の隙間を埋める判断もまた、モデルに委ねられている。
そしてその判断が人間の期待から外れたとき、「AIが嘘をついた」と見なされる。ハルシネーションと呼ばれる現象の少なくとも一部は、この構造から生まれている。宣言が不十分なとき、AIは何かを選ばなければならない。その選択が人間の暗黙の期待と一致する保証はない。
宣言をより精緻にするのか、判断を制約で縛るのか、不確実なときは人間に確認させるのか。そこが、宣言型AIの時代においても引き続き人間が注力すべき部分となる。
パラダイムシフトはいつも静かに起きる
Reactが登場したとき、多くの開発者は「jQueryで十分」と思っていた。Kubernetesが登場したとき、多くの運用者は「手順書とシェルスクリプトで十分」と思っていた。だが複雑さが一定を超えると、命令型は保守不能になる。そして人々は宣言型へ移行した。
生成AIも同じ道を歩む。今はまだ「プロンプトを工夫すれば何とかなる」「ワークフローを細かく定義すれば動く」という段階にいる。だが、AIに任せたいタスクが複雑になるにつれ、命令型は限界を迎える。
もちろん、完全な宣言型にはならない。Reactでもイベントハンドラは命令的に残り、K8sでもCI/CDパイプラインやアラート対応は命令的に書くように、AIでも「いつ人間が承認するか」「どこで処理を止めるか」といった制御は命令的に書くことになるだろう。宣言(ゴール・制約・判断基準)と命令(権限・承認・停止条件)のハイブリッドが現実解になる。
それでも、重心は宣言型へ移る。これは革新ではなく、複雑さを扱うための必然だ。