gns3-server/docs/ai-chat-api-design.md
YueGuobin 03ab9cdf6c feat(chat-api): refactor design document with concise architecture overview
- Replace detailed implementation plan with high-level architecture design
- Focus on core features: project isolation, streaming responses, session management
- Remove FlowNet-Lab reference and implementation specifics
- Streamline document from 1172 to 483 lines for better maintainability
2026-03-04 21:58:23 +08:00

484 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GNS3 Copilot Agent Chat API 设计文档
## 概述
本文档描述 GNS3 Copilot Chat API 的架构设计和实现方案。该 API 使客户端能够通过 RESTful 接口与 GNS3 Copilot Agent 进行交互,提供流式对话、会话管理、项目拓扑查询等功能。
## 核心特性
- **项目级隔离**:每个 GNS3 项目拥有独立的 Agent 实例和会话存储
- **流式响应**:使用 Server-Sent Events (SSE) 实现实时流式输出
- **会话管理**:支持会话列表、重命名、删除、历史记录查询
- **统计追踪**自动记录消息数量、LLM 调用次数、Token 使用量
- **用户隔离**:每个用户拥有独立的 LLM 配置和会话空间
## 架构设计
### 整体架构
```
Frontend (Web UI)
│ SSE Streaming
FastAPI Chat API Routes
│ Project-level Agent Management
AgentService (per project)
├─ SQLite Checkpointer (project_dir/gns3-copilot/)
│ ├─ checkpoints table (LangGraph state)
│ └─ chat_sessions table (session metadata)
└─ LangGraph Agent
├─ llm_call node
├─ should_continue node
└─ tool_node (GNS3 tools)
```
### 项目级 Checkpoint 设计
每个 GNS3 项目在项目目录下创建 `gns3-copilot/copilot_checkpoints.db` SQLite 数据库,包含两张表:
1. **checkpoints 表**LangGraph 自动管理):存储 Agent 的对话状态和记忆
2. **chat_sessions 表**(自定义):存储会话元数据和统计信息
**目录结构**
```
{project.path}/
├── gns3-copilot/
│ └── copilot_checkpoints.db
├── project-files/
└── project.gns3
```
**设计优势**
- 项目删除时自动清理所有相关数据
- 实现项目级别的会话隔离
- 便于备份和迁移
## 用户认证信息传递
### 背景需求
GNS3 Copilot Agent 需要以下信息才能正常工作:
1. **user_id**:获取用户专属的 LLM 配置
2. **jwt_token**:调用 GNS3 API 时进行身份验证
3. **llm_config**:包含 provider、model、api_key 等配置
### ContextVars 方案
使用 Python 的 `contextvars.ContextVar` 在请求作用域内传递数据,避免敏感信息持久化到 checkpoint。
**数据流**
```
1. API 层获取用户信息
├─ 从 FastAPI get_current_active_user 获取 user_id
├─ 从 Authorization header 提取 jwt_token
└─ 从数据库查询 LLM 配置(已解密 API key
2. 设置 ContextVars内存临时存储
├─ set_current_jwt_token(jwt_token)
└─ set_current_llm_config(llm_config)
3. 构建安全的 LangGraph config仅包含非敏感标识符
{
"configurable": {
"thread_id": session_id,
"project_id": project_id
},
"metadata": {
"user_id": user_id
}
}
4. LLM 节点从 ContextVars 获取配置
├─ get_current_jwt_token()
└─ get_current_llm_config()
```
**方案优势**
- 敏感数据JWT token、API key仅存储在内存中
- 请求结束后自动清理,不会持久化到数据库
- 避免序列化/反序列化开销
- 实现请求级别的数据隔离
## 会话管理
### chat_sessions 表结构
| 字段 | 类型 | 说明 |
|------|------|------|
| id | INTEGER | 主键(自增) |
| thread_id | TEXT | LangGraph thread_id唯一 |
| user_id | TEXT | 用户 ID |
| project_id | TEXT | GNS3 项目 ID |
| title | TEXT | 会话标题 |
| message_count | INTEGER | 消息数量 |
| llm_calls_count | INTEGER | LLM 调用次数 |
| input_tokens | INTEGER | 输入 token 总数 |
| output_tokens | INTEGER | 输出 token 总数 |
| total_tokens | INTEGER | 总 token 数 |
| last_message_at | TIMESTAMP | 最后消息时间 |
| created_at | TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | 更新时间 |
| metadata | TEXT | 预留元数据JSON |
| stats | TEXT | 额外统计信息JSON |
### ChatSessionsRepository
提供会话的 CRUD 操作:
- **create_session**:创建新会话
- **get_session_by_thread**:根据 thread_id 查询会话
- **list_sessions**:列出用户的会话(支持过滤和分页)
- **update_session**:更新会话(支持增量更新计数器)
- **delete_session**:删除会话及其 checkpoints
- **delete_all_sessions**:删除项目的所有会话
### 统计信息自动收集
`stream_chat` 流程中自动追踪:
- 每次用户消息message_count +1
- 每次 AI 响应message_count +1
- 每次工具调用llm_calls_count +1
- LLM 返回的 token 使用量:实时累加
流结束后一次性更新到数据库。
### Title 自动同步
会话标题由 `generate_title` 节点自动生成,保存在 LangGraph checkpoint 的 `conversation_title` 字段中。
**同步机制**
1. 流式 Chat 完成后,从 checkpoint 读取最终 state
2. 检查 `conversation_title` 是否有变化
3. 如果有变化,更新到 `chat_sessions`
**优势**
- 避免在节点中直接访问数据库(防止循环依赖)
- 所有数据库更新集中在流结束后
- 逻辑清晰,易于维护
## SSE 消息格式
Chat API 使用 Server-Sent Events (SSE) 进行流式传输。
### 消息类型
| type | 说明 | 包含字段 |
|------|------|----------|
| content | AI 文本内容(流式) | content |
| tool_call | 工具调用请求 | tool_call (id, name, arguments) |
| tool_start | 工具开始执行 | tool_name |
| tool_end | 工具执行完成 | tool_name, tool_output |
| error | 错误信息 | error |
| done | 流结束 | session_id |
| heartbeat | 心跳保活 | session_id |
### 消息示例
```json
// AI 文本流式输出
{"type": "content", "content": "Hello! How can I help"}
// 工具调用
{"type": "tool_call", "tool_call": {"id": "call_123", "function": {"name": "GNS3TopologyTool", "arguments": {"project_id": "xxx"}}}}
// 工具开始
{"type": "tool_start", "tool_name": "GNS3TopologyTool", "session_id": "xxx"}
// 工具完成
{"type": "tool_end", "tool_name": "GNS3TopologyTool", "tool_output": "{...}", "session_id": "xxx"}
// 完成
{"type": "done", "session_id": "xxx"}
// 错误
{"type": "error", "error": "Project not found", "session_id": "xxx"}
```
### 心跳机制
**作用**:防止代理服务器/负载均衡器因超时断开 SSE 连接。
**实现**:使用 `asyncio.wait` 设置超时,超时后发送 `heartbeat` 消息,然后继续等待下一个事件。
**前端处理**:收到 `heartbeat` 消息时直接忽略,不渲染任何内容。
## API 端点
所有端点都在 `/v3/projects/{project_id}/chat/` 路径下。
| 方法 | 端点 | 说明 |
|------|------|------|
| POST | `/stream` | 流式 Chat主要接口 |
| GET | `/sessions` | 列出会话 |
| GET | `/sessions/{session_id}/history` | 获取会话历史 |
| PATCH | `/sessions/{session_id}` | 重命名会话 |
| DELETE | `/sessions/{session_id}` | 删除会话 |
### POST /v3/projects/{project_id}/chat/stream
**功能**:流式对话接口
**请求参数**
- message: 用户消息内容
- session_id: 会话 ID可选不提供则自动创建新会话
- stream: 是否启用流式响应(默认 true
- mode: 交互模式(当前仅支持 "text"
**响应**SSE 流,包含多种类型的消息(见上文消息格式)
**项目状态检查**:只允许项目状态为 "opened" 时进行对话
### GET /v3/projects/{project_id}/chat/sessions
**功能**:列出项目的所有会话
**响应**会话列表包含统计信息消息数、token 使用量等)
### GET /v3/projects/{project_id}/chat/sessions/{session_id}/history
**功能**:获取会话的完整历史记录
**参数**
- session_id: 会话 ID
- limit: 最大消息数量(默认 100
**响应**
- thread_id: 会话 ID
- title: 会话标题
- messages: 消息列表OpenAI 格式)
- llm_calls: LLM 调用次数
### PATCH /v3/projects/{project_id}/chat/sessions/{session_id}
**功能**:重命名会话
**请求参数**
- title: 新标题1-255 字符)
**响应**:更新后的会话信息
### DELETE /v3/projects/{project_id}/chat/sessions/{session_id}
**功能**:删除会话及其所有 checkpoint 数据
**响应**204 No Content
## 数据模型
### ChatRequest
- message: str - 用户消息内容
- session_id: Optional[str] - 会话 ID可选
- stream: bool - 是否流式响应(默认 true
- mode: Literal["text"] - 交互模式
### ChatSession
- id: Optional[int] - 数据库 ID
- thread_id: str - Thread/Session ID
- user_id: str - 用户 ID
- project_id: str - 项目 ID
- title: str - 会话标题
- message_count: int - 消息数量
- llm_calls_count: int - LLM 调用次数
- input_tokens: int - 输入 token 数
- output_tokens: int - 输出 token 数
- total_tokens: int - 总 token 数
- last_message_at: Optional[str] - 最后消息时间
- created_at: Optional[str] - 创建时间
- updated_at: Optional[str] - 更新时间
- metadata: Dict - 预留元数据
- stats: Dict - 额外统计信息
### ConversationHistory
- thread_id: str - 会话 ID
- title: str - 会话标题
- messages: List[OpenAIMessage] - 消息列表
- created_at: Optional[str] - 创建时间
- updated_at: Optional[str] - 更新时间
- llm_calls: int - LLM 调用次数
### OpenAIMessage
- id: str - 消息 ID
- role: Literal["user", "assistant", "system", "tool"] - 角色
- content: str - 消息内容
- name: Optional[str] - 工具消息名称
- tool_call_id: Optional[str] - 关联的工具调用 ID
- tool_calls: Optional[List] - 工具调用列表assistant 消息)
- created_at: str - 创建时间
## 核心组件
### AgentService
**职责**:项目级的 Agent 管理服务
**主要方法**
- `stream_chat`:流式对话,自动管理会话和统计
- `get_history`:获取会话历史
- `list_sessions`:列出会话
- `delete_session`:删除会话
- `rename_session`:重命名会话
- `close`:关闭数据库连接
**核心流程**stream_chat
1. 获取或创建会话
2. 设置 ContextVarsJWT token、LLM config
3. 构建 LangGraph config
4. 流式执行 Agent收集统计信息
5. 流结束后更新会话统计
6. 同步 auto-generated title
**连接管理**
- 使用 `AsyncSqliteSaver` 作为 checkpointer
- 支持 WAL 模式提升并发性能
- 项目切换时自动关闭旧连接
- 防止连接被垃圾回收(保存引用)
### ProjectAgentManager
**职责**:全局单例,管理所有项目的 AgentService 实例
**方法**
- `get_agent(project_id, project_path)`:获取或创建项目的 AgentService
- `remove_agent(project_id)`:移除项目的 AgentService
- `close_all`:关闭所有 AgentService
### Chat API Routes
**文件**`gns3server/api/routes/controller/chat.py`
**路由注册**
```python
router.include_router(
chat.router,
prefix="/{project_id}/chat",
tags=["Chat"]
)
```
**主要端点实现**
- 所有端点都需要用户认证(`get_current_active_user`
- 所有端点都检查项目状态是否为 "opened"
- stream 端点使用 `StreamingResponse` 返回 SSE 流
## 项目生命周期集成
### 项目打开时
创建或获取 AgentService 实例:
```python
agent_manager = await get_project_agent_manager()
agent_service = await agent_manager.get_agent(project_id, project.path)
```
### 项目关闭时
移除 AgentService 实例,释放资源:
```python
agent_manager.remove_agent(project_id)
```
### 项目删除时
1. 调用 `delete_all_sessions(project_id)` 删除所有会话和 checkpoint 数据
2. 移除 AgentService 实例
3. 项目目录被删除,数据库文件也被删除
## 前端集成
### useChat Hook
根据 SSE 消息的 `type` 字段进行不同处理:
| type | 处理逻辑 |
|------|----------|
| content | 追加到当前 AI 消息内容 |
| tool_call | 创建 tool_call 类型消息,显示工具调用信息 |
| tool_start | 可选:显示工具开始执行状态 |
| tool_end | 创建 tool_result 类型消息,显示工具执行结果 |
| error | 显示错误信息 |
| done | 标记流结束,停止加载状态 |
| heartbeat | 忽略(保活信号) |
### 错误处理
- 网络错误:显示重试选项
- LLM 错误:显示错误消息
- 项目未打开:提示用户打开项目
- LLM 未配置:引导用户配置 LLM
## 安全考虑
### 用户隔离
- 每个用户只能访问自己的会话
- user_id 存储在 config.metadata 中
- 所有数据库查询都带 user_id 过滤
### 项目访问控制
- 只允许访问用户有权限的项目
- 项目状态检查:只允许 "opened" 状态的项目使用 Chat
### LLM 配置安全
- API key 加密存储在数据库
- 使用 ContextVars 传递,不持久化到 checkpoint
- 请求结束后自动清理内存中的敏感信息
## 性能优化
### 数据库连接管理
- 使用 WAL 模式提升并发写入性能
- 项目级连接复用
- 项目切换时自动关闭旧连接
### Checkpoint 优化
- LangGraph 自动管理 checkpoints 表
- 定期清理旧 checkpoint可选
- 使用索引加速查询thread_id, user_id + project_id
### 统计信息批量更新
- 流结束后一次性更新统计信息
- 避免频繁的数据库写入
## 依赖项
- `langchain` >= 0.3.0
- `langgraph` >= 0.2.0
- `langchain-core`
- `langgraph-checkpoint-sqlite` >= 3.0.1
- `aiosqlite`
## 扩展性
### 预留字段
- `metadata`TEXT JSON存储会话级别的元数据
- `stats`TEXT JSON存储额外的统计信息
### 未来可能的扩展
- 多模态支持(图片、文件)
- 语音输入/输出
- 多人协作会话
- 会话分享和导出
- 自定义工具注册
## 参考资料
- [LangGraph Checkpoint Documentation](https://langchain-ai.github.io/langgraph/how-tos/checkpointers/)
- [Server-Sent Events (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
- [OpenAI Chat Format](https://platform.openai.com/docs/api-reference/chat)