Zenn
🤗

Hugging Face open deep research(smolagent)の概要をわかりやすく解説

に公開

背景

OpenAIはWeb検索を活用して、インターネット上のコンテンツに基づいた質問応答が可能な「Deep Research」をリリースしました。このDeep Researchは、General AI Assistantsベンチマーク(GAIA)において、大幅にスコアを向上させました。これにより、Deep Researchの根幹となる「エージェントフレームワーク」の重要性が世界中で認識されるようになりました。

※エージェントフレームワークとは、LLM(大規模言語モデル)にWeb検索やWeb上のPDFファイルの読み取り、Web画像の認識など、特定のタスクを実行するためのツールを連携(バインド)させ、それらを自動的に実行することで、複雑なワークフローを整理する仕組みのことです。

こうした流れを受け、Hugging Faceはオープンソースソフトウェアとして「Open Deep Research(smolagent)」を公開しました。これにより、誰もが自由にDeep Researchの機能を活用できるようになりました。
また、Hugging Faceが提供することにより、特定の企業や環境に依存せず、透明性が高くカスタマイズ可能なエージェントフレームワークを、誰もが手軽に利用できるようになっています。

Open Deep Research(smolagent)には、技術的に優れた多くの特徴が含まれています。本ブログでは、その具体的な技術要素を解説していきます。

機能概要

smolagentは大きく分けて、CodeAgentとToolCallingAgentに分かれています。どちらもMultiStepAgentクラスを継承しています(MultiStepAgentが親クラスです)。

MultiStepAgent

すべてのエージェントタイプの基盤となる抽象クラスです。
タスクを複数のステップに分けて段階的に解決する、AIエージェントの「骨組み」です。

ToolCallingAgentやCodeAgentを構築するための共通の土台となっています。実際に稼働するToolCallingAgentやCodeAgentエージェントの共通部分であり、エージェントの基本的なライフサイクルと振る舞いを定義しています。

例えば、下記のような共通部分があります。

  • メモリ:エージェントが稼働した過去の内容や結果を記録し、メモリに記録した履歴を基に次のアクションを推論します。
  • ツールボックス:エージェントが使用できる「ツール」です。
  • 実行計画に基づくアクション:各ステップで意思決定を行い、その意思決定に基づいて具体的な行動を実行します。

MultiStepAgentはエージェントの基盤クラスなので、直接アクションを行うことはなく、子クラスを通じてアクションを実施します。
状態を管理し、アクションが行われたエージェントのログを記録し、履歴や中間結果を保持することができます。
また、step()メソッドを子クラスがオーバーライドして、具体的な処理を子クラスで実装する設計になっています。

MultiStepAgentの主な関数

下記の主な関数の流れ:

  1. ユーザーからタスク(クエリー)を受け取る: run()
  2. エージェントアクションをキック: _run()
  3. 全体の調査計画を立案: _create_planning_step()
  4. 具体的なアクションを実行: step() ※小クラスのCodeAgentやToolCallingAgentによって具体的なアクションの内容が異なる
  5. ツールをキック: execute_tool_call()
  6. エージェントがツールをキックした結果を確認し、最終回答ならば結果をリターン。最終回答でなければ_run()ループを継続
  • run()
    初回にエージェントをキックする関数。ユーザーのクエリーをパラメータとし、エージェントをキックする。

  • _run()
    実際にエージェントがアクションするループをキックする。最大ステップ数(検索回数の上限)に達するかエージェントから調査結果の最終回答が得られるまでループを続ける。各ステップでstep()メソッドを呼び出し、小クラスのCodeAgentやToolCallingAgentで具体的なstep()処理を行う。

  • step()
    1ステップを実行(抽象メソッド)。このメソッドは意図的に中身が実装されていないので、継承先のToolCallingAgentやCodeAgentで具体的な処理を実装する。具体的なステップの実行内容は子クラスが決定

  • _create_planning_step()
    計画立案。クエリーの基づいてどのような計画で調査するのかを作成。
    タスク全体の調査計画を立てる。初回ステップでは計画を作成するためにタスクに関する事実を収集し、
    全体的な実行計画を作成する。処理のループ内で、新しく取得した事実を整理し計画を更新する。

  • execute_tool_call()
    ツールの実行
    指定されたツールを実行し、ツールに基づいてエージェントがアクションをする。

  • call()
    マルチエージェント機能
    エージェントが他のエージェントをキックして、他のエージェントがアクションするマルチエージェントの基盤

