見出し画像

LangChainブログ:マルチエージェントAIによる情報リサーチアプリを開発する事例

元ネタ:https://blog.langchain.dev/how-to-build-the-ultimate-ai-automation-with-multi-agent-collaboration/

LangGraphを使用した自律的なリサーチアシスタントの構築方法:専門AIエージェントチームを活用して

GPT Researcherの最初のリリースからわずか1年ですが、AIエージェントの構築、テスト、および展開方法は大幅に進化しました。これは現在のAIの進化の速度と性質によるものです。単純なzero-shotまたはfew-shot promptingから始まり、エージェントのfunction callingRAG、そして現在ではagentic workflows(別名「flow engineering」)へと急速に進化しました。

Andrew Ngは最近、「今年、AIエージェントワークフローが大規模なAIの進歩を促進すると考えています。おそらく次世代の基盤モデルよりも大きな進展をもたらすでしょう。この重要なトレンドに、AIに関わるすべての人が注目することを強く勧めます」と述べています。

この記事では、なぜマルチエージェントワークフローが現在のベストスタンダードであるかと、LangGraphを使用して最適な自律的なリサーチマルチエージェントアシスタントを構築する方法を学びます。

このチュートリアルをスキップしたい場合は、GPT Researcher x LangGraphの最終コード実装をこちらで確認してください。

LangGraphは、エージェントとマルチエージェントフローを作成するためにLangChainの拡張機能です。サイクルフローを作成する能力を追加し、エージェントを作成するために重要なメモリが組み込まれています。

LangGraphは開発者に高いコントロール力を提供し、カスタムエージェントやフローを作成するために重要です。ほとんどのエージェントは特定のユースケースにカスタマイズされています。LangGraphは、直感的な開発者体験を提供しながら、任意のカスタムエージェントを作成する柔軟性を提供します。

では、始めましょう!

LangGraphを活用することで、複数の専門スキルを持つエージェントを利用して、研究プロセスの深さと質を大幅に向上させることができます。各エージェントが特定のスキルに焦点を合わせ、専門化することで、関心事の分離、カスタマイズ性、およびプロジェクトの成長に伴うさらなる開発が可能になります。

最近のSTORM論文に触発され、この例では、AIエージェントチームが特定のトピックについて、計画から発表までの研究をどのように共同で行うかを示しています。この例では、主要な自律的なリサーチエージェントであるGPT Researcherも活用します。

リサーチエージェントチーム

リサーチチームは、以下の7つのLLMエージェントで構成されています:

  • Chief Editor — リサーチプロセスを監督し、チームを管理します。これは、LangGraphを使用して他のエージェントを調整する「マスター」エージェントです。このエージェントは主要なLangGraphインターフェースとして機能します。

  • GPT Researcher — 特定のトピックに関する詳細なリサーチを行う専門の自律エージェントです。

  • Editor — リサーチのアウトラインと構造を計画する責任を持ちます。

  • Reviewer — 一連の基準に基づいてリサーチ結果の正確性を検証します。

  • Reviser — レビュワーからのフィードバックに基づいてリサーチ結果を修正します。

  • Writer — 最終報告書をまとめて執筆する責任を持ちます。

  • Publisher — 最終報告書を様々な形式で公開する責任を持ちます。

アーキテクチャ

以下に示すように、自動化プロセスは次のステージに基づいています:リサーチの計画、データ収集と分析、レビューと修正、報告書の執筆、そして最終的な公開。

プロセスの詳細

プロセスは以下のように進行します:

  • Browser (gpt-researcher) — 指定されたリサーチタスクに基づいてインターネットを閲覧し、初期リサーチを行います。このステップは、最新かつ関連性の高い情報に基づいてリサーチプロセスを計画するために重要であり、特定のタスクやトピックに対して事前に訓練されたデータに頼るだけでは不十分です。

  • Editor — 初期リサーチに基づいてレポートのアウトラインと構造を計画します。Editorは、計画されたアウトラインに基づいて並行してリサーチタスクを開始する責任も負います。

  • 各アウトラインのトピックに対して(並行して):

    • Researcher (gpt-researcher) — サブトピックに関する詳細なリサーチを実施し、ドラフトを作成します。このエージェントは、詳細かつ事実に基づいたリサーチレポートを最適化するために、GPT Researcher Pythonパッケージを活用します。

    • Reviewer — ガイドラインに基づいてドラフトの正確性を検証し、必要に応じてReviserにフィードバックを提供します。

    • Reviser — レビュワーのフィードバックに基づいてドラフトを修正し、満足のいく状態にします。

    • Writer — リサーチ結果から、序論、結論、参考文献セクションを含む最終レポートをまとめて執筆します。

    • Publisher — 最終レポートをPDF、Docx、Markdownなどの複数の形式で公開します。

すべてのコードを詳述することは避け、価値のある部分に焦点を当てて説明します。

グラフステートの定義

