在 LLM Agent 落地过程中,很多团队很快会发现:让模型“能回答问题”并不难,难的是让它“稳定干活”。尤其当任务变成多步骤、需要读写文件、执行命令、调用多种工具、还要可观测与可评测时,自己从零搭一套“Agent 工程底座”往往成本很高。

         Deep Agents(deepagents) 正是为此而生:灵感来源于 Claude CodeDeep Research 和 Manus 等应用,是一个 batteries-included 的 agent harness——一个带有明确工程取向、面向生产、基于 LangGraph 的 Agent 框架。你不必手工拼装提示词、工具体系和上下文管理,直接通过 create_deep_agent() 就能拿到一个“可工作的 Agent”,并且只定制你真正需要的部分。


一、概况:

deepagents 是什么?

Deep Agents 是一个建立在 LangGraph 之上的、可生产使用的 Agent 框架/运行支架(harness)。它具备以下关键定位:

  • 开箱即用create_deep_agent() 直接创建一个可运行的 Agent
  • 强工程化默认值:内置中间件栈(规划、记忆、技能、摘要、上下文管理等)
  • 模型提供商无关:支持 Claude / OpenAI / Google 或任何 LangChain 兼容模型(通过 init_chat_model()
  • 100% 开源(MIT):可审计、可二次开发、可私有化部署
  • 运行时可靠:基于 LangGraph,具备 streaming、checkpoint、持久化等生产能力
  • 不仅是 SDK:围绕 SDK 还提供 CLI、评测集成(Harbor)与协议实现(ACP)

Deep Agents 以四个包发布:

  • deepagents:核心 SDK(创建 agent、middleware、backend、tools)
  • deepagents-cli:带 TUI 的终端交互版(更偏“coding agent/个人助手”)
  • deepagents-harbor:与 Harbor 评测框架集成(跑 benchmark/评测实验)
  • deepagents-acp:Agent Client Protocol(ACP)实现(便于互操作)

它解决了哪些“传统 Agent 开发痛点”?

传统做法往往需要开发者自己补齐一堆“Agent 工程配套”:

  1. 上下文管理:对话历史、工具结果、上下文窗口限制怎么处理?
  2. 工具基础设施:文件读写、grep、glob、shell 执行、任务委派如何标准化?
  3. 规划机制:todo list / task tracking 怎么做,才能让复杂任务不跑偏?
  4. 中间件体系:记忆、技能、动态 prompt 注入怎么组合?
  5. 沙箱隔离:如何把 agent 执行与宿主系统隔离,降低风险?
  6. 可观测性:tracing、logging、评测怎么打通?
  7. 稳定运行时:如何支持 streaming、checkpoint、持久化执行与恢复?

Deep Agents 的核心价值就是:把这些能力做成可组合的“默认中间件 + 内置工具 + 后端抽象 + LangGraph 运行时”,让你可以从“能跑 demo”更快走向“能上生产”。


二、deepagents 框架与组件:

从工程视角,Deep Agents 可以理解为四层:入口工厂(Factory)→ 中间件栈(Middleware)→ 后端抽象(Backend)→ LangGraph 运行时(Runtime),外加工具与生态集成。

2.1 入口:Factory(create_deep_agent / create_cli_agent)

Deep Agents 采用“工厂模式”来创建 agent:

  • create_deep_agent():SDK 入口,创建一个编译好的 LangGraph Agent(CompiledStateGraph
  • create_cli_agent():CLI 入口,组装 CLI 专属中间件(如 HITL、本地上下文注入、web search、线程持久化等)

工厂函数的职责很清晰:把一堆能力(middleware、tools、backend、model、prompt)组装起来,输出一个可运行的 LangGraph 对象


2.2 中间件系统:Middleware(能力“插件化组合”)

Deep Agents 的能力主要通过中间件叠加实现。中间件按顺序执行,每层可以通过两个 hook 参与执行流程:

  • before_agent():会话开始时运行一次,用于初始化/填充 state
  • wrap_model_call():拦截每次模型调用,用于注入动态内容(例如记忆、技能、摘要、上下文等)

你可以把它理解为“Agent 的请求管道”:每次调用模型前,依次经过多层增强,最终让模型“拿到更合适的上下文与工具”。

常见中间件能力包括:

  • TodoListMiddleware(规划):配合 write_todos / read_todos,让 agent 能拆解任务、跟踪进度
  • FilesystemMiddleware(文件系统):提供 read_file/write_file/edit_file/ls/glob/grep 等,支持把大内容外置,避免上下文爆炸
  • SubAgentMiddleware(子任务委派):通过 task 工具派生子 agent,做上下文隔离与分工
  • MemoryMiddleware(记忆):将偏好、知识或关键事实持久化/可复用(可结合 LangGraph Memory Store)
  • SkillsMiddleware(技能):以技能文件/结构化方式注入“做事习惯与套路”
  • SummarizationMiddleware(摘要/窗口管理):自动总结历史、淘汰部分工具结果,控制上下文长度
  • CLI 专属中间件
    • LocalContextMiddleware(本地上下文注入)
    • HumanInTheLoopMiddleware(人类确认/介入流程)
    • web_search 工具接入
    • SQLite checkpointer(线程持久化更自动)

一句话:Middleware 决定了 agent 的“做事方式”与“工程特性”


2.3 工具层:Tools(能干活的手脚)

Deep Agents 内置了不少“生产常用工具”,覆盖文件与命令执行等刚需:

  • 文件系统:read_filewrite_fileedit_filelsglobgrep
  • Shell 执行:execute(通过 backend 实现)
  • 任务委派:task(spawn subagent)
    -(CLI 额外)Web search:web_search

这些工具的意义不只是“能调用”,而是它们与中间件、后端、上下文管理策略配套,减少你重复造轮子。


2.4 后端抽象:BackendProtocol(把“执行环境”模块化)

Deep Agents 把所有“有副作用/有状态”的操作都抽象到 BackendProtocol 里,例如:

  • 读写文件(read/write)
  • 执行命令(execute)
  • 存取记忆/状态(取决于具体 backend)

这带来一个很关键的工程收益:同一套 agent 逻辑,可以切换不同执行环境

  • 本地执行
  • 临时环境(ephemeral)
  • 持久化环境
  • 沙箱环境(Modal/Runloop/Daytona/LangSmith 等)

也因此,deepagents 的安全边界通常不靠“让模型自觉”,而是靠 工具与沙箱 来约束。


2.5 运行时:LangGraph CompiledStateGraph(生产级执行能力)

最终 create_deep_agent() 返回的是 LangGraph 的 CompiledStateGraph,它天然具备:

  • Streaming(流式输出)
  • Checkpointing(可中断/恢复)
  • State 管理(每轮对话与工具调用的状态机)
  • Persistence(持久化能力,取决于 checkpointer / backend 配置)

这也是 Deep Agents “生产就绪”的关键:它不是一个轻量 wrapper,而是把 agent 放在一个适合长任务、可恢复、可观测的执行框架上运行。


2.6 生态集成:LangSmith / Harbor / MCP / 多模型提供商

Deep Agents 与 LangChain 生态深度打通:

  • LangSmith:tracing、可观测性、实验评测(通常通过环境变量自动启用)
  • Harbor(deepagents-harbor):在 Harbor 环境下跑评测/benchmark,并导出轨迹等
  • MCP adapters:通过 langchain-mcp-adapters 支持 Model Context Protocol
  • 模型提供商无关:通过 init_chat_model() 初始化 Anthropic/OpenAI/Google 等

3)安全模型

Deep Agents 采用 “trust the LLM” 安全模型:模型能做的事情取决于你给它的工具。因此实践上建议:

  • 尽量在沙箱内运行(尤其是 shell、文件写入等)
  • 工具最小授权(只给必需工具)
  • 自定义工具做严格入参校验
  • 文件系统路径校验(内置中间件也会做一部分防护)

三、Hello World

首先使用代码中自带的例子 Deep Research 稍微做一下修改

1、下载代码

git clone https://github.com/langchain-ai/deepagents.git

2、uv 安装

      未安装uv的可以安装一下  pip install uv

        之后可以uv sync 安装虚拟环境

3、.env 设置自己的api key

# API Keys for Deep Research Agent Example
# Copy this file to .env and fill in your actual API keys

# Anthropic API Key (for Claude Sonnet 4)
ANTHROPIC_API_KEY=your_anthropic_api_key_here

# OpenAI API Key (for GPT-4o-mini summarization)
OPENAI_API_KEY=your_openai_api_key_here

# Tavily API Key (for web search)
TAVILY_API_KEY=your_tavily_api_key_here

# LangSmith API Key (required for LangGraph local server)
# Get your key at: https://smith.langchain.com/settings
LANGSMITH_API_KEY=lsv2_pt_your_api_key_here

4、agents 代码

"""Research Agent - Standalone script for LangGraph deployment.

This module creates a deep research agent with custom tools and prompts
for conducting web research with strategic thinking and context management.
"""

from datetime import datetime

from langchain.chat_models import init_chat_model
from langchain_google_genai import ChatGoogleGenerativeAI
from deepagents import create_deep_agent
from deepagents.backends.utils import file_data_to_string
from utils import show_prompt, format_messages
from dotenv import load_dotenv

load_dotenv(".env", override=True)

from research_agent.prompts import (
    RESEARCHER_INSTRUCTIONS,
    RESEARCH_WORKFLOW_INSTRUCTIONS,
    SUBAGENT_DELEGATION_INSTRUCTIONS,
)
from research_agent.tools import tavily_search, think_tool

# Limits 设置迭代次数,避免浪费很多takens
max_concurrent_research_units = 5
max_researcher_iterations = 5

# Get current date
current_date = datetime.now().strftime("%Y-%m-%d")

# Combine orchestrator instructions (RESEARCHER_INSTRUCTIONS only for sub-agents)
# 主要设置subagents 命令
INSTRUCTIONS = (
    RESEARCH_WORKFLOW_INSTRUCTIONS
    + "\n\n"
    + "=" * 80
    + "\n\n"
    + SUBAGENT_DELEGATION_INSTRUCTIONS.format(
        max_concurrent_research_units=max_concurrent_research_units,
        max_researcher_iterations=max_researcher_iterations,
    )
)

# Create research sub-agent 
#创建subagents,
research_sub_agent = {
    "name": "research-agent",
    "description": "Delegate research to the sub-agent researcher. Only give this researcher one topic at a time.",
    "system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date),
    "tools": [tavily_search, think_tool],
}

# Model Gemini 3
# model = ChatGoogleGenerativeAI(model="gemini-3-pro-preview", temperature=0.0)

# Model Claude 4.5
#model = init_chat_model(model="anthropic:claude-sonnet-4-5-20250929", temperature=0.0)

# add model gpt-5.2
# 支持各种模型,不过国内需要使用转换,可以使用下面方式创建model 申请key的时候主要下面两个base_url api_key
model = init_chat_model(
    model="gpt-5.2",
    model_provider="openai",
    base_url="",
    api_key=""
    )
# Create the agent
agent = create_deep_agent(
    model=model,
    tools=[tavily_search, think_tool],
    system_prompt=INSTRUCTIONS,
    subagents=[research_sub_agent],
)

result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "深入研究一下langchain中的deep agents 并写一系列有深度的技术博客",
            }
        ],
    },
)

