「Open Deep Research」技術解説

「Open Deep Research」技術解説

こんにちは!「Deep Research」界隈、にわかに盛り上がりを見せておりますね。

今日は昨日(2025/2/5)発表された、 Open Deep Researchについて、そのアーキテクチャや実装について解説したします!


1. はじめに

OpenAIが開発した「GPT Deep Research」が世間をにぎわせていますが、「●● Deep Research」は既出のものをふくめこれから各社がしのぎを削っていくのではないでしょうか。

「Open Deep Research」はHuggingFace 社が開発したオープンソースツールで、その名の通り従来人間がデスクトップで行っていた Web 情報調査の作業を自動化するツールです。

今日は、本ツールの設計思想、 Deep Research ってどうやってるの? っていうところをディープに解説してみたいとおもいます。

あくまでも仕組みの説明にフォーカスしており、使い方説明ではないのでご了承くだすぁい。

1.1. はじめに

近年、情報技術の進歩により扱える情報量は飛躍的に増加しております。デスクトップで Web 情報調査を行う際、膨大な情報の中から有用な知見を抽出し、わかりやすく整理する作業が非常に重要となってますね。

当然ながら、AIをつかってそういった作業を自動化できたら嬉しいわけで、Perplexity ,Gemini Deep Researh、GPT Deep Researchなど、商用サービスが次々と投入されております。
例にもれずOpen Deep Research システム(以後、OpenDeepResearch )は、最新の大規模言語モデル(LLM)を活用して、人間が手作業で行っていたリサーチのプロセスを自動化するために開発されました。オープンソースとして。

1.2. システムの背景と意義

当社もコンサルティング事業を提供していますが、コンサルティングといえば調査がまず基本。その一番最初は「デスクトップリサーチ」「デスクトップサーチ」といったWebを中心とした情報検索で集めた情報をまとめるというところからはじまります。

しかしながら、情報の急増に伴い、従来アナリストや担当者が複数の情報源からデータを収集し、手作業で整理していた作業は大変な負担となっておりました。


OpenDeepResearch は、LLM の高度な理解能力を活用して複数のソースから得た情報を自動で統合し、迅速かつ正確な情報整理を実現することを目指してるようです

1.3. システムアーキテクチャの概要と設計思想

OpenDeepResearch は、人間が実際に行うデスクトップリサーチのプロセス(情報収集、比較、分析、統合)を模倣するとともに、その作業を自動化・拡張するために設計されています。

私たちエンジニアは「じゃああそれ、ITやAIつかってどうやってやるの?」にとても興味がありますよね。

各工程を担う専門コンポーネントが分担し、マルチエージェントアーキテクチャにより連携することで、全体の効率化と柔軟性を実現しています。

1.4 アーキテクチャ

以下にアーキテクチャを示します。本ブログでは、主要コンポーネントのコードについて主に説明していきますが、ここで、最初に全体像をみておきましょう。

主要コンポーネント(と相互作用)

graph TB subgraph Core System RunPy[run.py - System Entry Point] Manager[Manager Agent] Reform[Reformulator] end subgraph Search & Analysis Browser[SimpleTextBrowser] TextInspector[TextInspectorTool] VisualQA[VisualQATool] Search[SearchInformationTool] Archive[ArchiveSearchTool] end subgraph Document Processing MDConv[MarkdownConverter] YouTubeConv[YouTubeConverter] PDFConv[PdfConverter] DocxConv[DocxConverter] XlsxConv[XlsxConverter] end subgraph File & Cache Management FileProc[File Processor] Cache[Cache System] end RunPy --> Manager Manager --> Reform Manager --> Browser Manager --> TextInspector Manager --> VisualQA Browser --> Search Browser --> Archive TextInspector --> MDConv MDConv --> YouTubeConv MDConv --> PDFConv MDConv --> DocxConv MDConv --> XlsxConv Search --> FileProc Archive --> FileProc FileProc --> Cache subgraph External Services SerpAPI[SerpAPI] WaybackMachine[Wayback Machine] IDEFICS[IDEFICS-2 Model] end Search --> SerpAPI Archive --> WaybackMachine VisualQA --> IDEFICS

Core System

  • Manager Agent: システム全体を統括する中心的なエージェントで、他のすべてのコンポーネントを制御します
  • Reformulator: 検索結果や分析結果を最終的な回答形式に再構成する役割を担います
  • run.py: システムのエントリーポイントとして、初期設定や実行フローを管理します

Search & Analysis

  • SimpleTextBrowser: Webページのクローリングと解析を行うコンポーネント
  • TextInspectorTool: テキストデータの詳細な分析と情報抽出を行うツール
  • VisualQATool: 画像理解と視覚的な質問応答を担当
  • SearchInformationTool: Web検索を実行し結果を整理
  • ArchiveSearchTool: 過去のWebページのアーカイブを検索・分析

Document Processing

  • MarkdownConverter: 様々な形式のドキュメントを統一フォーマットに変換
  • YouTubeConverter: YouTube動画からの情報抽出とトランスクリプト処理
  • PdfConverter: PDFファイルのテキスト抽出と構造解析
  • DocxConverter: Word文書の処理と解析
  • XlsxConverter: Excel形式のデータ処理