LangGraphの一つの魅力的な機能はステート管理です。LangGraphにおけるステートは、開発者がアプリケーションの全体のステートをカプセル化するGraphStateを定義する構造化されたアプローチによって容易に管理されます。グラフ内の各ノードはこのステートを変更でき、相互作用の進行に応じて動的なレスポンスを可能にします。

技術設計の最初に、アプリケーション全体のデータスキーマを考慮することが重要です。この場合、ResearchStateを以下のように定義します:

from dataclasses import dataclass, field
from typing import List, Dict, Any

@dataclass
class ResearchState:
    task_description: str
    initial_research: Dict[str, Any] = field(default_factory=dict)
    outline: List[str] = field(default_factory=list)
    drafts: Dict[str, str] = field(default_factory=dict)
    reviews: Dict[str, str] = field(default_factory=dict)
    revisions: Dict[str, str] = field(default_factory=dict)
    final_report: str = ""
    publication_formats: List[str] = field(default_factory=list)

    def add_initial_research(self, topic: str, content: Any):
        self.initial_research[topic] = content

    def set_outline(self, outline: List[str]):
        self.outline = outline

    def add_draft(self, topic: str, draft: str):
        self.drafts[topic] = draft

    def add_review(self, topic: str, review: str):
        self.reviews[topic] = review

    def add_revision(self, topic: str, revision: str):
        self.revisions[topic] = revision

    def compile_final_report(self, report: str):
        self.final_report = report

    def add_publication_format(self, format: str):
        self.publication_formats.append(format)

各フィールドの説明

  • task_description: リサーチタスクの概要。

  • initial_research: 初期リサーチの結果を保存する辞書。

  • outline: リサーチレポートのアウトライン。

  • drafts: 各トピックに対するドラフトを保存する辞書。

  • reviews: 各ドラフトに対するレビューを保存する辞書。

  • revisions: 各レビューに基づいて修正されたドラフトを保存する辞書。

  • final_report: 最終リサーチレポート。

  • publication_formats: 公開形式のリスト。

この構造により、各エージェントは適切なデータをステートに追加、変更することで、動的かつコンテキストに応じたレスポンスを生成できます。

class ResearchState(TypedDict):
    task: dict
    initial_research: str
    sections: List\\[str\\]
    research_data: List\\[dict\\]

    title: str
    headers: dict
    date: str
    table\\_of\\_contents: str
    introduction: str
    conclusion: str
    sources: List\\[str\\]
    report: str

上記のように、ステートはリサーチタスクとレポートレイアウトの内容という2つの主要な領域に分かれています。データがグラフエージェントを通じて循環する際、各エージェントは既存のステートに基づいて新しいデータを生成し、グラフ内の他のエージェントによる後続の処理のために更新します。

次に、以下のようにグラフを初期化できます:

from langgraph.graph import StateGraph
workflow = StateGraph(ResearchState)

LangGraphによるグラフの初期化

上記のように、マルチエージェント開発の大きな利点の一つは、各エージェントが専門的かつ特定のスキルを持つように構築することです。ここでは、GPT ResearcherのPythonパッケージを使用したResearcherエージェントの例を見てみましょう。

from gpt_researcher import GPTResearcher

class ResearchAgent:
    def \\_\\_init\\_\\_(self):
        pass

      async def research(self, query: str):

        researcher = GPTResearcher(parent\\_query=parent\\_query, query=query, report\\_type=research\\_report, config_path=None)

        await researcher.conduct_research()

        report = await researcher.write_report()

          return report

上記のように、Researchエージェントのインスタンスを作成しました。次に、チームの各エージェントに対しても同様の設定を行ったと仮定します。すべてのエージェントを作成した後、LangGraphを使用してグラフを初期化します。

def init\\_research\\_team(self):

    editor_agent = EditorAgent(self.task)
    research_agent = ResearchAgent()
    writer_agent = WriterAgent()
    publisher\\_agent = PublisherAgent(self.output\\_dir)

    workflow = StateGraph(ResearchState)

    workflow.add_node("browser", research\\_agent.run\\_initial_research)
    workflow.add_node("planner", editor\\_agent.plan\\_research)
    workflow.add_node("researcher", editor\\_agent.run\\_parallel_research)
    workflow.add_node("writer", writer_agent.run)
    workflow.add_node("publisher", publisher_agent.run)

        workflow.add_edge('browser', 'planner')
    workflow.add_edge('planner', 'researcher')
    workflow.add_edge('researcher', 'writer')
    workflow.add_edge('writer', 'publisher')

    workflow.set\\_entry\\_point("browser")
    workflow.add_edge('publisher', END)

        return workflow

上記のように、LangGraphのグラフ作成は非常にシンプルで、主に3つの関数で構成されます:add_nodeadd_edge、およびset_entry_point。これらの主な関数を使用して、まずノードをグラフに追加し、エッジを接続し、最後に開始点を設定します。

フォーカスチェック:コードとアーキテクチャを適切に追っている場合、上記の初期化にはReviewerとReviserエージェントが欠けていることに気付くでしょう。これについて詳しく見ていきましょう。