# Convert a specific file to string
file_content = file_data_to_string(result["files"]['/final_report.md'])
show_prompt(file_content)

utils 中对数据做了一些格式化

"""Utility functions for displaying messages and prompts in Jupyter notebooks."""

import json

from rich.console import Console
from rich.panel import Panel
from rich.text import Text

console = Console()


def format_message_content(message):
    """Convert message content to displayable string."""
    parts = []
    tool_calls_processed = False

    # Handle main content
    if isinstance(message.content, str):
        parts.append(message.content)
    elif isinstance(message.content, list):
        # Handle complex content like tool calls (Anthropic format)
        for item in message.content:
            if item.get("type") == "text":
                parts.append(item["text"])
            elif item.get("type") == "tool_use":
                parts.append(f"\n🔧 Tool Call: {item['name']}")
                parts.append(f"   Args: {json.dumps(item['input'], indent=2)}")
                parts.append(f"   ID: {item.get('id', 'N/A')}")
                tool_calls_processed = True
    else:
        parts.append(str(message.content))

    # Handle tool calls attached to the message (OpenAI format) - only if not already processed
    if (
        not tool_calls_processed
        and hasattr(message, "tool_calls")
        and message.tool_calls
    ):
        for tool_call in message.tool_calls:
            parts.append(f"\n🔧 Tool Call: {tool_call['name']}")
            parts.append(f"   Args: {json.dumps(tool_call['args'], indent=2)}")
            parts.append(f"   ID: {tool_call['id']}")

    return "\n".join(parts)