File & Cache Management

  • File Processor: ファイルの読み書きと形式変換を管理
  • Cache System: 検索結果やファイル処理結果のキャッシュを管理し、パフォーマンスを最適化

External Services

  • SerpAPI: Google検索結果を取得するための外部API
  • Wayback Machine: 過去のWebページを取得するためのInternet ArchiveのAPI
  • IDEFICS-2 Model: 画像理解と生成を行うAIモデル

これらのコンポーネントが相互に連携することで、複雑な情報検索と分析タスクを実現しています。特にManager Agentを中心としたコアシステムが、各コンポーネント間の調整と統合を行い、効率的な情報処理を可能にしています。

Open Deep Researchのアーキテクチャを構成する5つの主要コンポーネント(Core System、Search & Analysis、Document Processing、File & Cache Management、External Services)について、本記事では以下の順で説明していきます。

まず、Search & Analysisの中核であるTextInspectorToolから始め、続いて画像理解コンポーネント、Web探索システムの実装を解説します。

その後、マルチエージェントシステムの詳細に移り、最後にシステムの評価と応用例について説明します。

それでは、TextInspectorToolの実装詳細から見ていきましょう♪


2. 各コンポーネントの実装詳細

本セクションでは、OpenDeepResearch を構成する各主要コンポーネントの実装内容について、具体的なコード例などを交えて解説いたします。

しっかり理解するためには、まず githubからプロジェクトをクローンしましょう。

ライセンスは Apache 2.0 で、リポジトリは以下からクローンできます

git clone https://github.com/huggingface/smolagents

Open Deep Research 自体は以下にあります
https://github.com/huggingface/smolagents/tree/main/examples/open_deep_research

2.1. TextInspectorTool の実装詳細

さて、コードを取得できたところで、最初にTextInspectorToolをみていきましょう。

テキスト処理の中核を担うのが TextInspectorTool です。

このツールは、MarkdownConverter を利用して各種形式のドキュメントを統一フォーマットに変換し、文書の構造や文脈を踏まえて情報抽出や質問に基づいた回答生成を行います。また、エラーハンドリングも充実しており、画像ファイルの場合は適切な代替ツールの利用を促しております。

class TextInspectorTool(Tool):
    def __init__(self, model: Model, text_limit: int):
        super().__init__()
        self.model = model
        self.text_limit = text_limit
        self.md_converter = MarkdownConverter()

    def forward(self, file_path, question: Optional[str] = None) -> str:
        result = self.md_converter.convert(file_path)

        if file_path[-4:] in [".png", ".jpg"]:
            raise Exception("Cannot use inspect_file_as_text tool with images: use visualizer instead!")

        if not question:
            return result.text_content

        messages = [
            {
                "role": MessageRole.SYSTEM,
                "content": [
                    {
                        "type": "text",
                        "text": "You will have to write a short caption for this file, then answer this question:" + question,
                    }
                ],
            },
            {
                "role": MessageRole.USER,
                "content": [
                    {
                        "type": "text",
                        "text": "Here is the complete file:\n### " + str(result.title) + "\n\n" + result.text_content[: self.text_limit],
                    }
                ],
            }
        ]

2.2. 画像理解コンポーネントの技術詳細

VisualQATool は、最新の視覚言語モデル IDEFICS-2 を利用し、画像と質問テキストを統合的に処理するコンポーネントです。
画像を base64 エンコードしてテキストプロンプトと組み合わせることで、画像から情報を抽出できる仕組みとなっております。また、大きな画像サイズに対しては自動リサイズ処理も実装されています。これ24時間で作ったんでしょうかね。

def process_images_and_text(image_path, query, client):
    messages = [
        {
            "role": "user",
            "content": [
                {"type": "image"},
                {"type": "text", "text": query},
            ],
        },
    ]

    prompt_with_template = idefics_processor.apply_chat_template(
        messages, 
        add_generation_prompt=True
    )

    image_string = encode_local_image(image_path)
    prompt_with_images = prompt_with_template.replace(
        "<image>", 
        "![]({}) "
    ).format(image_string)

    payload = {
        "inputs": prompt_with_images,
        "parameters": {
            "return_full_text": False,
            "max_new_tokens": 200,
        },
    }

    return json.loads(client.post(json=payload).decode())[0]

2.3. ウェブ探索と情報収集の実装

SimpleTextBrowser は、従来のウェブクローラーを進化させ、ページ履歴や視点(viewport)の管理を通じて、人間のブラウジング行動を模倣する機能を有してます。

class SimpleTextBrowser:
    def __init__(self, start_page: Optional[str] = None,
                 viewport_size: Optional[int] = 1024 * 8,
                 downloads_folder: Optional[Union[str, None]] = None,
                 serpapi_key: Optional[Union[str, None]] = None):
        self.start_page = start_page if start_page else "about:blank"
        self.viewport_size = viewport_size
        self.downloads_folder = downloads_folder
        self.history = []
        self.page_title = None
        self.viewport_current_page = 0
        self.viewport_pages = []
        self.serpapi_key = serpapi_key
        self._mdconvert = MarkdownConverter()

2.4. マルチエージェントシステムの実装詳細

OpenDeepResearch では、各コンポーネントがそれぞれの役割を担いながら連携するマルチエージェントアーキテクチャが採用されております。
たとえば、情報検索用エージェントとマネージャーエージェントが協力して作業を進める仕組みが実装されています。

