本周工作总结

项目概述

Deep Research:输入一个问题,4 个 Agent 并行搜索 → 3 轮对抗验证 → 合成 final report。


一、本周主要工作

  1. 去除 CrewAI 依赖:自研轻量 multi-agent 框架(core/)
  2. 加固系统鲁棒性:JSON 解析、Pydantic 构造、ReAct 检测全面升级
  3. 容器化 + Web API:Docker 镜像 + FastAPI 服务
  4. 云部署上线:腾讯云运行,公网可访问

二、为什么要替换 CrewAI

问题 1: result.raw 行为不稳定

CrewAI 的 crew.kickoff_async() 返回的 result.raw 在不同场景下行为不一致:

  • 同一个任务调用不同模型,.raw 有时返回 JSON,有时返回空
  • Qwen 在 thinking-mode 下把输出放在 reasoning_content 而不是 content.raw 拿到的是空字符串
  • 模型正常输出了完整 JSON,但 CrewAI 内部多层转换后 .raw 丢失了内容

结果:同样的代码跑同一个问题,有时能解析成功有时不能。上一个运行的日志里,technical 研究员的 raw_transcript 里明明有完整的 JSON:

{"perspective": "technical", "research_question": "...", "key_findings": [...]}

parse_json_response(raw) 失败——说明 .raw 没把这个 JSON 传回来。

问题 2: Qwen thinking-mode 空 content

Qwen 模型在推理模式下不填充 content 字段:

choice.message.content = None
choice.message.reasoning_content = "完整的 JSON 响应"

CrewAI 读 content 拿到 None,认为 Agent 没输出。只能通过 monkey-patch litellm.completion 修复——如果 LiteLLM 内部 API 改名,patch 就静默失效。

问题 3: 样板代码臃肿

每次让 Agent 执行任务需要:

agent = create_researcher(perspective)              # 创建 Agent
task = Task(description=..., agent=agent)           # 创建 Task
crew = Crew(agents=[agent], tasks=[task], ...)      # 创建 Crew
result = await crew.kickoff_async()                 # 启动
raw = result.raw                                     # 取值

5 步。自研后:

raw = await agent.run(task_description)
问题 4: Agent 间数据共享靠 SQLite

Phase 1 产出的 ResearchCards 要传给 Phase 2 和 Phase 3,只能通过 SQLite:

Phase 1 → 写 SQLite → Phase 2 → 读 SQLite → Phase 3 → 读 SQLite

每次读写都是 Pydantic → JSON string → SQLite → JSON string → Pydantic 的序列化往返,还引入了 database is locked 的并发风险。

自研后加了 Blackboard(内存共享存储),Agent 间数据传递零延迟。

替换前后对比
CrewAI 版 自研版
Agent 调用 5 行样板 1 行
raw 可靠性 不可预测 直接返回 agent.run() 结果
工具系统 BaseTool._run() BuildTool 4 阶段管线(验证→权限→执行→格式化)
Agent 间通信 SQLite Blackboard 内存共享
重试 每次重建 Agent+Task+Crew 复用 Agent 实例
依赖包数 30+ 6

三、鲁棒性加固

问题 1: JSON 解析过于脆弱

原因:原始代码的 JSON 提取逻辑只处理 ```json ... ``` 一种 fence 格式,且分散在 3 个文件里各自实现。模型稍微换个输出方式(前缀加 **、不用 fence 直接 {...}、中文格式),系统就崩。

修复:

  • 4 级多策略解析器:直接 JSON → fence 提取(支持 ``` / ~~~ / ''')→ 括号配对正则提取 → 常见错误自动修复(trailing commas、Python True/None 等)
  • 统一为 response_parser.py:3 个文件的重复实现全部删除,单一事实来源
问题 2: Pydantic 构造零容错

原因:模型输出被直接解包为 Pydantic 模型——VerificationEntry(**model_output)。模型多加了一个字段、enum 值大小写不匹配("HIGH" vs "high"),直接 ValidationError

修复:safe_construct 安全构造层——过滤模型多出的字段、enum 值大小写归一化、ValidationError 时尝试部分字段构造(model_construct),单个元素失败不影响整批。

问题 3: ReAct 检测只匹配英文

原因:_is_valid_output 只检查 "Thought:""Action:"。中文模型输出"思考:...行动:...",完全检测不到。

修复:多语言 ReAct 模式匹配(中/日/英 + Step N: + Plan: 变体),同时改为不直接拒绝——如果文本里同时有 {},先提取 JSON 再决定。


四、容器化 + Web API + 云部署

架构
浏览器 / curl → http://118.25.102.38:8000/docs → FastAPI → DeepSeek API

7 个 API 端点:

POST   /research              → 提交问题,立即返回 run_id
GET    /research/{id}         → 轮询状态
GET    /research/{id}/report  → 下载报告
GET    /research/{id}/evidence → 证据 JSON
GET    /research              → 历史列表
GET    /health                → 健康检查
云部署中遇到的问题和解决
问题 原因 解决
pip install 超时(90 秒+) PyPI 官方源 files.pythonhosted.org 从国内服务器访问极慢 Dockerfile 加 PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
hatchling 构建失败 pip 24.0 镜像解析 + 构建系统下载超时 跳过 pip install -e .,改用 PYTHONPATH=/app/src 直接导入
database is locked 本地的 data/ 目录通过 scp 上传后,带有本地残留的 SQLite 锁文件 删掉 data 目录让 Docker 重建,chmod 777 确保容器用户可写
UNIQUE constraint failed Web API 和 pipeline.run_research() 各自创建了一次 Run 记录,两者 timestamp 相同导致重复 ID pipeline.pyrun_id 参数,API 预先创建后传入,pipeline 跳过重复创建
No module named 'orjson' openai 包的内置 JSON 序列化依赖 orjson,Dockerfile 没显式安装 Dockerfile 补上 openaiorjson
Compressor 上下文集压缩失败 Compressor._summarise() 硬编码 model="openai/deepseek-chat" 但没传 api_key Compressor 和 Agent 共享 LLM 配置,自动注入 api_baseapi_key

五、最终指标

指标 数值
代码文件 30 个
测试 35 个,全部通过
外部依赖 0 个重量框架
一次研究耗时 3-5 分钟
服务器 腾讯云 2 核 2GB
Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