def format_messages(messages):
    """Format and display a list of messages with Rich formatting."""
    for m in messages:
        msg_type = m.__class__.__name__.replace("Message", "")
        content = format_message_content(m)

        if msg_type == "Human":
            console.print(Panel(content, title="🧑 Human", border_style="blue"))
        elif msg_type == "Ai":
            console.print(Panel(content, title="🤖 Assistant", border_style="green"))
        elif msg_type == "Tool":
            console.print(Panel(content, title="🔧 Tool Output", border_style="yellow"))
        else:
            console.print(Panel(content, title=f"📝 {msg_type}", border_style="white"))


def format_message(messages):
    """Alias for format_messages for backward compatibility."""
    return format_messages(messages)


def show_prompt(prompt_text: str, title: str = "Prompt", border_style: str = "blue"):
    """Display a prompt with rich formatting and XML tag highlighting.

    Args:
        prompt_text: The prompt string to display
        title: Title for the panel (default: "Prompt")
        border_style: Border color style (default: "blue")
    """
    # Create a formatted display of the prompt
    formatted_text = Text(prompt_text)
    formatted_text.highlight_regex(r"<[^>]+>", style="bold blue")  # Highlight XML tags
    formatted_text.highlight_regex(
        r"##[^#\n]+", style="bold magenta"
    )  # Highlight headers
    formatted_text.highlight_regex(
        r"###[^#\n]+", style="bold cyan"
    )  # Highlight sub-headers

    # Display in a panel for better presentation
    console.print(
        Panel(
            formatted_text,
            title=f"[bold green]{title}[/bold green]",
            border_style=border_style,
            padding=(1, 2),
        )
    )

