AIエージェントの設計思想:Chain機構と手続き記憶

スパイスコード CTO の櫻木です (X: https://x.com/ysrhspyoshi)。これまでの記事では、AI Agentにおける「コンテキストエンジニアリング」の重要性と、tool chain toolを用いた実装アプローチ、そして実際のブラウザ自動化タスクにおける定量的な実験結果を報告してきました。

前回までの記事:

今回は、私たちがエージェント設計の際に参考にしている理論的基盤について、認知科学神経科学の観点から説明します。Chain機構と自己修復機能のハイブリッドアーキテクチャは、人間の脳における「二重過程(Dual-Process)」の制御戦略を参考に設計されています。

目次

  1. なぜ理論的基盤が重要なのか
  2. Chain機構の神経科学的基盤:手続き記憶とチャンキング
  3. 自己修復機能の神経科学的基盤:エラーモニタリングと認知制御
  4. 二重過程理論:不確実性に基づく調停メカニズム
  5. 実装への示唆:生物学的知能から学ぶ設計原則
  6. まとめ

1. なぜ理論的基盤が重要なのか

前回の記事で報告したように、Method C(Chain方式)は Method A(毎回探索する方式)と比較して3.4倍高速、コスト1/5.5という結果が得られました。このアーキテクチャの効率性は、単なる工学的な最適化だけでなく、生物学的知能の仕組みを参考にした設計に基づいています。

1.1 「固定のワークフローとの違い」という問い

Chain機構の説明をすると、よく「それは単なる固定のワークフローでは?」という質問を受けます。確かに、決定的な操作シーケンスを事前定義して実行するという点では似ています。

しかし、決定的な違いがあります。固定のワークフローは、予期せぬ状況(サプライズ)に対して脆弱です。UIが変わる、要素が見つからない、タイムアウトが発生する——こうした状況で、固定ワークフローは単に失敗します。

私たちのChain機構は、人間の手続き記憶(Procedural Memory)を参考にしています。人間は、習熟した作業を無意識に(低コストで)遂行しますが、予期せぬ障害に直面した時、瞬時に宣言的記憶(Declarative Memory)エピソード記憶(Episodic Memory)を動員し、柔軟に行動を変容させます。これは、複数の記憶システムが協調する脳のアーキテクチャによって実現されています。

本記事では、この「低コストな自動実行」と「柔軟な適応」の両立が、どのような認知神経科学的メカニズムに基づいているのかを説明します。

1.2 生物学的知能を参考にした設計

生物学的脳は、限られた計算リソース(エネルギー)の中で、複雑な環境下での意思決定と行動実行を数億年かけて最適化してきました。その結果として獲得された神経メカニズムは、効率性と柔軟性のトレードオフに対する参照可能な解となっています。

私たちのChain機構と自己修復機能は、これらの理論を参考に設計しており、その理論的背景を理解することで:

  • 設計判断の根拠が明確になる:「なぜこの実装が正しいのか」を説明できる
  • 改善の方向性が見える:脳の仕組みから、次のステップへのヒントを得られる
  • 未知の問題への対応力が上がる:原理原則を理解していれば、新しい課題にも応用できる


2. Chain機構の神経科学的基盤:手続き記憶とチャンキング

第1回の記事で紹介したChain機構は、一連の操作を単一の実行単位としてカプセル化し、LLMの推論を介さずに高速実行する仕組みです。これは、生物学的脳における「手続き記憶(Procedural Memory)」の形成プロセスを参考にしています。

2.0 記憶システムの分類と役割

脳の記憶システムは大きく以下のように分類されます:

手続き記憶(Procedural Memory)

運動技能や習慣(例:自転車の乗り方、タイピング)。意識的なアクセスが不要で、高速・低コスト。大脳基底核を中心とした神経回路で実現されます。

宣言的記憶(Declarative Memory)

言語化可能な知識。意識的なアクセスが必要で、柔軟だがコストが高い。海馬と大脳皮質の連携で実現されます。

  • エピソード記憶(Episodic Memory):個人的な経験(例:昨日のミーティングで何が起きたか)
  • 意味記憶(Semantic Memory):一般的な事実(例:東京は日本の首都)

私たちのアーキテクチャとの対応:

固定のワークフローは「手続き記憶のみ」の状態に相当します。一方、私たちのアーキテクチャは、通常時は手続き記憶(Chain)で高速実行し、エラー時には宣言的記憶(LLMの知識)やエピソード記憶(過去の操作ログ)を参照して適応するという、複数の記憶システムの協調を実装しています。

この協調メカニズムこそが、固定ワークフローにはない「柔軟性」を実現する鍵となります。

重要な注意:機能局在主義について

以降の説明では便宜上、特定の脳領域と機能を対応付けて説明しますが、これは古典的な機能局在主義(functional localization) の立場を取るものではありません。現代の神経科学が示すように、脳機能は特定の領域に局在するのではなく、広範な皮質・皮質下領域を含む分散的なネットワークとして実現されています。例えば、大脳基底核による習慣学習も、前頭前皮質視床線条体など複数の領域が協調して初めて機能します。

ここで示す脳領域と機能の対応関係は、各神経回路が担う中心的な役割を概念的に示したものであり、設計の着想を得るための理論的な参照点として理解してください。私たちのAIアーキテクチャも同様に、各コンポーネントが独立して機能するのではなく、相互に連携するシステムとして設計されています。

2.1 大脳基底核によるシーケンス学習

MIT Ann Graybielらの研究は、ラットが迷路タスクを学習する過程で、線条体大脳基底核の一部)の神経活動パターンが変化することを示しました[1][2]。

学習初期(探索フェーズ)

  • 神経活動は動作全体に分散して発火
  • 高い認知負荷、逐次的な意思決定
  • AIの対応:Method A(毎回UIを探索)、Agentic Loop

学習後期(習慣フェーズ)

  • 神経活動はシーケンスの開始と終了時に集中
  • 低い認知負荷、バリスティック(弾道的)な実行
  • AIの対応:Method C(Chain実行)

この変化は「チャンキング(Chunking)」と呼ばれ、複雑な動作シーケンスが脳内で単一の「実行ユニット」として再表現されたことを意味します。

┌─────────────────────────────────┐
│    学習初期(探索フェーズ)      │
├─────────────────────────────────┤
│ ステップ1 → 推論 → 実行          │
│ ステップ2 → 推論 → 実行          │
│ ステップ3 → 推論 → 実行          │
│         (高コスト)               │
└─────────────────────────────────┘
              ↓ チャンキング
┌─────────────────────────────────┐
│    学習後期(Chain化)           │
├─────────────────────────────────┤
│ Chain開始 → [1+2+3] → Chain終了  │
│         (低コスト)               │
└─────────────────────────────────┘

2.2 ACT-R理論:宣言的知識から手続き的知識へ

認知アーキテクチャACT-R(John Anderson)は、人間の知識が「宣言的知識」から「手続き的知識」へと変換されるメカニズムを定式化しています[3][4]。

プロダクション・コンパイル(Production Compilation)

  1. 構成(Composition):連続して実行される複数のルールを結合

    • Before: ルールA(検索)→ ルールB(実行)
    • After: ルールAB(一括実行)
  2. 手続き化(Proceduralization):変数を定数に置き換え、メモリアクセスを不要化

    • Before: IF 目標=ログイン AND 要素=変数x THEN x を検索して実行
    • After: IF 目標=ログイン THEN #login-button をクリック(検索なし)

私たちのChain生成プロセスとの対応:

# 第1回記事で紹介したChain生成の流れ
1. Agenticモードで操作を実行(探索)
2. 操作ログを解析
3. playwright-mcpのbrowser_run_codeを含むChainとして保存
4. 次回以降はChain IDを参照するだけで実行(コンパイル済み)

これは、ACT-Rのプロダクション・コンパイルを、LLMとブラウザ自動化の文脈で実装したものです。

2.3 階層的強化学習(HRL):オプションとしてのChain

強化学習の分野では、行動の階層化は「オプション(Options)」として定式化されています[5][6]。オプションは以下の3要素で定義されます:

  • 開始条件(I):どの状態でこのオプションが実行可能か(例:ログインページ)
  • 方策(π):オプション内部での行動選択ルール(例:Chainに含まれる一連の操作)
  • 終了条件(β):いつこのオプションを終了するか(例:ダッシュボード到達)

Chain機構の数学的表現:

Chain_Login = <I, π, β>
  I = {state: login_page}
  π = [enter_username, enter_password, click_submit]
  β = {state: dashboard}