def create_agent_hierarchy(model: Model):
    text_limit = 100000
    ti_tool = TextInspectorTool(model, text_limit)
    browser = SimpleTextBrowser(**BROWSER_CONFIG)

    WEB_TOOLS = [
        SearchInformationTool(browser),
        VisitTool(browser),
        PageUpTool(browser),
        PageDownTool(browser),
        FinderTool(browser),
        FindNextTool(browser),
        ArchiveSearchTool(browser),
        TextInspectorTool(model, text_limit),
    ]

    text_webbrowser_agent = ToolCallingAgent(
        model=model,
        tools=WEB_TOOLS,
        max_steps=20,
        verbosity_level=2,
        planning_interval=4,
        name="search_agent",
        description="""A team member that will search the internet to answer your question.
        Ask him for all your questions that require browsing the web."""
    )

    manager_agent = CodeAgent(
        model=model,
        tools=[visualizer, ti_tool],
        max_steps=12,
        verbosity_level=2,
        managed_agents=[text_webbrowser_agent]
    )

情報検索用エージェント(text_webbrowser_agent)とマネージャーエージェント(manager_agent)は、階層的な構造で連携して動作しています。具体的には、manager_agentがCodeAgentとして全体を統括し、その管理下にtext_webbrowser_agentを配置する形で構成されています。

この構成において、text_webbrowser_agentはWeb検索やページ閲覧、ナビゲーションといったWeb関連のタスクを専門的に担当します。このエージェントはSearchInformationTool、VisitTool、PageUpToolなどのWeb関連ツールを使用して、Google検索の実行やWebページの閲覧、ページ内検索、アーカイブページの検索などの機能を提供してるようですね

一方で、manager_agentは全体のタスク管理と調整を担当し、コード実行機能を持ちながら、visualizerとTextInspectorToolを直接使用することができます。また、必要に応じてtext_webbrowser_agentにタスクを委任する権限も持っています。

def answer_single_question(example, model_id, answers_file, visual_inspection_tool):
    model = LiteLLMModel(
        model_id,
        custom_role_conversions=custom_role_conversions,
        max_completion_tokens=8192,
        reasoning_effort="high",
    )
    
    agent = create_agent_hierarchy(model)
    
    augmented_question = """You have one question to answer...""" + example["question"]
    
    # エージェントの実行
    final_result = agent.run(augmented_question)
    
    # 結果の整形
    agent_memory = agent.write_memory_to_messages(summary_mode=True)
    final_result = prepare_response(augmented_question, agent_memory, reformulation_model=model)

実際の連携の流れとしては、まず質問を受け取ったmanager_agentが実行を開始し、Web検索が必要な場合にtext_webbrowser_agentにタスクを委任します。text_webbrowser_agentは検索を実行し、その結果をmanager_agentに返却します。その後、manager_agentがこれらの情報を統合して最終的な回答を生成します。

このプロセスで重要なのは、質問が入力されると、最初にmanager_agentがそれを受け取り、必要に応じてtext_webbrowser_agentに作業を委任するという流れです。実行結果は agent_memory に保存され、最終的に prepare_response で整形されます。

エージェント間のコミュニケーションは、ToolCallingAgentの機能を通じて行われます。

text_webbrowser_agentは以下のようなWebツールを使用できます

class SearchInformationTool(Tool):
    name = "web_search"
    description = "Perform a web search query (think a google search) and returns the search results."
    
    def forward(self, query: str, filter_year: Optional[int] = None) -> str:
        self.browser.visit_page(f"google: {query}", filter_year=filter_year)
        header, content = self.browser._state()
        return header.strip() + "\n=======================\n" + content

このような各ツールを使って、text_webbrowser_agentは必要な情報を収集し、その結果をmanager_agentに返します。manager_agentはこれらの情報を統合して最終的な回答を生成します。

このアーキテクチャにより、Web検索や情報収集といった特定のタスクを情報検索エージェントに委任しながら、全体の制御と最終的な意思決定をmanager_agentが行うという効率的な分業が実現されています。各エージェントはそれぞれの専門分野に特化したツールセットを持ち、それらを適切に組み合わせることで複雑なタスクに対応することができているようです

2.5. 実行システムの実装

システム全体の動作は run.py モジュールで一元管理されておりタスクの割り当てから結果の保存までが自動化されてます。まぁ、これはメインクラスですね。いわゆる。

def main():
    args = parse_args()
    print(f"Starting run with arguments: {args}")

    answers_file = f"output/{SET}/{args.run_name}.jsonl"
    tasks_to_run = get_examples_to_answer(answers_file, eval_ds)

    with ThreadPoolExecutor(max_workers=args.concurrency) as exe:
        futures = [
            exe.submit(answer_single_question, example, args.model_id, answers_file, visualizer)
            for example in tasks_to_run
        ]
        for f in tqdm(as_completed(futures), total=len(tasks_to_run)):
            f.result()

3. システムの最適化と運用管理

本セクションでは、システムの動作の安定化、最適化、及び運用管理に関する工夫についてご説明いたします

3.1. パフォーマンス最適化とシステムチューニング