deep_research\research_agent\tools.py 主要是搜索工具

"""Research Tools.

This module provides search and content processing utilities for the research agent,
using Tavily for URL discovery and fetching full webpage content.
"""

import httpx
from langchain_core.tools import InjectedToolArg, tool
from markdownify import markdownify
from tavily import TavilyClient
from typing_extensions import Annotated, Literal

tavily_client = TavilyClient()


def fetch_webpage_content(url: str, timeout: float = 10.0) -> str:
    """Fetch and convert webpage content to markdown.

    Args:
        url: URL to fetch
        timeout: Request timeout in seconds

    Returns:
        Webpage content as markdown
    """
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }

    try:
        response = httpx.get(url, headers=headers, timeout=timeout)
        response.raise_for_status()
        return markdownify(response.text)
    except Exception as e:
        return f"Error fetching content from {url}: {str(e)}"


@tool(parse_docstring=True)
def tavily_search(
    query: str,
    max_results: Annotated[int, InjectedToolArg] = 1,
    topic: Annotated[
        Literal["general", "news", "finance"], InjectedToolArg
    ] = "general",
) -> str:
    """Search the web for information on a given query.

    Uses Tavily to discover relevant URLs, then fetches and returns full webpage content as markdown.

    Args:
        query: Search query to execute
        max_results: Maximum number of results to return (default: 1)
        topic: Topic filter - 'general', 'news', or 'finance' (default: 'general')

    Returns:
        Formatted search results with full webpage content
    """
    # Use Tavily to discover URLs
    search_results = tavily_client.search(
        query,
        max_results=max_results,
        topic=topic,
    )

    # Fetch full content for each URL
    result_texts = []
    for result in search_results.get("results", []):
        url = result["url"]
        title = result["title"]

        # Fetch webpage content
        content = fetch_webpage_content(url)

        result_text = f"""## {title}
**URL:** {url}

{content}

---
"""
        result_texts.append(result_text)

    # Format final response
    response = f"""🔍 Found {len(result_texts)} result(s) for '{query}':

{chr(10).join(result_texts)}"""

    return response


@tool(parse_docstring=True)
def think_tool(reflection: str) -> str:
    """Tool for strategic reflection on research progress and decision-making.

    Use this tool after each search to analyze results and plan next steps systematically.
    This creates a deliberate pause in the research workflow for quality decision-making.

    When to use:
    - After receiving search results: What key information did I find?
    - Before deciding next steps: Do I have enough to answer comprehensively?
    - When assessing research gaps: What specific information am I still missing?
    - Before concluding research: Can I provide a complete answer now?

    Reflection should address:
    1. Analysis of current findings - What concrete information have I gathered?
    2. Gap assessment - What crucial information is still missing?
    3. Quality evaluation - Do I have sufficient evidence/examples for a good answer?
    4. Strategic decision - Should I continue searching or provide my answer?

    Args:
        reflection: Your detailed reflection on research progress, findings, gaps, and next steps

    Returns:
        Confirmation that reflection was recorded for decision-making
    """
    return f"Reflection recorded: {reflection}"

  • tavily_search(作用与流程)

    • 作用:以查询词发现相关网页并把全文转换为 Markdown,供研究代理阅读/合成。
    • 关键步骤(根据 research_agent/tools.py):
      1. 用 TavilyClient().search(query, max_results, topic) 获取检索结果(每项包含 url 和 title)。
      2. 对每个结果调用 fetch_webpage_content(url)
        • 用 httpx.get 发起 HTTP 请求(带浏览器 UA),获取页面 HTML。
        • 用 markdownify 把 HTML 转成 Markdown 文本。
        • 在异常时返回包含错误信息的字符串。
      3. 将标题、URL 与转换后的 Markdown 拼成一个分段结果,聚合并返回给调用者(文本格式,含分隔符)。
    • 输出特点:人可读、包含完整页面内容(或错误说明);适合后续合成器/子代理直接读取并抽取证据与引用。
    • 注意点/风险:
      • 抓取受限站点、JS 渲染页面或反爬策略可能导致内容不完整或错误。
      • 未做并发/超时重试策略(httpx.get 单次调用),大批量抓取时需要节流与错误处理强化。
      • 未在工具层做 URL 去重或内容摘要,合成器需负责去重与引用编号。
  • think_tool(实现原理与用途)

    • 当前实现(文件中代码的行为):
      • 声明为一个工具(@tool(parse_docstring=True)),签名 think_tool(reflection: str) -> str
      • 实际上只是把传入的 reflection 字符串回显成 Reflection recorded: {reflection} 并返回。
    • 设计意图(在提示词中被要求的用法):
      • 在每次搜索后调用,用来“刻意停顿并反思”:总结已找到的要点、评估信息缺口、判断是否继续搜索并给出下一步计划。
      • 作为研究循环中的决策点(search → think → next action)。