ステートフルな並列化をサポートするグラフ内のグラフ

LangGraphを使用した作業で最もエキサイティングだったのは、並列処理をサポートするためのグラフ内のグラフの作成です。この自律アシスタントのもう一つの魅力は、各リサーチタスクの並列実行を行い、事前定義されたガイドラインに基づいてレビューと修正を行う機能です。

プロセス内で並列作業を活用する方法を知ることは、速度の最適化にとって重要です。しかし、すべてのエージェントが同じステートを報告する場合、どうやって並列エージェント作業をトリガーしますか?これは競合状態や最終データレポートの不整合を引き起こす可能性があります。これを解決するために、メインのLangGraphインスタンスからトリガーされるサブグラフを作成できます。このサブグラフは、各並列実行のための独自のステートを保持し、これにより生じる問題を解決します。

前述のように、LangGraphステートとそのエージェントを定義しましょう。このサブグラフは基本的にリサーチドラフトのレビューと修正を行うため、ドラフト情報でステートを定義します。

class DraftState(TypedDict):
    task: dict
    topic: str
    draft: dict
    review: str
    revision_notes: str

DraftStateでは、主に議論されるトピックと、それに対するレビュアーおよびリビジョンのノートが重要です。これらは互いにコミュニケーションを取りながら、サブトピックのリサーチレポートを最終化します。この循環条件を作成するために、LangGraphの重要な機能である条件付きエッジを利用します。

async def run\\_parallel\\_research(self, research_state: dict):
    workflow = StateGraph(DraftState)

        workflow.add_node("researcher", research\\_agent.run\\_depth_research)
    workflow.add_node("reviewer", reviewer_agent.run)
    workflow.add_node("reviser", reviser_agent.run)

    workflow.set\\_entry\\_point("researcher")
    workflow.add_edge('researcher', 'reviewer')
    workflow.add_edge('reviser', 'reviewer')
    workflow.add\\_conditional\\_edges('reviewer',
                                   (lambda draft: "accept" if draft\\['review'\\] is None else "revise"),
                                   {"accept": END, "revise": "reviser"})

条件エッジの定義

条件エッジを定義することにより、レビューノートがレビュアーから存在する場合はグラフがリバイザーに指示され、最終ドラフトでサイクルが終了します。作成したメイングラフに戻ると、この並行作業は ChiefEditor エージェントによって呼び出される "researcher" というノードの下にあることがわかります。

リサーチアシスタントの実行

エージェント、ステート、グラフを最終化した後は、リサーチアシスタントを実行する時です!カスタマイズを容易にするために、このアシスタントは与えられた task.json ファイルと共に実行されます。

{
  "query": "Is AI in a hype cycle?",
  "max_sections": 3,
  "publish_formats": {
    "markdown": true,
    "pdf": true,
    "docx": true
  },
  "follow_guidelines": false,
  "model": "gpt-4-turbo",
  "guidelines": \\[
    "The report MUST be written in APA format",
    "Each sub section MUST include supporting sources using hyperlinks. If none exist, erase the sub section or rewrite it to be a part of the previous section",
    "The report MUST be written in spanish"
  \\]
}

タスクオブジェクトについて

タスクオブジェクトは非常にわかりやすいものですが、follow_guidelines が false の場合、グラフはリビジョンステップと定義されたガイドラインを無視することに注意してください。また、max_sections フィールドは調査するサブヘッダーの数を定義します。少ない数に設定すると、短いレポートが生成されます。

アシスタントを実行すると、最終的なリサーチレポートが Markdown、PDF、および Docx 形式で生成されます。

例をダウンロードして実行するには、GPT Researcher x LangGraph オープンソースページをご覧ください。

今後の展望

AI体験を最適化するためには、人間がループに関与することが重要です。人間がアシスタントを支援して、適切なリサーチプラン、トピック、アウトラインに集中させることで、全体の品質と体験が向上します。また、一般的に言えば、AIフロー全体において人間の介入に依存することで、正確さ、制御感、および決定的な結果を保証することができます。LangGraph がすでにこの機能をサポートしていることがこちらで確認できてうれしいです。

さらに、ウェブおよびローカルデータに関するリサーチのサポートを提供することは、多くのビジネスおよび個人的なユースケースにとって重要です。

最後に、取得された情報源の品質を向上させ、最終レポートが最適なストーリーラインで構築されるようにするために、さらなる努力が求められます。

LangGraph およびマルチエージェントの協力の次のステップは、アシスタントが与えられたタスクに基づいて動的にグラフを計画および生成できるようになることです。このビジョンは、アシスタントが特定のタスクのためにエージェントのサブセットを選択し、この記事で提示されたグラフの基本原理に基づいて戦略を立てることを可能にし、新しい可能性の世界を開くでしょう。AI分野のイノベーションの進展速度を考えると、新しい破壊的なバージョンの GPT Researcher が間もなく登場することでしょう。未来が何をもたらすのか楽しみです。

この記事が気に入ったらサポートをしてみませんか?