オープンソース系Deep Researchをローカル環境で試した結果のまとめ

　本記事は最近評判のOpenAIの「Deep Research」を越えようと、様々なプレイヤーがDeep Research系のパッケージやAPIサービスを開始する中で、主にオープンソース系でPythonベースのパッケージを幾つか実際に試してみて、各パッケージのローカル環境の設定方法や簡単な評価結果と注意点を参考にまとめてみましたのでその紹介になります。
　Deep Researchは一般的にWeb検索を利用するため、Node・Typescript系のパッケージも存在しますが、今回は対象から外してます。また、ローカル環境下でLLMを実行するプラットフォームはOllamaを利用しました。

## １. オープンソース系Deep Researchの対象リスト
　今回試したのは下記のパッケージになります。

  | Repo id | 特　徴　及　び　概　要 |ライセンス|
|:-----------:|:------------:|:------------:|
| [A. langchain-ai/local-deep-researcher](https://github.com/langchain-ai/local-deep-researcher) |LangGraph Functional APIを使ってリサーチしたいトピックをエージェントに対して指定し、その内容を基にWeb検索クエリを作成＆検索。単純な質問に適する。|MIT|
| [B. langchain-ai/open-deep-research](https://github.com/langchain-ai/open_deep_research)  |最初のPlan作成時にHuman Feedbackが可能で、その後はWeb検索と深掘りを繰り返す。複雑な質問にも対応可能。 |MIT|
| [C. huggingface/smolagents/open_deep_research](https://github.com/huggingface/smolagents/tree/main/examples/open_deep_research)  |HuggingFaceチームがAIによるコード生成(Code Agent)機能を使って作成したもの。長時間を要する。 | Apache2.0|
|[D. zilliztech/deep-searcher](https://github.com/zilliztech/deep-searcher) |ローカルファイル(PDF等)とWebクロールの両方をベースにレポート作成してくれる。 | Apache2.0|

　最初にお試しでローカルテストしたい場合は、Ollama対応が前提のlangchain-ai/local-deep-researcherが比較的構築も楽でお勧めです。次のlangchain-aiとhuggingfaceは同じ”open_deep_research”という名前になってますが中身はかなり異なっており、前者は調査の初期段階でPlan作成を行いこれに対してHuman Feedbackが出来るようになっており、後者はWeb検索とレポートのまとめをCode Agentを使いコードを生成してそれを使って処理を進める構造になっています。どちらもOllamaでのローカル環境に対応可能なので対象にしました。
　最後のzilliztech/deep-searcherはGitHubでの評価（Fork/Star/Watch）も高く、Webで特定URLを指定してクローリングによるデータ取得をするだけで無く、ファイルも読み込ませる事が出来るので、企業内でのプライベート利用としてRAG的な活用が期待できそうなので追加しました。
 
 
## ２.　ローカル環境下で使用する場合の設定方法

　私が実際に直面した各パッケージ共通の課題は以下の通りです。各論へ入る前にまずこれらを説明して行きます。
    　　① 日本語化をするにはどうしたら良いか？
    　　② llmモデルの選択の基準は何か？
    　　③ Web検索エンジンの選択の基準は何か？
　①については各パッケージのメインスクリプト内のAgentへの指示文のプロンプト相当部分を日本語に翻訳することで解決出来ます（下記の例を参照）。但し②のモデル選択で日本語対応モデルを選択する必要があります。モデル選択のもう一つの条件はFunction Calling(Tool Calling)とstructured outputsに対応している事です。実際にEZO-Llama-3.2-3B-Instruct-dpoE/llama3.2:1b/llama3.2:3b/gemma3:1b/gemma3:3bの各モデルを試しましたが、お勧めはllama3.2:3b(Q4_K_M)です。③Web検索エンジンもDuckDuckGo/Tavily/Searxng/SerpApiを実際試しましたが、あまり差が感じられない結果でしたので、APIキー無しに一番簡単に使えるDuckDuckGoから始めることをお勧めします。

 ``` local-deep-researcher/src/ollama_deep_researcher/graph.pyの日本語化の例
    （略）
    
     result = llm_json_mode.invoke(
        [SystemMessage(content=formatted_prompt),
        #HumanMessage(content=f"Generate a query for web search:")]
        HumanMessage(content=f"Web検索用のクエリーを生成して下さい:")]
    　)
     
    （略）
    
    # Build the human message
    if existing_summary:
        human_message_content = (
            f"<Existing Summary> \n {existing_summary} \n <Existing Summary>\n\n"
            f"<New Context> \n {most_recent_web_research} \n <New Context>"
            f"このトピックの新しいコンテキストで既存の要約文を更新します: \n <User Input> \n {state.research_topic} \n <User Input>\n\n"
        )
    else:
        human_message_content = (
            f"<Context> \n {most_recent_web_research} \n <Context>"
            f"このトピックにおけるコンテキストを使って要約文を生成して下さい: \n <User Input> \n {state.research_topic} \n <User Input>\n\n"
            
    （略）

    result =　llm_json_mode.invoke([SystemMessage(content=reflection_instructions.format(research_topic=state.research_topic)),
        HumanMessage(content=f"既存の知識を振り返って下さい: \n === \n {state.running_summary}, \n === \n そして知識ギャップを特定し、フォローアップのウェブ検索クエリを生成して下さい:")]
    )

    （略）
 ```

　ここからは各パッケージ毎の具体的な設定方法になります。
 
### A.　langchain-ai/local-deep-researcher：
　GithHubに使用方法を解説したVideoもあるので一番設定が楽かと思います。ローカルフレームワークはOllamaかLMStudioかを選べるようになっていますが、レポジトリには”src/ollama_deep_researcher”というフォルダがあり、まさにOllamaを使う前提のコードサンプルが用意されているのでこれを使います。また設定はルートフォルダにある.env.exampleを書き換えて使います。

``` .env
# Which search service to use, either 'duckduckgo', 'tavily', 'perplexity', Searxng
SEARCH_API='duckduckgo'
# For Searxng search, defaults to http://localhost:8888
SEARXNG_URL=

# Web Search API Keys (choose one or both)
TAVILY_API_KEY=tvly-xxxxx      # Get your key at https://tavily.com
PERPLEXITY_API_KEY=pplx-xxxxx  # Get your key at https://www.perplexity.ai

# LLM Configuration
LLM_PROVIDER=ollama      # Options: ollama, lmstudio
LOCAL_LLM=llama3.2:latest   # Model name in LMStudio/Ollama, This is 3b model
#LMSTUDIO_BASE_URL=http://localhost:1234/v1  # LMStudio OpenAI-compatible API URL
OLLAMA_BASE_URL=http://localhost:11434 # the endpoint of the Ollama service
MAX_WEB_RESEARCH_LOOPS=2   # Web検索の繰り返し数, 3では少し時間がかかり過ぎか？
FETCH_FULL_PAGE=False　　 # 精度優先で時間がかかっても良い場合はTrue
```
 ここでWeb検索エンジンのAPI設定が出来ますが、前述の通りAPIキー不要のデフォルトのduckduckgoで十分使えると思います。あとは"git clone"して、"ollama pull llama3.2:latest"でモデルをダウンロードします。その後公式の記載通り自分の環境に合ったLangGraph serverのインストールをして、LangGraphを起動すれば自動でブラウザからUIが立ち上がるので、指示を入力すればレポートが作成されます。
 ```
MacOSの場合：
# Install uv package manager
curl -LsSf https://astral.sh/uv/install.sh | sh
# Start the LangGraph server with uv
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.11 langgraph dev

WindowsOSの場合：
# Install dependencies
pip install -e .
pip install -U "langgraph-cli[inmem]"            
# Start the LangGraph server
langgraph dev
 ```

### B.　langchain-ai/open-deep-research：
　OpenAI等がデフォルトなのでOllama用に修正するには三手間必要になりますが、下記リンクに分かりやすい修正方法の解説がありますので、参考にして下さい。
　　[・参考: Ollam用の修正方法](https://github.com/langchain-ai/open_deep_research/issues/65#issuecomment-2743586318)

 まずはconfiguration.pyの修正例です。
```  open_deep_research/src/open_deep_research/configuration.py
# Graph-specific configuration
    number_of_queries: int = 2 # Number of search queries to generate per iteration
    max_search_depth: int = 2 # Maximum number of reflection + search iterations
    planner_provider: str = "ollama"  # Defaults to Anthropic as provider
    planner_model: str = "llama3.2:latest" # Defaults to claude-3-7-sonnet-latest
    planner_model_kwargs: Optional[Dict[str, Any]] = None # kwargs for planner_model
    writer_provider: str = "ollama" # Defaults to Anthropic as provider
    writer_model: str = "llama3.2:latest" # Defaults to claude-3-5-sonnet-latest
    writer_model_kwargs: Optional[Dict[str, Any]] = None # kwargs for writer_model
    search_api: SearchAPI = SearchAPI.DUCKDUCKGO # Default to TAVILY
    search_api_config: Optional[Dict[str, Any]] = None
```
次はgraph.pyにmethod="json_schema"を追加する修正です。
```  open_deep_research/src/open_deep_research/graph.py
80: structured_llm = writer_model.with_structured_output(Queries, method="json_schema")

123: structured_llm = planner_llm.with_structured_output(Sections, method="json_schema")

211: structured_llm = writer_model.with_structured_output(Queries, method="json_schema")

319: reflection_model = init_chat_model(model=planner_model, 
                                           model_provider=planner_provider, 
                                           max_tokens=20_000, 
                                           thinking={"type": "enabled", "budget_tokens": 16_000}).with_structured_output(Feedback, method="json_schema")

324: reflection_model = init_chat_model(model=planner_model, 
                                           model_provider=planner_provider, model_kwargs=planner_model_kwargs).with_structured_output(Feedback, method="json_schema")
```

　最後にstate.pyの修正で完成です。あとはlocal-deep-researcherと同様にLangGraphを起動します。
    
```  open_deep_research/src/open_deep_research/state.py
12:  research: Literal[True,False] = Field(     #boolean to Literal[True,False]
        description="Whether to perform web research for this section of the report."
     )
```

### C.　huggingface/smolagents/open_deep_research：
　インストールは公式通り問題ないと思いますが、pythonのバージョンに影響を受ける可能性があるので、pythonのバージョンを選択できる仮想環境下での構築をお勧めします。Web検索はSERPERがデフォルトのようですが、残念ながらDuckDuckGoが使えなさそうなので今回は下記のように設定変更してSERPAPIにしてみました（予めSERPAPIのAPIキーを取得して下さい）。
[・SerpApiのFreeプラン（100検索/月まで）](https://serpapi.com/users/sign_up?plan=free)

```  smolagents/examples/open_deep_research/run.py

73:    GoogleSearchTool(provider="serpapi"), # serper → serpapi
```
　これに伴い、.envファイルに"SERPAPI_API_KEY={YOUR API KEY}"を設定しておきます。あとはOllamaを立ち上げておいてから、コマンドラインで下記のようにモデルとしてOllamのLlama 3.2:latestを指定すればローカルで起動できます。

```
python run.py --model-id "ollama/llama3.2:latest" "Who is present prime minister of Japan?"
```
　日本語対応は上記run.py内のプロンプト相当文を翻訳すれば可能だと思います（まだ未実施）。

### D.　zilliztech/deep-searcher：
　最後はPDF等のローカルドキュメント、JSON等の構造化データ、及びストリーミングデータを読み込ませ、それとWebクロールを組み合わせてリサーチしてくれるパッケージになります。 普通にpipでインストールできるようですが、Ollama利用の場合は下記のようなオプションが可能です（私は下記のオプションインストール出来ませんでしたが、deepsearcherの普通のインストールで問題なく動きました）。
```
pip install "deepsearcher[ollama]"
```
　RAGと同じようにドキュメントの解析にベクトルDB（デフォルトはMilvus）と埋め込みモデル（デフォルトはPyMilvusにビルトインされているMilvusEmbedding）等を使います。リポジトリの公式トップページあるクイックデモ用Pythonコード中の"config.set_provider_config"の"llm"部分を下記のようにOllama用に修正します。

```
config.set_provider_config("llm", "Ollama", {"model": "llama3.2:latest"})
```
　同様に"config.set_provider_config"を追加して、埋め込みモデル（"emmbeding"）、ベクトルDB（"vector_db"）、File Loader（"file_loader"）、Webクロールエンジン（"web_crawler"）がそれぞれ個別に設定可能です。
　これでデモ用Pythonコードが起動できるようになりますが、コマンドラインからの起動も可能で、更にFastAPIを使ってブラウザUIでの利用も出来るようですので、興味のある方は試してみて下さい。

## ３. 各パッケージのテスト評価のまとめと今後の展開（予定）

　全てOllamaを使ったローカル環境でLLMをLlama3.2:3bに固定して、テスト評価した結果をまとめると次のようになります。
 
 ⓪　レポートの精度に関してはどのパッケージも使用するLLMモデルの影響を最も受けるようです。今回Llama3.2:3bに固定するまでは幾つかのモデルを試してみましたが、モデルによってレポート内容の品質は大きく変わりました。今後はLlama3.3:70B/Llama4やgemma3:27Bも試してみる予定です。
 ①　処理時間は、A < B = D < C　でしたが、Cは生成したコードのエラーが頻発してそれによる遅延が大半を占めています。
 ②　レポート品質は条件によって変動しますが、　単純な課題に関しては　A > D > B > C で、複雑な課題に関しては B = D > A > C でした（あくまでも個人的な主観評価です）。
 ③　C 及び D は機能的に特殊なので相対評価は難しいですが、Cについては生成するコードの品質が良くなくコード生成を繰り返しているので、コード生成が強いモデルに変更した方が良さそうです。DはWeb検索ではなく指定されたURLをスクレイピングして読み込んだドキュメントと共にRAG的な処理（ベクトルDBによるクエリーとの近似を計算して出力を構成する）をしてくれるので、既に社内データの活用にRAGを導入している企業では、次のシステム拡張用に検討してみるのは良いかもしれません。ベクトルDBもChromaDB等を利用したり、スクレイピングも社内Webに限定すれば完全ローカルで運用可能な社内データ調査用汎用AIも可能になり、ランニングコストの削減や柔軟な拡張性を手に入れられるのではないかと思います。
 ④Web検索エンジンでDuckDuckGo以外はGoogle Search等をバックで使うAPIが殆どなので、どれを使っても精度に影響は無いと思います。

 　最後になりましたが、本記事がこれからDeep Researchを自分でローカル構築してみたいと考えている方の参考になれば幸いです。今後はよりDeep Researchに合う、日本語対応可能でFunction calling & Structure outputsも出来るモデルを探して行くつもりですので、見つかったらまた投稿したいと思います。また間違いの指摘やコメントも頂けたら嬉しいです。