Research Agent 示例(L3)

Prompt / Planner / Executor / Tool / State 全部串起来,形成一个 L3(Planner + Sequential Executor) 的可落地参考实现

  1. 这个 Research Agent 要解决什么问题
  2. 架构与执行流程
  3. 项目目录
  4. 数据模型
  5. Prompt 文件
  6. Tool 定义
  7. Planner 实现
  8. Executor 实现
  9. Final Synthesizer
  10. Orchestrator 串联
  11. 一次完整请求的执行示例
  12. 可以直接抄的伪代码/简化代码

1. 场景定义

我们先定义一个具体任务。

用户请求

请分析 2024~2025 年 AI 编码工具市场趋势,并输出一份简短报告。

这是一个典型的 Research Agent 场景,因为它通常需要:

  • 先搜索信息
  • 再抽取关键事实
  • 再做趋势分析
  • 最后生成报告

这类任务:

  • 不是单次问答
  • 也不是高自由探索的纯 ReAct
  • 非常适合 L3:Planner + Executor

2. 总体架构

这个示例采用:

  • Task Interpreter(可选)
  • Planner
  • Sequential Executor
  • Tool Registry
  • State Store
  • Final Synthesizer

整体链路如下:

text 复制代码
用户请求
  ↓
任务理解(可选)
  ↓
Planner 生成线性计划
  ↓
Plan Validator
  ↓
Sequential Executor 顺序执行
  ├── Tool Step: web_search
  ├── LLM Step: extract_signals
  └── LLM Step: write_report_draft
  ↓
Final Synthesizer
  ↓
最终回答

3. 目录结构

我们给这个 Research Agent 一个尽量小但完整的目录结构。

text 复制代码
research_agent/
├── app/
│   ├── domain/
│   │   ├── models.py
│   │   └── enums.py
│   │
│   ├── prompts/
│   │   └── prompts_zh.py
│   │
│   ├── tools/
│   │   ├── base.py
│   │   ├── registry.py
│   │   └── web_search.py
│   │
│   ├── planner/
│   │   ├── planner.py
│   │   └── parser.py
│   │
│   ├── executor/
│   │   ├── sequential_executor.py
│   │   └── step_prompt_builder.py
│   │
│   ├── llm/
│   │   ├── base.py
│   │   └── mock_llm.py
│   │
│   ├── runtime/
│   │   ├── state.py
│   │   ├── orchestrator.py
│   │   └── synthesizer.py
│   │
│   └── main.py
└── README.md

4. 数据模型

我们先定义核心数据结构。


4.1 models.py

python 复制代码
from pydantic import BaseModel, Field
from typing import List, Dict, Any, Optional


class TaskRequest(BaseModel):
    user_id: str
    query: str
    session_id: Optional[str] = None
    metadata: Dict[str, Any] = Field(default_factory=dict)


class PlanStep(BaseModel):
    name: str
    kind: str  # tool | llm
    tool_name: Optional[str] = None
    instruction: Optional[str] = None
    args: Dict[str, Any] = Field(default_factory=dict)
    depends_on: List[str] = Field(default_factory=list)


class ExecutionPlan(BaseModel):
    goal: str
    steps: List[PlanStep]


class ToolResult(BaseModel):
    success: bool
    output: Any
    error: Optional[str] = None

4.2 state.py

python 复制代码
class AgentState:
    def __init__(self):
        self.values = {}

    def set(self, key, value):
        self.values[key] = value

    def get(self, key, default=None):
        return self.values.get(key, default)

    def to_dict(self):
        return self.values

5. Prompt 文件

这里直接给你一套中文版 prompt。


5.1 prompts_zh.py

python 复制代码
SYSTEM_ZH_V1 = """
你是一个运行在受控软件系统中的 AI 组件。

请严格遵守以下规则:
1. 只能使用当前提供的上下文、工具结果和状态信息。
2. 不要编造事实、工具结果、执行结果或外部信息。
3. 如果信息不足,请明确说明不确定性。
4. 严格遵守要求的输出格式。
5. 除非提示中明确要求,否则不要自行执行动作或假设动作已完成。
"""