CodeAgent

CodeAgentはMultiStepAgentを継承した子クラスで、Pythonコードブロックを生成・実行してユーザーのタスクを解決するためのクラスです。設定したLLMがPythonコードを生成し、そのコードが実行されることでツールが呼び出され、その結果を処理します。JSON形式のツール呼び出しではなく、Pythonを生成・実行することにより、複雑な計算やデータ処理を柔軟に実行可能です。

複数のツールを組み合わせた高度な処理が可能で、セキュアにPythonを実行するため、コード実行環境の選択やインポート可能なモジュールを制限する機能があります。

  • Python実行について、コード実行環境を選択可能(ローカルまたはリモート分離実行)
  • インポート可能なモジュールを制限することが可能

CodeAgentがJSON形式より優れた結果となった背景は下記のブログを参照。
Hugging Face Open-source DeepResearch – Freeing our search agents

バインドしたツールにより、データ分析、計算、ビジュアライゼーション、Web情報検索、などなどの複雑なタスクに対応可能。柔軟なPythonコードを使用できるため、複雑なタスクを段階的に解決できることがポイントです。

CodeAgentの主な関数

  • initialize_system_prompt()
    システムプロンプト初期化。インポートを許可しているパッケージを設定。

  • step()
    アクションステップ(具体的な実行計画)に基づいて、具体的なアクションを行います。
    対象のツールを実行し、柔軟にエージェントが調査結果を取得することができます。

  1. メモリからメッセージを取得
    過去の対話履歴からメッセージを取得し、過去の履歴に基づいて次のアクションを推論することができる。

  2. LLMがPythonコードを生成
    アクションステップに基づいて、対象のLLMを呼び出してコードを生成

  3. 生成されたコードの実行
    コードをツール呼び出しとしてメモリに記録
    コードを実行する前にメモリに記録
    python_executorを使用してコードを実行
    実行結果、ログ、最終回答フラグを取得

  4. 観察結果の記録
    実行ログを観察結果としてメモリに記録

  5. 結果の返却
    is_final_answer が True の場合: 実行結果を返す(処理終了)
    False の場合: None を返し、次のステップに進む

ToolCallingAgent

ToolCallingAgentはJSON形式でツール呼び出し機能を活用するエージェントです。OpenAIのFunction Callingのような構造化されたAPIをネイティブにサポートするLLMと連携する際に最適で、LLMに対してツールの情報を提供し、LLMが明示的にツールをJSON形式で呼び出せるようにしています。

ToolCallingAgentはMultiStepAgentを継承しており、ReAct(Reasoning + Acting)フレームワークに基づいています。具体的なアクションは、JSON形式で生成されたツール呼び出し指示に従って行われます。

ToolCallingAgentの主な関数

アクションステップ(具体的な実行計画)に基づいて、具体的なアクションを行います。
CodeAgentはPythonを生成/実行するのに対し、ToolCallingAgentはLLMが生成したJSON形式のツール呼び出し指示に基づいてツールを実行する。

  1. メモリからメッセージを取得
    過去の対話履歴からメッセージを取得し、過去の履歴に基づいて次のアクションを推論することができる。

  2. LLM からのツール呼び出し取得
    LLMに使用可能なツールのリストを提供し、LLM がどのツールを呼び出すかを決定させます。
    後続処理で、ここで設定したツールをJSON形式で実行します。

  3. ツール呼び出しの検証と実行
    ツールを実行し結果の取得

  4. 観察結果の記録
    実行ログを観察結果としてメモリに記録

  5. 結果の返却
    is_final_answer が True の場合: 実行結果を返す(処理終了)
    False の場合: None を返し、次のステップに進む

