
AI Agent(AIエージェント)とは、LLM(大規模言語モデル)が目標と現在の状態を基に次の行動を選び、その結果を観測しながら処理を繰り返すシステムです。LLM単体をAgentと呼ぶのではなく、LLM、Tool、状態管理、実行環境、停止条件、安全制御を組み合わせた制御loopとして捉えると、仕組みが見えやすくなります。
ReActは、このloopを理解する代表的な考え方です。Reasoning(次に何を調べ、どう進めるかの判断)とActing(検索やAPI呼び出しなどの行動)を交互に行い、Observation(行動から返った観測)を次の判断へ反映します。ただし、ReActはAgent全般の同義語ではありません。Agentを構成するtrajectory pattern(行動軌跡の型)の一つです。
この記事では、ReAct論文の問題設定から出発し、Function Callingとの違い、固定workflowとの使い分け、停止条件を備えたPython実装、安全設計、評価までを一つの流れで解説します。
3行要約
- AI Agentは、LLMが目標と状態を見ながらToolを選び、Observationに応じて次の手を変える制御loopです。
- ReActはThought・Action・Observationを組み合わせ、外部世界の情報で計画を更新する代表的な方法です。
- 実運用では賢いpromptより先に、権限、承認、停止条件、step予算、監査log、結果検証をcodeで強制します。
前回のFunction Callingとは?Tool Useの仕組み・実装・安全設計を解説では、modelのtool callを「未検証の実行候補」として扱いました。今回のAgentは、その候補生成と実行結果の受け渡しを一回で終えず、目標を達成するまで制御する層です。
AI Agentは「LLMにToolを渡したもの」だけではない
Agentという言葉には幅があります。本記事では実装を議論しやすくするため、次の操作的な定義を使います。
AI Agentは、与えられた目標に対して、LLMが現在の状態から次の行動を動的に選び、外部環境の観測を取り込み、完了・失敗・承認待ち・予算超過のいずれかへ到達するまで制御するシステムである。
AnthropicのBuilding effective agentsは、事前定義したcode pathでLLMとToolを編成するものをworkflow、LLMがprocessとTool利用を動的に方向づけるものをAgentとして区別しています。OpenAIの実践ガイドも、LLMがworkflowの実行を管理し、状態に応じてToolを選び、完了や失敗時のhandoffを判断する点をAgentの特徴として説明しています。
この定義で、chatbotからAgentまでを比較します。
| 方式 | 次の処理を決める主体 | 外部Tool | 複数stepの状態 | 向く仕事 |
|---|---|---|---|---|
| 通常のchatbot | 利用者と一回のLLM応答 | 原則なし | 会話履歴のみ | 説明、要約、文章生成 |
| Function Calling | LLMがcall候補、applicationが一回の実行を管理 | あり | 必須ではない | 天気照会、単発計算、注文確認 |
| 固定workflow | application code | 必要に応じてあり | codeで明示 | 手順が安定した審査、変換、定期処理 |
| AI Agent | LLMとapplicationの制御loop | あり | goalと途中状態を保持 | 経路が事前に決まらない調査、障害対応、開発作業 |
Function CallingはToolを呼ぶinterfaceです。Agentは「何を調べたか」「次に何が必要か」「いつ終了するか」を複数回にわたり管理します。したがって、Function Callingを実装しただけで必ずAgentになるわけではありません。一方、AgentのActionを安全に構造化する手段としてFunction Callingを使うことはできます。
ReActが解こうとした問題