Botvinick (2012)の研究によれば、前頭前皮質(PFC)が現在の文脈に応じて適切なオプション(Chain)を選択し、大脳基底核がその内部の具体的なアクション(チャンク)を実行するという役割分担が行われています[7]。

実装への示唆:

  • Chainを選択するための「メタエージェント」の設計
  • Chain間の依存関係を考慮した階層的構造の導入

3. 自己修復機能の神経科学的基盤:エラーモニタリングと認知制御

Chain機構は効率的ですが、環境変化に対して脆弱です。この問題に対処するのが「自己修復(Self-Repair)」機能であり、これは脳における「認知制御(Cognitive Control)」システムに対応します。

3.1 前帯状皮質(ACC)によるエラー検知

脳波(EEG)研究において、エラー発生直後(50-100ms後)に前頭部で観測されるERN(Error-Related Negativity)という信号の発生源は、前帯状皮質(ACC)であることが特定されています[8][9]。

ACCは単なるエラーだけでなく、「予測誤差(Prediction Error)」全般を監視しています。Haydenらの研究は、予期せぬ結果がACCニューロンを活性化させ、行動戦略の変更を引き起こすことを示しました[10]。

AIエージェントにおける実装:

# Chain実行中の予測誤差検知
try:
    execute_chain(chain_id)  # 期待:正常実行
except NoSuchElementException:
    # 実際:要素が見つからない → 予測誤差
    trigger_self_repair()  # ACCによる介入要請に相当

3.2 期待される制御価値(EVC)理論

Shenhav & Botvinick (2013)のEVC理論によれば、ACCは「認知制御を発動するコスト」と「それによって得られる報酬の増加分」を常に天秤にかけています[11]。

通常時(Chain実行):
  - 制御コスト: 低
  - 成功確率: 高
  → 自動処理を継続

エラー時(予測誤差大):
  - 成功確率: 低下
  - 制御を発動した場合の期待価値: 上昇
  → Agenticモードへ切り替え

この理論は、常時LLMを使うのではなく、エラー時のみ高コストな推論を行うという設計方針に対応しています。

3.3 頭頂葉による高速な微調整

より軽微なエラーに対しては、大規模な再プランニングではなく、局所的な調整で対応できます。後頭頂皮質(PPC)は、意識的な介入なしに即座に行動を修正する「オートパイロット」機能を持つことが示されています[12][13]。

実装例:

# 軽微な変動への対応(小脳・頭頂葉的)
element = wait_for_element(selector, timeout=5)  # 待機
if not element:
    element = find_nearby(selector, radius=10)  # 近傍探索

これにより、完全な再プランニング(前頭前皮質的)を発動する前に、低コストな修正で対応する階層的なエラー処理が可能になります。


4. 二重過程理論:不確実性に基づく調停メカニズム

ここまで、Chain(習慣)とAgentic(熟慮)を個別に論じてきましたが、最も重要なのはこれらをいかに統合・制御するかです。

4.1 モデルフリーとモデルベースの競合

Daw, Niv, & Dayan (2005)の研究によれば、脳内には二つの並列する強化学習システムが存在します[14][15]:

システム 神経基盤 特徴 AI対応
モデルフリー(MF) 背外側線条体 低コスト、環境変化への適応が遅い Chain機構
モデルベース(MB) 前頭前皮質、海馬 高コスト、柔軟で適応的 Agentic推論

4.2 ベイズ的不確実性による調停(Arbitration)

脳はこれらのシステムをランダムに切り替えるのではなく、各システムの予測の「不確実性(Uncertainty)」を常に推定し、より確実性の高いシステムに制御権を委ねています[14]。

安定期(エラー率低):
  MFの不確実性: 低 → Chain実行
  MBの不確実性: 高(不要)

不安定期(エラー発生):
  MFの信頼度: 急低下
  MBの相対的信頼性: 上昇 → Agenticへ切替

私たちのアーキテクチャとの対応:

# デフォルトでChainを試行
confidence_chain = estimate_chain_reliability(chain_id, recent_errors)

if confidence_chain > threshold:
    execute_chain()  # モデルフリー的実行
else:
    execute_agentic()  # モデルベース的探索

第2回の記事で報告したように、Method Cは標準偏差4.0秒という安定性を示しました。これは、Chainの信頼性が維持されている限り、低コストな決定的実行を継続できることを意味します。

4.3 代理試行錯誤(VTE):海馬による未来シミュレーション

新規環境やChainが失敗した場合、エージェントはAgenticモードに移行します。この時、複数の候補アクションを評価する必要があります。

David Redishらの研究によれば、ラットが迷路の分岐点で左右を見やる「代理試行錯誤(VTE)」の最中、海馬の場所細胞が未来の経路を高速で再生(スイープ)する現象が観測されています[16][17]。

VTEの構成要素 機能的役割 AIエージェント
停止(Pause) 自動行動の抑制 Chainの中断
場所細胞のスイープ 将来経路のシミュレーション Tree of Thoughts / Planner
評価(Valuation) 報酬予測 LLMによるスコアリング

実装例:

# Planner / MCTS的なアプローチ
candidates = extract_possible_actions(dom)
for action in candidates:
    simulated_outcome = llm.predict(action, current_state)
    score = evaluate_goal_proximity(simulated_outcome)
best_action = max(candidates, key=score)


5. 実装への示唆:生物学的知能から学ぶ設計原則

これらの神経科学的知見から、次のような設計原則が導かれます。

5.1 階層的なエラー処理

前回の実験で、自己修復フェーズのコストが初回実行より低い傾向が見られました(例: Site B Chain で$1.74 → $0.30)。これをさらに洗練させるために:

レベル1(小脳・頭頂葉的):Wait / Retry / 近傍探索
  → コスト: 極小、成功率: 中

レベル2(ACC的):Chain内の部分修正
  → コスト: 小、成功率: 高

レベル3(PFC的):完全な再プランニング
  → コスト: 大、成功率: 最高

実装案:

try:
    execute_chain()
except MinorError:
    retry_with_wait()  # レベル1
except ChainError:
    repair_chain_step()  # レベル2
except MajorError:
    full_agentic_replanning()  # レベル3

5.2 成功パターンの自動コンパイル

ACT-Rのプロダクション・コンパイルを参考に、Agenticモードでの成功した修復シーケンスを新しいChainとして保存する機能を検討しています。

# 修復成功時
if self_repair_succeeded:
    new_chain = compile_from_trace(repair_log)
    save_chain(new_chain, context=current_task)
    # 次回から同じエラーには新Chainで対応

5.3 メタ認知的パラメータの動的調整

脳内ドーパミンレベルが探索と活用のバランスを調整するように、エラー率に応じて探索の「温度」を動的に調整するメタパラメータを導入:

# エラー率が高い → 探索を増やす
exploration_temp = base_temp * (1 + error_rate)

# エラー率が低い → Chainを優先
if error_rate < threshold:
    prefer_chain = True

5.4 Chainの選択的抑制(RIF)

認知心理学における「検索誘導性忘却(Retrieval-Induced Forgetting)」の知見から、失敗したChainを積極的に抑制する機能:

# 失敗したChainの信頼度を下げる
if chain_failed:
    reliability_scores[chain_id] *= decay_factor
    mark_as_bad_path(chain_id)

これにより、エージェントは過去の失敗(保続エラー)にとらわれず、新しい解を効率的に探索できます。


6. まとめ

6.1 理論と実践の統合

第1回の記事で提示したコンテキストエンジニアリングの設計思想、第2回で実証した定量的な性能改善、そして今回示した認知科学神経科学的な理論的基盤は、一本の線でつながっています。

アーキテクチャの設計方針:

  1. 生物学的な制御戦略の参照:脳における「習慣と熟慮の使い分け」を参考にした設計
  2. 計算論的なトレードオフの考慮:コストと柔軟性のバランスをベイズ的調停理論に基づいて実装
  3. 階層的な制御構造:低次の自動処理から高次の計画まで、段階的に対応

なお、本記事で示した脳領域とAIコンポーネントの対応関係は、あくまで設計の着想源としての概念的な対応です。機能局在主義的に特定の機能が特定の領域に厳密にマッピングされているという考えは支持していません。

6.2 実験結果との対応

第2回の実験結果は、これらの理論に基づく設計方針を支持するものとなっています:

設計方針 実験結果
Chainによる高速化 Method C: 26.3秒 vs A: 88.2秒(3.4倍)
決定的実行による安定性 Method C: σ=4.0秒 vs A: σ=25.9秒(1/6)
初回学習コストの償却 2-3回で損益分岐、年間$100以上削減
選択的な高コスト処理 Method C: $0.188/回 vs A: $1.034/回(1/5.5)