PLANNER_ZH_V1 = """
你是一个任务规划模块。

请将用户请求转换为一个可执行的线性步骤计划。

要求:
1. 计划尽量控制在 3 到 5 步之间。
2. 每一步只能是 tool 或 llm。
3. 你只负责规划,不执行。
4. 只能使用可用工具列表中的工具。
5. 只返回合法 JSON。
6. 如果任务是研究/分析/报告类任务,优先采用:
   搜索 -> 提取关键信息 -> 生成报告
7. 不要创建重复搜索步骤。

可用工具列表:
{{tool_descriptions}}

输出格式:
{
  "goal": "字符串",
  "steps": [
    {
      "name": "步骤名称",
      "kind": "tool | llm",
      "tool_name": "工具名或 null",
      "instruction": "步骤说明或 null",
      "args": {},
      "depends_on": []
    }
  ]
}

用户请求:
{{user_input}}
"""

STEP_ANALYSIS_ZH_V1 = """
你是一个分析模块。

任务:
{{instruction}}

要求:
1. 只能使用提供的上下文。
2. 不要补充外部事实。
3. 输出简洁、结构化。
4. 区分"事实"和"分析判断"。
5. 如果证据不足,请明确说明不确定性。

上下文:
{{context}}
"""

REPORT_DRAFT_ZH_V1 = """
你是一个报告草稿生成模块。

请根据上下文,输出一份简洁专业的报告草稿。

结构要求:
1. 概要
2. 主要趋势
3. 关键玩家或产品动态
4. 不确定性 / 局限性

要求:
1. 只能使用上下文中的内容。
2. 不得编造事实。
3. 语言适合业务场景阅读。
4. 控制在 300~500 字左右。

上下文:
{{context}}
"""

FINAL_ANSWER_ZH_V1 = """
你是最终回答生成模块。

请基于执行状态,为用户生成最终答复。

要求:
1. 只能使用执行状态中的信息。
2. 不要编造事实。
3. 答案清晰、有条理。
4. 不暴露系统内部执行细节,除非用户明确要求。
5. 如果信息不足,请说明限制。

用户请求:
{{user_input}}

执行状态:
{{state}}
"""

FALLBACK_ZH_V1 = """
你是一个兜底回答生成模块。

当前系统未能完整完成任务。请基于已有信息给出尽可能有帮助的部分回答。

要求:
1. 说明当前限制。
2. 不要假装任务已完成。
3. 如合适,给出下一步建议。

用户请求:
{{user_input}}

当前可用状态:
{{state}}

执行问题:
{{issue}}
"""

6. Tool 定义

这个 Research Agent 最小只需要一个 web_search 工具。


6.1 base.py

python 复制代码
from abc import ABC, abstractmethod

class BaseTool(ABC):
    name: str
    description: str

    @abstractmethod
    async def run(self, args, state):
        ...

6.2 registry.py

python 复制代码
class ToolRegistry:
    def __init__(self):
        self._tools = {}

    def register(self, tool):
        self._tools[tool.name] = tool

    def get(self, name):
        if name not in self._tools:
            raise ValueError(f"工具不存在: {name}")
        return self._tools[name]

    def describe_all(self):
        lines = []
        for name, tool in self._tools.items():
            lines.append(f"- {name}: {tool.description}")
        return "\n".join(lines)

6.3 web_search.py

这里用 mock 数据模拟搜索结果,方便你理解流程。

python 复制代码
from app.tools.base import BaseTool

class WebSearchTool(BaseTool):
    name = "web_search"
    description = "用于搜索网页信息,适合市场、产品、公司、趋势相关研究"

    async def run(self, args, state):
        query = args["query"]

        # 这里是 mock 数据;真实项目中接搜索 API
        return {
            "query": query,
            "results": [
                {
                    "title": "Cursor 持续增长,AI coding 工具市场加速",
                    "snippet": "2024 年以来,AI coding 工具成为开发者效率产品热点,Cursor、GitHub Copilot 等持续增长。"
                },
                {
                    "title": "GitHub Copilot 企业化推进",
                    "snippet": "GitHub Copilot 在企业开发场景中持续扩张,强调团队协作和代码质量。"
                },
                {
                    "title": "AI coding 市场竞争加剧",
                    "snippet": "新进入者围绕 agentic coding、IDE integration、团队协作能力展开竞争。"
                },
                {
                    "title": "资本关注开发者 AI 工具",
                    "snippet": "2024~2025 年,开发者 AI 工具持续受到资本市场关注,重点在生产力提升与企业采购。"
                }
            ]
        }

