人工知能の深い研究は、単一のモノリシックなモデルではなく、答えが見つかるまで検索、読み取り、推論を繰り返すプロセス、反復的なワークフローです。ChatGPTやGPT-4を動かすOpenAIの独自システムは、回答を継続的に洗練させる複雑なパイプラインを使用しています。今、オープンソースのツールを使用して同様のシステムを構築できると想像してみてください。本記事では、jina-ai/node-DeepResearchプロジェクトを使用して、Deep Researchシステムを再現する方法を説明します。コードを分解し、各コンポーネントを詳細に説明し、システムをセットアップして拡張する方法を示します。
1. 概要と目的
DeepResearchは、シンプルでありながら強力なアイデアに基づいて構築されています:
答えが見つかるまで(またはトークンの予算を超えるまで)ウェブページを検索し、読み続けること。
システムは、クエリ(例:「誰が大きいか?cohere、jina ai、voyage?」)を受け取り、ループに入ります。各ステップで、エージェント(知的モジュール)がアクションを決定します。新しいキーワードを検索したり、URLの内容を読み取ったり、フォローアップの質問を生成して反省したり、確信がある場合は回答を提供したりします。この反復的なサイクルは、答えが明確になるか、トークンの予算を超えるまで続きます。
インストールとセットアップ
コードに入る前に、必要な依存関係をインストールし、APIキーを設定する必要があります。このプロジェクトでは、言語モデリングにGemini、ウェブ検索にBraveまたはDuckDuckGo、ウェブページの内容を取得するためにJina Readerを使用します。プロジェクトのセットアップ方法は次のとおりです:
export GEMINI_API_KEY=... # Gemini API用、Hanに問い合わせる
export JINA_API_KEY=jina_... # 無料のJina APIキー、https://jina.ai/readerから取得
export BRAVE_API_KEY=... # 任意;指定しない場合はDuckDuckGo検索にデフォルト設定されます
git clone https://github.com/jina-ai/node-DeepResearch.git
cd node-DeepResearch
npm install
READMEには、さまざまなクエリでシステムを実行するための例も記載されています:
- シンプルなクエリ:
npm run dev "1+1="
あるいはnpm run dev "フランスの首都は何ですか?"
- マルチステップのクエリ:
npm run dev "Jina AIからの最新ニュースは何ですか?"
npm run dev "jina aiの創設者のtwitterアカウントは何ですか?"
- 曖昧で研究のようなクエリ:
npm run dev "誰が大きいか?cohere、jina ai、voyage?"
npm run dev "2028年にアメリカの大統領になるのは誰ですか?"
npm run dev "2025年にjina aiの戦略は何であるべきですか?"
コマンドラインインターフェースに加えて、このプロジェクトにはクエリを送信し、進行状況の更新をストリーミングするためのエンドポイントを公開するウェブサーバAPIも含まれています。
2. アーキテクチャと主要コンポーネント
システムの主要コンポーネントをコアファイルを探ることで分解してみましょう:
2.1 agent.ts – コアロジック
agent.ts
ファイルはシステムの心臓部です。それは「深い研究」サイクルのロジックを実装しています:プロンプトを生成し、アクションを決定し、検索、読み取り、反省、回答のステップを繰り返します。
agent.tsの主要要素:
インポートとセットアップ:
このファイルは、さまざまなツールとライブラリをインポートすることから始まります:
- GoogleGenerativeAIは言語生成に使用されます。
- readUrlは
./tools/read
からウェブページの内容を取得して処理します。 - duckSearchおよびbraveSearchは外部検索機能を提供します。
- rewriteQuery、dedupQueries、evaluateAnswer、analyzeStepsなどのユーティリティ関数はクエリを洗練し、応答を評価します。
- 設定値(APIキー、トークン予算、モデル設定)は
config.ts
からインポートされます。 - トークンおよびアクショントラッカーのユーティリティはトークンの使用状況とエージェントの進行状況を監視します。
- 最後に、このファイルは
types.ts
で定義された型(例:StepAction
、ResponseSchema
)をインポートします。
スリープ関数:
async function sleep(ms: number) {
const seconds = Math.ceil(ms / 1000);
console.log(`待機中 ${seconds}s...`);
return new Promise(resolve => setTimeout(resolve, ms));
}
このヘルパー関数は操作を遅延させるために使用されます—外部APIを呼び出す際のレート制限を回避するのに便利です。
スキーマ生成:
getSchema
関数はエージェントの応答のJSONスキーマを定義します。それは以下のプロパティを含むスキーマを動的に構築します:
- search: キーワードベースの
searchQuery
が必要です。 - answer: 最終的な回答には自然言語のテキストとサポートされるリファレンス(正確な引用とURL)が含まれる必要があります。
- reflect: 知識のギャップを埋めるための明確化サブ質問のリストです。
- visit: 外部コンテンツを読むためのURLターゲットが含まれます。
厳格なJSONスキーマを強制することで、エージェントの出力は一貫して機械可読性を保ちます。
プロンプト生成:
getPrompt
関数は、言語モデルに送信される詳細なプロンプトを作成します。これは以下のセクションを集約します:
- ヘッダー: 現在の日付と元の質問が含まれます。
- コンテキストと知識: 以前のアクションと収集した中間知識が含まれます。
- 未成功の試み: 以前のアクションが明確な回答を引き出せなかった場合、その失敗(理由と改善点)を記録します。
- アクション: 可能なアクションのリストが表示されます。フラグ(
allowSearch
、allowRead
など)に応じて、許可されている操作を列挙します。「ビーストモード」では、エージェントに対して不確実性が残っていても、できる限りの努力をして回答を生成するよう促す指示が与えられます。
この層状プロンプトは、生成AIモデルに対して一歩ずつ考えて、一度に1つのアクションを選択するように導きます。
getResponseのメインループ:
getResponse
関数は、エージェントの反復ループのコアです。それは初期コンテキストを設定します:
- トラッカー: トークン使用量を監視するためのTokenTrackerと、各ステップとその結果を追跡するアクショントラッカーの2つのトラッカーが使用されます。
- ギャップと知識: 「ギャップ」(元の質問)で始まり、システムが推論を再考する必要がある場合は中間質問を追加します。
whileループの中で、エージェントは:
- APIのレート制限を回避するために(スリープ関数を使用して)待機します。
- 現在のコンテキストに基づいてプロンプトを生成します。
- 生成モデル(GoogleGenerativeAI経由)を呼び出して応答を生成します。
- JSON応答を解析して、どのアクションが取られたかを判断します(回答、反映、検索、または訪問)。
- アクションに応じて:
- 回答:回答を評価し、明確な場合はループを終了します。
- 反映:知識のギャップを埋めるためのサブ質問を処理します。
- 検索:検索クエリを書き換え、以前に使用されたキーワードを重複除去し、DuckDuckGoまたはBraveから新しいURLを取得します。
- 訪問:提供されたURLから内容を読み込み、知識ベースを更新します。
ループが予算を使い果たしたり、あまりにも多くの失敗した試みが発生した場合、システムは「ビーストモード」に入り、答えを出すための最終的で攻撃的な試みが行われます。
コンテキストストレージ:
storeContext
関数は現在のプロンプトとさまざまなメモリ状態(コンテキスト、クエリ、質問、集めた知識)をファイルに書き込みます。このアーカイブプロセスはデバッグを助け、意思決定プロセスのさらなる分析を可能にします。
最終実行:
main()
関数はagent.ts
の最後でコマンドライン引数(クエリ)を使用し、getResponse
を呼び出して最終的な回答とトークン使用の概要を印刷します。
2.2 config.ts – 環境の設定
config.ts
ファイルでは、環境とモデルの設定が定義されています:
- 環境変数:
dotenv
を使用して、Gemini、Jina、およびBraveのAPIキーをロードします。HTTPSプロキシの設定もサポートしています。 - 検索プロバイダー: システムはBrave APIキーが提供されているかどうかに基づいて動的に検索プロバイダーを選択します。提供されていない場合、DuckDuckGoにデフォルト設定されます。
- モデル設定: このファイルでは、クエリの書き換え、重複除去、評価などのさまざまなタスクのためのデフォルトおよび特定のモデル設定を作成します。例えば、エージェントの生成モデルは創造性と決定論のバランスを取るために0.7の温度で設定されています。
- トークン予算と遅延: 定数
STEP_SLEEP
は1000ミリ秒に設定されており、各ステップ間に1秒の間隔を確保します。
この設定ファイルにより、設定を変更し、システムをさまざまな環境やモデルの動作に適応させることが容易になります。
2.3 server.ts – ウェブサーバAPI
ユーザーがHTTPリクエストを介してDeepResearchと対話できるように、システムにはserver.ts
に単純なExpressベースのサーバが含まれています。このファイルは、クエリの送信とリアルタイムでの進行状況の更新を処理するエンドポイントを設定します。
server.tsの主なポイント:
Express設定:
サーバはExpressとCORSを使用してクロスオリジンリクエストをサポートします。ポート3000(または環境で指定されたポート)で待機します。
クエリエンドポイント(POST /api/v1/query
):
- クライアントは、クエリ、トークン予算、許可される最悪の試行回数を含むJSONペイロードを送信します。
- サーバーは新しいトラッカー(トークン使用とアクション状態用)を作成し、一意の
requestId
を割り当てます。 - リクエストは、
getResponse
を呼び出すことによって非同期に処理されます。 - 処理が完了すると、最終回答が保存され、進行状況が
EventEmitter
を使用して送出されます。
ストリーミングエンドポイント(GET /api/v1/stream/:requestId
):
- このエンドポイントはサーバー送信イベント(SSE)を使用して、クライアントに更新を継続的にプッシュします。
- エージェントがアクション(検索、反映、訪問、回答)を取るたびに進行イベントが送出されます。各イベントには現在のステップ情報、トークン使用、アクションの詳細が含まれます。
- これにより、クライアントはリアルタイムで研究プロセスを監視できます。
タスクの保存と取得:
サーバーはタスクの結果をファイルシステム(tasks
ディレクトリ内)に書き込み、保存された結果を取得するためのエンドポイント(GET /api/v1/task/:requestId
)を提供します。
このウェブサーバーコンポーネントにより、研究エージェントはHTTPを介してアクセス可能となり、インタラクティブな実験と大規模システムへの統合の両方を実現します。
2.4 test-duck.ts – 検索テスト用ユーティリティ
test-duck.ts
ファイルは、Axiosを使用して外部API(この場合、jsonplaceholder.typicode.com
)へのHTTP GETリクエストを送信するスタンドアロンスクリプトです。主な機能はHTTPリクエストが正しく機能することを確認することであり(適切なヘッダーの設定やエラー処理を含む)、システム内で外部リクエストがどのように処理されるかの例としても役立ちます。より複雑な設定では、DuckDuckGoやBraveのような検索APIをクエリする際に似たパターンが使用されます。
2.5 types.ts – 一貫したデータ構造の定義
types.ts
ファイルでは、プロジェクト全体で使用されるすべてのカスタム型が定義されています:
アクションタイプ:
これにはエージェントが実行できるさまざまなアクションが含まれます:
- SearchAction:
searchQuery
文字列を含みます。 - AnswerAction:
answer
文字列とサポートされるreferences
を必要とします。 - ReflectAction:
questionsToAnswer
の配列を含みます。 - VisitAction:
URLTargets
のリストを含みます。
レスポンスタイプ:
ファイルは、検索結果、URLの読み込み、評価、エラー分析などの構造化された応答を定義します。これにより一貫性が維持され、すべてのモジュールが同様にデータを解釈することが保証されます。
スキーマタイプ:
JSONスキーマの定義は、言語モデルによって生成された応答が期待される形式に厳格に従うことを保証します。これは後処理にとって重要です。
トラッカーコンテキスト:
トークンおよびアクショントラッカーのカスタム型も定義されており、会話と研究プロセスの状態を監視するために使用されます。
3. 反復的な深い研究プロセス
全体的なシステムは、ヒューマンリサーチャーの作業方法を模倣する体系的で反復的なプロセスに従っています:
初期化:
プロセスは元の質問から始まり、これは「ギャップ」リスト(埋める必要がある未知が含まれます)に追加されます。
プロンプト生成:
エージェントは、現在の質問、以前のコンテキスト、集めた知識、さらには未成功の試みを使用してプロンプトを構築します。このプロンプトは、その後、生成AIモデルに送信されます。
アクション選択:
モデルの出力に基づいて、エージェントは複数のアクションの1つを選択します:
- 検索: より多くのデータを収集するための新しいクエリを作成します。
- 訪問: 特定のURLから詳細なコンテンツを取得します。
- 反映: 知識のギャップを埋めるための明確化サブ質問を生成します。
- 回答: 情報が明確な場合には最終的な回答を提供します。
コンテキスト更新:
各ステップで内部トラッカー(トークン使用量とアクション状態)が更新され、現在の状態がファイルにアーカイブされます。これにより透明性が保証され、デバッグや後のレビューが可能になります。
評価とループ:
回答が提案されると、評価ステップでそれが明確かどうかを確認します。そうでない場合、システムは失敗した試みの詳細を保存し、戦略を調整します。このサイクルは、満足のいく回答が見つかるまで、またはトークン予算が尽きるまで続きます。
ビーストモード:
通常の手順が制約の中で明確な回答を生み出すことに失敗した場合、システムは「ビーストモード」に入ります。このモードでは、生成AIは蓄積されたコンテキストに基づいて回答を生成するよう強いられます—それが教育的な推測を行うことを意味してもです。
4. リアルタイムの進行状況とフィードバック
DeepResearchシステムの重要な特徴は、リアルタイムのフィードバックメカニズムです。ウェブサーバーのストリーミングエンドポイントを通じて:
- クライアントは、どのアクションが取られたか、トークン使用統計、アクショントラッカーの現在の状態を詳細に示す進行状況の更新を受け取ります。
- 進行イベント(READMEの例で示したように)には、エージェントの思考プロセスや現在のステップ、トークンの内訳などの詳細が含まれます。
- このライブフィードバックループは、デバッグ、リソース使用の監視、およびシステムの推論の理解において非常に有価です。
たとえば、進行イベントは次のように見えるかもしれません:
data: {
"type": "progress",
"trackers": {
"tokenUsage": 74950,
"tokenBreakdown": {
"agent": 64631,
"read": 10319
},
"actionState": {
"action": "search",
"thoughts": "テキストはJina AIのいくつかの投資家を言及していますが、所有権の割合は指定していません。直接の検索が必要です。",
"URLTargets": [],
"answer": "",
"questionsToAnswer": [],
"references": [],
"searchQuery": "Jina AIの投資家の所有権割合"
},
"step": 7,
"badAttempts": 0,
"gaps": []
}
}
この詳細な進行報告により、開発者はエージェントの推論が時間とともにどのように進化するかを把握し、成功と改善を要する領域の両方について洞察を得ることができます。
5. DeepResearchの拡張とカスタマイズ
このプロジェクトのオープンソースの性質により、ニーズに合わせてシステムを適応させることができます。DeepResearchを拡張するためのいくつかのアイデアを以下に示します:
カスタム検索プロバイダー:
追加の検索プロバイダーを統合したり、ドメイン固有の検索に向けてクエリの書き換えプロセスをカスタマイズしたりすることができます。
強化された読み取りモジュール:
より詳細なテキスト処理が必要な場合、代替のNLPモデルを統合したり、新しいコンテンツタイプに対応するためにJina Readerコンポーネントを調整したりできます。
評価の改善:
現在、評価モジュールは回答が明確であるかどうかをチェックしています。感情分析やファクトチェックアルゴリズムなど、より微妙なメトリックを取り入れるようにこの評価を拡張できます。
ユーザーインターフェース:
現在のシステムはコマンドラインインターフェースとストリーミングイベント用の簡単なウェブサーバを使用していますが、インタラクティブな研究セッションのための本格的なウェブおよびモバイルインターフェースを構築することができます。
スケーラビリティの向上:
現在の実装はシングルノードサービスとして実行されています。生産用途向けには、アプリケーションをコンテナ化し、Kubernetesまたは他のオーケストレーションプラットフォームを使用して高トラフィックと分散処理を処理する展開を検討してください。
6. セキュリティ、パフォーマンス、およびベストプラクティス
DeepResearchのようなAI駆動システムを展開する際には、いくつかの追加的な考慮事項があります:
APIキー管理:
Gemini、Jina、Brave用のAPIキーは安全に保管し、ソースコードにハードコーディングしないでください。環境変数や安全な保管所が推奨されます。
レート制限:
組み込まれたsleep
関数は、連続するリクエストを遅延させることによりレート制限を回避するのに役立ちます。ただし、サーバーまたはAPIゲートウェイレベルで追加のレート制限メカニズムを実装することを検討してください。
データバリデーション:
入力クエリと応答を厳密にバリデーションします。エージェントによって定義されたJSONスキーマが役に立ちますが、不正な入力を防ぐために、受信したHTTPリクエストもバリデーションするべきです。
エラー処理:
頑健なエラー処理(サーバーコードやtest-duck.tsに見られるように)は重要です。これにより、予期しないAPIの失敗や不正な応答でシステムがクラッシュすることを防ぎます。
リソースモニタリング:
トークン使用量を追跡することは重要です。TokenTrackerとActionTrackerクラスはリソース消費に関する洞察を提供します。これらのメトリクスを監視することで、システムのパフォーマンスを微調整し、過度な使用を避けることができます。
7. 結論
Jina AIによるDeepResearchプロジェクトは、複雑で反復的な研究プロセスがオープンソースツールを使用して構築できる方法を示しています。検索エンジン、生成AIモデル、およびインテリジェントな推論ループを統合することで、システムは確信が持てるまで、またはリソース制限が達成されるまで回答を継続的に洗練します。
この記事では、オープンソースアプローチを使用してOpenAI Deep Researchを再現する方法を探りました:
- インストール手順と環境設定について話しました。
- 反復エージェントロジックの
agent.ts
から設定、ウェブサーバAPI、型定義まで、コアモジュールを分解しました。 - 推論プロセスの透明性を提供しながら、進行状況の更新をストリーミングするリアルタイムフィードバックメカニズムを検討しました。
- 最後に、潜在的な拡張、カスタマイズオプション、およびそのようなシステムを展開するためのベストプラクティスを見ました。
これらの高度な研究技術をオープンソースとして提供することにより、DeepResearchのようなプロジェクトは最先端のAI方法論に対するアクセスを民主化します。あなたが研究者であれ、開発者であれ、ワークフローに深い研究能力を統合しようとする企業であれ、このプロジェクトはあなた自身のソリューションを構築するためのインスピレーションと実用的な基盤の両方を提供します。
検索、読み取り、反映、回答を連続的にループ処理する反復設計は、曖昧または複雑なクエリが多層的な精査を経て処理されることを保証します。トークン使用を追跡し、ライブフィードバックを提供する詳細なアーキテクチャを備えているため、各回答の背後にある推論プロセスに深い洞察を得ることができます。
実験に意欲的であれば、リポジトリをクローンし、記載されたとおりに環境を設定し、単純な算数から多面的な研究問題に至るまでのクエリを実行してみてください。少しカスタマイズすれば、システムを新しいドメインに合わせることができ、その推論能力を強化することさえできます。このようなオープンソースプロジェクトは、AI研究におけるコミュニティ主導の革新の道を開きます。
この詳細な分解と分析に従うことで、完全にオープンソースの方法でOpenAIのDeep Researchの背後にあるアイデアを再現し、拡張することができます。既存のコードベースを構築したり、同様の方法論をプロジェクトに統合しようとするかどうかにかかわらず、ロードマップは明確です:反復し、洗練し、自動研究の限界を押し広げましょう。