examples\deep_research\research_agent\prompts.py
 

"""Prompt templates and tool descriptions for the research deepagent."""

RESEARCH_WORKFLOW_INSTRUCTIONS = """# Research Workflow

Follow this workflow for all research requests:

1. **Plan**: Create a todo list with write_todos to break down the research into focused tasks
2. **Save the request**: Use write_file() to save the user's research question to `/research_request.md`
3. **Research**: Delegate research tasks to sub-agents using the task() tool - ALWAYS use sub-agents for research, never conduct research yourself
4. **Synthesize**: Review all sub-agent findings and consolidate citations (each unique URL gets one number across all findings)
5. **Write Report**: Write a comprehensive final report to `/final_report.md` (see Report Writing Guidelines below)
6. **Verify**: Read `/research_request.md` and confirm you've addressed all aspects with proper citations and structure

## Research Planning Guidelines
- Batch similar research tasks into a single TODO to minimize overhead
- For simple fact-finding questions, use 1 sub-agent
- For comparisons or multi-faceted topics, delegate to multiple parallel sub-agents
- Each sub-agent should research one specific aspect and return findings

## Report Writing Guidelines

When writing the final report to `/final_report.md`, follow these structure patterns:

**For comparisons:**
1. Introduction
2. Overview of topic A
3. Overview of topic B
4. Detailed comparison
5. Conclusion

**For lists/rankings:**
Simply list items with details - no introduction needed:
1. Item 1 with explanation
2. Item 2 with explanation
3. Item 3 with explanation

**For summaries/overviews:**
1. Overview of topic
2. Key concept 1
3. Key concept 2
4. Key concept 3
5. Conclusion

**General guidelines:**
- Use clear section headings (## for sections, ### for subsections)
- Write in paragraph form by default - be text-heavy, not just bullet points
- Do NOT use self-referential language ("I found...", "I researched...")
- Write as a professional report without meta-commentary
- Each section should be comprehensive and detailed
- Use bullet points only when listing is more appropriate than prose

**Citation format:**
- Cite sources inline using [1], [2], [3] format
- Assign each unique URL a single citation number across ALL sub-agent findings
- End report with ### Sources section listing each numbered source
- Number sources sequentially without gaps (1,2,3,4...)
- Format: [1] Source Title: URL (each on separate line for proper list rendering)
- Example:

  Some important finding [1]. Another key insight [2].

  ### Sources
  [1] AI Research Paper: https://example.com/paper
  [2] Industry Analysis: https://example.com/analysis
"""