7. LLM 接口

为了演示清晰,我们用一个 BaseLLMClient,再给一个 mock 实现。


7.1 base.py

python 复制代码
from abc import ABC, abstractmethod

class BaseLLMClient(ABC):
    @abstractmethod
    async def generate(self, prompt: str) -> str:
        ...

7.2 mock_llm.py

这个 mock 用于演示,不是真实模型行为。

python 复制代码
import json

class MockLLMClient:
    async def generate(self, prompt: str) -> str:
        if "任务规划模块" in prompt:
            plan = {
                "goal": "分析 2024~2025 年 AI 编码工具市场趋势并生成简短报告",
                "steps": [
                    {
                        "name": "search_market",
                        "kind": "tool",
                        "tool_name": "web_search",
                        "instruction": None,
                        "args": {"query": "2024 2025 AI 编码工具 市场趋势"},
                        "depends_on": []
                    },
                    {
                        "name": "extract_signals",
                        "kind": "llm",
                        "tool_name": None,
                        "instruction": "从搜索结果中提取市场趋势、主要玩家、融资和产品变化信号",
                        "args": {},
                        "depends_on": ["search_market"]
                    },
                    {
                        "name": "write_report_draft",
                        "kind": "llm",
                        "tool_name": None,
                        "instruction": "基于提取结果生成简短专业报告草稿",
                        "args": {},
                        "depends_on": ["extract_signals"]
                    }
                ]
            }
            return json.dumps(plan, ensure_ascii=False)

        if "分析模块" in prompt:
            return """
事实:
1. 2024~2025 年 AI 编码工具市场持续升温,开发者效率工具成为热点。
2. Cursor、GitHub Copilot 等是高频出现的代表性产品。
3. 市场竞争焦点集中在 IDE 集成、agentic coding、团队协作和企业化能力。
4. 资本市场持续关注开发者 AI 工具,企业采购潜力被反复提及。

分析判断:
1. 市场正在从"单点代码补全"向"更完整的开发工作流辅助"演进。
2. 企业场景将成为下一阶段的重要竞争点。
3. 新进入者仍有机会,但差异化将更多体现在产品体验和团队协作能力。

不确定性:
1. 当前信息主要来自搜索摘要,缺少一手财务和用户规模数据。
"""
        if "报告草稿生成模块" in prompt:
            return """
概要:
2024~2025 年,AI 编码工具市场保持快速发展,已从单纯代码补全逐步走向更完整的开发流程辅助。开发者效率提升与企业落地是市场的两大核心驱动力。

主要趋势:
一是头部产品持续强化 IDE 集成和开发工作流覆盖能力;二是市场竞争从基础能力比拼,逐渐转向 agentic coding、团队协作与企业级特性;三是企业场景的重要性明显上升,采购和部署潜力受到更多关注。

关键玩家或产品动态:
Cursor、GitHub Copilot 等是当前讨论度较高的代表产品。相关信息显示,这些产品都在强化面向实际开发流程的能力,而不仅仅局限于代码补全。

不确定性 / 局限性:
当前结论主要基于公开搜索结果摘要,缺少更系统的市场份额、收入和用户留存数据,因此更适合作为趋势性判断,而非严格定量结论。
"""
        if "最终回答生成模块" in prompt:
            return """
以下是对 2024~2025 年 AI 编码工具市场趋势的简要分析:

一、概要  
AI 编码工具市场在 2024~2025 年持续升温,正在从"代码补全工具"逐步演化为"覆盖更完整开发流程的生产力工具"。市场关注点已不仅是生成代码本身,而是如何提升开发效率、协作效率与企业可落地性。

二、主要趋势  
1. 市场从单点代码补全向更完整开发工作流辅助演进。  
2. IDE 集成、agentic coding、团队协作成为竞争重点。  
3. 企业级落地能力正在成为下一阶段的重要竞争因素。  
4. 资本市场持续关注开发者 AI 工具,说明该方向仍具增长预期。

三、关键玩家  
搜索结果中高频出现的代表产品包括 Cursor、GitHub Copilot 等。它们都在强化面向真实开发流程的能力,而不是仅提供基础补全功能。

四、局限性  
以上结论主要基于公开搜索摘要,缺少一手市场规模、收入、用户留存等定量数据,因此更适合作为趋势判断,而非严格定量结论。
"""
        return "无法处理该 prompt。"