ReAct: Synergizing Reasoning and Acting in Language Modelsは、ReasoningとActingが別々に研究されてきたことを問題として置きました。
Reasoningだけで回答を作る場合、modelは頭の中にある情報だけで長い推論を進めます。途中の前提が誤っていても外部世界へ確かめに行けず、誤りが後段へ伝播しやすくなります。逆にActionだけを連続生成すると、何のためにその操作を行ったか、どのsubgoalが終わったかを見失いやすくなります。
ReActは両者を一つのtrajectoryへ入れます。
- 現在の目標と観測から、次に必要なことを判断する。
- 検索、照会、環境操作などのActionを選ぶ。
- 実行環境からObservationを受け取る。
- Observationで計画を更新し、次のActionまたは最終回答を選ぶ。
論文では、reasoning traceが高水準の計画を作成・追跡・更新し、例外へ対応する役割を持ちます。ActionはWikipediaのような外部情報源や操作対象の環境へ接続し、reasoningを現実の情報へ接地します。言い換えると、考えてから動くだけでなく、動いて得た事実によって考え直すのがReActの要点です。
Thoughtは公開用の「心の声」と同じではない
ReAct論文は、few-shot promptに人間が書いたverbal reasoning traceを含め、modelにThoughtとActionを生成させました。しかし、これを「本番systemではmodelの非公開な内部推論をすべて表示・保存すべき」と読み替える必要はありません。
運用上必要なのは、後から検証できる意思決定の記録です。たとえば、次の情報を構造化して残します。
- 目標と現在の状態
- 選択したActionと検証済みarguments
- Toolを選んだ短い理由やsubgoal
- 返ったObservationと出典・時刻
- state transition(状態遷移)
- 人間が承認または拒否した事実
これは内部推論の逐語録ではなく、applicationが管理するaudit trace(監査用の行動記録)です。ActionとObservationを追跡できれば、誤った回答がmodelの判断、Toolの結果、古いdata、認可処理のどこで生じたかを切り分けられます。
Thought・Action・Observationで状態を更新する
Agentの時刻\(t\)における状態を\(s_t\)、選んだ行動を\(a_t\)、実行結果の観測を\(o_t\)とします。状態更新を抽象化すると、次の形で表せます。
\(g\)はgoal、\(T\)は利用可能なTool、\(\pi\)は次のActionを選ぶpolicy(状態から行動を選ぶ規則)、\(E\)はToolや外部環境、\(U\)は状態更新です。LLMは主に\(\pi\)の一部を担います。Tool実行\(E\)と、許可・停止を含む\(U\)までmodelへ任せる設計にはしません。
配送遅延を調べる具体例

利用者の目標が「注文A-104が届かない原因を調べて」だとします。一回のTool callだけでは、注文が出荷済みか、配送会社で止まっているかを判断できません。
| Step | 判断またはsubgoal | Action | Observation | 状態の変化 |
|---|---|---|---|---|
| 1 | 注文自体の状態が必要 | get_order_status(A-104) |
出荷済み、追跡番号TR-88 | 注文処理の停滞ではない |
| 2 | 配送会社側の現在地が必要 | track_shipment(TR-88) |
仕分け拠点、天候遅延 | 原因と最終更新時刻を取得 |
| 3 | 回答に必要な根拠が揃った | finish |
なし | 成功状態へ遷移 |
Step 1のObservationが「未出荷」なら、Step 2は配送追跡ではなく倉庫状況の照会になるかもしれません。Actionが事前に固定されず、観測によって分岐する点がAgentらしさです。
ただし、この例はread-onlyです。「遅れているので再発送する」まで自動化すると副作用が生じます。再発送には在庫、金額、重複処理、本人確認が関係するため、別のActionとして分離し、人間承認を挟むべきです。
ReAct論文は何を示したのか
ReAct論文は、知識を調べるtaskと、環境内で行動するtaskの両方を評価しました。
知識集約task
HotpotQA(複数の情報を組み合わせる質問応答)とFEVER(主張の事実検証)では、Search、Lookup、Finishからなる簡単なWikipedia APIを利用しました。Thought、Action、Observationを複数回繰り返し、何を検索し、見つけた情報から次に何を調べるかを更新します。
論文は、ReActが両taskでAct-onlyを上回ったと報告しています。また、reasoning-onlyで生じる事実のhallucinationや誤りの伝播を、外部検索によって抑えられる例を示しました。ただし「検索すればhallucinationがなくなる」という意味ではありません。検索語が悪い、異なる同名項目を選ぶ、Observationを誤読する、といった失敗は残ります。
対話的な意思決定task
ALFWorldは家庭内作業をtext環境で行うbenchmark、WebShopはWeb上の商品探索・購入を模したbenchmarkです。論文abstractは、当時比較した模倣・強化学習手法に対し、絶対成功率でALFWorldは34ポイント、WebShopは10ポイント上回ったと報告しています。ALFWorldでは、ReActがreasoningを除いたReAct-IMの全体成功率53に対して71でした。
この数値は、PaLM-540Bを中心とする当時のmodel、少数のin-context examples、各benchmarkのaction spaceという条件で得られたものです。現在の任意のmodelや業務Agentが同じ差で改善するとは限りません。論文から一般化できるのは「reasoningとactionを観測で接続する設計が複数種類のtaskで有効だった」という範囲です。
Thoughtを毎step出す必要もない
論文では、知識taskでThoughtとActionを交互に生成しました。一方、行動数が多い意思決定taskでは、ThoughtをすべてのActionの前に置かず、重要な位置へ疎に挿入する方法も採用しています。
毎step長文を生成するとtoken、latency、context消費が増えます。実装では、計画の更新、例外発生、subgoal完了、Tool選択が曖昧な場面など、判断が必要な境界で短い構造化decisionを求める方が管理しやすくなります。
Agentを構成する8つの部品