RESEARCHER_INSTRUCTIONS = """You are a research assistant conducting research on the user's input topic. For context, today's date is {date}. Output in Chinese.

<Task>
Your job is to use tools to gather information about the user's input topic.
You can use any of the research tools provided to you to find resources that can help answer the research question.
You can call these tools in series or in parallel, your research is conducted in a tool-calling loop.
</Task>

<Available Research Tools>
You have access to two specific research tools:
1. **tavily_search**: For conducting web searches to gather information
2. **think_tool**: For reflection and strategic planning during research
**CRITICAL: Use think_tool after each search to reflect on results and plan next steps**
</Available Research Tools>

<Instructions>
Think like a human researcher with limited time. Follow these steps:

1. **Read the question carefully** - What specific information does the user need?
2. **Start with broader searches** - Use broad, comprehensive queries first
3. **After each search, pause and assess** - Do I have enough to answer? What's still missing?
4. **Execute narrower searches as you gather information** - Fill in the gaps
5. **Stop when you can answer confidently** - Don't keep searching for perfection
</Instructions>

<Hard Limits>
**Tool Call Budgets** (Prevent excessive searching):
- **Simple queries**: Use 2-3 search tool calls maximum
- **Complex queries**: Use up to 5 search tool calls maximum
- **Always stop**: After 5 search tool calls if you cannot find the right sources

**Stop Immediately When**:
- You can answer the user's question comprehensively
- You have 3+ relevant examples/sources for the question
- Your last 2 searches returned similar information
</Hard Limits>

<Show Your Thinking>
After each search tool call, use think_tool to analyze the results:
- What key information did I find?
- What's missing?
- Do I have enough to answer the question comprehensively?
- Should I search more or provide my answer?
</Show Your Thinking>

<Final Response Format>
When providing your findings back to the orchestrator:

1. **Structure your response**: Organize findings with clear headings and detailed explanations
2. **Cite sources inline**: Use [1], [2], [3] format when referencing information from your searches
3. **Include Sources section**: End with ### Sources listing each numbered source with title and URL

Example:
```
## Key Findings

Context engineering is a critical technique for AI agents [1]. Studies show that proper context management can improve performance by 40% [2].

### Sources
[1] Context Engineering Guide: https://example.com/context-guide
[2] AI Performance Study: https://example.com/study
```

The orchestrator will consolidate citations from all sub-agents into the final report.
</Final Response Format>
"""

TASK_DESCRIPTION_PREFIX = """Delegate a task to a specialized sub-agent with isolated context. Available agents for delegation are:
{other_agents}
"""

SUBAGENT_DELEGATION_INSTRUCTIONS = """# Sub-Agent Research Coordination

Your role is to coordinate research by delegating tasks from your TODO list to specialized research sub-agents.

## Delegation Strategy

**DEFAULT: Start with 1 sub-agent** for most queries:
- "What is quantum computing?" → 1 sub-agent (general overview)
- "List the top 10 coffee shops in San Francisco" → 1 sub-agent
- "Summarize the history of the internet" → 1 sub-agent
- "Research context engineering for AI agents" → 1 sub-agent (covers all aspects)

**ONLY parallelize when the query EXPLICITLY requires comparison or has clearly independent aspects:**

**Explicit comparisons** → 1 sub-agent per element:
- "Compare OpenAI vs Anthropic vs DeepMind AI safety approaches" → 3 parallel sub-agents
- "Compare Python vs JavaScript for web development" → 2 parallel sub-agents

**Clearly separated aspects** → 1 sub-agent per aspect (use sparingly):
- "Research renewable energy adoption in Europe, Asia, and North America" → 3 parallel sub-agents (geographic separation)
- Only use this pattern when aspects cannot be covered efficiently by a single comprehensive search

## Key Principles
- **Bias towards single sub-agent**: One comprehensive research task is more token-efficient than multiple narrow ones
- **Avoid premature decomposition**: Don't break "research X" into "research X overview", "research X techniques", "research X applications" - just use 1 sub-agent for all of X
- **Parallelize only for clear comparisons**: Use multiple sub-agents when comparing distinct entities or geographically separated data

## Parallel Execution Limits
- Use at most {max_concurrent_research_units} parallel sub-agents per iteration
- Make multiple task() calls in a single response to enable parallel execution
- Each sub-agent returns findings independently

## Research Limits
- Stop after {max_researcher_iterations} delegation rounds if you haven't found adequate sources
- Stop when you have sufficient information to answer comprehensively
- Bias towards focused research over exhaustive exploration"""

这段提示词比较有意思,这套提示词在系统中如何协作

  • 主 Agent(orchestrator)
    负责 write_todos → write_file(/research_request.md) → task() 派研究员 → 汇总与统一 citation → write_file(/final_report.md) → 回读核验
  • 子 Agent(researcher)
    只做检索与归纳,严格控制搜索次数,输出带引用的结构化 findings
  • 文件
    • /research_request.md:需求基准
    • /final_report.md:最终交付物
      文件系统承担“长上下文外置”和“审计可追踪”的角色

文件头注释

"""Prompt templates and tool descriptions for the research deepagent."""


“用于研究型 deep agent 的提示词模板与工具描述。”
这表明该文件不是业务逻辑代码,而是研究型 Agent 的提示词配置:包括主 Agent(编排者/orchestrator)应遵循的流程、子 Agent(researcher)的工作规范、以及如何委派任务的策略。