OpenDeepResearch システムは、各種ドキュメントの処理パイプラインを最適化することで、スムーズな動作を実現しているようです


たとえば、MarkdownConverter クラスでは各種フォーマットの変換を効率的に行う仕組みが実装されております。

class MarkdownConverter:
    def __init__(self, requests_session: Optional[requests.Session] = None, 
                 mlm_client: Optional[Any] = None,
                 mlm_model: Optional[Any] = None):
        self._requests_session = requests_session or requests.Session()
        self._mlm_client = mlm_client
        self._mlm_model = mlm_model
        self._page_converters = []
        
        self.register_page_converter(PlainTextConverter())
        self.register_page_converter(HtmlConverter())
        self.register_page_converter(WikipediaConverter())
        self.register_page_converter(YouTubeConverter())
        self.register_page_converter(DocxConverter())
        self.register_page_converter(XlsxConverter())

3.2. エラー処理とロバスト性の確保

実運用において、想定外のエラーが発生しがちなものですが、エラー発生時にも処理を中断せず、可能な限り情報を保存してシステムの回復を図る仕組みが実装されていますね。
たとえば、以下は、エラー処理の一例です。

def answer_single_question(example, model_id, answers_file, visual_inspection_tool):
    try:
        model = LiteLLMModel(
            model_id,
            custom_role_conversions=custom_role_conversions,
            max_completion_tokens=8192,
            reasoning_effort="high",
        )
        
        agent = create_agent_hierarchy(model)
        final_result = agent.run(augmented_question)
        
    except Exception as e:
        print(f"Error processing question: {e}")
        output = None
        intermediate_steps = []
        raise_exception = True
        
    finally:
        append_answer({
            "agent_name": model.model_id,
            "question": example["question"],
            "prediction": output,
            "error": str(e) if raise_exception else None,
        }, answers_file)

3.3. 並行処理とスケーラビリティ

大量のタスクを効率的に処理するため、並行処理やタスク管理の工夫がなされているようです。
既に処理済みのタスクを適切に管理することで、再起動時などにもスムーズな運用が可能になります。

def get_tasks_to_run(data, total: int, base_filename: Path, tasks_ids: list[int]):
    f = base_filename.parent / f"{base_filename.stem}_answers.jsonl"
    done = set()
    if f.exists():
        with open(f, encoding="utf-8") as fh:
            done = {json.loads(line)["task_id"] for line in fh if line.strip()}

    tasks = []
    for i in range(total):
        task_id = int(data[i]["task_id"])
        if task_id not in done:
            if tasks_ids is not None and task_id in tasks_ids:
                tasks.append(data[i])

4. 高度な情報統合と処理機能

本セクションでは、複数の情報源からのデータ統合や、検索・解析のための高度な処理機能についてご説明いたします。

4.1. 高度な情報統合機能

システムは、複数の情報源から得たデータを統合し、エージェント間の対話内容をもとに最終的なレスポンスを生成する仕組みを有しています。

def prepare_response(original_task: str, inner_messages, reformulation_model: Model):
    messages = [
        {
            "role": MessageRole.SYSTEM,
            "content": [
                {
                    "type": "text",
                    "text": f"Earlier you were asked: {original_task}\nYour team then worked diligently to address that request. Read below a transcript of that conversation."
                }
            ]
        }
    ]
    
    try:
        for message in inner_messages:
            if not message.get("content"):
                continue
            message = copy.deepcopy(message)
            message["role"] = MessageRole.USER
            messages.append(message)
    except Exception:
        messages += [{"role": MessageRole.ASSISTANT, "content": str(inner_messages)}]

ここは結構おもしろくて、

この prepare_response 関数は、エージェント間のやり取りを整理し、最終的な回答を生成するための準備を行っています。具体的な処理をみていきましょう。

  1. まず、新しい会話コンテキストを作成
messages = [
    {
        "role": MessageRole.SYSTEM,
        "content": [
            {
                "type": "text",
                "text": f"Earlier you were asked: {original_task}\nYour team then worked diligently to address that request. Read below a transcript of that conversation."
            }
        ]
    }
]

このシステムメッセージは、元の質問(original_task)を示し、これから続くやり取りがその質問に対する作業の記録であることを説明してますね

2.次に、エージェント間のやり取り(inner_messages)を処理しています

try:
    for message in inner_messages:
        if not message.get("content"):
            continue
        message = copy.deepcopy(message)
        message["role"] = MessageRole.USER
        messages.append(message)

ここでは以下のような処理を行っています

  • inner_messages の各メッセージをループで処理
  • 内容が空のメッセージはスキップ
  • メッセージの深いコピーを作成して元のメッセージを保護
  • すべてのメッセージの役割を USER に統一
  • 処理したメッセージを新しい会話コンテキストに追加

3.エラー処理:

except Exception:
    messages += [{"role": MessageRole.ASSISTANT, "content": str(inner_messages)}]

メッセージの処理中にエラーが発生した場合、inner_messages全体を文字列として1つのメッセージにまとめてます

つまりこの関数の主な目的は、複数のエージェントによる複雑なやり取りを、整理された形式の会話履歴に変換することです。これにより、モデルが最終的な回答を生成する際に、すべての関連情報を適切なコンテキストで理解できるようになります。