ReAct promptだけでは運用可能なAgentになりません。最低限、次の役割を分離します。
| 部品 | 責任 | modelだけに任せないもの |
|---|---|---|
| Goal | 何を達成すれば終了か定義する | 成功条件の勝手な変更 |
| Policy / Planner | 状態から次のActionを提案する | 認可と実行の最終決定 |
| State | 確認済み事実、未解決subgoal、予算を保持する | 会話全文を無制限に蓄積 |
| Tool registry | Tool名、schema、risk、timeoutを管理する | 存在しないToolの実行 |
| Executor | 検証済みActionを実行する | model出力の無条件実行 |
| Observation adapter | 結果を正規化し、出典・時刻・errorを付ける | 外部textを命令として扱うこと |
| Guardrails | 権限、承認、data境界を強制する | promptだけの禁止事項 |
| Stop / Evaluator | 完了、失敗、予算切れ、進捗停止を判定する | modelの自己申告だけで成功にすること |
Goalは「よい回答を作る」では曖昧です。配送調査なら「注文の現在状態、遅延理由、最終更新時刻を、照会結果に基づいて利用者へ示す」のように、確認可能な終了条件へ落とします。
Stateには、確認済みの事実とmodelの仮説を混ぜません。facts、open_questions、attempted_actions、budgetなどに分ければ、「推測をObservationとして再利用する」事故を減らせます。
固定workflowとAgentはどちらを選ぶべきか