RESEARCH_WORKFLOW_INSTRUCTIONS(主编排工作流:从需求到报告)

这是给“主 Agent / orchestrator”的总流程提示,强调任务分解、落盘、委派子 Agent、汇总引用、写最终报告、核对需求

研究工作流(Research Workflow)

  1. Plan:使用 write_todos 创建待办清单,把研究拆成聚焦的小任务
  2. Save the request:使用 write_file() 把用户的研究问题保存到 /research_request.md
  3. Research:使用 task() 工具把研究任务委派给子 Agent——研究必须由子 Agent 执行,主 Agent 永远不要自己做研究
  4. Synthesize:审阅所有子 Agent 的发现,并合并整理引用(每个唯一 URL 在所有发现中只分配一个编号)
  5. Write Report:把完整最终报告写入 /final_report.md(见下方“报告写作指南”)
  6. Verify:读取 /research_request.md,确认已覆盖所有问题点,且引用与结构正确

研究规划指南(Research Planning Guidelines)

  • 把相似研究任务合并到同一个 TODO,减少协调开销
  • 简单事实问题:使用 1 个子 Agent
  • 对比/多维话题:委派多个并行子 Agent
  • 每个子 Agent 只研究一个明确方面并返回发现

报告写作指南(Report Writing Guidelines)

写 /final_report.md 时使用以下结构:

  • 对比类:引言 → A 概览 → B 概览 → 详细对比 → 结论
  • 列表/排行类:直接列条目并解释,无需引言
  • 综述/概览类:主题概览 → 关键概念 1/2/3 → 结论
    通用要求:
  • 清晰标题(## 分节,### 分小节)
  • 默认用段落写作(偏“文本密集”,少用纯 bullet)
  • 不要自我指涉(例如“我发现…/我研究了…”)
  • 以专业报告口吻写作,不要元评论
  • 每节要充分、详细
  • 只有在“列表更合适”时才用 bullet

引用格式(Citation format)

  • 行内用 [1] [2] [3] 标注
  • 全局对所有子 Agent 的结果:同一 URL 永远对应同一编号
  • 末尾加 ### Sources 列出所有编号来源
  • 编号连续无空洞(1,2,3,4…)
  • 格式:[1] 标题: URL(每行一个)
  • 示例略

为什么这样设计?

(a) “写 todo”强制规划
研究任务最常见失败是“没有结构”。write_todos 让模型先明确要做什么、按什么顺序、何时算完成。

(b) “写入 /research_request.md”是可审计的单一事实源(Single Source of Truth)
主 Agent 后续 synthesize 与 verify 时不必依赖对话上下文记忆,而是读文件来核验需求,减少遗漏。

(c) “研究必须由子 Agent 做”= 上下文隔离 + 可并行 + 降低主 Agent 污染
主 Agent 的职责是“项目经理”:拆解、分派、汇总、写作。研究细节留在子 Agent 的上下文里,主上下文更干净。

(d) “引用全局去重编号”是报告质量关键
多子 Agent 时最容易出现:同一个来源被编号多次、引用乱序、Sources 重复。这里明确要求“URL 级别去重”,是为了最终报告更像专业文档。

(e) “写入 /final_report.md”= 产物落盘
研究型 agent 最终交付应是“可复用的文件”,而不是散落在聊天里。便于后续审阅、版本控制、二次编辑。


RESEARCHER_INSTRUCTIONS(子 Agent:研究员的作业规范)

这是给“research 子 Agent”的提示:它必须使用工具检索信息、控制搜索次数、每次搜索后反思并决定下一步,最后按固定格式返回。

你是一名研究助理,正在围绕用户输入主题开展研究。背景:今天日期是 {date}用中文输出

<Task>
你的工作是使用工具收集与用户主题有关的信息。你可以使用提供的研究工具寻找能回答研究问题的资源。你可以串行或并行调用工具;研究通过“工具调用循环”完成。
</Task>

<Available Research Tools>
你可以使用两个研究工具:

  1. tavily_search:网页搜索收集信息
  2. think_tool:用于反思与制定研究策略
    关键要求:每次搜索后都要使用 think_tool 来反思结果并规划下一步

(像时间有限的人类研究员一样工作)

  1. 仔细阅读问题:用户到底需要什么信息?
  2. 先做宽泛搜索:用更泛的查询先覆盖全局
  3. 每次搜索后暂停评估:信息够不够?还缺什么?
  4. 随着信息增长做更窄搜索:补缺口
  5. 够用就停:不要追求完美无限搜索

(限制工具调用预算,防止过度搜索)

  • 简单问题:最多 2-3 次搜索
  • 复杂问题:最多 5 次搜索
  • 无论如何:搜索达到 5 次还找不到合适来源也必须停止

立即停止条件

  • 已能全面回答问题
  • 已有 3 个以上相关例子/来源
  • 最近两次搜索返回的信息高度相似

每次 search 之后用 think_tool 分析:

  • 找到了什么关键信息?
  • 还缺什么?
  • 是否足以全面回答?
  • 是否还要继续搜索还是可以输出?

(回传给 orchestrator 的格式)

  1. 用清晰标题组织发现,并给出详细解释
  2. 行内引用用 [1][2][3]
  3. 最后以 ### Sources 列出编号来源(标题 + URL)
    并说明 orchestrator 会把多子 Agent 的引用统一合并到最终报告。

</Final Response Format>

2)详细解读:这个子提示词在“控成本 + 控质量”