8. Planner 实现


8.1 planner.py

python 复制代码
import json
from app.domain.models import ExecutionPlan
from app.prompts.prompts_zh import PLANNER_ZH_V1

def render_prompt(template: str, **kwargs) -> str:
    prompt = template
    for k, v in kwargs.items():
        prompt = prompt.replace("{{" + k + "}}", str(v))
    return prompt


class ResearchPlanner:
    def __init__(self, llm_client, tool_registry):
        self.llm = llm_client
        self.tool_registry = tool_registry

    async def create_plan(self, task_request):
        prompt = render_prompt(
            PLANNER_ZH_V1,
            user_input=task_request.query,
            tool_descriptions=self.tool_registry.describe_all()
        )
        raw = await self.llm.generate(prompt)
        plan_dict = json.loads(raw)
        return ExecutionPlan(**plan_dict)

9. Executor 实现

Sequential Executor 会逐步执行:

  • tool step
  • llm step
  • 更新 state

9.1 step_prompt_builder.py

python 复制代码
from app.prompts.prompts_zh import STEP_ANALYSIS_ZH_V1, REPORT_DRAFT_ZH_V1

def render_prompt(template: str, **kwargs) -> str:
    prompt = template
    for k, v in kwargs.items():
        prompt = prompt.replace("{{" + k + "}}", str(v))
    return prompt


def build_llm_step_prompt(step, state):
    if step.name == "extract_signals":
        return render_prompt(
            STEP_ANALYSIS_ZH_V1,
            instruction=step.instruction,
            context=state.get("search_market")
        )

    if step.name == "write_report_draft":
        return render_prompt(
            REPORT_DRAFT_ZH_V1,
            context=state.get("extract_signals")
        )

    return render_prompt(
        STEP_ANALYSIS_ZH_V1,
        instruction=step.instruction or "请完成该步骤",
        context=state.to_dict()
    )

9.2 sequential_executor.py

python 复制代码
from app.runtime.state import AgentState
from app.executor.step_prompt_builder import build_llm_step_prompt

class SequentialExecutor:
    def __init__(self, tool_registry, llm_client):
        self.tool_registry = tool_registry
        self.llm = llm_client

    async def execute(self, plan, state: AgentState):
        for step in plan.steps:
            if step.kind == "tool":
                tool = self.tool_registry.get(step.tool_name)
                result = await tool.run(step.args, state)
                state.set(step.name, result)

            elif step.kind == "llm":
                prompt = build_llm_step_prompt(step, state)
                result = await self.llm.generate(prompt)
                state.set(step.name, result)

            else:
                raise ValueError(f"不支持的 step.kind: {step.kind}")

        return state

10. Final Synthesizer


10.1 synthesizer.py

python 复制代码
from app.prompts.prompts_zh import FINAL_ANSWER_ZH_V1

def render_prompt(template: str, **kwargs) -> str:
    prompt = template
    for k, v in kwargs.items():
        prompt = prompt.replace("{{" + k + "}}", str(v))
    return prompt


class FinalSynthesizer:
    def __init__(self, llm_client):
        self.llm = llm_client

    async def generate(self, task_request, state):
        prompt = render_prompt(
            FINAL_ANSWER_ZH_V1,
            user_input=task_request.query,
            state=state.to_dict()
        )
        return await self.llm.generate(prompt)

11. Orchestrator 串联


11.1 orchestrator.py

python 复制代码
from app.runtime.state import AgentState