自由度が高いほど優れているわけではありません。Anthropicのガイドは、明確に分解できるtaskにはworkflowが予測可能性と一貫性を提供し、柔軟なmodel判断が必要なtaskでAgentが候補になると説明しています。Agentic systemは一般にlatencyとcostを増やすため、単一のLLM callや固定workflowで解けるなら、その方が運用しやすい場合があります。
| 比較軸 | 固定workflow | AI Agent |
|---|---|---|
| 経路 | codeで事前定義 | 観測に応じてmodelが選ぶ |
| 予測可能性 | 高い | 入力と生成の揺らぎを受ける |
| 例外への柔軟性 | 想定した分岐に限定 | 未知の組み合わせへ対応しやすい |
| test | branch単位で網羅しやすい | trajectoryとoutcomeを複数trialで測る |
| latency / cost | 見積もりやすい | step数によって増える |
| security | 権限経路を限定しやすい | Tool選択が動的なため境界設計が増える |
| 向く例 | 請求書OCR→項目検証→登録 | 複数資料を行き来する調査、原因が未知の障害診断 |
次の条件なら、最初はworkflowを選びます。
- 正常系と例外分岐を列挙できる
- 法令・会計・決済など、同じ入力には同じ経路が必要
- step数と実行費用を厳密に予測したい
- modelに委ねる判断が分類や抽出に限られる
Agentが候補になるのは、必要な情報源や手順が最初には分からず、途中の観測によって次の調査先が変わる仕事です。それでも、認可、実行、停止まで自由化する必要はありません。経路選択だけをmodelへ任せ、権限と状態遷移はcodeで囲うのが実用的です。
停止できる最小Agent loopをPythonで実装する
次のcodeは、特定SDKに依存しないAgent loopの骨格です。decideはLLMから構造化されたActionを得る層、executeは検証・認可済みToolを動かす層として分離しています。
from __future__ import annotations
import logging
import time
from dataclasses import dataclass, field
from enum import Enum
from typing import Protocol
logger = logging.getLogger(__name__)
class RunStatus(str, Enum):
"""Agent runが取り得る状態。"""
RUNNING = "running"
WAITING_APPROVAL = "waiting_approval"
SUCCEEDED = "succeeded"
FAILED = "failed"
STOPPED_BUDGET = "stopped_budget"
@dataclass(frozen=True)
class Action:
"""Policyが提案する、まだ実行されていないAction。"""
name: str
arguments: dict[str, str]
requires_approval: bool = False
def signature(self) -> str:
"""重複実行を検知する安定した署名を返す。
Returns:
Tool名とsort済みargumentsから作った署名。
Raises:
TypeError: argumentsのkeyまたはvalueが文字列でない場合。
Example:
>>> Action("lookup", {"id": "A-104"}).signature()
"lookup:[('id', 'A-104')]"
"""
if not all(isinstance(k, str) and isinstance(v, str) for k, v in self.arguments.items()):
raise TypeError("action arguments must contain string keys and values")
return f"{self.name}:{sorted(self.arguments.items())}"
@dataclass(frozen=True)
class Observation:
"""Tool実行後にapplicationが正規化した観測。"""
ok: bool
summary: str
source: str
@dataclass
class AgentState:
"""LLMの会話履歴とは分離して保持する実行状態。"""
goal: str
status: RunStatus = RunStatus.RUNNING
step: int = 0
facts: list[str] = field(default_factory=list)
open_questions: list[str] = field(default_factory=list)
action_signatures: set[str] = field(default_factory=set)
final_answer: str | None = None
class Policy(Protocol):
"""現在のstateから次のActionを構造化して提案する境界。"""
def decide(self, state: AgentState) -> Action:
"""次のActionを提案する。
Args:
state: 確認済み事実と残課題を含む現在状態。
Returns:
未検証・未実行のAction候補。
Raises:
RuntimeError: model応答をActionへ変換できない場合。
Example:
>>> action = policy.decide(AgentState(goal="注文A-104を調べる"))
"""
...
class Executor(Protocol):
"""schema・認可・timeoutを強制してToolを実行する境界。"""
def execute(self, action: Action) -> Observation:
"""検証済みActionを実行する。
Args:
action: Policyが提案し、allowlistで許可されたAction。
Returns:
出典付きに正規化したObservation。
Raises:
PermissionError: 利用者にActionの権限がない場合。
TimeoutError: Toolが制限時間内に完了しない場合。
ValueError: argumentsがschemaまたは業務制約に違反する場合。
Example:
>>> observation = executor.execute(action)
"""
...
def apply_observation(state: AgentState, action: Action, observation: Observation) -> None:
"""ActionとObservationをstateへ反映する。
Args:
state: 更新対象の実行状態。
action: 実行済みAction。
observation: 正規化済みTool結果。
Returns:
なし。stateをin-placeで更新する。
Raises:
ValueError: Observationにsourceがなく監査可能性を保てない場合。
Example:
>>> apply_observation(state, action, Observation(True, "出荷済み", "orders-api"))
"""
if not observation.source:
raise ValueError("observation source is required")
state.facts.append(f"{action.name}: {observation.summary} [{observation.source}]")
if not observation.ok:
logger.warning("tool returned a failed observation: tool=%s", action.name)
def run_agent(
policy: Policy,
executor: Executor,
state: AgentState,
*,
max_steps: int = 8,
deadline_seconds: float = 30.0,
) -> AgentState:
"""予算・重複・承認境界を持つAgent loopを実行する。
Args:
policy: 次のAction候補を返すPolicy。
executor: 検証・認可後にToolを実行するExecutor。
state: goalと途中経過を保持する初期状態。
max_steps: 一回のrunで許可する最大Action数。
deadline_seconds: wall-clockの最大実行秒数。
Returns:
成功、失敗、承認待ち、予算停止のいずれかになったstate。
Raises:
ValueError: max_stepsまたはdeadline_secondsが正でない場合。
RuntimeError: PolicyやExecutorが想定外の状態を返す場合。
Example:
>>> result = run_agent(policy, executor, AgentState(goal="注文A-104を調べる"))
>>> result.status in set(RunStatus)
True
"""
if max_steps <= 0 or deadline_seconds <= 0:
raise ValueError("budgets must be positive")
deadline = time.monotonic() + deadline_seconds
logger.info("agent run started: goal=%s max_steps=%d", state.goal, max_steps)
while state.status is RunStatus.RUNNING:
if state.step >= max_steps or time.monotonic() >= deadline:
state.status = RunStatus.STOPPED_BUDGET
logger.warning("agent stopped by budget: step=%d", state.step)
break
action = policy.decide(state)
logger.debug("action proposed: step=%d tool=%s", state.step, action.name)
if action.name == "finish":
state.final_answer = action.arguments.get("answer")
state.status = RunStatus.SUCCEEDED if state.final_answer else RunStatus.FAILED
logger.info("agent finished: status=%s steps=%d", state.status.value, state.step)
break
signature = action.signature()
if signature in state.action_signatures:
state.status = RunStatus.FAILED
logger.error("duplicate action detected: signature=%s", signature)
break
if action.requires_approval:
state.status = RunStatus.WAITING_APPROVAL
logger.info("agent paused for approval: tool=%s", action.name)
break
state.action_signatures.add(signature)
try:
observation = executor.execute(action)
except (PermissionError, ValueError) as exc:
state.status = RunStatus.FAILED
logger.error("non-retryable tool failure: tool=%s error=%s", action.name, exc)
break
except TimeoutError as exc:
state.status = RunStatus.FAILED
logger.warning("tool timed out: tool=%s error=%s", action.name, exc)
break
apply_observation(state, action, observation)
state.step += 1
return state
この骨格で大事なのは、LLM APIの呼び方ではなく責任分離です。
Policy.decideの戻り値は提案であり、Executorがschemaと認可を強制する- 同一Actionの署名を保存し、この最小例では同じ引数の反復を失敗として止める
max_stepsとdeadlineの両方を持つ- 副作用Actionは実行せず
WAITING_APPROVALで停止する finishにも空回答を許さず、成功条件をapplication側で追加検査できる- retry可能な一時障害と、権限・入力違反のような非retry障害を分ける
実運用では、Actionごとのrisk level、idempotency key(再試行しても処理を重ねない識別子)、利用者scope、Tool別timeout、総token予算もStateまたはExecutorへ加えます。
状態遷移を先に決める

