基于 ReAct 模式的工具调用 Agent 框架:多步推理、插件化工具、RAG 知识库、全链路 tracing、流式输出、MCP 工具生态与 Plan-and-Execute 规划模式。
上面这段是真实录屏,未剪辑、未加速。输入任务后 Agent 逐步完成 「列目录 → 读取 CSV → 转 Markdown 写入 → 读回校验」,每一步的思考过程、 工具入参、工具输出与耗时都实时呈现在页面上。
更高清的完整版:下载 demo.mp4 (2.1 MB,1440×900,H.264,37 秒)。 演示视频随时可重录:先
make serve起服务,再make video录制。
一行命令打开这个界面:
python main.py serve # 浏览器自动打开 http://localhost:8000| 维度 | 说明 |
|---|---|
| ReAct 推理循环 | Thought → Action → Observation,迭代直到任务完成 |
| 并发工具执行 | 同一轮内多个 tool_calls 并发执行(受 max_parallel_tools 限流),保序返回 |
| 插件化工具系统 | 注册机制灵活扩展;内置文件 / Python 沙箱 / 搜索 / 数据分析 |
| MCP 工具生态 | 以子进程挂载外部 MCP Server 的工具,运行时注入能力 |
| RAG 知识库 | 混合检索(BM25 + 向量 → RRF)+ 连坐召回 + Rerank 精排 |
| 上下文管理 | Token 感知 + 自动摘要压缩,保留原始任务,避免 Agent 中途「失忆」 |
| 全链路 tracing | 每次执行生成 trace / span,可导出 JSON 回放,定位「跑偏」 |
| 真流式 | HTTP 接口边执行边推送 step / token 事件(SSE),非跑完补发 |
| 安全防护 | 代码沙箱 AST 静态分析拦截危险调用;文件工具工作区隔离;df.query 表达式白名单 |
| 多 LLM 适配 | 兼容任何 OpenAI Chat Completions 接口(DeepSeek / Qwen / MiniMax / Ollama) |
flowchart TD
User[User Task] --> Core[Agent Core: ReAct Loop]
Core -->|无工具调用| Final[Final Answer]
Core -->|tool_calls| Exec[并发工具执行]
Exec -->|file_*| WS[Workspace 隔离]
Exec -->|python_exec| Sandbox[AST 沙箱 + 子进程]
Exec -->|mcp_*| MCP[MCP Server 子进程]
Exec -->|knowledge_search| RAG[RAG 链路]
RAG --> Hybrid[BM25 + 向量 → RRF]
RAG --> Parent[连坐召回]
RAG --> Rerank[Cross-Encoder 精排]
Core --> LLM[LLM Client<br/>兼容 OpenAI]
Core --> Tracer[Trace / Span]
Plan[Planner] -.可选: 先规划再执行.-> Core
# 1. 安装依赖
pip install -r requirements.txt
# 2. 配置 API Key(三选一)
export LLM_API_KEY="your-key-here" # 方式 A:环境变量
# 或复制 .env.example 为 .env 填写 # 方式 B:.env 文件
# 或直接改 config.yaml 的 llm.api_key # 方式 C:配置文件
# 3. CLI 单任务
python main.py -t "分析 workspace/sales_data.csv 并生成销售报表"
# 4. CLI 交互
python main.py
# 5. Web UI(推荐,浏览器里直接提问)
python main.py serve # 打开 http://localhost:8000
# 6. API 服务(只要后端,不要前端)
uvicorn agent.server:app --host 0.0.0.0 --port 8000
# 7. Docker 一键编排
docker compose up -dpython main.py serve 会同时起 API 和前端,浏览器打开 http://localhost:8000 即可提问。
所见即所得地看到 Agent 的完整思考链:
| 区域 | 内容 |
|---|---|
| 步骤卡片 | 每一步的思考过程、工具名 + 入参(JSON 格式化)、工具输出、耗时 |
| 流式答案 | 最终回答边生成边渲染(Markdown + 代码高亮 + 表格),带光标 |
| 统计条 | 状态 / 步数 / tokens / 耗时 / trace_id |
| 侧栏 | 当前挂载的工具清单、模型与运行配置 |
| 轨迹抽屉 | 最近 20 条执行记录回放(耗时、span 数、错误数) |
前端在 web/ 目录,纯原生 HTML/CSS/JS,零依赖零构建——不需要 npm、不需要打包,
改完文件刷新页面即可生效。
两个实现上的点值得留意:
- SSE 走 POST:浏览器原生
EventSource只支持 GET,而任务描述可能很长, 不适合塞进 query string。所以前端用fetch+ReadableStream手工解析 SSE 帧, 并按空行切包、处理被拆到两次read()之间的半个事件。 - Markdown 自绘:内容来自 LLM 输出,必须先转义再渲染,
否则模型输出里的
<img onerror=...>会直接在页面上执行。
若只部署后端(如容器里不放 web/),服务会自动降级为纯 API 模式,不影响任何接口。
============================================================
ToolAgent v0.2.0
============================================================
🎯 Task: 分析 sales_data.csv 并生成报表
📦 Tools: ['file_read', 'file_write', 'file_list', 'python_exec', 'web_search', 'data_analysis']
📏 Max steps: 20
──────────────────────────────────────────────────
💭 Step 1/20
🔧 Tool: data_analysis
Args: {"file_path": "sales_data.csv", "operation": "summary"}
✓ (0.31s): Shape: 1000 rows x 6 columns
💭 Step 2/20
🔧 Tool: python_exec
✓ (1.02s): 报表已生成 -> report.md
✅ Task completed in 3.4s (4 steps)
📊 [4 steps, 1820 tokens, 3.4s, trace=9f3a...]
| 模式 | 触发 | 适用场景 |
|---|---|---|
| ReAct(默认) | planner.enabled: false |
探索性强、下一步依赖上一步结果的任务 |
| Plan-and-Execute | planner.enabled: true |
步骤间有依赖、需先验拆解的多步任务;受阻时自动重规划 |
# config.yaml
planner:
enabled: true
max_plan_steps: 8
max_revisions: 2 # 最多重规划次数MCP(Model Context Protocol)让 Agent 不必把工具写死在代码里——社区已有的 filesystem / git / postgres / browser 等 Server 可直接挂载:
# config.yaml
mcp:
enabled: true
tool_prefix: "mcp"
servers:
- name: filesystem
enabled: true
command: ["npx", "-y", "@modelcontextprotocol/server-filesystem"]
args: ["./workspace"]单个 Server 启动失败或握手超时只记 warning 并跳过,不影响内置工具与其他 Server。
内置生产级 RAG 检索链路,支持文档导入、混合检索和智能问答。
文档 → 清洗 → 元数据增强 → 自适应分块 → 双路索引
│
查询 → Query Rewrite → 混合检索 → 连坐召回 → Rerank → 上下文
│
BM25 + 向量 → RRF 融合
| 设计点 | 解决什么问题 |
|---|---|
| 自适应分块 | 短文档整篇入库保语义;长文档递归切分,切片带标题/摘要/关键词前缀 |
| 混合检索 | 向量懂语义、BM25 懂关键词,RRF 融合免调权 |
| 连坐召回 | 摘要块语义太强霸榜导致正文被截断 → 命中即拉回整篇文档 |
| Rerank 精排 | Cross-Encoder 重排。粗排候选 Top-50→Top-20,重排耗时 12.6s→2.8s,召回率保持 99%+ |
| Query Rewrite | 多轮对话把指代词改写成自包含问题,提升检索准确率 |
python scripts/eval_rag.py \
--knowledge-dir ./knowledge \
--evalset ./scripts/evalset_example.jsonl \
--top-k 5| 检索模式 | Recall@5 |
|---|---|
| hybrid (no rerank) | 0.83 |
| hybrid + rerank | 1.00 |
数值随知识库与评测集变化;脚本内置
knowledge/docs/*+evalset_example.jsonl,clone 后开箱即跑。
每一步都留下可回放轨迹。CLI 运行后导出:
python main.py -t "..." --trace-out trace.jsonAPI 侧可回放历史轨迹:
curl http://localhost:8000/api/v1/traces
curl http://localhost:8000/api/v1/traces/<trace_id>trace 内含每个 span 的耗时、状态、工具名与错误信息——定位「Agent 为什么调错工具 / 在哪一步发散」时比看最终答案高效得多。
- 代码沙箱:基于 AST 静态分析 拦截危险导入 / 调用 / 内省逃逸
(
os.system、__import__('os')、双下划线逃逸均覆盖),再叠加子进程隔离 + 超时强杀 + 输出截断 + 步数限制。 - 工作区隔离:文件 / 数据分析工具强制路径约束,
../与绝对路径越权一律拒绝。 - 表达式白名单:
data_analysis的filter表达式经标识符白名单校验后再下推df.query, 禁止@(局部变量引用)与双下划线,规避 pandas query 的表达式执行风险。
| Method | Path | 说明 |
|---|---|---|
| POST | /api/v1/tasks |
同步执行任务 |
| POST | /api/v1/tasks/stream |
SSE 流式(start / token / step / done / error / : ping) |
| GET | /api/v1/tools |
列出可用工具(含 MCP 挂载的) |
| GET | /api/v1/traces |
最近执行轨迹列表 |
| GET | /api/v1/traces/{trace_id} |
单条轨迹完整 span |
| GET | /api/v1/health |
健康检查(含组件状态) |
SSE 事件类型:start(含 trace_id)、token(LLM 文本增量)、
step(每步工具名与观测摘要)、done(最终结果与统计)、error、heartbeat。
make test # 全量测试(150 passed)
make test-cov # 带覆盖率
make lint # ruff + mypy
make eval # RAG 检索 Recall@K 评测(CI 中作为端到端冒烟)- 工具函数用纯单测覆盖(无 LLM 依赖)
- 文件 / 数据分析工具用
tmp_path沙盒验证工作区隔离 - SSE 测试直接驱动 Agent + 队列,验证事件在
run()结束前就产生(真流式而非补发) - 上下文压缩测试验证「原始任务被保留、不留孤儿 tool 消息」
- 思维链剥离用逐字符喂入的极端切分打边界(标签被切断时最容易漏)
- 前端用
TestClient真的取一次页面,并锁住「静态挂载必须排在 API 路由之后」的顺序 - 故障隔离测试:工具抛
SystemExit时 Agent 继续跑完,CancelledError不被吞
tool-agent/
├── agent/ # Agent 核心
│ ├── core.py # ReAct 循环 + 并发工具执行 + 上下文压缩 + tracing
│ ├── llm.py # LLM 客户端(含 streaming)
│ ├── schema.py # 数据模型
│ ├── prompts.py # Prompt 模板
│ ├── config.py # 配置(含 .env 加载)
│ ├── factory.py # 装配工厂(CLI 与服务共用工具注册)
│ ├── planner.py # Plan-and-Execute 规划器
│ ├── tracing.py # Trace / Span / 结构化日志
│ └── server.py # FastAPI(每请求独立 Agent + SSE 流式)
├── rag/ # RAG 知识库
│ ├── pipeline.py # 清洗 + 自适应分块 + 元数据增强
│ ├── retriever.py # 混合检索 (BM25 + 向量 → RRF) + 连坐召回
│ ├── reranker.py # Rerank 精排 + Query Rewrite + Recall@K 评测
│ └── engine.py # 知识库引擎 + Agent 工具封装
├── tools/ # 工具系统
│ ├── base.py # 工具基类 + 注册表 + 工作区隔离基类
│ ├── file_ops.py # 文件操作(工作区隔离)
│ ├── code_exec.py # Python 沙箱(AST 静态分析)
│ ├── search.py # 网页搜索
│ ├── data_analysis.py # 数据分析(工作区隔离 + 表达式白名单)
│ └── mcp.py # MCP 工具适配器
├── web/ # 前端(零依赖零构建)
│ ├── index.html # 页面结构
│ ├── styles.css # 主题变量 + 组件样式
│ └── app.js # SSE 解析 + Markdown 渲染 + 交互
├── examples/ # 示例(离线演示 / 报表 / 调研)
├── scripts/ # 评测脚本 + 示例评测集
├── knowledge/ # 知识库文档(含示例 docs/)
├── tests/ # 测试套件
├── config.yaml # 配置
├── Dockerfile # 多阶段 + 非 root + 健康检查
├── docker-compose.yml # 一键编排
├── Makefile # 常用命令
└── pyproject.toml # ruff / mypy / pytest 配置
对这个项目想做更深入沉淀(30 秒讲清项目、高频追问预演、潜在质疑回应), 见 INTERVIEW.md;关键架构决策记录见 docs/ADR。
- 工具调用 Human-in-the-loop 确认
- 长期记忆(跨会话的对话摘要沉淀)
- 可视化 trace 面板(前端回放 span 时间线)
- RAG 接入向量数据库(Milvus / Qdrant)替换内存索引
- 多 Agent 协作(主管-Worker 分工)
