多 Agent 调研助手实战:Supervisor-Worker 模式 + Tavily + FastAPI

4 阅读3分钟

多 Agent 调研助手实战:Supervisor-Worker 模式 + Tavily + FastAPI

用 LangChain 的 Supervisor-Worker 模式,构建一个能自动拆解问题、搜索、分析、生成报告的多 Agent 系统。


一、为什么做这个项目

面试 AI Agent 开发岗时,有 3 类高频问题:

  1. "多 Agent 怎么协作?" — 多个 Agent 之间怎么通信、分工、汇总
  2. "你做过真实 API 集成吗?" — 不只是模拟数据
  3. "你的项目有部署经验吗?" — 能通过 HTTP 调用吗

这个项目一次覆盖了这 3 个问题。


二、架构:Supervisor-Worker 模式

用户输入调研主题
       │
       ▼
  Supervisor(主管 Agent)
  ┌─────────────────────────┐
  │  create_plan(topic)     │  ← LLM 将主题拆成多个子问题
  │  输出: ResearchPlan     │
  └──────────┬──────────────┘
             │
     ┌───────┼───────┐
     ▼       ▼       ▼
  Worker 1  Worker 2  Worker 3 ...
  (搜索)    (搜索)    (搜索)
     │       │       │
     ▼       ▼       ▼
  Analyzer  Analyzer Analyzer
  (分析)    (分析)    (分析)
     │       │       │
     └───────┼───────┘
             ▼
        Writer(撰写 Agent)
             │
             ▼
      Markdown 调研报告

关键设计

每个 Worker 只做一件事:

Agent职责技术
Supervisor拆解问题model.with_structured_output() → ResearchPlan
Searcher搜索资料Tavily Search API(真实数据)
Analyzer提炼关键信息LLM → Finding(结构化输出)
Writer生成报告LLM → Markdown

三、结构化数据流

核心是用了 Pydantic 定义数据结构,让 LLM 的输出可预测、可验证:

class ResearchPlan(BaseModel):
    topic: str
    background: str
    questions: List[ResearchQuestion]

class Finding(BaseModel):
    question: str
    summary: str
    details: str
    sources: List[str]

每个 Agent 的输入和输出都是结构化的 Pydantic 对象,而不是自由文本。这就避免了"LLM 乱说话"的问题——格式错了就会抛出 ValidationError,不会静默地生成垃圾数据。


四、Supervisor 是怎么拆问题的

核心代码只有几行:

def create_plan(topic: str) -> ResearchPlan:
    prompt = f"针对「{topic}」拆解出 3-5 个子问题..."
    response = model.invoke([HumanMessage(content=prompt)])
    data = json.loads(clean_json(response.content))
    return ResearchPlan(**data)  # ← Pydantic 自动校验

难点在于 LLM 返回的 JSON 可能有格式问题(markdown 代码块、缺少外层括号等),需要做一层清理

# 去掉 ```json 标记
if raw.startswith("```"):
    raw = raw.split("\n", 1)[1]
if raw.endswith("```"):
    raw = raw.rsplit("\n", 1)[0]

# 补全缺少的 {}
if not raw.startswith("{"):
    raw = "{" + raw
if not raw.endswith("}"):
    raw = raw + "}"

这就是生产环境的现实——LLM 输出永远不会完全规则,必须有一层防御性解析。


五、真实搜索:Tavily API

用 Tavily(专为 AI Agent 设计的搜索 API)代替模拟数据:

from tavily import TavilyClient

client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])
results = client.search(query=keyword, max_results=5)

为什么选 Tavily 而不是普通搜索 API?

  • Tavily 返回的是已经处理过的内容(摘要、标题、URL),而不是原始 HTML
  • 专为 LLM 消费设计,结构干净
  • 免费额度 1000 次/月,够学习和演示

六、FastAPI 部署

用了 50 行代码加了一个 Web 接口:

@app.post("/research")
async def do_research(req: ResearchRequest) -> ResearchResponse:
    plan = create_plan(req.topic)
    findings = [analyze(q, search(q)) for q in plan.questions]
    report = write_report(plan.topic, findings)
    return ResearchResponse(report=report)

启动:

uvicorn app:app --reload --port 8000

调用:

curl -X POST http://localhost:8000/research \
  -H "Content-Type: application/json" \
  -d '{"topic": "AI Agent 开发工程师 广州 2025"}'

返回的就是一份完整的 Markdown 调研报告。


七、总结

这个项目的核心模式可以概括为:用结构化数据流驱动多 Agent 协作。每一步的输入输出都是 Pydantic 对象,create_plan 产出 ResearchPlan,Searcher 产出原始文本,Analyzer 产出 Finding,Writer 产出 Markdown。流程清晰、可追踪、容易 debug。

适用场景:

  • 市场调研
  • 竞品分析
  • 技术方案调研
  • 求职市场分析

可以改进的方向:

  1. 用 Tavily 的分页参数获取更多结果
  2. 给每个 Worker 加独立的状态跟踪(有没有搜完、有没有失败)
  3. 加缓存层,相同主题不重复请求 API

八、项目链接

GitHub: github.com/cuzz123/res…

配套项目:


这是第三个实战项目。前两个覆盖了单 Agent + MCP,这个覆盖了多 Agent 协作 + 真实 API + 部署。三个项目组合起来覆盖了 Agent 开发的主要技术栈。接下来还会做第四个(MCP API Gateway),关注后续更新。