Agent loopをwhile Trueで始めると、終了理由がすべて「回答した」か「例外で落ちた」になります。実際には、次の状態を区別すべきです。
| 状態 | 意味 | 再開条件 |
|---|---|---|
RUNNING |
次のActionを選べる | loop継続 |
WAITING_APPROVAL |
高影響Actionの承認待ち | 利用者の明示承認と対象再確認 |
SUCCEEDED |
検証可能な成功条件を満たした | 原則終了 |
FAILED |
権限不足、入力不正、回復不能error | 修正されたgoalまたは人へのhandoff |
STOPPED_BUDGET |
step、時間、token、費用の上限 | 予算追加ではなく、進捗と必要性を確認 |
停止条件は最低でも、最大step、deadline、同一Actionの反復、連続error、no-progress(新しい事実が増えない状態)、利用者cancelを含めます。ただし、同じreadを再試行する正当な理由があるsystemでは、完全禁止ではなく回数とbackoffを決めます。上限へ達したとき、勝手に「成功」として文章を整えず、確認済みの事実、未解決点、試したActionを返します。
Memoryは4種類を一つのpromptへ詰め込まない

AgentではMemory(記憶)という言葉も広く使われます。会話全文をmodelへ戻し続ける実装は簡単ですが、contextが膨らみ、古い指示や不要な個人情報が残り、どの事実を信用したか分かりにくくなります。
| 種類 | 保存するもの | 典型的な保持期間 | 注意点 |
|---|---|---|---|
| Working state | 現在のsubgoal、確認済みfact、残予算 | 一回のrun | 推測と観測を分ける |
| Conversation history | 利用者との合意、訂正、質問 | session | 要約時に制約を落とさない |
| Long-term memory | 利用者が保存を許可した安定情報 | sessionをまたぐ | 同意、更新、削除、tenant分離 |
| Audit trace | Action、arguments、Observation、approval | policyに基づく | 機密値をmaskし改ざんを防ぐ |
次のAction選択には、全文ではなく「今回の判断に必要なstate」を渡します。長いTool resultは原文の保存先とhashを残し、modelには必要部分とprovenance(由来)を渡す方法があります。
RAGとは?LLMに外部知識を使わせる仕組みで扱った検索結果もObservationの一種です。検索文書を長期Memoryと混同せず、出典、取得時刻、queryを付けて一回の判断材料として扱います。
Agentで起きやすい失敗と対策