また、すべてのメッセージをUserロールに変換することで、モデルが各メッセージを新しい入力として扱い、それらを総合的に分析できるようになります。これは、複数のエージェントによる異なる視点や情報を統合して、最終的な回答を導き出す役割ですね

4.2. リアルタイムファイル処理システム

さまざまな形式のファイルをリアルタイムに処理することで、Web 上の情報調査をより豊かにする機能が実装されています。ファイル拡張子に応じた適切な処理方法が自動的に選択されます。

def get_single_file_description(file_path: str, question: str, visual_inspection_tool, document_inspection_tool):
    file_extension = file_path.split(".")[-1]
    if file_extension in ["png", "jpg", "jpeg"]:
        file_description = f" - Attached image: {file_path}"
        file_description += f"\n     -> Image description: {get_image_description(file_path, question, visual_inspection_tool)}"
        return file_description
    elif file_extension in ["pdf", "xls", "xlsx", "docx", "doc", "xml"]:
        file_description = f" - Attached document: {file_path}"
        image_path = file_path.split(".")[0] + ".png"
        if os.path.exists(image_path):
            description = get_image_description(image_path, question, visual_inspection_tool)
        else:
            description = get_document_description(file_path, question, document_inspection_tool)
        file_description += f"\n     -> File description: {description}"
        return file_description

class PdfConverter(DocumentConverter):
    def convert(self, local_path, **kwargs) -> Union[None, DocumentConverterResult]:
        extension = kwargs.get("file_extension", "")
        if extension.lower() != ".pdf":
            return None

        text_content = pdfminer.high_level.extract_text(local_path)
        
        layout = extract_layout(local_path)
        sections = analyze_document_structure(layout)
        
        metadata = extract_pdf_metadata(local_path)
        
        return DocumentConverterResult(
            title=metadata.get('Title'),
            text_content=format_content(text_content, sections)
        )

4.3. クエリ最適化エンジン

ユーザーの問いを効果的な検索クエリに変換し、情報収集の精度を向上させるためのクエリ最適化エンジンが実装されております。

class ArchiveSearchTool(Tool):
    def forward(self, url, date) -> str:
        no_timestamp_url = f"https://archive.org/wayback/available?url={url}"
        archive_url = no_timestamp_url + f"&timestamp={date}"
        response = requests.get(archive_url).json()
        response_notimestamp = requests.get(no_timestamp_url).json()
        
        if "archived_snapshots" in response and "closest" in response["archived_snapshots"]:
            closest = response["archived_snapshots"]["closest"]
        elif "archived_snapshots" in response_notimestamp and "closest" in response_notimestamp["archived_snapshots"]:
            closest = response_notimestamp["archived_snapshots"]["closest"]
        else:
            raise Exception(f"URL {url} was not archived")
            
        target_url = closest["url"]
        header, content = self.browser._state()
        
        return f"Web archive for url {url}, snapshot taken at date {closest['timestamp'][:8]}\n=======================\n{content}"

ArchiveSearchToolは、Wayback Machine(Internet Archive)を使用して、特定のWebページの過去のバージョンを検索し閲覧するためのツールです。

この処理では、まずWayback MachineのAPIに対して2種類のリクエストを行います。1つ目は指定された日付のスナップショットを探すリクエスト、2つ目は日付指定なしで最も近いスナップショットを探すリクエストです。これにより、指定された日付のアーカイブが存在しない場合でも、利用可能な最も近い時期のスナップショットを取得することができます。

APIからの応答を確認し、アーカイブされたスナップショットが存在する場合、そのURLを取得します。まず指定日付での検索結果を確認し、それが存在しない場合は日付指定なしでの検索結果を使用します。どちらの方法でもアーカイブが見つからない場合は、そのURLはアーカイブされていないというエラーを発生させます。

アクセス可能なアーカイブが見つかった場合、そのページの内容を取得し、URLと撮影された日付(タイムスタンプの最初の8文字を使用して日付のみを表示)とともに返却します。これにより、ユーザーは特定のWebページが過去のある時点でどのような内容だったのかを確認することができます。主にウェブページの歴史的な変遷を追跡したり、現在は存在しないページの内容を参照したりする際に役立ちます。

4.4. 高度なコンテキスト管理

マルチターンの対話においては過去のやり取りを踏まえた新たな情報統合が重要です。この仕組みは、対話履歴を適切に管理し、最適なレスポンスを生成するために活用されております。

まず元の質問(original_task)とエージェントの作業履歴(inner_messages)を受け取り、それらを適切な形式の会話履歴に再構成します。最初に、システムメッセージを作成して元の質問の文脈を設定し、これから続く会話がその質問に対する取り組みの記録であることを明示します。

続いて、エージェントの作業履歴を処理します。各メッセージをループで処理し、内容が空でないメッセージを抽出します。このとき、元のメッセージを変更しないよう深いコピーを作成し、全てのメッセージの役割をユーザーロールに統一します。これは、モデルが各メッセージを新しい情報として扱い、より客観的に分析できるようにするためです。

この処理の目的は、複数のエージェントによる複雑な作業プロセスを、モデルが理解しやすい形式に整理することです。

ひもといてみれば、基本的には user とAI の対話に落とし込んでやるというわけです。