6.3 今後の展望

今後、認知科学神経科学の知見をさらに参考に、以下の方向で開発を進めていく予定です:

  1. エピソード記憶の活用:海馬的な経験の索引化と再利用
  2. メタ学習の導入前頭前皮質的な「学習の学習」
  3. 多Agent協調:脳の機能分化に倣った役割分担

また現在の開発状況では、ツールセットを事前定義してあるため、小さな問題空間向けの実装としては必要十分ですが、人の脳と比較すると陳述記憶との連携が不十分で、大きな問題空間におけるタスクでは課題が残ります。 大きな問題空間向けの実装として陳述記憶との連携強化を通し、直面したタスクに合わせて自身で問題空間を調整する仕組みを構築することを検討・検証しています。

6.4 終わりに

AI Agentの実装において、理論的裏付けのある設計は、単なる試行錯誤を超えた確実性と予測可能性を提供します。

認知科学神経科学の知見は、以下のような実践的な価値を提供します:

  • 設計判断の根拠:「なぜこのアーキテクチャなのか」を説明できる
  • デバッグの指針:どこが壊れているか、脳のモデルから推測できる
  • 拡張の方向性:次に実装すべき機能が見えてくる

スパイスコードでは、このような学術的な思想に基づいたAI Agent開発を推進しています。興味を持った方、一緒にチャレンジングな開発に取り組みたい方は、ぜひお話ししましょう!

https://corp.spicescode.co.jp/


主要引用文献

神経科学・認知神経科学

  1. Graybiel, A. M. (1998). The basal ganglia and chunking of action repertoires. Neurobiology of Learning and Memory.
  2. Jin, X., & Costa, R. M. (2010). Shaping action sequences in basal ganglia circuits. Current Opinion in Neurobiology.
  3. Anderson, J. R. (1993). Rules of the mind. Lawrence Erlbaum Associates.
  4. Taatgen, N. A., & Anderson, J. R. (2002). Production compilation: A simple mechanism to model complex skill acquisition. Human Factors.

強化学習・計算論的神経科学:

  1. Sutton, R. S., Precup, D., & Singh, S. (1999). Between MDPs and semi-MDPs: A framework for temporal abstraction in reinforcement learning. Artificial Intelligence.
  2. Botvinick, M. M. (2012). Hierarchical reinforcement learning and decision making. Current Opinion in Neurobiology.
  3. Botvinick, M. M., Niv, Y., & Barto, A. C. (2009). Hierarchically organized behavior and its neural foundations: A reinforcement-learning perspective. Cognition.

エラーモニタリング・認知制御:

  1. Botvinick, M. M., Cohen, J. D., & Carter, C. S. (2004). Conflict monitoring and anterior cingulate cortex: An update. Trends in Cognitive Sciences.
  2. Hayden, B. Y., et al. (2011). Surprise signals in anterior cingulate cortex: Neuronal encoding of unsigned reward prediction errors driving adjustment in behavior. Journal of Neuroscience.
  3. Shenhav, A., Botvinick, M. M., & Cohen, J. D. (2013). The expected value of control: An integrative theory of anterior cingulate cortex function. Neuron.

頭頂葉・小脳:

  1. Desmurget, M., et al. (1999). Role of the posterior parietal cortex in updating reaching movements to a visual target. Nature Neuroscience.
  2. Pisella, L., et al. (2000). An 'automatic pilot' for the hand in human posterior parietal cortex. Nature Neuroscience.

意思決定・調停メカニズム:

  1. Daw, N. D., Niv, Y., & Dayan, P. (2005). Uncertainty-based competition between prefrontal and dorsolateral striatal systems for behavioral control. Nature Neuroscience.
  2. Redish, A. D. (2016). Vicarious trial and error. Nature Reviews Neuroscience.
  3. Cisek, P. (2007). Cortical mechanisms of action selection: The affordance competition hypothesis. Philosophical Transactions of the Royal Society B.

検索誘導性忘却:

  1. Tempel, T., et al. (2013). Retrieval-induced forgetting in motor memory. Psychological Science.

関連記事


高階関数ツールを使ったAI Agent検証 - ブラウザ操作自動化タスクで3.4倍高速・コスト1/5を実現

スパイスコード CTO の櫻木です. 前回の記事では, スパイスコードでのAI Agent 開発におけるコンテキストエンジニアリングの考え方, 高階関数的なツール設計, 安全なコード実行, そして自己学習サイクルの重要性を紹介しました. 今回はその延長として, 実際のブラウザ操作タスクを題材に, その効果を検証したアウトプットイメージを共有したいと思います.


1. はじめに

前回の記事: tech-blog.localmet.com

前回の記事では, AI Agent開発における

  • コンテキストエンジニアリングの重要性
  • 高階関数的なツール設計とコード実行によるアプローチ
  • Artifact IDによる参照
  • 自己修復による継続的改善

を紹介しました. 今回はその設計思想を実際のブラウザ業務自動化タスクに適用し, ウォームアップ+90回の試行を通じて3つの実行方式を定量的に比較した実験結果を報告します.

実験のポイントは毎回LLMがUIを探索するのではなく, 初回に操作手順を学習し, 以降は決定的に実行する仕組みが, 実運用でどれだけ効果を発揮するかです.

1.1 実験の目的

  • ブラウザ操作タスクにおける 3 方式の性能比較
  • 成果物の回収精度 (欠損・重複なし) の検証
  • 操作ログを再利用したチェーン再生の有効性評価
  • tool chain toolの設計が, 実運用でどれだけ効くかの実証

1.2 概要

前回記事で紹介したコンテキストエンジニアリングの設計思想を, 実際のブラウザ業務自動化タスクに適用し, 3つの実行方式を比較しました.

比較した3方式:

  • Method A (Baseline): 毎回UIを探索する従来方式
  • Method B (Workflow): 操作ログから内製CDPツールのワークフローを生成し, 再生
  • Method C (Chain): 操作ログからplaywrightコードを生成し, 前回記事で紹介したtool chain toolで再生

実験結果 - Method Cが最も優れた性能を示しました

  • 速度: Method Aと比較して3.4倍高速(88.2秒 → 26.3秒)
  • コスト: Method Aと比較して約1/5.5($1.034/回 → $0.188/回)
  • 安定性: 標準偏差4.0秒と最も小さく、変動係数15.3%で予測可能性が高い

主要な示唆:

  • 「探索」と「実行」の分離により、LLMの推論能力を判断に集中させ、実行は決定的な機構に任せることで高速・低コスト・安定を実現
  • 初回に操作手順を学習すれば、以降は機械的に実行可能(学習への投資として正当化)
  • 複雑なサイトほど連鎖的実行のメリットが大きい(Site B: 113秒 → 31.5秒)
  • 安定的なワークフロー実行とエージェントによる柔軟性の担保の良いとこどり

2. 実験の全体設計

2.1 対象タスク

  • 対象期間の発注書や証憑をブラウザ経由でダウンロードする業務を想定
  • 欠損や重複がない状態 (MECE) で成果物を取得することがゴール

2.2 比較する 3 方式

  • Method A (Baseline): 毎回UIを探索し操作を逐次実行する従来方式
  • Method B (Workflow): 操作ログから内製CDPツールのワークフローを生成し, 再生
  • Method C (Chain): 操作ログからplaywrightコードを生成し, 前回記事で紹介したtool chain toolを使って再生

※ 方式BとCは思想としては近く, 実装ランタイムの違いが主な差分です.

methods