CodeAgent vs ToolCallingAgent

見出し CodeAgent ToolCallingAgent
呼び出し方法 Pythonコードブロックを使用 JSON形式のツール呼び出しを使用
特徴 1回のLLM呼び出しで複数のツール呼び出しや計算を含む複雑なコードを実行可能 1回のLLM呼び出しにつき1つのツールを呼び出す
適用内容 より複雑な計算や複数ステップの処理が必要な場合に適している。どのLLMでも使用可能だが、コード生成能力の高いモデルで効果的。 Function CallingをネイティブにサポートするLLMに最適。OpenAI API (GPT-4/3.5), Claude API (Claude 3), Mistral API など
セキュリティ より柔軟だが、適切な制限がないと潜在的なリスクが高い ツール呼び出しが明示的で制限されているため、より制御しやすい

ToolCallingAgentの呼び出し例:

{
  "name": "search",
  "arguments": { "query": "population of Tokyo" }
}

CodeAgentの呼び出し例:

search_results = search(query="population of Tokyo")
print(search_results)

全体処理フロー

smolagentの処理フローの概要です。
処理の流れ番号順

  1. エージェント起動。クエリーを設定したrun()を実行
  2. MultiStepAgentの初期化(ツール・モデル・設定)
  • ツール・モデル設定: 提供されたツール(web_searchなど)とLLMモデルをインスタンス変数として保存
  • 実行環境構築: CodeAgentの場合、LocalPythonExecutor/E2BExecutor/DockerExecutorのいずれかを初期化
  • 設定構成: max_steps=20のような実行パラメータ、メモリ管理システム、ロギングシステムの設定
  • 検証機能: 最終回答を検証するためのfinal_answer_checks関数リストの設定
  • コールバック: 各ステップ後に実行される関数(メトリクス収集、進捗モニタリングなど)の登録
  1. run(task)メソッド呼び出し。
  2. タスク保存、システムプロンプト初期化
  • ユーザーからのタスク実行リクエストの初期処理
  1. 条件分岐: reset=True? (新規対話か継続か)
  • リセット処理: reset=Trueの場合、前回の会話履歴をクリアして新規会話として扱う
  • プロンプト初期化: エージェントの種類に応じたシステムプロンプトを生成(ツール情報や指示を含む)
  • ロギング: タスク内容と基本情報をログに記録し、デバッグや追跡を可能にする
  1. メモリリセット実行 (reset=Trueの場合)
  • 履歴リセット: reset=Trueの場合は以前の会話をクリア
  1. 既存メモリ維持 (reset=Falseの場合)
  2. タスク記録と実行環境準備。特にPythonコード実行用の環境を設定(主にCodeAgent用)
    ツール登録: すべてのツールとエージェントをPython実行環境に登録し、web_search()のような関数として呼び出せるようにする
    実行コンテキスト: 安全で隔離された実行環境でのPythonを実行
  3. 条件分岐: stream=True? (ストリーミング実行か一括実行か)
  • Trueだとエージェントの途中家庭をストリーミング出力し、最終結果もストリーミング出力。Falseだと最終結果のみを出力
  1. _run()をジェネレータとして返却 (stream=Trueの場合)
  • 初期状態設定: final_answer = Noneとself.step_number = 1で実行開始状態を設定
  • メインループ条件: 「最終回答が得られていない」かつ「最大ステップ数に達していない」場合にループ継続
  • ジェネレータ方式: Pythonのジェネレータパターンを使用し、各ステップの結果を順次生成・返却
  • 実行制御: 「最終回答が得られていない」かつ「最大ステップ数に達していない」条件により、タスク完了または最大試行回数到達まで処理を繰り返す仕組みを構築
  1. 最終結果のみを返却 (stream=Falseの場合)
  2. 実行初期化 (final_answer=None, step_number=1)
  3. ループ条件: final_answer is None AND step_number <= max_steps
  4. 条件分岐: planning_interval設定 AND step_number % planning_interval == 1
  5. プランニングステップ実行 (条件合致時のみ)。一定間隔でタスクの戦略立案を行う特殊ステップ
  • 実行条件: planning_intervalが設定されたタイミングに応じて、計画作成
  • 初期計画: 最初のステップでは「計画のための初期事実分析」と「初期計画」を立てる
  • 計画更新: 定期的なステップでは、planning_intervalで設定したタイミングで、それまでの進捗を考慮して計画を更新
  • 計画記録: 生成された計画文章をPlanningStepとしてメモリに記録し、将来のステップでの参照を可能にする
  1. アクションステップ作成。実際の思考・行動・結果観察サイクルの実行
  • ステップ作成: 開始時間を記録し、ActionStepオブジェクトを作成して基本情報を設定
  • ステップ実行: 具体的なエージェント種類(CodeAgentやToolCallingAgent)に応じたステップメソッドを呼び出す
  • 回答検証: 最終回答が返された場合、オプションのfinal_answer_checks関数で検証
  1. _execute_step実行 (try-exceptブロックでエラーハンドリング)
  • 子クラスのCodeAgentかToolCallingAgentをキックする
  1. 条件分岐: エージェントタイプ (CodeAgent or ToolCallingAgent)
  2. CodeAgent: コード生成・実行 - python_executor(code)
  • 会話履歴構築: これまでの履歴をLLMへの入力形式に変換
  • コード生成、準備: Pythonコードを生成し、final_answer関数の呼び出しを処理ができるようにする。
  • 実行準備: 生成されたコードを「python_interpreter」ツールとして記録
  1. ToolAgent: ツール呼び出し - execute_tool_call(name, args)
  2. 条件分岐: CodeAgentでis_final_answer=True?
  • Trueの場合は、ループを抜けて最終回答をリターン
  • Falseの場合は、ループを継続し最終回答結果が得られるまでエージェントは稼働する。(max_stepsを超えない限り、ループを継続)
  1. 条件分岐: ToolAgentでtool_name=='final_answer'?
  • final_answe()ツールがキックされたら、エージェントが最終回答を生成したとみなす。
  1. 最終回答として結果を返す (final_answer検出時)
  2. 継続処理としてNoneを返す (継続処理時)
  • 最終回答が得られないときは、Noneがリターンされるので、ループが継続される
  1. ステップ完了処理: 時間記録・メモリ追加・コールバック実行
  • メトリクス記録: 終了時間の記録、実行時間の計算、Token使用量の集計
  • メモリ更新: 完了したステップ情報をエージェントのメモリに追加
  • コールバック実行: 登録された各コールバック関数を呼び出し(モニタリング、ロギング、メトリクス更新など)
  1. ステップカウンタ増加 (self.step_number += 1)
  • ステップ進行: self.step_number += 1でカウンタを増加させ、次のステップへ
  1. 最大ステップ到達時の処理 (_handle_max_steps_reached)
  • 強制終了処理: _handle_max_steps_reachedメソッドを呼び出し、中断処理を実行
  • エラー記録: AgentMaxStepsErrorをメモリに記録し、最大ステップ数到達を示す
  1. 最終回答生成 (provide_final_answer)
  • 最終回答生成: provide_final_answerを使ってLLMに直接最終回答を生成させる
  1. FinalAnswerStep形式で結果返却
  2. 処理終了

標準成功パス:
1 → 2 → 3 → 4 → 5 → 6/7 → 8 → 9 → 10 → 12 → 13 → 14 → 15/16 → 17 → 18 → 19/20 → 21/22 → 23 → 25 → 26 → 13 → 28 → 29 → 30
※1回で最終結果を取得することはあまりないと思うので、2回目の13の処理は再度「Yes」のフローに流れる。

最大ステップ到達パス:
1 → 2 → 3 → 4 → 5 → 6/7 → 8 → 9 → 10 → 12 → 13 → 14 → 15/16 → 17 → 18 → 19/20 → 21/22 → 23 → 25 → 26 → 13 → 27 → 28 → 29 → 30

引用

DXC Lab

Discussion

ログインするとコメントできます