これにより、モデルは全ての関連情報を適切なコンテキストで理解し、より正確で包括的な最終回答を生成することができます。また、まぁ、これはLLMの制約といえば制約ですが、各エージェントからのすべてのメッセージはユーザーロールとして扱います。これにより、モデルが特定のエージェントの視点に偏ることなく、当然どのエージェントからの情報だからどうこうなどというえこひいきは発生しないので、結果的に客観的な立場から情報を評価することができます

def prepare_response(original_task: str, inner_messages, reformulation_model: Model) -> str:
    messages = [
        {
            "role": MessageRole.SYSTEM,
            "content": [
                {
                    "type": "text",
                    "text": f"Earlier you were asked: {original_task}\nYour team then worked diligently to address that request. Read below a transcript of that conversation."
                }
            ]
        }
    ]

    try:
        for message in inner_messages:
            if not message.get("content"):
                continue
            message = copy.deepcopy(message)
            message["role"] = MessageRole.USER
            messages.append(message)
    except Exception:
        messages += [{"role": MessageRole.ASSISTANT, "content": str(inner_messages)}]


5. 応用例と高度なデータ処理

本セクションでは、OpenDeepResearch システムの応用例や、高度なデータ処理機能についてご紹介いたします。

5.1. 情報調査支援システムとしての活用

さて、おさらいもかねて、活用法について考えてみましょう。

OpenDeepResearch は、従来のデスクトップでの Web 情報調査を自動化するツールとして、幅広い応用が可能です。
例えば、特定のトピックに関する情報を複数の情報源から収集・整理し、全体像を把握するために活用できます。

5.2. マルチモーダルデータの統合処理

Open Deep Researchでは、たんにWebのテキストを検索するだけでなく、テキスト、画像、音声など異なる形式のデータを統合して処理することで、より豊かな洞察が得られるようになってます。
各種データから特徴量を抽出し、統合後に最終的なインサイトを生成します。

def integrate_multimodal_data(text_data, image_data, audio_data):
    text_features = self.text_processor.extract_features(text_data)
    visual_features = self.visual_processor.process_images(image_data)
    audio_features = self.audio_processor.analyze_audio(audio_data)
    
    integrated_features = self.feature_integrator.combine_features(
        text_features,
        visual_features,
        audio_features
    )
    
    return self.analyzer.generate_insights(integrated_features)

integrate_multimodal_data 関数は、異なる種類のデータ(テキスト、画像、音声)を統合して包括的な分析を行うための重要な機能を提供しています。

この関数の主な目的は、複数のモダリティ(データの種類)から得られる情報を意味のある形で組み合わせ、特徴分析することです。具体的には、まずテキストデータから言語的特徴を、画像からは視覚的特徴を、音声からは音響的特徴をそれぞれ専用のプロセッサーで抽出します。これらの異なる種類の特徴は、それぞれのモダリティに特化した方法で処理されます。

次に、feature_integratorを使用してこれらの異なる特徴を統合します。この統合プロセスは単純な結合以上の意味を持ち、各モダリティ間の相互関係や補完関係を考慮に入れた処理を行います。例えば、テキストで述べられている内容と画像が示す視覚情報の整合性を確認したり、音声のトーンとテキストの感情的な内容を関連付けたりすることが可能になります。

最後に、統合された特徴をアナライザーに渡して最終的な洞察を生成します。この段階では、複数のモダリティからの情報を総合的に解釈し、より深い理解や新しい発見を導き出すことができます。

このようなマルチモーダル統合アプローチは、例えば感情分析、コンテンツ理解、異常検知などの分野で特に重要です。単一のデータタイプだけでは見落とされがちな微妙なニュアンスや文脈の理解を可能にし、より正確で信頼性の高い分析結果を提供することができます。当社の開発しているAIヒューマン、デジタルヒューマンでもこのモダリティ統合技術をがんばっているところです。

5.3. 時系列データの分析と追跡

対象トピックの変化を時系列で追跡することで、過去から現在までの動向を把握することが可能です。特に重要な変化点を抽出する仕組みが実装されています。

class TemporalAnalyzer:
    def analyze_temporal_changes(self, topic: str, start_date: str, end_date: str):
        timeline = []
        current_date = start_date
        
        while current_date <= end_date:
            snapshot = self.archive_tool.get_snapshot(topic, current_date)
            
            changes = self.change_detector.analyze(
                previous_snapshot=timeline[-1] if timeline else None,
                current_snapshot=snapshot
            )
            
            if changes.is_significant():
                timeline.append({
                    'date': current_date,
                    'snapshot': snapshot,
                    'changes': changes.get_description()
                })
            
            current_date = self.increment_date(current_date)
        
        return self.summarize_timeline(timeline)

ここで、まず指定された開始日から終了日までの期間について、順次分析を行います。各時点において、archive_toolを使用してそのトピックに関するスナップショット(その時点での状態)を取得します。これは例えばウェブページの過去のバージョンや、データベースの特定時点でのレコードなどが該当します。

取得したスナップショットは、change_detectorを使用して前回のスナップショットと比較されます。この比較では、2つの時点間での変化を検出し、その重要性を評価します。例えば、テキストの大幅な書き換え、重要な情報の追加や削除、数値データの顕著な変動などが検出対象となります。