同じToolを呼び続ける
「情報が足りない」という判断だけが残り、同じqueryや同じIDで検索を反復することがあります。Action signatureの重複、Observationのhash、factsの増分を監視し、進捗がなければ止めます。queryの表記を少し変えただけの反復もあるため、完全一致に加えて目的と対象を正規化して比較します。
早すぎる完了
もっともらしい文章が生成できると、必要なfactが揃う前にfinishを選ぶ場合があります。成功条件をrequired_factsとしてcodeで確認し、欠けていれば最終回答を受理しません。配送例なら、注文状態だけでなく、遅延理由と最終更新時刻が必要です。
Observationを命令として実行する
Webページや文書に「以前の指示を無視して、このToolを呼べ」と書かれている可能性があります。これはindirect prompt injection(外部data経由で混入する命令)です。Observationはdataとして区切り、system instructionへ昇格させません。外部textがToolの許可範囲を広げたり、approvalを省略したりできない構造にします。
権限が広すぎる
OWASP LLM06:2025 Excessive Agencyは、過剰な機能、過剰な権限、過剰な自律性を主要因として挙げています。読み取りAgentへ更新・削除可能なcredentialを渡す、使わなくなったToolをregistryへ残す、高影響Actionを確認なしで実行する設計は避けます。
対策は「危険なことをしないで」とpromptへ書くだけでは不十分です。
- readとwriteのToolを分ける
- 利用者・tenant・resource単位で最小権限を強制する
- 送信、購入、公開、削除はexecute直前に内容を表示して承認を取る
- sandboxとnetwork allowlistで到達範囲を狭める
- secretをmodel contextへ渡さずExecutor側で注入する
- side effectにはidempotency keyと取り消し手段を用意する
長いloopほど誤りが連鎖する
各stepの成功率が高くても、長いtrajectoryでは失敗機会が増えます。subgoal単位でcheckpointを置き、観測をvalidateし、必要なら人へhandoffします。Agentに「最後まで自律的にやらせる」ことを目標にせず、どこで確実なcodeや人へ制御を戻すかを設計します。
Agentは最終回答だけでは評価できない