class ResearchAgentOrchestrator:
    def __init__(self, planner, executor, synthesizer):
        self.planner = planner
        self.executor = executor
        self.synthesizer = synthesizer

    async def run(self, task_request):
        # 1. 规划
        plan = await self.planner.create_plan(task_request)

        # 2. 初始化状态
        state = AgentState()

        # 3. 执行
        state = await self.executor.execute(plan, state)

        # 4. 汇总最终回答
        final_answer = await self.synthesizer.generate(task_request, state)

        return {
            "plan": plan.model_dump(),
            "state": state.to_dict(),
            "final_answer": final_answer
        }

12. 主程序入口


12.1 main.py

python 复制代码
import asyncio
from app.domain.models import TaskRequest
from app.tools.registry import ToolRegistry
from app.tools.web_search import WebSearchTool
from app.llm.mock_llm import MockLLMClient
from app.planner.planner import ResearchPlanner
from app.executor.sequential_executor import SequentialExecutor
from app.runtime.synthesizer import FinalSynthesizer
from app.runtime.orchestrator import ResearchAgentOrchestrator


async def main():
    # 1. 初始化工具
    tool_registry = ToolRegistry()
    tool_registry.register(WebSearchTool())

    # 2. 初始化 LLM
    llm = MockLLMClient()

    # 3. 初始化核心组件
    planner = ResearchPlanner(llm, tool_registry)
    executor = SequentialExecutor(tool_registry, llm)
    synthesizer = FinalSynthesizer(llm)

    orchestrator = ResearchAgentOrchestrator(
        planner=planner,
        executor=executor,
        synthesizer=synthesizer
    )

    # 4. 构造请求
    request = TaskRequest(
        user_id="u_001",
        query="请分析 2024~2025 年 AI 编码工具市场趋势,并输出一份简短报告"
    )

    # 5. 执行
    result = await orchestrator.run(request)

    print("=== PLAN ===")
    print(result["plan"])

    print("\n=== STATE ===")
    print(result["state"])

    print("\n=== FINAL ANSWER ===")
    print(result["final_answer"])


if __name__ == "__main__":
    asyncio.run(main())

13. 一次完整执行流程拆解

现在我们把整个执行过程串起来看一遍。


Step 1:用户输入

text 复制代码
请分析 2024~2025 年 AI 编码工具市场趋势,并输出一份简短报告

Step 2:Planner 看到 Prompt

Planner 收到的是:

  • 用户请求
  • 可用工具描述
  • 规划约束

模型输出:

json 复制代码
{
  "goal": "分析 2024~2025 年 AI 编码工具市场趋势并生成简短报告",
  "steps": [
    {
      "name": "search_market",
      "kind": "tool",
      "tool_name": "web_search",
      "args": {
        "query": "2024 2025 AI 编码工具 市场趋势"
      },
      "depends_on": []
    },
    {
      "name": "extract_signals",
      "kind": "llm",
      "instruction": "从搜索结果中提取市场趋势、主要玩家、融资和产品变化信号",
      "depends_on": ["search_market"]
    },
    {
      "name": "write_report_draft",
      "kind": "llm",
      "instruction": "基于提取结果生成简短专业报告草稿",
      "depends_on": ["extract_signals"]
    }
  ]
}

Step 3:Executor 执行 search_market

调用 web_search.run(...)

得到:

python 复制代码
{
  "query": "2024 2025 AI 编码工具 市场趋势",
  "results": [
    ...
  ]
}

写入 state:

python 复制代码
state["search_market"] = {...}

Step 4:Executor 执行 extract_signals

构造 Prompt:

  • instruction = 从搜索结果中提取市场趋势、主要玩家、融资和产品变化信号
  • context = state["search_market"]

LLM 输出:

text 复制代码
事实:
1. ...
分析判断:
1. ...
不确定性:
1. ...

写入 state:

python 复制代码
state["extract_signals"] = "..."

Step 5:Executor 执行 write_report_draft

构造 Prompt:

  • context = state["extract_signals"]

LLM 输出报告草稿。

写入 state:

python 复制代码
state["write_report_draft"] = "..."

Step 6:Final Synthesizer

输入:

  • user_input
  • 整个 state

生成最终面向用户的输出。

注意这里的好处:

  • 规划与最终回答分离
  • 中间 state 可追踪
  • 每步都可调试

14. 这个示例体现了什么设计思想

这个 Research Agent 其实正好体现了前面所有关键结论。


