内容
# 多智能体系统
<p align="center">
<img src="https://github.com/SeydinaBANE/multi-agent-system/actions/workflows/ci.yml/badge.svg" />
<img src="https://img.shields.io/badge/Python-3.11%2B-3776AB?logo=python&logoColor=white" />
<img src="https://img.shields.io/badge/Next.js-14-000000?logo=nextdotjs&logoColor=white" />
<img src="https://img.shields.io/badge/FastAPI-0.111-009688?logo=fastapi&logoColor=white" />
<img src="https://img.shields.io/badge/LangGraph-0.2-FF6B35" />
<img src="https://img.shields.io/badge/MCP-Brave%20Search-orange?logo=brave&logoColor=white" />
<img src="https://img.shields.io/badge/Docker-Compose-2496ED?logo=docker&logoColor=white" />
<img src="https://img.shields.io/badge/Railway-Backend-0B0D0E?logo=railway&logoColor=white" />
<img src="https://img.shields.io/badge/Vercel-Frontend-000000?logo=vercel&logoColor=white" />
<img src="https://img.shields.io/badge/Qdrant-Cloud-DC244C?logo=qdrant&logoColor=white" />
<img src="https://img.shields.io/badge/tests-78%20passed-4CAF50?logo=pytest&logoColor=white" />
<img src="https://img.shields.io/badge/license-MIT-blue" />
</p>
<p align="center">
由 <strong>LangGraph</strong> 协调的多智能体系统,具有自动路由<br/>
<strong>直接聊天</strong> 或 <strong>完整流水线</strong>,多源 RAG 检索,<br/>
可扩展的 <strong>MCP</strong> 工具(Brave Search),通过 <strong>Redis pub/sub</strong> 实现 WebSocket 流式传输<br/>
以及 <strong>Next.js</strong> 界面。
</p>
---
## 架构
```mermaid
graph LR
U([用户]) -->|WebSocket /ws/run| WS[WebSocket]
U -->|POST /run| API[FastAPI]
WS --> RT[🔀 路由器]
API --> RT
RT -->|聊天| LLM[🤖 直接 LLM]
RT -->|流水线| PL[🧠 计划器]
PL --> RE[🔍 研究人员]
RE -->|RAG| QD[(Qdrant)]
RE -->|MCP| BS[🌐 Brave Search]
RE --> CR[⚖️ 批评者]
CR -->|需要修订\n迭代 < 2| RE
CR -->|已批准| WR[✍️ 作者]
LLM -->|流式传输令牌| WS
WR --> DB[(PostgreSQL)]
WR -->|Redis pub/sub| WS
```
| 服务 | 角色 | 端口 |
|---|---|---|
| Next.js 14 | Web 界面(应用路由器 + Tailwind) | `3000` |
| FastAPI | REST + WebSocket | `8000` |
| PostgreSQL 16 | 会话和运行持久化 | `5432` |
| Redis 7 | WebSocket 流式传输发布/订阅 | `6379` |
| Qdrant | RAG 向量内存 | `6333` |
| Adminer | PostgreSQL Web 界面 | `8080` |
| RedisInsight | Redis Web 界面 | `5540` |
| OpenRouter | LLM 代理(Claude Haiku 4.5) | 外部 |
---
## 快速入门
```bash
# 1. 环境变量
cp .env.example .env
# → 配置 OPENROUTER_API_KEY
# 2. 启动所有服务
docker compose up -d
# 界面 → http://localhost:3000
# API → http://localhost:8000
# Swagger → http://localhost:8000/docs
# 健康检查 → http://localhost:8000/health
# Adminer → http://localhost:8080 (PostgreSQL GUI)
# RedisInsight → http://localhost:5540 (Redis GUI)
```
---
## 云部署
| 组件 | 服务 | 免费 |
|---|---|---|
| FastAPI + 代理 | **Railway**(Dockerfile) | $5 信用/月 |
| PostgreSQL + Redis | Railway 插件 | 包含 |
| Qdrant | **Qdrant Cloud**(免费层) | 是 |
| Next.js 前端 | **Vercel** | 是 |
```
Railway 变量:
OPENROUTER_API_KEY, DATABASE_URL, REDIS_URL
QDRANT_HOST, QDRANT_API_KEY, ALLOWED_ORIGINS
Vercel 变量:
NEXT_PUBLIC_API_URL=https://xxx.up.railway.app
根目录 = frontend-next
```
Dockerfile 在启动时自动运行 `alembic upgrade head` — 无需手动操作。
---
## 自动路由
系统自动检测查询是需要完整流水线还是直接回答:
| 模式 | 示例 | 流程 |
|---|---|---|
| **直接聊天** | "你好", "什么是 Python?", "讲个笑话" | 路由器 → LLM → 流式传输回答 |
| **流水线** | "分析 RAG 与微调的区别", "撰写一份关于……的报告" | 路由器 → 计划器 → 研究人员(RAG + MCP)→ 批评者 → 作者 |
流水线在聊天模式下自动灰显 — 无需用户操作。
---
## Web 界面
可在 **http://localhost:3000** 访问 — Next.js 14,应用路由器,Tailwind CSS,TypeScript。
| 区域 | 功能 |
|---|---|
| **流水线** | 4 个动画步骤 — 在直接聊天中灰显,在流水线中动画 |
| **聊天** | 令牌流式传输,带有闪烁的光标 |
| **侧边栏 — 会话** | 更改会话,加载历史记录 |
| **侧边栏 — 检索** | 3 个选项卡:文本/文件(拖放 PDF DOCX TXT)/ URL |
| **侧边栏 — 历史记录** | 会话的所有运行,可点击 |
---
## MCP — 外部工具
研究人员并行执行 **RAG Qdrant + 所有 MCP 工具**,并将结果注入提示。
### 启用 Brave Search
1. 在 [brave.com/search/api](https://brave.com/search/api/) 获取免费密钥
2. 添加到 `.env`:
```env
BRAVE_API_KEY=bsv-您的密钥
```
网页搜索结果自动出现在研究人员的上下文中。如果密钥不存在,系统将正常运行,无需网页搜索。
### 添加新 MCP 工具
```python
# app/services/mcp_client.py
class GitHubSearchTool(MCPTool):
@property
def name(self) -> str: return "github_search"
@property
def description(self) -> str: return "GitHub 仓库搜索"
def is_available(self) -> bool: return bool(settings.github_token)
async def run(self, query: str) -> str:
# ... 调用 GitHub API
return 结果
# 在 setup_mcp() 中:
registry.register(GitHubSearchTool())
```
**保证:** 工具崩溃 → 警告日志,流水线继续。密钥不存在 → 工具被忽略。
---
## RAG 检索
```bash
# 原始文本
curl -X POST http://localhost:8000/api/v1/documents \
-H "Content-Type: application/json" \
-d '{"text": "RAG 结合向量搜索和 LLM 生成。", "id": "doc-1"}'
# PDF / DOCX / TXT 文件
curl -X POST http://localhost:8000/api/v1/documents/upload \
-F "file=@报告.pdf"
# 网页
curl -X POST http://localhost:8000/api/v1/documents/url \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/article"}'
# 批量文档
curl -X POST http://localhost:8000/api/v1/documents/batch \
-H "Content-Type: application/json" \
-d '{"documents": [{"text": "...", "id": "doc-2"}, {"text": "...", "id": "doc-3"}]}'
```
---
## 端点
### 运行工作流(批量模式)
```bash
curl -X POST http://localhost:8000/api/v1/run \
-H "Content-Type: application/json" \
-d '{"task": "解释 RAG 与微调的优势", "session_id": "session-1"}'
```
### 会话历史记录
```bash
curl http://localhost:8000/api/v1/sessions/session-1/runs
```
### 实时流式传输(WebSocket)
```javascript
const ws = new WebSocket("ws://localhost:8000/ws/run");
ws.onopen = () => ws.send(JSON.stringify({
task: "分析 2025 年 AI 趋势",
session_id: "session-1"
}));
ws.onmessage = ({ data }) => {
const { type, mode, agent, content, final_answer } = JSON.parse(data);
// type: "mode" | "agent_start" | "token" | "agent_done" | "done" | "error"
// mode(在 "mode" 事件中): "chat" | "pipeline"
// "done" 包含 final_answer 和迭代
};
```
---
## 测试
```bash
pip install -r requirements-dev.txt
pytest -v
```
```
tests/test_agents/test_critic.py ....
tests/test_agents/test_orchestrator.py ........
tests/test_agents/test_planner.py ....
tests/test_agents/test_researcher.py ......
tests/test_agents/test_router.py .....
tests/test_agents/test_writer.py ....
tests/test_api/test_documents.py ...........
tests/test_api/test_routes.py .......
tests/test_api/test_websocket.py ................
tests/test_services/test_mcp_client.py .............
78 passed in 15.55s
```
---
## 结构
```
frontend-next/ # Next.js 14 界面
└── src/
├── app/
│ ├── layout.tsx # 根布局
│ ├── page.tsx # 主页面(状态,WebSocket,路由)
│ └── globals.css
└── components/
├── Pipeline.tsx # 4 个动画步骤
├── ChatArea.tsx # 消息 + 流式传输
├── Sidebar.tsx # 会话,检索,历史记录
└── DocumentUpload.tsx# 文本/文件/URL 选项卡
app/
├── agents/
│ ├── router.py # 分类:"聊天" 或 "流水线"
│ ├── state.py # 共享 AgentState TypedDict
│ ├── orchestrator.py # stream_and_publish,_stream_direct_chat,_stream_pipeline
│ ├── planner.py # 任务分解
│ ├── researcher.py # RAG + LLM
│ ├── critic.py # 评估,触发修订
│ └── writer.py # 撰写最终回答
├── api/
│ ├── routes.py # POST /run · GET /sessions/{id}/runs
│ ├── documents.py # POST /documents · /batch · /upload · /url
│ └── websocket.py # 通过 Redis pub/sub 流式传输 + 持久化
├── services/
│ ├── llm.py # chat_completion + stream_completion + 嵌入
│ ├── document_parser.py# PDF(pdfplumber)· DOCX · TXT · URL(bs4)
│ ├── vector_store.py # Qdrant 客户端(query_points)
│ ├── cache.py # Redis pub/sub 客户端
│ └── mcp_client.py # MCPTool ABC · MCPRegistry · BraveSearchTool
├── db/
│ ├── models.py # 会话 · 运行(SQLAlchemy 2.0)
│ └── session.py # 异步引擎 + get_session
└── core/
├── config.py # pydantic-settings
└── logging.py # structlog — 结构化日志
alembic/ # 版本控制迁移
```
---
## 优势
| | 详情 |
|---|---|
| **自动路由** | 路由器 LLM 区分直接聊天和流水线 — 零用户摩擦 |
| **反馈循环** | 批评者将研究人员最多发送回 2 次,如果搜索结果不足 |
| **解耦流式传输** | Redis pub/sub — N 个客户端可以同时订阅同一个运行 |
| **全持久化** | 运行在 REST 或 WebSocket 下持久化到数据库 |
| **多源 RAG** | Qdrant + 文本,PDF,DOCX,TXT,URL 检索与自动提取 |
| **LLM 路由** | 廉价模型(计划器/批评者/路由器)与智能模型(作者/聊天)— 成本优化 |
| **可扩展 MCP** | Brave Search 通过 API 密钥启用 — 添加工具 = 1 个类 + 1 行代码,无中断 |
| **检查点** | LangGraph 通过 `session_id` 在查询之间记住状态 |
| **可观察性** | 每个代理中的 structlog — 模型,持续时间,迭代,使用的 MCP 工具 |
| **迁移** | 版本控制迁移 — `make migrate` |
| **Next.js 界面** | 应用路由器,Tailwind,TypeScript — 动画流水线,RAG 检索,历史记录 |
| **CI** | GitHub Actions — 78 个测试在 Python 3.11 和 3.12 上每次推送 |
| **云部署** | Railway(后端)+ Vercel(前端)+ Qdrant Cloud — 零 VPS 管理 |
| **生产就绪** | Docker Compose,健康检查,Makefile,异步代码 |