重要な変化が検出された場合(changes.is_significant()がTrueを返す場合)、その変化の情報がタイムラインに記録されます。各記録には日付、その時点でのスナップショット、そして検出された変化の説明が含まれます。これにより、トピックの進化や発展の重要なポイントを時系列で追跡することが可能になります。

最後に、summarize_timelineメソッドを使用してタイムライン全体を要約し、トピックの時間的な変遷の全体像を把握しやすい形で提示します。この手法は、例えばニュース記事の発展、企業の製品開発の歴史、政策の変更過程など、時間とともに変化する情報の分析に特に有用です。また、大量の時系列データから重要な変化点のみを抽出することで、効率的な情報把握を可能にしています。

5.4. 知識グラフの構築と活用

収集した情報同士の関連性を視覚的に把握するため、知識グラフとして構造化する機能が実装されています。
エンティティ間の関係をグラフ上に表現することで、新たな洞察を得ることが可能となります。

class KnowledgeGraphBuilder:
    def build_knowledge_graph(self, research_data):
        graph = nx.DiGraph()
        
        for item in research_data:
            entities = self.entity_extractor.extract(item.content)
            relationships = self.relationship_analyzer.analyze(entities)
            
            for entity in entities:
                graph.add_node(entity.id, 
                             type=entity.type,
                             properties=entity.properties)
            
            for rel in relationships:
                graph.add_edge(rel.source, 
                             rel.target,
                             type=rel.type,
                             properties=rel.properties)
        
        return self.optimize_graph_structure(graph)

5.5. 高度な情報検証システム

収集した情報の信頼性を多角的に評価するため、複数の検証手法を組み合わせたシステムが実装されています。
情報源の評価やクロスリファレンスチェックを通じ、正確性の高い情報提供を目指しております。

class InformationVerifier:
    def __init__(self, model: Model):
        self.model = model
        self.fact_checker = FactCheckingTool()
        self.source_analyzer = SourceAnalyzer()
        self.cross_referencer = CrossReferenceSystem()

    def verify_information(self, content: str, sources: List[str]) -> VerificationResult:
        source_credibility = self.source_analyzer.evaluate_sources(sources)
        cross_references = self.cross_referencer.find_corroborating_sources(content)
        fact_check_results = self.fact_checker.verify_claims(content)
        verification_summary = self.integrate_verification_results(
            source_credibility,
            cross_references,
            fact_check_results
        )
        
        return self.generate_verification_report(verification_summary)

KnowledgeGraphBuilderの主な目的は、Web上から収集した情報を体系的に整理し、関連性のある知識グラフとして構築することです。

具体的には、Webの情報調査(research)で集めたデータには以下のようなものが含まれます。

  • Web検索結果
  • Webページの内容
  • ニュース記事
  • ソーシャルメディアの投稿
  • アーカイブされたWebページの履歴

例えば、特定のトピックについてWeb検索を行い、複数のWebページから情報を収集した場合、build_knowledge_graphメソッドは

  • 各Webページから重要な概念や用語(エンティティ)を抽出
  • それらの間の関連性を分析(例:あるWebページが別のページを参照している、同じトピックについて異なる視点を提供しているなど)
  • これらの情報を有向グラフとして構造化

このような知識グラフの構築は、大量のWeb情報を整理し、情報間の関連性を明確にすることで、より効果的な情報理解と活用を可能にします。


6. システム評価と検索・レポート生成

本セクションでは、システム全体の評価、検索最適化、レポート生成および運用監視についてご説明いたします。

6.1. システムの評価とパフォーマンス分析

システムの性能を多角的に評価するため、応答速度、精度、リソース使用状況など各種メトリクスを用いて測定する仕組みが実装されています。

6.2. 高度な検索最適化

検索クエリの意味解析、検索範囲の最適化、並列検索戦略の計画などを行い、効率的な情報調査をサポートする高度な検索最適化アルゴリズムが実装されています。

6.3. レポート生成システム

情報調査の結果を効果的にまとめ、わかりやすいレポートとして出力するため、データの可視化、引用管理、レポート構造の生成などの機能を備えたシステムが実装されています。

6.4. システムの実運用と監視

システムの安定運用のため、各種メトリクスの収集、性能分析、異常検知、リソース最適化、ログ記録などを行う監視システムが実装されています。


7.まとめと今後の展望

さて、まとめにはいります!

OpenDeepResearch は、GPT Deep Research発表からわずか1日で登場しました。しかしながら、コードをみてわかるとおり、RAGのように検索結果をまとめたものではなく、こういったリサーチ系エージェントとして必要となるコンポーネントがふんだんに活用されていることがわかりました。おそらく今後もさらなる機能拡張と性能向上に向けた開発が進められるとおもいます。

より高度な自然言語理解の実装(とくに日本語対応をする上ではBertなどで日本語表現に特化したエンジンをつかうことで、さらなるインサイト抽出性能が向上できそうとおもいました)、マルチモーダルデータ統合の強化、リアルタイム性能の向上、スケーラビリティの改善、そしてユーザーインターフェースの最適化など、多くの改善が期待されますね。これをベースに独自の Deep Researchもどんどん登場するのではないでしょうか!

