🐕

LLMエージェントの基礎実装

2025/02/01に公開

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

https://zenn.dev/53able/articles/c0451eeba6a6fb

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

近年、LLM(Large Language Model) を活用したエージェント技術が急速に発展しています。LLMエージェントとは、大規模言語モデルを基盤とし、外部ツールと連携しながら特定のタスクを自律的に実行するシステムです。本記事では、LLMエージェントの基本概念、構成要素、および実装方法をTypeScriptのコードスニペットを交えて解説します。

LLMエージェントの定義と分類

LLMエージェントは、動作の特性に応じて大きく以下の2つに分類されます。

① 自律的なシステム(Autonomous Agent)

  • LLMが自ら意思決定を行い、外部ツールを活用しながらタスクを遂行します。
  • 例:カスタマーサポートボット、ドキュメント要約システム。

② 定義済みワークフローに従うシステム(Scripted Workflow)

  • 事前に定義された手順に従い、LLMがツールと連携しながらタスクを処理します。
  • 例:特定の質問に対するFAQボット、データ抽出パイプライン。

エージェントの本質的な違いは、「動的な意思決定を行うか(エージェント型)」 vs 「事前に決められた手順を実行するか(ワークフロー型)」という点にあります。

LLMエージェントの主要構成要素

LLMエージェントは、主に以下の3つの機能を備えた「拡張LLM」を中心に設計されます。

🔍 検索(Retrieval)

  • LLMが外部のデータベースやWeb検索エンジンを利用し、リアルタイムで情報を取得する。
  • 例:Google検索API、ベクトルデータベース。

🛠 ツール連携(Tool Integration)

  • LLMが特定のAPIやツールを活用し、計算・翻訳・データ処理などの機能を実行する。
  • 例:電卓API、翻訳API、コード実行環境。

🧠 メモリ(Memory)

  • 過去の会話履歴やタスク実行履歴を保持し、文脈を理解しながら対話や処理を進める。
  • 例:短期記憶(セッション内履歴)、長期記憶(永続的データストレージ)。

代表的なワークフローの種類

LLMエージェントは、以下のようなワークフローと組み合わせて活用できます。

ワークフロー名 概要 適用例
プロンプトチェーン タスクを複数のステップに分解し、各ステップでLLMを活用 分析レポート生成、プログラム修正支援
ルーティング ユーザー入力を分類し、適切な処理フローへ誘導 FAQチャットボット、問い合わせ対応
並列化 複数のLLMが同時に異なる処理を実行し、結果を統合 大規模データ要約、マルチモーダル処理
オーケストレーター&ワーカー 中央のLLMがタスクを分解し、サブエージェントに割り当て 自動記事生成、プロジェクト管理
評価器&最適化器 LLMの出力品質を評価し、フィードバックループで改善 コードレビュー、文章校正

TypeScriptによるLLMエージェントの実装

以下のTypeScriptコードは、LLMと外部ツールを連携させるエージェントの基本構造を示したものです。

interface LLM {
  generate(task: string): Promise<string>;
}
interface Tool {
  name: string;
  description: string;
  execute: (input: string) => Promise<string>;
}

class LLMAgent {
  private llm: LLM;
  private tools: Tool[];
  private parser: ToolCallParser;
  
  constructor(llm: LLM, tools: Tool[], parser: ToolCallParser) {
    this.llm = llm;
    this.tools = tools;
      this.parser = parser;
  }

  async run(task: string): Promise<string> {
    let currentTask = task;

    while (true) {
      const response = await this.llm.generate(currentTask);
        const toolCall = this.parser.parse(response);

        if (toolCall) {
          const tool = this.findTool(toolCall.name);
        if (!tool) {
           console.error(`Error: Tool "${toolCall.name}" not found.`);
           return "Error: Tool not found."; // より具体的なエラーメッセージ
          }
        const result = await tool.execute(toolCall.input);
        currentTask = this.updateTask(currentTask, result);
      } else {
        return response;
      }
    }
  }

  private updateTask(currentTask: string, result: string): string {
    return `${currentTask} -> ツール実行結果: ${result}`;
  }

  private findTool(name: string): Tool | undefined {
    return this.tools.find(tool => tool.name === name);
  }
}

interface ToolCall {
  name: string;
  input: string;
}

interface ToolCallParser {
  parse(response: string): ToolCall | null;
}

class JsonToolCallParser implements ToolCallParser {
  parse(response: string): ToolCall | null {
    try {
      return JSON.parse(response);
    } catch (e) {
        console.error("Error parsing tool call", e);
        return null;
    }
  }
}


const searchTool: Tool = {
  name: "search",
  description: "ウェブ検索を実行",
  execute: async (input: string) => {
    return `検索結果: ${input} に関する情報を取得しました。`;
  }
};


const mockLLM: LLM = {
  generate: async (task: string) => {
    if (task.includes("search")) {
      return `{"name": "search", "input": "LLMエージェント"}`;
    }
    return `タスク完了: ${task}`;
  }
};

const jsonParser = new JsonToolCallParser();
const agent = new LLMAgent(mockLLM, [searchTool], jsonParser);

(async () => {
  const result = await agent.run("LLMエージェントについて調べてください。");
  console.log(result);
})();

この実装のポイント

  • ✅ LLMの応答を解析し、必要に応じてツールを呼び出す。
  • ✅ ツールの実行結果を考慮し、タスクを継続的に更新する。
  • ✅ モックLLMを活用し、実際のAPIなしでもテスト可能。

LLMエージェント実装時の注意点

  1. コスト管理:APIの呼び出し回数が増えると、コストが急増する可能性がある。
  2. エラー処理:エージェントの意思決定ミスを防ぐため、エラー検出とフェイルセーフを設計する。
  3. ツールの安全性:エージェントが実行するツールが適切に制限されているか検証する。

まとめ

LLMエージェントは、検索・ツール連携・メモリ機能を備えた拡張LLMを活用し、柔軟にタスクを処理する強力なシステムです。本記事では、LLMエージェントの基本概念と構成要素を解説し、TypeScriptでの実装例を紹介しました。

Discussion