👻

LLMエージェントのワークフローパターン

2025/02/01に公開

効率的なエージェントを開発するための考え方とパターンをまとめた資料(Building effective agents)をAnthropic 社が発表しました。この資料は、どういうタスクには対してはどういうエージェントが適しているかという観点から、エージェントの設計についての指針を提供しています。
本記事は、この資料を3つのストーリーポイントに分けてキャッチアップしたもののひとつになります。

https://zenn.dev/53able/articles/85016de44fdde6
https://zenn.dev/53able/articles/457ab1e5e3085c

LLMエージェントのワークフローパターン

TypeScriptコード例と実践的な活用法

LLM(Large Language Model)エージェントを最大限に活用するには、タスクの性質に応じた適切なワークフロー設計が不可欠です。本記事では、主要なワークフローパターンを解説し、TypeScriptのコード例を交えて、それぞれの特性と活用方法を紹介します。

プロンプトチェイニング(Prompt Chaining)

タスクを複数のステップに分解し、各ステップごとにLLMを呼び出す手法です。

コード例

async function promptChaining(input: string): Promise<string> {
  const step1Output = await callLLM(`ステップ1: ${input} に基づいて概要を作成してください。`);
  if (!isValidOutline(step1Output)) throw new Error("無効な概要です。");

  return await callLLM(`ステップ2: ${step1Output} を詳細に展開してください。`);
}

ポイント

✅ メリット: タスクの精度向上・管理しやすい構造
❌ デメリット: ステップ数が増えると処理時間が長くなる

ルーティング(Routing)

入力を分類し、適切な処理に振り分けるワークフローです。

コード例

async function routing(input: string): Promise<string> {
  const category = await classifyInput(input);
  switch (category) {
    case "general": return await handleGeneralQuery(input);
    case "refund": return await handleRefundRequest(input);
    case "technical": return await handleTechnicalSupport(input);
    default: throw new Error("未知のカテゴリです。");
  }
}

ポイント

✅ メリット: 入力ごとに最適な処理を適用可能
❌ デメリット: 分類の精度が低いと誤った処理が実行される

並列化(Parallelization)

タスクを分割し、並行処理で効率化を図る手法です。

セクション化の例(異なる処理を並行実行)

コード例

async function parallelSectioning(input: string): Promise<string[]> {
  return await Promise.all([
    processUserQuery(input),
    screenInappropriateContent(input),
  ]);
}

投票の例(同じ処理を複数回実行して最適解を抽出)

コード例

async function parallelVoting(input: string, numRuns: number): Promise<string[]> {
  return await Promise.all(
    Array(numRuns).fill(null).map(() => reviewCodeVulnerabilities(input))
  );
}

ポイント

✅ メリット: 高速処理・多様な結果の獲得
❌ デメリット: 並列処理のコスト増大

オーケストレーターワーカー(Orchestrator-Worker)

中央のLLMがタスクを細分化し、個別のワーカーLLMに委任する手法です。

コード例

async function orchestratorWorker(input: string): Promise<string> {
  const subtasks = await orchestratorLLM(`タスクを分割: ${input}`);
  const results = await Promise.all(subtasks.map((task) => workerLLM(task)));
  return synthesizeResults(results);
}

ポイント

✅ メリット: 複雑なタスクを動的に処理可能
❌ デメリット: 中央LLMの判断精度に依存

評価者オプティマイザー(Evaluator-Optimizer)

生成した応答を別のLLMが評価・改善する反復的なプロセスです。

コード例

async function evaluatorOptimizer(input: string): Promise<string> {
  let response = await generatorLLM(input);
  for (let i = 0; i < maxIterations; i++) {
    const feedback = await evaluatorLLM(`評価してください: ${response}`);
    if (isSatisfactory(feedback)) break;

    response = await generatorLLM(`改善: ${response}, フィードバック: ${feedback}`);
  }
  return response;
}

ポイント

✅ メリット: 応答の品質向上
❌ デメリット: 改善効果が限定的な場合も

実践的な考慮事項

🔹 シンプルな設計: 複雑すぎるワークフローは管理が困難
🔹 透明性の確保: 計画ステップを明示し、ユーザーの信頼を獲得
🔹 適切なエージェント設計: 文書化・テストを徹底し、最適なACI(Agent-Computer Interface)を構築
🔹 継続的な改善: ワークフローは定期的な評価とチューニングが必要

まとめ

ワークフロー 概要 メリット デメリット
プロンプトチェイニング ステップごとにLLMを呼び出す 精度向上 処理遅延
ルーティング 入力を分類し適切な処理を実行 効率化 誤分類のリスク
並列化 タスクを並行処理 高速化 コスト増大
オーケストレーターワーカー 中央LLMがワーカーLLMを管理 柔軟なタスク処理 中央LLMに依存
評価者オプティマイザー 応答の評価・改善を繰り返す 品質向上 効果が限定的な場合も

この記事がLLMエージェントの設計・実装の参考になれば幸いです! 🚀

Discussion