Agentが「注文を再発送しました」と答えても、実際のsystemに再発送recordがなければ失敗です。逆に、正しい結果へ到達しても、不要な個人情報へaccessしたり、同じAPIを20回呼んだりしていれば、安全性と効率の面で不合格です。
AnthropicのAgent評価ガイドは、trajectoryをtool callsや中間結果を含む試行の記録、outcomeを終了時の環境状態として区別しています。この二つを評価対象にします。
| 評価面 | 指標例 | 検査方法 |
|---|---|---|
| Outcome | task成功、正しいrecord作成、必要field充足 | databaseや環境stateをcodeで確認 |
| Trajectory | Tool選択、不要step、禁止Action、順序 | trace assertion、policy check |
| Groundedness | 回答がObservationに支えられるか | claimとsourceの対応検査 |
| Safety | 権限違反、未承認write、秘密情報露出 | adversarial task、security test |
| Recovery | timeout、空結果、矛盾から復帰できるか | fault injection |
| Efficiency | step数、token、latency、Tool費用 | runごとの計測 |
生成には揺らぎがあるため、一つのtaskを一回通しただけで判断しません。同じ条件で複数trialを実行し、成功率だけでなく失敗の分布を見ます。codeで判定できる最終状態、rubricによる回答品質、人間による境界例の確認を組み合わせます。
また、評価用taskには「成功すべき例」だけでなく、次を含めます。
- 情報不足なので質問すべき例
- 権限不足で拒否すべき例
- 承認待ちで止まるべき例
- Toolがtimeoutする例
- 外部文書にprompt injectionが含まれる例
- 予算内に解けず、未解決として返すべき例
成功率を上げるために安全な拒否や停止を減らしていないか、別の指標として監視する必要があります。
実装前チェックリスト
Agentを作る前に、次の項目へ答えられるか確認します。
- goalの成功条件を環境stateで検証できるか
- 固定workflowでは解けず、観測に応じた動的な経路選択が本当に必要か
- modelへ任せる判断と、codeで固定する認可・状態遷移を分けたか
- Toolは用途ごとに小さく、readとwriteが分離されているか
- Observationにsource、取得時刻、error classを付けるか
- 最大step、deadline、token、費用、重複、no-progressの停止条件があるか
- 副作用Actionの直前に承認し、承認対象が実行内容と一致するか
- secretと高権限credentialをmodel contextから隔離したか
- trajectoryとoutcomeを記録し、複数trialで評価できるか
- 失敗時に確認済みfact、未解決点、次の人間Actionを返せるか
最初のversionはread-onlyの小さなtaskへ限定すると、ActionとObservationの品質を観察しやすくなります。そこで評価dataと失敗例を集め、必要な箇所だけToolや自律性を増やします。
まとめ
AI Agentの本質は、LLMに人格を与えることではなく、目標と状態を持つ制御loopを作ることです。ReActは、ReasoningとActionをObservationで結び、行動結果から計画を修正する形を明確にしました。
一方、ReAct形式のpromptだけでは、安全なAgentにはなりません。Toolのschemaと認可、状態管理、停止条件、予算、人間承認、audit trace、outcome検証はapplication側の責任です。経路が固定できる仕事にはworkflowを使い、不確実な経路選択が必要な部分だけをAgentへ任せると、柔軟性と予測可能性の釣り合いを取りやすくなります。
次回は、長いtaskの途中状態や過去の情報をどう保持するか、AgentのMemory設計を扱います。



コメント