🎃

自律型エージェントの構築とベストプラクティス

2025/02/01に公開

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

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

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

自律型エージェントの構築とベストプラクティス

自律型エージェントは、大規模言語モデル(LLM)を活用して、自律的にタスクを処理するシステムです。これらのシステムは、複雑なタスクを自律的に処理する能力を持ち、さまざまな分野での応用が期待されています。本記事では、自律型エージェントの概念、設計原則、ツール開発におけるベストプラクティスについて解説します。

自律型エージェントとは?

「エージェント」という言葉はさまざまな意味で使われますが、本記事ではLLMが自身のプロセスとツール利用を動的に決定し、タスクを遂行するシステムを指します。

従来のワークフローとは異なり、自律型エージェントは以下の能力を持ちます。

✅ 入力の理解:ユーザーの意図を適切に解釈
✅ 推論と計画:タスクを細分化し、最適な手順を決定
✅ ツールの利用:適切なAPIや関数を呼び出し、結果を取得
✅ 環境との対話:外部のフィードバックを活用し、動的に対応

この仕組みにより、事前に決められたコードフローに依存せず、より柔軟な動作が可能になります。

活用事例

自律型エージェントは、以下のような分野で活用されています。

  • コード作成エージェント
  • SWE-benchタスクを解決し、複数のファイルを編集
  • コンピューター操作エージェント
  • GUIやAPIを利用し、タスクを自動実行
  • カスタマーサポート
  • 顧客データや注文履歴をもとに、払い戻しやサポート対応を自動化
  • 検索エージェント
  • 複数の情報源を横断して検索・分析

自律型エージェントの設計原則

エージェントを効果的に構築するためには、以下の3つの原則が重要です。

シンプルな設計を心がける

  • 過度に複雑なフレームワークよりも、シンプルで拡張しやすい構成を選択。
  • 小さなモジュールを組み合わせる形で設計する。

透明性を確保する

  • エージェントの思考プロセスをログとして出力。
  • ブラックボックス化を避け、デバッグしやすい仕組みを構築。

エージェントとコンピュータ間のインターフェース(ACI)を最適化する

  • ツールのドキュメントを充実させ、使用例やエッジケースを明示する。
  • ユーザーが直感的に操作できるよう、わかりやすいパラメータ設計を心がける。

ツール開発とプロンプトエンジニアリング

エージェントが外部のAPIやツールを活用する際、ツール設計が重要になります。以下のポイントに注意しましょう。

✅ 適切なトークン管理

  • モデルが「考える」ための十分なトークンを確保。

✅ 自然なデータ形式を使用

  • モデルの学習データに近いフォーマットを採用し、誤解を防ぐ。

✅ オーバーヘッドを排除

  • 不要なデータ変換やフォーマット処理を最小限に。

✅ ツールのわかりやすさ

  • 明確なパラメータ名と説明を設定し、誤入力を防ぐ(ポカヨケ)。

✅ テストとフィードバックの活用

  • エージェントが実際にツールを使用する際の挙動を検証し、改善を繰り返す。

TypeScriptによるツール定義の例

以下に、エージェントが使用するツールの実装例を示します。

import fetch from "node-fetch";
import fs from "node:fs/promises";

interface Tool {
  name: string;
  description: string;
  parameters: { [key: string]: { type: string; description: string } };
  execute: (args: { [key: string]: unknown }) => Promise<string>;
}

class FilePath {
  constructor(public path: string) {}
}

class CodeContent {
  constructor(public content: string) {}
}

// 検索ツールの定義
class SearchTool implements Tool {
  name = "search";
  description = "指定されたクエリでインターネット検索を実行。";
  parameters = {
    query: { type: "string", description: "検索クエリ" },
  };
  async execute(args: { [key: string]: unknown }) {
    try {
      // 実際の検索処理 (例: node-fetch)
      const response = await fetch(
        `https://example.com/search?q=${args.query}`
      );
      const result = await response.text();
      return `検索結果: ${result}`;
    } catch (error) {
      console.error("検索エラー:", error);
      throw new Error("検索処理に失敗しました。");
    }
  }
}

// コード編集ツールの定義
class CodeEditTool implements Tool {
  name = "codeEdit";
  description = "指定されたファイルのコードを編集。";
  parameters = {
    filePath: { type: "FilePath", description: "編集するファイルのパス" },
    content: { type: "CodeContent", description: "新しいコードの内容" },
  };
  async execute(args: { [key: string]: unknown }) {
    const { filePath, content } = args as {
      filePath: FilePath;
      content: CodeContent;
    };
    try {
      // 実際のファイル編集処理 (例: fs モジュール)
      await fs.writeFile(filePath.path, content.content);
      return `ファイル ${filePath.path} を編集しました。`;
    } catch (error) {
      console.error("コード編集エラー:", error);
      throw new Error("コード編集処理に失敗しました。");
    }
  }
}

// ツールストラテジー
interface ToolStrategy {
  getTool(task: string): Tool | null;
}

class TaskBasedToolStrategy implements ToolStrategy {
  constructor(private tools: Tool[]) {}

  getTool(task: string): Tool | null {
    // タスクを解析してツールを選択する
    if (task.includes("検索")) {
      return this.tools.find((tool) => tool.name === "search") ?? null;
    }
    if (task.includes("コード編集")) {
      return this.tools.find((tool) => tool.name === "codeEdit") ?? null;
    }
    return null;
  }
}

const searchTool = new SearchTool();
const codeEditTool = new CodeEditTool();
const allTools = [searchTool, codeEditTool];
const toolStrategy = new TaskBasedToolStrategy(allTools);

// エージェントがツールを利用する例
async function executeAgent(task: string) {
  const selectedTool = toolStrategy.getTool(task);
  if (selectedTool) {
    try {
      let args = {};
      if (selectedTool.name === "search") {
        args = { query: task.split("検索")[1] };
      } else if (selectedTool.name === "codeEdit") {
        args = {
          filePath: new FilePath("sample.txt"),
          content: new CodeContent(task.split("コード編集")[1]),
        };
      }
      const result = await selectedTool.execute(args);
      console.log(result);
    } catch (error) {
      console.error("ツール実行エラー:", error);
    }
  } else {
    console.log("適切なツールが見つかりませんでした。");
  }
}

executeAgent("TypeScript 検索");
executeAgent("sample.txt のコード編集しなさい。これは新しいコードだ");
executeAgent("ファイルを削除したい");

索引

用語 説明
自律型エージェント LLMが自身のプロセスを動的に決定し、タスクを遂行するシステム。
ワークフロー 事前に定義されたコードパスに沿って処理を進める方式。
ツール エージェントが外部サービスと連携するための機能。
ACI (Agent-Computer Interface) エージェントとコンピュータ間のインターフェース。
プロンプトエンジニアリング LLMがツールを適切に活用できるように最適化する技術。

まとめ

自律型エージェントの開発では、設計のシンプルさ・透明性・インターフェースの最適化が重要です。また、ツールの設計とプロンプトエンジニアリングを適切に行うことで、より信頼性の高いエージェントを構築できます。

技術の進化とともに、これらの原則を活かしながら、より実用的で効果的なエージェントの開発を進めていきましょう。

Discussion