第4章 既存アプリケーションへのAI Agent融合手法(実践レシピ)
対象=一般Web/アプリ開発者・バックエンドエンジニア。すでに稼働しているWebアプリや社内基幹システムを壊さずに、安全かつ段階的にAI Agentを組み込むためのアーキテクチャパターンを解説する。
1. 3大統合アーキテクチャパターン
既存システムにAgentを追加する場合、システムの性質に応じて以下の3つのパターンから選択します。
【パターンA: API Gateway 方式】
ユーザー ──► [AI Gateway (Agent)] ──(REST/GraphQL)──► [既存バックエンド]
・自然言語の意図をAPIコールに変換
【パターンB: Sidecar / Copilot 方式】
ユーザー ──► [既存フロントエンド画面] ◄── [Copilot Panel (Agent)]
・画面コンテキストを参照し、入力補助・提案・ドラフト生成
【パターンC: Event-driven / Background Worker 方式】
[既存DB / MQ] ──(Webhook/Queue)──► [Agent Worker] ──► [ドラフトDB] ──► [承認画面]
・非同期バッチ・例外監視・バックグラウンド分析
| パターン | ユースケース | 実装難易度 | 既存コードへの影響 |
|---|---|---|---|
| A. API Gateway方式 | 自然言語インターフェース、Slack/Teams連携 | 中 | 低(既存APIをツールとして呼ぶだけ) |
| B. Sidecar方式 | 業務画面の入力支援、見積作成支援 | 低〜中 | 低(横にチャット/提案パネルを追加) |
| C. Event-driven方式 | 請求書自動照合、障害検知、夜間バッチ | 中〜高 | 極小(Webhookやメッセージキューで疎結合) |
2. パターン別の詳細設計とコード構成例
パターンA:API Gateway / Proxy 方式
既存のREST API群を手前に置いたAgentから「Function Calling」経由で叩かせる構成です。
// agent-runner.ts (Node.js / Express / Next.js API)
import { OpenAI } from "openai";
const tools = [
{
type: "function",
function: {
name: "search_orders",
description: "既存の受注管理APIから顧客名または注文ステータスで注文を検索する",
parameters: {
type: "object",
properties: {
customerName: { type: "string", description: "顧客名" },
status: { type: "string", enum: ["pending", "shipped", "delivered"] }
},
required: ["status"]
}
}
}
];
// Agentループ実行部
async function handleUserRequest(userMessage: string) {
const messages = [
{ role: "system", content: "あなたは社内受注管理アシスタントです。既存APIを呼び出して回答してください。" },
{ role: "user", content: userMessage }
];
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages,
tools,
tool_choice: "auto"
});
const message = response.choices[0].message;
if (message.tool_calls) {
for (const toolCall of message.tool_calls) {
if (toolCall.function.name === "search_orders") {
const args = JSON.parse(toolCall.function.arguments);
// 既存のバックエンドAPIを直接コール(サービス間認証トークンを使用)
const apiResult = await existingBackendClient.getOrders(args);
// 実行結果を会話に追加して最終回答を生成
messages.push(message);
messages.push({
role: "tool",
tool_call_id: toolCall.id,
content: JSON.stringify(apiResult)
});
return await openai.chat.completions.create({ model: "gpt-4o", messages });
}
}
}
return response;
}
3. 実践:安全な Human-in-the-Loop(HITL)承認フローの実装
業務システムにおいて、Agentが直接 UPDATE orders SET status = 'cancelled' などの更新を行うのは重大な事故の元です。 必ず 「ドラフト作成 → 承認キュー投入 → 人間が画面で確認 → 既存APIで正式確定」 の2段階構成(State Machine)をとります。
承認ステートマシンのテーブル設計(例: PostgreSQL)
-- Agentが生成したドラフトと承認状態を管理するテーブル
CREATE TABLE agent_action_proposals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
task_id VARCHAR(64) NOT NULL,
action_type VARCHAR(64) NOT NULL, -- 例: 'INVOICE_APPROVE', 'SEND_EMAIL'
proposed_payload JSONB NOT NULL, -- 実行しようとしているパラメータ
reasoning_log TEXT NOT NULL, -- なぜこの判断をしたかのAgent思考ログ
status VARCHAR(32) NOT NULL DEFAULT 'PENDING', -- PENDING, APPROVED, REJECTED, EXECUTED
created_by_agent VARCHAR(64) NOT NULL,
reviewed_by VARCHAR(64), -- 承認者の社員ID
reviewed_at TIMESTAMP WITH TIME ZONE,
execution_error TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
バックエンドでの安全な分離処理
// Agentに持たせるツールは「実行ツール」ではなく「提案作成ツール」にする
async function proposeInvoiceApproval(payload: InvoiceApprovalPayload, reasoning: string) {
// DBの提案テーブルに保存するだけで、本番の決済APIは呼ばない!
const proposal = await db.insert(agentActionProposals).values({
actionType: 'INVOICE_APPROVE',
proposedPayload: payload,
reasoningLog: reasoning,
status: 'PENDING'
}).returning();
// 承認者のSlack/Teamsに通知
await notifyReviewer(proposal.id, payload.invoiceId, payload.amount);
return {
success: true,
message: `承認申請ドラフト (ID: ${proposal.id}) を作成しました。人間の確認待ちです。`
};
}
// 人間が承認画面で「承認」ボタンを押したときに呼ばれる正式確定ハンドラー
async function approveProposalByHuman(proposalId: string, reviewerUserId: string) {
const proposal = await db.getProposal(proposalId);
if (proposal.status !== 'PENDING') throw new Error('すでに処理済みです');
// ここで初めて、本来の本番決済・更新トランザクションを実行する
const result = await realProductionService.executeAction(proposal.actionType, proposal.proposedPayload);
// 監査ログ(Audit Trail)を更新
await db.updateProposal(proposalId, {
status: 'EXECUTED',
reviewedBy: reviewerUserId,
reviewedAt: new Date()
});
return result;
}
4. 本番運用のためのオブザーバビリティ(可観測性)と防御策
- タイムアウトと最大リトライ回数のハードリミット:
- 1リクエストのAgentループは最大8ターン、全体の実行時間は最大60秒で強制終了する。
- トークン上限と月次コスト監視:
- ユーザー/テナントごとに日次・月次の消費トークン上限を設定し、超過時はHTTP 429(Too Many Requests)を返す。
- トレーシング(OpenTelemetry / Langfuse):
- 各ReActターンの Thought、Action、Latency、消費Token、エラー率をすべてトレースIDで紐づけ、ダッシュボードで異常なループやレイテンシ劣化を即座に検知する。
- キルスイッチ(緊急停止機能):
- システム環境変数やRedisフラグで
AGENT_KILL_SWITCH_ENABLED=trueにすると、Agent機能のみをバイパスして即座に従来の手動運用へフォールバックできる設計にしておく。