2.3 試行条件

  • 各方式 × 3サイト × 10回の試行
  • 合計試行数: 90回(各方式30回ずつ)
  • 収集する指標: 実行時間, LLMコスト, 成功率, 実行の安定性(標準偏差
  • 使用LLMモデル: anthropic.claude-sonnet-4-20250514-v1:0 (Claude Sonnet 4.5)

全ての試行は同一のタスク「指定月の発注書を全てダウンロード」を入力プロンプトとし,各サイトの特性(ページ構造,認証方式,ダウンロードフロー)が異なる環境で評価しています.

重要な前提条件:

  • Method A: 毎回ゼロからUI探索を実行(学習なし)
  • Method B・C: 事前に初回実行と自己修復によってワークフロー/チェーンが確立された状態での実行を評価

つまり,Method BとCの実験結果は「既に学習済みの手順を再生する性能」を測定したものです.初回実行や自己修復のコストは別途5.4節で補足データとして記載しています.


3. エージェントの設計思想

前回の記事で紹介した「コンテキストエンジニアリング」の考え方は, 今回の実験でも中心的な役割を果たします.

  • 入出力を最小化し, 大量データを LLM に流し込まない
  • 中間結果は外部成果物として保存し, 参照は ID のみ
  • エージェントが複雑な処理を直接抱え込まず, ツールの連鎖で解決する

この設計により, 長い UI 操作や大量の成果物処理でも, LLM の負荷と失敗率を抑制を期待します.

context_compression_avoidance


4. エージェントとツールの概要

4.1 共通ツール

  • デスクトップ共有ツール: waylandスタックで構成される仮想ディスプレイをnovncを利用することでエージェントが見る画面をユーザに共有する機能
  • Artifact系ツール: LLMに大量データを渡さず,IDのみで参照する機能の提供. コンテキスト圧縮ツール
  • ログイン情報取得ツール: ログイン用のid,passwordなどを安全に扱うためのツール
  • 2要素認証対応ツール: totpやメールでの2段階認証を解くためのツール

4.2 3種の方式

4.2.1 MethodA

  • 役割: UIを毎回探索して操作を実行
  • 主要ツール:
    • Playwright MCPツール群(ブラウザ操作の基本)
  • 特徴: ReActパターンで毎回LLMが推論しながらツールを選択・実行

4.2.2 MethodB

  • 役割: ワークフロー(Blueprint)を管理・実行
  • 主要ツール:
    • 既存ワークフロー取得ツール
    • 内製CDPワークフロー実行ツール
    • 操作ログ解析ツール
    • ログからワークフロー生成・保存を行うツール
  • サブエージェント
    • 失敗時のフォールバック用エージェント(探索的再実行)
    • ワークフロー修復エージェント
  • 特徴: 決定的な操作シーケンスをJSON形式で保存・再生

4.2.3 MethodC

  • 役割: browser_run_codeを含む高階関数連鎖をtool chain toolを使い実行
  • 主要ツール:
    • tool chain tool(探索モードでの操作収集も兼ねる)
    • 操作ログ解析ツール
    • Chainの取得・保存などを行う管理ツール
    • Playwright MCPツール群(探索モード用)
  • サブエージェント
    • 解析された操作ログからtool chain toolへのinputを生成するエージェント
    • tool chain修復エージェント
  • 特徴: playwright-mcpbrowser_run_code を呼び出す高階関数としてtool chain toolを利用. 探索モードではplaywright-mcpの通常ツールで操作収集を行う

Method CにおけるAgent-Tool連携の典型的なフロー:

  1. 初回実行(探索フェーズ)

    • BrowserChainAgent: Chain実行ツール呼び出し
    • 既存Chainなし → playwright-mcpで操作を実行・ログ収集
    • 操作ログを解析しサブエージェントへartifact_idを渡す
    • サブエージェントが browser_run_code呼び出しを含む高階関数連鎖を生成(Artifact化)
    • Chain保存: ArtifactをChainとして保存
  2. 2回目以降(実行フェーズ)

    • BrowserChainAgent: Chain実行ツール呼び出し
    • 既存Chain取得 → tool chain toolで直接実行
    • LLMの推論は最小限(「このChainを実行」のみ)
  3. 失敗時(自己修復フェーズ)

    • Chain実行失敗 → サブエージェントに修復依頼
    • 修復用のエージェントがステップバイステップで操作を実行し、エラーを元にchainの修復を実施
    • 新しいChainを生成・保存

ブラウザ操作タスクでは, 様々な外的要因により, 決定的な実行が毎回成功する保証はありません. その要因がサイト側のUI変更であるのか, 一時的な障害起因であるのか, はたまたネットワーク障害要因なのか, 自律的に検証・修復を実施できることでLLMの柔軟性と決定的な実行の高速性・安定性を両立しようという試みです.

4.3 特徴的なツール

4.3.1 高階関数的なツール

  • ワークフローJSONを引数に取り,CDP経由で実行するツール (MethodB)
  • playwright-mcpbrowser_run_code: playwrightコードを引数に取り,Playwright操作を実行 (MethodC)
  • playwright-codeを含んだ「操作手順」を引数として受け取るメタツール (MethodC)

4.3.2 自己修復ツール

  • ワークフロー失敗時に差分修正するツール
  • ワークフローが使えない場合に探索的実行に切り替えるツール
  • UI変更への自動対応を実現

5. 実験結果

5.1 実行時間の比較

5.1.1 エージェント別の比較

全90回の試行から得られた実行時間の統計は以下の通りです.

方式 平均 中央値 最小 最大 標準偏差
A (Baseline) 88.2秒 78.8秒 54.9秒 172.6秒 25.9秒
B (Workflow) 40.2秒 37.2秒 33.8秒 54.5秒 5.4秒
C (Chain) 26.3秒 24.2秒 21.5秒 33.1秒 4.0秒

performance_summary

主要な結果:

  • Method Cは Method Aと比較して3.4倍高速(88.2秒 → 26.3秒)
  • Method Bは Method Aと比較して2.2倍高速(88.2秒 → 40.2秒)
  • Method Cは最も安定しており,標準偏差はMethod Aの約1/6(25.9秒 → 4.0秒)

5.1.2 サイト別の内訳

各サイトの特性により,実行時間には顕著な違いが見られました.

サイト Method A Method B Method C
Site A 78.4秒 37.6秒 23.3秒
Site B 112.8秒 47.0秒 31.5秒
Site C 73.4秒 36.1秒 24.1秒

site_breakdown

Site Bは全方式で実行時間が長い傾向にあり,これはページ構造の複雑さや待機時間の影響と考えられます.一方で,Method CはSite Bでも31.5秒と,Method AのSite A/C(最速サイト)よりも高速です.

5.1.3 個別試行のばらつき

各試行の実行時間とコストの関係を散布図で可視化しました.

scatter

Method Aは試行ごとのばらつきが大きく,特にSite Bでは54秒から173秒まで約3倍の差があります.これは,LLMが毎回UIを探索するため,推論経路が安定しないことが原因です.

一方,Method BとCは初回にワークフロー/チェーンを生成した後は決定的な実行が可能なため,ばらつきが小さく安定しています.

5.2 LLMコストの比較

5.2.1 エージェント別の比較

各方式のLLM利用コスト(USD)を比較しました.

方式 平均コスト/回 総コスト(30回) Aとの比率
A (Baseline) $1.034 $31.02 1.0x
B (Workflow) $0.262 $7.87 0.25x(1/4
C (Chain) $0.188 $5.63 0.18x(1/5.5

cost_comparison

Method Cは Method Aと比較してコストが約1/5.5と圧倒的に経済的です.これは,チェーン実行時にはLLMの推論が最小限で済むためです.

5.2.2 時間とコスト

time_cost_tradeoff

この散布図から,Method Cが「高速かつ低コスト」の理想的な領域に位置していることが分かります.Method Aは全ての試行が右上(遅くて高コスト)に分布しています.

5.3 成功率と安定性

成功率(全30回中の成功数):

  • Method A: 96.7%(29/30)
  • Method B: 100%(30/30)
  • Method C: 100%(30/30)

Method BとCは全試行で成功しました.Method Aは1回の失敗があり,実行時間が54.9秒となり,早期に処理が終了しました.これは,毎回UIを探索する方式特有の不安定性を示しています.

一方,Method BとCは既に確立された手順を実行するため,より高い信頼性を示しました.

注意: 今回の実験は各方式30回の試行であり,統計的に十分な回数とは言えません.特にMethod BとCの100%成功率は,より多くの試行を重ねることで変動する可能性があります.本結果は3方式の傾向を示すものとして解釈してください.

実行時間の安定性(標準偏差):

stability_comparison

方式 標準偏差 変動係数(CV)
A (Baseline) 25.9秒 29.4%
B (Workflow) 5.4秒 13.4%
C (Chain) 4.0秒 15.3%

Method Cは標準偏差が4.0秒と最も小さく,実行時間の予測可能性が高いことを示しています.変動係数(標準偏差/平均)で見ると,Method Aは約30%のばらつきがあるのに対し,Method BとCは約13-15%と安定しています.

5.4 初回実行と自己修復のコスト(ウォームアップ)

Method BとCには,初回実行時にワークフロー/チェーンを生成する「探索フェーズ」と, 生成されたものが失敗した場合に修復する「自己修復フェーズ」があります.これらのコストを参考値として記載します.
共に今回のウォームアップでは初回実行 -> ログからワークフロー/チェーンを生成 -> 失敗 -> 自己修復 -> 完了の流れを観測しました.

Method B (Workflow)

サイト 初回実行 自己修復
Site A $0.54, 58秒 $1.75, 428秒
Site B $0.94, 102秒 $0.65, 72秒
Site C $0.62, 64秒 $1.44, 167秒

Method C (Chain)

サイト 初回実行 自己修復
Site A $1.45, 129秒 $0.70, 68秒
Site B $1.74, 164秒 $0.30, 42秒
Site C $1.91, 155秒 $1.09, 107秒

考察:

  • 初回実行のコストは$0.54〜$1.91と幅があるが,一度チェーン/ワークフローを生成すれば,以降は$0.19〜$0.26の低コストで実行可能
  • 自己修復が必要な場合でも,多くのケースで初回よりも短時間・低コストで修復できている
  • これは,操作ログから学習する仕組みが有効に機能していることを示す

6. 解説

実験結果から,Method BとCがMethod Aを大きく上回る性能を示しました.以下,技術的な観点から各方式の差異をより詳しく解説します.

6.1 コンテキスト効率の違い

6.1.2 Method A(逐次探索型)の課題

Method Aは毎回LLMがUIを探索するため,以下のコンテキスト消費が発生します:

  1. ページ全体のスナップショット: 各ステップでDOMやスクリーンショットを解析
  2. 要素の探索と推論: どのボタンをクリックすべきか,どのフィールドに入力すべきかを毎回推論
  3. エラーリカバリ: UI要素が見つからない場合の再試行ループ

これらが積み重なり,1回の実行で平均35,000トークン程度を消費しました(実測値).結果として:

  • 実行時間が長い: LLMの推論時間が支配的
  • コストが高い: トークン消費量に比例してコストが増大
  • 不安定: 推論経路が毎回異なるため,実行時間にばらつきが生じる

6.1.3 Method BとC(再生型)の優位性

一方,Method BとCは初回と失敗した場合のみ探索し,その結果を決定的な手順として保存します:

  1. 初回: UIを探索して操作ログを収集(コスト高・時間長)
  2. 連鎖生成: ログからワークフローJSON(Method B)またはbrowser_run_code呼び出しを含む高階関数連鎖(Method C)を生成
  3. 2回目以降: 生成された手順を機械的に実行(コスト低・時間短)

2回目以降は,LLMの推論がほぼ不要で,確立された手順を直接実行するため:

  • トークン消費は約5,000〜8,000程度(約1/5)
  • 実行時間は決定的な処理のみなので安定
  • コストは劇的に削減

speed_comp

6.2 標準偏差から見る安定性の本質

Method Aの標準偏差25.9秒(変動係数29.4%)という値は,業務自動化において致命的です.なぜなら:

  • スケジューリング困難: 実行時間が予測できないため,バッチ処理の計画が立てられない
  • SLA違反リスク: 最悪ケースで172秒かかる可能性があり,タイムアウトリスクが高い
  • リソース確保の困難: ピーク時のリソースに合わせる必要があり,コスト効率が悪化

Method CとBの標準偏差4.0秒・5.4秒(変動係数13-15%)は,安定した業務運用が可能なレベルです. なんらかの業務を柔軟性を担保したLLMを用いて自動化したいとき, 実際に人がやる場合と比べてどれぐらい安く済むのかは非常に重要になってきます.

6.3 初回コストの償却可能性

Method BとCの初回実行コストは$0.54〜$1.91と,Method Aの平均$1.03より高い場合もあります.しかし:

  • 2回目以降: $0.19〜$0.26で実行可能
  • 損益分岐点: 約2〜3回の実行で初回コストを回収
  • 月次運用: 毎月10回実行する場合,年間で約$100以上のコスト削減

業務の反復頻度を考えると,Method BとCの初回投資は十分に正当化されます.

6.4 なぜMethod CはMethod Bより速いのか

Method CとBの平均実行時間を比較すると,Cが約35%高速です(26.3秒 vs 40.2秒).この差の要因は:

  • 実装ランタイムの違い
  • 実行の最適化度(タイムアウトなど)
  • 補助ツールの利用

により生まれており, 工夫次第で大差ないものになると考えています.

6.5 前回記事で紹介したコンテキストエンジニアリングとの関連

前回の記事で紹介した「高階関数的なツール設計」と「コンテキスト圧迫の回避」が,今回の実験結果で実証されました.

高階関数的なツール設計:

  • playwright-mcpbrowser_run_code は「playwrightコードを引数に取り,ブラウザ操作を実行する」高階関数
  • 更にMethod Cの連鎖は,複数のbrowser_run_code呼び出しを含む高階関数として構成される
  • 中間結果はartifact IDとして保存され,LLMのコンテキストに載らない

コンテキスト圧迫の回避:

  • 操作ログ(数万トークン)→ Chain ID(数トークン)に圧縮
  • LLMは「このChainを実行する」という判断のみを処理
  • 実際の実行はLLMの外で,高階関数実行エンジンがbrowser_run_codeを順次呼び出す

これにより,Method CはLLMの能力を「判断」に集中させ,「実行」は決定的な機構に任せることで,高速・低コスト・安定を実現しています.


7. 今回の実験で得られた示唆

7.1 「探索」と「実行」の分離が鍵

今回の実験で最も重要な示唆は,探索フェーズと実行フェーズを分離することの有効性です.

従来のアプローチ(Method A)は,毎回「探索しながら実行する」ため,コストと時間がかかります.一方,Method BとCは:

  1. 初回: 探索に集中し,決定的な手順を抽出
  2. 2回目以降: 抽出した手順を機械的に再生

この分離により,LLMの「柔軟な推論能力」と「決定的な実行の高速性」を両立できます.

7.2 コード連鎖生成は「学習」の一形態

Method BとCの連鎖生成は,実質的にAI Agentが業務手順を学習していると言えます.

  • 人間が初めて業務を行う際も,最初は試行錯誤しながら手順を理解します
  • 2回目以降は,覚えた手順を素早く実行できます
  • AI Agentも同様に,初回の「学習コスト」を払えば,以降は効率的に実行できる

この視点で見ると,初回実行の$0.54〜$1.91というコストは「学習への投資」であり,十分にROIが見込めます.

7.3 エージェント連携の実証

前回の記事で紹介したtool chain toolが,実運用レベルで機能することが実証されました. 更にメインエージェントと各サブエージェントが明確な役割を持ち,成果物をIDで引き渡すことで,全体として複雑な業務を自動化できています.

7.4 自己修復の重要性

実験データから,自己修復フェーズのコストは初回実行より低い傾向が見られました(例: Site B Chain で$1.74 → $0.30).

これは,Healer Agentが:

  • 失敗の原因を特定
  • 最小限の修正で対応
  • 全体を再生成せず,部分修正で済ませる

という賢い動作をしていることを示します.この自己修復能力により,UI変更などの環境変化にも柔軟に対応できます.


8. 今後の展開

今回の実験で得られた知見をもとに,以下の方向で開発を進めていきます.

8.1 自動メンテナンス

現在は初回実行と自己修復で手動的に連鎖を更新していますが,今後は:

  • UI変更の自動検知: 定期実行で失敗パターンを検知
  • 差分修復: 全体を再生成せず,変更箇所のみを更新するようにプロンプト
  • バージョン管理の有効活用: 保持している業務フローの履歴をエージェントが活用できるように

これにより,Webサイトの仕様変更にも自動で追従できる仕組みを目指します.

8.2 連鎖の横展開と再利用

類似した業務間で連鎖を再利用できれば,初回実行コストをさらに削減できます:

  • パターンライブラリ: 「ログイン→検索→ダウンロード」などの汎用パターンを抽出
  • テンプレート化: サイト固有の部分だけをパラメータ化
  • 転移学習的なアプローチ: Site Aで学習した連鎖をSite Bに適用

これにより,新しいサイトへの対応時間を大幅に短縮できます.

8.3 他の業務領域への展開

今回はブラウザ操作に焦点を当てましたが,同様のアプローチは他の領域にも適用可能です:

  • データ抽出・変換: 複雑なExcel処理やPDF解析の手順を連鎖として定式化
  • API連携: 複数サービス間のデータ連携フローを連鎖として定式化
  • レポート生成: データ収集→分析→可視化の一連の流れを連鎖として定式化

前回の記事で紹介した「コード実行」と組み合わせることで,より幅広い業務を自動化できます.

8.4 知識の蓄積と活用

実行ログと連鎖を蓄積することで,組織全体の「業務知識ベース」を構築できます:

  • ベストプラクティスの抽出: 成功率の高い連鎖パターンを分析
  • Knowledge化: 前回記事で紹介したElasticsearch索引を活用し,類似タスクに推薦
  • LLM as a Judgeによる評価: 連鎖の品質を自動評価し,改善提案

これにより,AIエージェントが「使うほど賢くなる」システムを実現します.


9. おわりに

今回の実験では,90回の試行を通じて,ブラウザ操作自動化タスクにおける3つのアプローチを定量的に比較しました.

主要な成果:

  • Method Cは Method Aと比較して3.4倍高速,コスト1/5.5を達成
  • 標準偏差4.0秒という高い安定性で,業務運用に耐える信頼性を実証
  • 初回の「学習コスト」を払えば,以降は決定的な実行が可能
  • 複雑なサイトほど連鎖的実行の効果が大きい

前回の記事で紹介した「コンテキストエンジニアリング」の考え方が,実験結果として明確に裏付けられました.特に:

  1. 高階関数的なツール設計: 連鎖をartifact IDで管理し,LLMのコンテキストを圧迫しない
  2. 探索と実行の分離: LLMの推論能力を「判断」に集中させ,「実行」は決定的な機構に任せる
  3. エージェント連携: 複数エージェントの協調により,複雑な業務を段階的に処理

これらの設計思想が組み合わさることで,AI Agentを実運用レベルに引き上げることができます.

実験を通じて:

  • AIは「毎回考える」必要はない.一度学習した手順は機械的に実行すべき
  • 複雑さは敵ではなく,むしろ連鎖的実行のメリットを最大化する要因
  • 初回コストは投資であり,反復業務では確実にROIが見込める

今後も,実験結果を蓄積しながら,より実用的なAI Agent開発のノウハウを発信していきます.

スパイスコードでは,このような AI を使ったチャレンジングな機能開発に取り組んでいます.興味を持った方は,ぜひお話ししましょう!

https://corp.spicescode.co.jp/


参考 (前回の記事)

tech-blog.localmet.com

高階関数によるAI Agentのコンテキストエンジニアリング

スパイスコード CTO の櫻木です. スパイスコードは,「ロカルメ・オーダー」

order.localmet.com

という AI Agent を内包した ERP サービスを開発・提供しているスタートアップです. 本記事では我々の AI Agent 開発におけるコンテキストエンジニアリングに関する取り組みを紹介します.

1 コンテキストエンジニアリングの重要性

AI Agentの作成において「コンテキストエンジニアリング」が重要であることは以前より周知されています. blog.langchain.com

以前は人間がLLMへ直接プロンプトを与えていたため「プロンプトエンジニアリング」に意識が向いていました. しかし,AI Agentは人の手がほとんど介在しないまま自律的に推論と実行を繰り返します. そのため単なるプロンプトエンジニアリングだけでなく,「エージェントが参照するコンテキストをどう設計するか」が決定的に重要になります. コンテキストエンジニアリングでは必要十分な情報だけを安全に供給することにより

  1. LLM の推論力を最大限引き出す
  2. LLM が抱える入力長制約を回避する
  3. “Lost in the Middle”問題を回避する

ことを目的とします. この視点がなければ,エージェントは複雑な業務フローの途中で意図せぬ結果に陥りがちです. 本記事ではこのコンテキストエンジニアリングに対するスパイスコードでの取り組みをロカルメオーダーに内包している電子メール注文書受領Agentとデータ抽出 Agentの例を使って紹介します.

2 tool利用によるコンテキスト圧迫問題

我々が提供しているAI Agentは以下のようなツールを使うことができます

  • 自前のmcpサーバ経由でユーザに代わりロカルメオーダー上のデータを操作・抽出
  • お客様の代わりに電子メールの確認・操作(注文書のダウンロード・アップロード)

これらのツールで課題になるのが目的を達成するために使うツールの入出力によるコンテキストの圧迫です. 以下の例のように

  1. ツールの入出力のデータ量が大きい場合
  2. 繰り返し処理が生じる場合

それぞれでコンテキストの圧迫が発生することは想像に難くないと思います.

例1

特定の店舗にある在庫の平均原価額取得

TOOL CALL: localmet_mcp.get_shop(query: "shopA")
    returns shopA

TOOL CALL: localmet_mcp.get_stocks(shopId: shopA.id)
    returns 10000 rows stock info -> ❌

TOOL CALL: average_cost_by_sku(stocks: 10000 rows stock info) -> ❌
    returns 1000 rows averaged stock info -> ❌

例2

特定期間における電子メールから注文書を抽出, 一部のメールではいわゆるPPAPが利用されている

TOOL CALL: get_emails(from: 'YYYYMMDD', to: 'YYYYMMDD')
    returns 1000 emails -> ❌

以下対象全てに対して実行 -> ❌
TOOL CALL: evaluate_order_email(id: 'email1')
    returns {
        is_order_related,
        urls,
        password_candidates,
        attachments
    }

TOOL CALL: handle_file(attachmentId: 'attachment1', passwordCandidates: tooManyCandidates) -> ❌

TOOL CALL: handle_url(url 'example.com', passwordCandidates: tooManyCandidates) -> ❌

3 高階関数, コード実行によるアプローチ

上記のような問題に対して我々は大きく分けて2つのアプローチを用いて対策しています. 現在それぞれ独立した機構として設計しユースケースに応じて使い分けていますが全てを統合したアプローチも検討中です. 重要な点はどちらも高階関数ツールを用意し,中間処理をコンテキストに載せないことです. 更に最終結果に関してもartifactとして保存し, idのみを返すことで次に実行するchainに再利用したり, サマリのみをユーザに提供することができます.

3.1 tool chain tool

mcpツール含め, ツール群を全てchain toolを経由して使用させることで無駄な中間ツールの出力を削減した上で最終的な結果はartifactとして保存しidのみ返す

例1

Tool Input

{
    context: "shopAある在庫の平均原価額取得"
    purpose: "shopAの存在確認をした上で全在庫を取得しskuごとに平均コストを算出する"
    tool_names: ["mcp_get_shop", "mcp_get_stocks", "average_cost_by_sku"]
    each_args: {
        "mcp_get_shop": { "query": "shopA"},
        "mcp_get_stocks": { "shopId": "get_shop.id"},
        "average_cost_by_sku": { "stocks": "get_stocks.result" },
    },
    expected_result: "skuごとの平均在庫金額リスト"
}

Tool Output

{
    "artifactId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

例2

こちらはより内部状態の管理を明確にしたもの,artifactとして保存されるのは最終的結果に加えてプロセス全体の最終状態なども含める

Tool Input

{
    "initial_state": {
        "purpose": "YYYYMMDDからYYYYMMDDまでのメールから注文書googleDriveへアップロード",
        "password_candidates_all": []
    },
    {
        "steps": [
            {
                "tool_name": "mailbox_message_search_tool",
                "arguments": {
                    "after": "2025/11/01",
                    "before": "2025/12/31"
                },
                "result_key": "search"
            },
            {
                "items_key": ".search.messages",
                "item_alias": "message",
                "body": [
                    {
                        "tool_name": "mailbox_body_extractor_tool",
                        "arguments": {
                            "subject": ".message.subject",
                            "body": ".message.snippet"
                        },
                        "result_key": "body_collect"
                    },
                    {
                        "predicate": ".body_collect.password_candidates",
                        "body": [
                            {
                                "operation": "extend",
                                "target_key": "password_candidates_all",
                                "value_from": ".body_collect.password_candidates"
                            }
                        ]
                    }
                ]
            },
            {
                "items_key": ".search.messages",
                "item_alias": "message",
                "body": [
                    // 1件づつに対する処理
                ]
            }
        ]
    }
}

Tool Output

{
    "artifactId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

3.2 コード実行

tech-blog.localmet.com で紹介しているような安全な実行環境においてコード実行し, 最終的な結果はartifactとして保存しidのみ返す. こちらはまだ実験段階ではありますがDSLよりAI Agentにとって明瞭で強力なツールになると考えています.

Tool Input

def exec():
    shop = mcp_get_shop("shopA")
    stocks = mcp_get_stocks(shop.id)
    return average_cost_by_sku(stocks)

Tool Output

{
    "artifactId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

4 自己学習サイクルによる継続的改善

我々は以下の3段階のフィードバックループを実装することにより複雑なDSL, コード生成の難しさを解決しています.

実行時の自己修復

Pydantic modelによるバリデーションに加え,実行可能性も評価します.エラー時は詳細なエラー情報をエージェントに返却し,自己修復を促します.

# 例: shopIdが存在しないケース
{
    "error": "ValidationError",
    "detail": "shopId 'invalid-id' not found",
    "suggestion": "Use mcp_search_shop first"
}

成功パターンのKnowledge化

成功した実行ログに対して:

  1. LLM as a Judge または人間が品質評価
  2. 高評価のパターンをElasticsearchに索引化
  3. タスクコンテキストでベクトル検索
  4. 類似パターンをSystem Promptに注入

学習曲線

  • 初回実行: 平均2-3回のvalidation失敗を経て成功
  • Knowledge化後: 同様のタスクはほぼ一発で成功
  • 運用数ヶ月: 頻出タスクの初回成功率が向上

この仕組みにより,エージェントは使うほど賢くなり, 懸念される可能性のあるJSON生成の不安定性も実運用では問題になりません.

┌─────────────────────────────────────────┐
│         初回実行 (Cold Start)            │
├─────────────────────────────────────────┤
│ LLM生成 → ❌ → 修正 → ❌ → 修正 → ✅     │
│              (平均2-3回)                 │
└─────────────────────────────────────────┘
                    ↓
         ┌──────────────────┐
         │  評価 + 索引化    │
         └──────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│      2回目以降 (Warm Start)              │
├─────────────────────────────────────────┤
│ Knowledge検索 → LLM生成 → ✅            │
│              (ほぼ一発)                  │
└─────────────────────────────────────────┘

5 高階関数で表現していることのメリット

どのアプローチも,「処理定義(関数)を引数に取り,新しい複合処理(関数)を返す」高階関数として設計されています. これによってエージェントが逐次実行した場合と比較してコンテキストエンジニアリングの文脈で大きな強みがあります. それ以外にも以下のようなメリットがありました.

  • 宣言と実行の分離:エージェントは JSON 宣言を返すだけで,実動作に費やすトークンを削減.実行側は常に同じ検証ルールで評価するため,一貫性を確保.
  • 合成の安全性:chain tool は バリデーションや入出力の取り扱いを内部に閉じ込めているので,関数合成(ツール連鎖)時の型崩れや引数抜けをチェーン内で吸収可能
  • 秘匿情報の安全性:秘匿情報の受け渡しをchain内に閉じ込めることでLLMに対する入出力にそれらの情報が顕在化しない.
  • 知識の再利用:高階関数ツールへのInputとその結果をロギング -> LLM as a Judgeなどで評価 -> Knowledge として保存することで,同じ宣言を別エージェントが再評価したり,成果の出た構成をテンプレ化したりできる.
  • プロセスの階層化:テンプレ化したchain toolをchain toolで利用することで複雑なパターンを容易に表現
  • スケーリング: 並列実行可能な箇所が明確かつ処理機構は閉じているため,スケール時にコントロールが容易

6 今後の展望

  • Python サンドボックス応用:既存の sandbox を使って,chain tool が動的な Python タスクを compile/decompile できるモジュールを挟み,宣言の一部をコード化 → 検証 → 再宣言するハイブリッド実行を検討中.
  • コード実行との連携:Anthropic が提唱するような MCP ベースのコード実行環境とも親和性が高く,彼らが提唱するsearch_toolsと組み合わせることによってより効率的で安全な状態に拡張できる可能性が高い.
  • healerの構築, playwright-healerのように出力されたtool inputに対してより適切な構造に自律的に修正を促すエージェントとの協調

tech-blog.localmet.com www.anthropic.com

終わりに

スパイスコードでは現在積極的に採用を行なっています. この記事を読んでAIを使ったチャレンジングな機能を開発してみたいと思った方,興味を持った方はぜひお話ししましょう!

corp.spicescode.co.jp

引用

arxiv.org blog.langchain.com www.anthropic.com

コンパイラ技術を使用したAI Agentの安定性を向上させる仕組み

スパイスコード代表の中河です。 スパイスコードは、「ロカルメ・オーダー」

order.localmet.com

という AI Agent を内包した ERP サービスを開発・提供しているスタートアップです。
本ブログでは、私たちの AI Agent がどのような仕組みで動いているのか、そして他の Agent とは何が異なるのかについて、ご紹介していく予定です。
第 2 回目となる今回は、「コンパイラ技術を使用した AI Agent の安定性を向上させる仕組み」として、第 1 回目に続いて古の技術を用いた弊社の AI Agent の仕組みについて、その概要をご紹介します。
※ Sandbox 技術の続きはそのうち別記事として書く予定です

AIエージェントアーキテクチャ

TL;DR: ロカルメ・オーダーでは Automation RPA と呼ばれる RPA を内蔵しているのですが、同じく内蔵している AI Agent がそれらを構築・実行する機能を持っており、特に構築部分では少し特殊なアーキテクチャを採用しています。


1. なぜいま “コンパイラ技術” なのか

  • AI Agentをプロダクトに投入するためにはパフォーマンスの安定性が大きな壁となる。

  • AI Agentが確率的に出力した結果をコンパイラ技術を通じて、確定的な物へ変換する事が出来る。

  • コンパイラ技術を元にAI Agentにフォードバックを行い自己改善するループを構築できる。

Automation RPA

例えば上記のようなワークフローを弊社のAI Agentが構築する場合、いきなりワークフローのデータ構造を出力するのではなく

  1. Python コードの出力
  2. Python コードを中間コードへ変換
  3. 中間コードをAutomationの処理構造に変換

という処理を行なっており、コンパイラのフロントエンド・バックエンドのようなアーキテクチャになっています。


2. アーキテクチャと実装

コンパイラアーキテクチャ概要

2.1 Python コードの出力

AI Agentはドキュメントや人間からの自然言語での指示を入力として以下のテンプレートを元にprocess関数を実装したPythonコードを出力します

import copy
from typing import Any
from {evaluate_module} import ( # you must keep this line
    {evaluate_functions}
)
from {process_module} import ( # you must keep this line
    {process_functions}
)

def process(arg: Any) -> Any:
    target = copy.deepcopy(arg)
    # your code, should convert target object
    return target

# you should keep following code
target = {target}
print(process(target))
2.2 Python コードを中間コードへ変換

コンパイラ・フロントエンドは Python の ast モジュールを使用し、AI Agent が出力した Python コードを構文解析して S 式で記述された中間コードを生成します。 この段階で文法エラーや事前に定めた制約に出力コードが従っていない場合にはエラーを返し、AI Agent にフィードバックします。 また、S 式で設計された中間コードは様々な用途に応用可能な仕様になっています。

2.3 中間コードをAutomationの処理構造に変換

コンパイラ・バックエンドは S 式フォーマットの中間コードを Automation の処理構造に変換します。 Automation の処理構造は JSON で記述され、ロカルメ・オーダー本体で実行されます。

構築されたAutomation


3. プロダクトでの構成例

実際のプロダクトでは、今回紹介した AI Agent 機構は独自の Agent OS(仮称)の上で独立したマイクロサービスとして動作し、Automation の実行系などは Go で実装されたロカルメ・オーダー本体で実行されるようになっています。


4. まとめと展望

  • 本手法のメリット: コンパイラ技術を使用して、ある程度 AI Agent の安定性を担保できる
  • 課題: 相変わらず実装が複雑で運用が大変
  • 次の一手: 今回はコンパイラ部分のみにフォーカスしてご紹介しました。そのうちアーキテクチャ図には記載したもののご紹介していなかった逆コンパイラ部分の仕組みもご紹介したいと思います。こちらはいわゆる human‑in‑the‑loop を実現する部分で、少し? 特殊な実装になっています ※ 他のコンテンツとの兼ね合いで、順番が前後する可能性があります ;-)

参考文献

AI Agent向けSandbox実装

はじめましての方も、お久しぶりの方もこんにちは。スパイスコード代表の中河です。 スパイスコードは、「ロカルメ・オーダー」

order.localmet.com

というAI Agentを内包したERPサービスを開発・提供しているスタートアップです。 近年、AI Agentという言葉を耳にする機会が増えましたが、実は私たちはこのブームが来る前から、AI Agentの実用化に向けた開発に取り組んできました(例えば、本日ご紹介する機能の実装を行っていたのは2023年12月〜2024年1月頃です)。そして現在では、ERPの中核機能として、AI Agentを実際にお客様にご利用いただいています。 本ブログでは、私たちのAI Agentがどのような仕組みで動いているのか、そして他のAgentとは何が異なるのかについて、ご紹介していく予定です。 第1回目となる今回は、「AI Agentが生成したコードを安全に実行する独自のSandbox機構」について、その概要をご紹介します。

Agent Architecture

TL;DR: ロカルメ・オーダーでは ptrace を使った独自のSandbox機構を運用しています。本稿は短いサンプルコードでどのようにサンドボックスを構築しているのか?をご紹介いたします。

1. なぜいま “軽量サンドボックス” なのか

  • Agent 時代の要請: LLM エージェントは外部プラグインや自己生成コードを即座に実行する─安全な実行環境が必須。

  • 実行情報のフィードバック: ptrace で取得したシステムコールやリソース統計をリアルタイムに Agent へ返送し、自己改善するループを構築できる。

  • 実行環境の柔軟性: ptrace ベースはイメージ再ビルド不要でポリシーをコードから動的変更可能。オンプレや制約の厳しい閉域ネットワーク環境でも、Python 実行権さえあればそのまま導入できる。CI・サーバレス・ローカルデバッグまで同一コードで再利用可。

1.1 類似実装・活用例

ツール/プロジェクト 主な用途 ptrace の役割
gVisor (runsc, 旧 ptrace モード) コンテナ向けユーザ空間カーネル すべての syscall を ptrace でフックし、Go 実装の仮想カーネルで処理
LangChain Sandbox Agent向けsandbox Python codeをWebAssemblyにコンパイルして実行

2. ptraceとは?

ptrace() は トレーサ (親) がトレース対象プロセス (子) を観察・制御できる LinuxUNIX 系 OS のシステムコールです。レジスタやメモリの読み書き、シグナル挿入、システムコール前後での割り込み停止などを行えるため、デバッガ (gdb)、システムコールトレーサ (strace)、軽量サンドボックス、故障解析ツールなどの基盤として利用されています

2.1 主要リクエストと用途(代表例)

区分 リクエスト (req) 典型的な用途・ポイント
トレース開始 PTRACE_TRACEME 子→親へ「自分をトレースしてほしい」と通知。execve() 直後に gdb などが使う入口。
アタッチ/デタッチ PTRACE_ATTACH / PTRACE_DETACH 既存プロセスの動的アタッチと解除。解除時はシグナルや PTRACE_O_EXITKILL で安全に終了させることも可能。
停止制御 PTRACE_CONT / PTRACE_SYSCALL / PTRACE_SINGLESTEP 連続実行・システムコール毎停止・命令単位ステップ実行。監査・改竄ポイントとして SYSCALL 前後 で介入できる。
レジスタ I/O PTRACE_GETREGS / PTRACE_SETREGSx86 以外は PTRACE_GETREGSET 戻り値を -EPERM に書き換える、引数を編集するなど動的パッチを実施。
メモリ I/O PTRACE_PEEKDATA / PTRACE_POKEDATA, PTRACE_PEEKUSER ダンプ取得、コードインジェクション、データ改竄。
イベント通知 PTRACE_O_TRACECLONE TRACEFORK TRACEEXEC TRACESECCOMP など clone(2) / fork(2) / execve(2) / seccomp 発火時点で自動停止し、親がハンドリングできる。
安全終了 PTRACE_O_EXITKILL トレーサ異常終了時に tracee を自動 SIGKILLstrace --kill-on-exit が内部で利用 (man7.org)。

2.2 典型的なイベントループ (高水準フロー)

  1. waitpid(-1, &status, __WALL) で子の停止を待つ。

  2. WIFSTOPPED(status) のときかつ WSTOPSIG(status) が SIGTRAP+0x80 (PTRACE_O_TRACESYSGOOD) → システムコール入口 or 退出、同じくWIFSTOPPED(status) のときかつ status>>16 に PTRACE_EVENT_* が入っていれば PTRACE_GETEVENTMSG で追加情報を取得。

  3. システムコール入口なら PTRACE_GETREGS で orig_rax (x86-64)、第一引数(rdi など) を読み取る。

  4. ホワイトリスト外なら 戻り値を改竄 (PTRACE_SETREGS) したり PTRACE_KILL / SIGKILL を送る。

  5. 次の停止条件に合わせて PTRACE_SYSCALL または PTRACE_CONT で再開。

性能コスト: system call ごとに 2 回 ユーザ空間⇆カーネルを往復。I/O 多用処理は体感で~3× 遅れる。

なぜseccompを使用しないのか?: 単純なsystem callの許可/不許可だけではなく、Agent向けの他の処理も行っているからです(ここは別の機会にご紹介..)


3. Sandboxアーキテクチャ・実装

┌ sandbox.py (parent tracer)
│  ├─ fork → child (untrusted code)
│  ├─ waitSignals ⇆ syscall enter/exit
│  └─ timeout / rlimit / decision engine
└─→ 親が子の終了コードを返して終了

構成としては、コードを実行する worker プロセスと、その挙動を監視する親プロセス(Tracerプロセス)の二層に分かれています。Tracerプロセスでは、syscall(システムコール)の呼び出しを検知・制御するほか、必要に応じた各種セキュリティ処理を担っています。

3.1 実装(抜粋)

実装の主要な部分を抜粋してご紹介します。 本Sandboxはpython-ptrace https://pypi.org/project/python-ptrace/ を使用したPure Pythonで実装されており、対象のPythonコードを exec(3) を使わずに実行できる構造になっています。この仕組みにより、対象コードが利用するライブラリや機械学習モデルなどを、事前にロードしておくことが可能となり、より効率的かつ柔軟な実行環境を実現しています。

  def _spawn_sandbox_worker(self, server: socket.socket) -> None:
      pid, r, w = None, None, None
      try:
          r, w = os.pipe()
          pid = os.fork()   # MLモデル等を使用する場合はCoWを効かせる為、forkより前にロードする

          if pid == 0:
              try:
                  self._sync_child(r, w)
                  r, w = None, None

                  self._start_sandbox_worker(server)
              finally:
                  os._exit(0)
      finally:
          self._sync_parent(pid, r, w)

      self._logger.debug(f"Spawned sandbox worker {pid}")

  def _watch_sandbox_worker(self, pid: int, status: int) -> None:
      if os.WIFSTOPPED(status) and os.WSTOPSIG(status) == signal.SIGTRAP:
          reg = self._get_registers(pid)
          nr = get_syscall_number(reg)

          if not is_approved_syscall(pid, nr, reg):   # syscallが許可されたものか判断
              self._logger.warning(f"Unapproved syscall PID:{pid},SYSCALL_NR:{nr}")
              ptrace_kill(pid)
              return

      ptrace_syscall(pid)

  def _get_registers(self, pid: int) -> linux_reg | freebsd_reg | openbsd_reg:
      if HAS_PTRACE_GETREGS or HAS_PTRACE_GETREGSET:
          return ptrace_getregs(pid)

      words: List[Any] = []
      num_words = sizeof(ptrace_registers_t) // CPU_WORD_SIZE
      for offset in range(num_words):
          word: Any = ptrace_peekuser(pid, offset * CPU_WORD_SIZE)
          bytes = word2bytes(word)
          words.append(bytes)

      bytes = "".join(words)
      return bytes2type(bytes, ptrace_registers_t)

...

def get_syscall_number(
    reg: linux_reg | freebsd_reg | openbsd_reg,
) -> Any:
    if platform.machine() == "x86_64":
        return reg.orig_rax
    elif platform.machine() == "aarch64":   # docker on mac 環境用
        return reg.r8
    else:
        raise NotImplementedError


def is_approved_syscall(pid: int, nr: int, reg: linux_reg | freebsd_reg | openbsd_reg) -> bool:
    if platform.machine() == "x86_64":
        name = X86_64_SYSCALL_WHITE_LIST.get(nr)
    elif platform.machine() == "aarch64":
        name = AARCH64_SYSCALL_WHITE_LIST.get(nr)
    else:
        raise RuntimeError("Unsupported platform")

    if name is None:
        return False

    ...  # プロダクトではここでsystem callのgateway処理などを行っている

    return confirm_func(pid, reg)

実装上の工夫として、共通で使用され、かつメモリ消費の大きい機械学習モデルや大規模データは、fork前にロードする設計を採用しています。 これにより、Copy-on-Write(CoW)機構が有効に働き、子プロセス側で不要なメモリ確保が発生せず、全体として効率的なメモリ利用が可能になります。


4. プロダクトでの構成例

実際のプロダクトでは、Sandbox本体は「Server」と、Agentがツールとして利用する「Client」に分かれた構成になっています。 両者の通信は Unix Domain Socket を用いており、Sandbox自体はKubernetes環境上でSidecarとしてデプロイされています。


5. まとめと展望

  • 本手法のメリット: ユーザランドPython実装だけでAgent用の隔離環境が実現できる
  • 課題: ptrace オーバーヘッド、カーネル exploit への脆弱性、安全性を適切担保する為にはsystem call実装の理解が必要
  • 次の一手: 今回は、Sandbox機能にフォーカスしてご紹介しました。次回は、Agentの再現性を高めるために、処理系からどのような実行時情報をAgent側にフィードバックしているのかについて、ご説明できればと考えています。 ※他のコンテンツとの兼ね合いで、順番が前後する可能性があります;-)

参考文献