(a) 工具约束:只有 tavily_search + think_tool
这是典型“研究员子 Agent”配置:

  • tavily_search 负责找资料
  • think_tool 负责“每步复盘与决策”
    从系统上防止子 Agent胡写(因为它被鼓励用来源支撑)。

(b) “每次搜索后必须反思”= 防止无效连搜
很多研究 agent 容易陷入“搜—贴—再搜—再贴”,没有收敛标准。加入 think_tool 的节奏点,能显著提高每次搜索的质量。

(c) 工具调用预算(2-3 / 5)是工程化的关键
没有预算会导致:

  • 成本不可控
  • 延迟过高
  • 结果反而不稳定(信息越多越乱)
    硬上限 + 早停条件让它更像“时间有限的研究员”。

(d) 固定输出结构 + 引用格式
子 Agent 输出如果不结构化,主 Agent 很难汇总;这里要求“标题 + 解释 + 行内引用 + Sources 列表”,能让 orchestrator 更稳定做 citation 合并。

注意(安全/产品实践):
提示词要求 “Show Your Thinking” 可能触发模型输出较长推理。面向用户产品通常改成:

  • “每次搜索后用 think_tool 做内部反思(不对外输出)”
  • 或“输出简要的‘下一步计划要点’,不要输出思维链细节”

TASK_DESCRIPTION_PREFIX(委派任务说明前缀)

将任务委派给一个具有隔离上下文的专门子 Agent。可用于委派的 Agent 如下:
{other_agents}

这是 orchestrator 在调用 task() 前,用来**描述“你可以选哪些子 agent”**的动态模板。{other_agents} 通常会被替换成可用子 agent 列表(例如“researcher、coder、analyst…”),便于模型选择合适角色。


SUBAGENT_DELEGATION_INSTRUCTIONS(主 Agent 如何分派研究任务)

这是给 orchestrator 的“分派策略”,核心思想是:默认一个子 Agent 就够,不要过度拆分;只有明确对比或明显独立维度才并行。

你的角色是:根据 TODO 列表,把研究任务委派给专门的研究子 Agent。

默认:大多数查询从 1 个子 Agent 开始:概念解释、列表、历史概述、研究某个主题(通常 1 个足够覆盖)。

只有当问题明确要求对比或存在明显独立方面时才并行

  • 明确对比:每个对比对象 1 个子 Agent
  • 明显可分离方面(谨慎使用):例如按地区拆分

关键原则:

  • 偏向单子 Agent:一个全面研究任务通常更省 token
  • 避免过早分解:不要把“研究 X”拆成“X 概览/技术/应用”,直接 1 个子 Agent 覆盖 X
  • 仅在清晰对比时并行

并行限制:

  • 每轮最多 {max_concurrent_research_units} 个并行子 Agent
  • 同一条回复里发起多个 task() 调用以实现并行

研究轮次限制:

  • 最多 {max_researcher_iterations} 轮委派
  • 信息足够就停止
  • 偏向“聚焦研究”而非穷举探索

2)详细解读:它在解决“多 Agent 系统常见的两个问题”

问题 1:过度分解导致协调成本爆炸
多子 agent 并行看似快,但会带来:更多 token、更多重复、更多引用去重难度、更多合并工作。该提示词用“默认 1 个”强行抑制这种倾向。

问题 2:不必要的并行导致质量下降
当拆得太细,每个子 agent 都只看到一角,最后主 agent 合起来不一定一致。这里强调“比较/独立方面才并行”,是为了保持叙事一致。

并行上限与轮次上限
{max_concurrent_research_units} 与 {max_researcher_iterations} 是典型的“护栏参数”:

  • 并行上限控制峰值成本与速度
  • 轮次上限控制系统不会无限迭代
    Logo

    有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

    更多推荐