本ブログを最後までお読みいただき、誠にありがとうございます。私たちQualitegは自社AIサービスの提供、本日ご紹介した Deep Research 、LLM技術をはじめとした生成AIの最先端の知見、AIを活用した新規ビジネス創出に関する研修およびコンサルティングを提供しております。もしご興味をお持ちいただけた場合、また具体的なご要望がございましたら、どうぞお気軽にこちらのお問い合わせフォームまでご連絡くださいませ。

また、新規事業創出のステップを体得したいという方にご好評のワークショップも実施しております。それぞれの担当者の方が役員目線で事業を考えるという点にフォーカスしたトレーニング内容となっており、企画担当者の方だけではなく、カウンターパートのエンジニア、デザイナー、マーケターの方にもご受講いただけるコンテンツとなっております。


navigation

Read more

【解説】Tekken トークナイザーとは何か? 〜 Mistral が採用する新世代トークナイザーの特徴

【解説】Tekken トークナイザーとは何か? 〜 Mistral が採用する新世代トークナイザーの特徴

こんにちは! 本日は、Tekkenについて解説いたします! 皆さま Tekken と聞いて何を思い浮かべますか? 格ゲーの鉄拳でしょうか? 私は、昔プレイした Age of Empires に登場する鉄剣戦士を思い浮かべました🤗 ちょっと古いかもしれませんが、名作です! さてつかみはこのくらいにして、、 LLMはご存じのとおり驚異的なスピードで進化しています。そんな中でひそかに注目されているのが、トークナイザーの改善です。 たとえば、Meta の Llama 系モデルのトークナイザーは Sentence Piece から BPE系へ進化するなど、LLM業界では従来よりも高効率なトークナイズ(テキスト分割)の方法を導入し始めています。 そして Mistral AI もまた、新たに「Tekken トークナイザー」という仕組みを採用し、大規模言語モデルの性能を底上げしています。 本記事では、Tekken トークナイザーの登場背景や技術的特徴、他のトークナイザーとの違い、さらには Mistral との関係などをわかりやすく解説していきます。 1. Tekken トーク

By Qualiteg プロダクト開発部
[AI新規事業創出]Qualitegオリジナル、アイディア評価、事業アイディア選定方法

[AI新規事業創出]Qualitegオリジナル、アイディア評価、事業アイディア選定方法

Qualiteg blogを訪問してくださった皆様、こんにちは。Micheleです。AIを活用した新規事業やマーケティングを手がけている私には、クライアントからよく寄せられる質問があります。AIを用いた事業展開を検討されている方々が共通して直面するであろう課題に対して、このブログを通じて私なりの解答をご提供したいと思います。 はじめに AI技術の急速な発展は、スタートアップから大企業まで、あらゆるビジネスに新たな可能性をもたらしています。クライアントとの会話の中でも、AIを活用した革新的な事業アイディアに関する相談が増えています。 しかし、多くの企業が「素晴らしいアイディアを思いついた!」と興奮しながらも、そのアイディアを具体化し、成功に導くための方法論に悩んでいるのも事実です。特にAIを用いた事業展開においては、従来のビジネスモデルとは異なる視点が必要となるため、その難しさはさらに増します。 本記事では、Qualitegオリジナルのアイディア評価、事業アイディア選定方法について解説します。特に、AIを用いた事業展開を検討されている方々が共通して直面するであろう課題に対して、

By Join us, Michele on Qualiteg's adventure to innovation
日本語対応!Mistral Small v3 解説

日本語対応!Mistral Small v3 解説

こんにちは! Mistral AIは2025年1月30日、新しい言語モデル「Mistral Small v3」を発表しました。このモデルは、24Bという比較的小規模なパラメータ数ながら、70B以上の大規模モデルに匹敵する性能を実現しています。また日本語対応も謳われており期待の高い小型モデルです! https://huggingface.co/mistralai/Mistral-Small-24B-Instruct-2501 動画 こちら本ブログの解説動画もご覧いただけます😊 きわだってるのは、レイテンシー最適化 Mistral Small 3のめだった特徴は、その処理性能とレイテンシーの絶妙なバランスではないでしょうか。 公開されている以下の性能評価のグラフによると、トークンあたり約11ミリ秒という業界最速レベルのレイテンシーを達成しています。これは、Qwen-2.5 32Bの約15ミリ秒やGemma-2 27Bの約14ミリ秒と比較して、明確な優位性を示しています。さらに注目すべきは、GPT-4o Miniと比較しても、より低いレイテンシーで同等以上の性能を実現し

By Qualiteg プロダクト開発部
[vLLM] To use CUDA with multiprocessing, you must use the 'spawn' start method の対処法

[vLLM] To use CUDA with multiprocessing, you must use the 'spawn' start method の対処法

WSLで vLLM を使用するとき、 tensor parallel を使って複数枚のGPUで1つのLLMをサーブしようとしたとき以下のようなエラーが発生しがちです RuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing, you must use the 'spawn' start method 遭遇するシーンとしてはvLLMの起動オプションに以下のようにテンソル並列化オプションを指定したときです。 --tensor-parallel-size 2 つまり、マルチプロセッシングでCUDA使うときは、 "fork"じゃなくて"spawn" 使ってね、というエラーです。 これを vLLM に教えるために、以下の2行目のように環境変数を設定してあげるとvLLMが "spawn" を使ってくれるようになります。 export

By Qualiteg プロダクト開発部