14.1 它不是 L2 loop agent

它没有:

python 复制代码
while not done:
    think
    act
    observe

所以它避免了:

  • 无限循环
  • 步数不稳定
  • 重复搜索
  • 不可预测成本

14.2 它是 L3:Planner + Executor

控制权在系统,而不是 LLM 自己无限循环。

  • LLM 负责规划
  • 程序负责执行
  • Tool 负责能力
  • State 负责上下文传递

这就是现代生产系统更偏爱的形态。


14.3 Prompt 是模块化的

这里至少用了 4 类 Prompt:

  • PLANNER_ZH_V1
  • STEP_ANALYSIS_ZH_V1
  • REPORT_DRAFT_ZH_V1
  • FINAL_ANSWER_ZH_V1

每个 Prompt 只负责一件事。


15. 如何升级到 L4

如果未来你想升级这个 Research Agent 到 L4,可以这样做。


15.1 当前 L3 的计划

text 复制代码
search_market
   ↓
extract_signals
   ↓
write_report_draft

15.2 升级成 L4 的思路

如果你发现要同时搜索多个维度,比如:

  • 搜趋势
  • 搜代表产品
  • 搜融资信息
  • 搜企业落地案例

就可以改成 graph:

text 复制代码
search_trends ─────┐
search_products ───┼──> extract_signals ──> write_report
search_funding ────┤
search_enterprise ─┘

这样:

  • 多个搜索节点可并行
  • 节点级重试更方便
  • 节点级缓存更容易

15.3 代码层怎么预留

你现在就已经预留了 depends_on 字段,所以升级很自然:

  • SequentialExecutor 替换成 GraphExecutor
  • planner 改成 GraphPlanner
  • state 和 tool 基本可复用

16. 再给你一个更接近生产的增强建议

如果你要把这个示例变成真实可用版本,建议加上下面这些模块。


16.1 加 Plan Validator

校验:

  • JSON 是否合法
  • 是否用了未注册工具
  • step 数是否超限
  • depends_on 是否合理

16.2 加 Fallback

如果某一步失败:

  • 返回部分结果
  • 不直接报错给用户

16.3 加 Tracing

记录:

  • request_id
  • plan
  • step latency
  • token usage
  • final answer

16.4 加 Cache

特别是:

  • 搜索结果缓存
  • 分析节点缓存

16.5 加 Critic

在最终输出前做一次轻量审查:

  • 是否有 unsupported claims
  • 是否遗漏明显事实
  • 是否太长

17. 最终你可以怎样理解这套示例

你可以把这个 Research Agent 理解成:

  • LLM 不是万能自治体
  • LLM 是一个会规划、会分析、会写报告的组件
  • 系统负责把这些能力组织成稳定 workflow

所以它不是:

"让一个大模型自由发挥"

而是:

"让大模型在受控框架下分工完成研究任务"

这就是今天大多数成熟 Agent 系统真正的工程方向。


相关推荐
optimistic_chen2 小时前
【AI Agent 全栈开发】提示词技巧(prompt)
java·人工智能·ai·prompt·agent
世rui睿2 小时前
RAG 知识库质检:三级 Gate 如何拦截垃圾知识
人工智能·agent
Trouvaille ~3 小时前
零基础入门 LangChain 与 LangGraph(九):LangGraph 收官——运行时上下文、流式输出、子图、与项目结构
数据库·langchain·agent·streaming·langgraph·ai应用开发·运行上下文
火车叼位3 小时前
planning-with-files 完全指南:从入门到高阶,规范你的 AI 编程智能体
agent·vibecoding
新知图书3 小时前
LangGraph 基础图创建思路
人工智能·agent·智能体·langgraph·langchian
山顶夕景3 小时前
【Agent】Openclaw架构(Gateway|subagent|工具过滤|Sandbox)
大模型·llm·agent·智能体·openclaw
深海鱼在掘金14 小时前
深入浅出 LangChain — 第一章:AI Agent 开发导论
typescript·langchain·agent
深海鱼在掘金14 小时前
深入浅出 LangChain — 导读
typescript·langchain·agent
潘锦17 小时前
深度拆解 Claude Code 系统提示词中的记忆管理逻辑
agent·claude