feat: 新增 teams 目录

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 14:26:47 +08:00
parent 85b4c51fd7
commit 8249f67351
33 changed files with 4841 additions and 0 deletions

137
teams/ai/ai-core-api.md Normal file
View File

@@ -0,0 +1,137 @@
# AI-Core 文档解析服务 API 对接文档
## 服务地址
```
localhost:50051
```
## VLM 配置(可选)
VLM 用于提升图片文件的解析效果。如果不配置 VLM则使用默认的 MarkItDown 解析。
### 方式一:环境变量
```bash
# 设置环境变量
export VLM_API_KEY="your-api-key"
export VLM_PROVIDER="openai" # openai / anthropic / qwen
export VLM_MODEL="gpt-4o"
```
### 方式二:配置文件
`ai-core/config.yaml` 中配置:
```yaml
vlm:
enabled: true
provider: "openai" # openai / anthropic / qwen
model: "gpt-4o" # 模型名称
api_key: "sk-xxx" # API Key
base_url: "" # 自定义 API 地址(可选)
prompt: "" # 自定义提示词(可选)
```
### 支持的 VLM 提供商
| 提供商 | 示例模型 |
|--------|----------|
| openai | gpt-4o, gpt-4o-mini |
| anthropic | claude-3-opus, claude-3-sonnet |
| qwen | qwen-vl-max, qwen2-vl-72b |
---
## gRPC API 定义
### 1. ParseDocument - 解析文档
**请求 (ParseRequest)**
```protobuf
message ParseRequest {
string file_url = 1; // 文件 URL必填
string file_name = 2; // 文件名,带扩展名(必填)
string file_type = 3; // 文件类型(可选,自动检测)
string parser_engine = 4; // 解析引擎(可选)
map<string, string> engine_overrides = 5; // 引擎配置
// VLM 配置(可选,优先级高于全局配置)
VLMConfig vlm_config = 6;
}
message VLMConfig {
bool enabled = 1;
string provider = 2;
string model = 3;
string api_key = 4;
string base_url = 5;
string prompt = 6;
}
```
**响应 (ParseResponse)**
```protobuf
message ParseResponse {
bool success = 1;
string content = 2; // Markdown 内容
string message = 3;
int32 content_length = 4;
string file_type = 5;
string parser_engine = 6;
}
```
---
## Golang 对接示例
### 基础调用(无 VLM 配置时使用 MarkItDown
```go
req := &pb.ParseRequest{
FileUrl: "https://example.com/document.pdf",
FileName: "document.pdf",
}
resp, client.ParseDocument(ctx, req)
```
### 带 VLM 配置调用
```go
req := &pb.ParseRequest{
FileUrl: "https://example.com/image.png",
FileName: "image.png",
VlmConfig: &pb.VLMConfig{
Enabled: Provider: "open true,
ai",
Model: "gpt-4o",
ApiKey: "sk-xxx",
},
}
resp, err := client.ParseDocument(ctx, req)
```
---
## 解析逻辑
1. **图片文件** (jpg, png, webp 等)
- 如果配置了 VLM → 使用 VLM 解析
- 如果没有配置 VLM → 使用 MarkItDown 解析
2. **PDF/DOCX/PPTX 等文档**
- 使用 MarkItDown 解析
3. **VLM 优先级**
- gRPC 请求中的 vlm_config > 全局配置config.yaml/环境变量)
---
## 注意事项
1. **文件 URL**: 必须是可直接访问的 URL
2. **文件名**: 必须带扩展名(如 `.pdf`, `.png`
3. **返回内容**: Markdown 格式文本

20
teams/ai/todo.md Normal file
View File

@@ -0,0 +1,20 @@
# AI 服务需求 TODO
## 2026年3月
### 2026-03-09
- [ ] **AI-Core 文档解析服务对接**
- 服务ai-core (gRPC, 端口 50051)
- 功能将文档PDF/DOCX/PPTX/图片等)转换为 Markdown
- 对接方式gRPC 调用
- 详细需求:[ai-core-api.md](./ai-core-api.md)
- [ ] **VLM 调用支持**
- 支持 OpenAI GPT-4o、Anthropic Claude、阿里 Qwen VL
- 通过 vlm_config 配置启用
- 适用场景图片文件jpg, png 等)自动使用 VLM 解析
---
> 需求完成后请完成者打 ✔

18
teams/api/README.md Normal file
View File

@@ -0,0 +1,18 @@
# API 接口文档
## 目录
### Database 相关
- [检查数据库连接并获取表结构](database-check.md)
- [创建数据库配置](database-create.md)
- [获取数据库列表](database-list.md)
- [获取子表列表](subtable-list.md)
### Neo4j 相关
- [Neo4j 连接测试](neo4j-check.md)
---
> 接口如有更新,请同步更新此文档

200
teams/api/database-check.md Normal file
View File

@@ -0,0 +1,200 @@
# 检查数据库连接并获取表结构
## 接口地址
```
POST /database/check
```
## 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| db_type | string | 是 | 数据库类型:`mysql``postgres``neo4j` |
| host | string | 是 | 数据库主机 |
| port | int | 是 | 数据库端口 |
| username | string | 是 | 用户名 |
| password | string | 否 | 密码 |
| database | string | 是 | 数据库名 |
| charset | string | 否 | 字符集,默认 `utf8mb4` |
| ssl_mode | string | 否 | SSL 模式 |
| database_id | string | 否 | 已存在的数据库ID用于恢复字段映射 |
| uri | string | 否 | Neo4j 连接地址(如 bolt://localhost:7687Neo4j 类型必填 |
## 请求示例MySQL/PostgreSQL
```json
{
"db_type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "root",
"database": "students",
"charset": "utf8mb4",
"database_id": "xxx-xxx-xxx"
}
```
## 请求示例Neo4j
```json
{
"db_type": "neo4j",
"uri": "bolt://localhost:7687",
"username": "neo4j",
"password": "password",
"database": "neo4j"
}
```
## 返回参数
| 参数 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否连接成功 |
| message | string | 消息 |
| database | string | 数据库名 |
| tables | array | 表结构列表MySQL/PostgreSQL |
| graphs | object | 图谱数据Neo4j |
### tables[] 详情(关系型数据库)
| 参数 | 类型 | 说明 |
|------|------|------|
| table_name | string | 表名 |
| table_comment | string | 表注释 |
| ddl | string | 建表 DDL带 COMMENT 的映射后 DDL |
| columns | array | 列信息列表 |
### columns[] 详情
| 参数 | 类型 | 说明 |
|------|------|------|
| column_name | string | 列名 |
| data_type | string | 数据类型 |
| column_type | string | 完整列类型 |
| is_nullable | string | 是否可空YES/NO |
| default_value | string | 默认值 |
| column_key | string | 主键标识PRI/MUL/UNI |
| extra | string | 额外信息(如 auto_increment |
| column_comment | string | 列注释 |
| mapped_name | string | 字段中文映射名(已保存的映射) |
### graphs 详情Neo4j
| 参数 |类型| 说明 |
|------|------|------|
| labels | array | 标签列表 |
| relationshipTypes | array | 关系类型列表 |
| nodes | array | 节点属性定义 |
| relationships | array | 关系属性定义 |
### graphs.labels[]
| 参数 | 类型 | 说明 |
|------|------|------|
| name | string | 标签名称 |
| count | int | 节点数量 |
### graphs.relationshipTypes[]
| 参数 | 类型 | 说明 |
|------|------|------|
| name | string | 关系类型名称 |
| count | int | 关系数量 |
### graphs.nodes[]
| 参数 | 类型 | 说明 |
|------|------|------|
| label | string | 节点标签名 |
| properties | array | 属性列表 |
### graphs.relationships[]
| 参数 | 类型 | 说明 |
|------|------|------|
| type | string | 关系类型名 |
| startLabel | string | 起始节点标签 |
| endLabel | string | 目标节点标签 |
| properties | array | 属性列表 |
## 返回示例MySQL/PostgreSQL
```json
{
"success": true,
"message": "connection successful",
"database": "students",
"tables": [
{
"table_name": "users",
"table_comment": "用户表",
"ddl": "CREATE TABLE `users` (\n `id` int(10) unsigned NOT NULL COMMENT '用户ID'\n ...\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
"columns": [
{
"column_name": "id",
"data_type": "int",
"column_type": "int(10) unsigned",
"is_nullable": "NO",
"default_value": "",
"column_key": "PRI",
"extra": "auto_increment",
"column_comment": "",
"mapped_name": "用户ID"
}
]
}
]
}
```
## 返回示例Neo4j
```json
{
"success": true,
"message": "connection successful",
"database": "neo4j",
"graphs": {
"labels": [
{"name": "User", "count": 100},
{"name": "Order", "count": 50}
],
"relationshipTypes": [
{"name": "KNOWS", "count": 30},
{"name": "OWNS", "count": 20}
],
"nodes": [
{
"label": "User",
"properties": [
{"name": "id", "type": "string"},
{"name": "name", "type": "string"}
]
}
],
"relationships": [
{
"type": "KNOWS",
"startLabel": "User",
"endLabel": "User",
"properties": [
{"name": "since", "type": "date"}
]
}
]
}
}
```
## 使用场景
1. **关系型数据库**
- 首次连接:不传 `database_id`,获取实时表结构
- 恢复映射:传入 `database_id`,返回已保存的 `mapped_name``ddl`
2. **Neo4j 图数据库**
- 连接 Neo4j 并获取图谱概览数据(标签、关系类型、属性定义)
- 用于前端图可视化展示

View File

@@ -0,0 +1,104 @@
# 创建数据库配置
## 接口地址
```
POST /database/add
```
## 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | string | 是 | 数据库名称 |
| description | string | 否 | 描述 |
| db_type | string | 是 | 数据库类型 |
| host | string | 是 | 主机 |
| port | int | 是 | 端口 |
| username | string | 是 | 用户名 |
| password | string | 否 | 密码 |
| database | string | 是 | 数据库名 |
| charset | string | 否 | 字符集 |
| ssl_mode | string | 否 | SSL 模式 |
| sub_tables | array | 否 | 子表配置列表 |
### sub_tables[] 详情
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| parent_table | string | 是 | 原始表名 |
| sub_table_name | string | 是 | 子表别名 |
| sub_table_comment | string | 否 | 子表注释 |
| mapping_type | string | 否 | 映射类型 |
| relation_field | string | 否 | 关联字段 |
| relation_type | string | 否 | 关联类型 |
| fields | array | 否 | 字段映射列表 |
### fields[] 详情
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| column_name | string | 是 | 列名 |
| mapped_name | string | 是 | 中文映射名 |
## 请求示例
```json
{
"name": "学生数据库",
"description": "用于存储学生信息",
"db_type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "root",
"database": "students",
"charset": "utf8mb4",
"sub_tables": [
{
"parent_table": "users",
"sub_table_name": "用户表",
"sub_table_comment": "用户信息",
"fields": [
{"column_name": "id", "mapped_name": "用户ID"},
{"column_name": "name", "mapped_name": "用户名"}
]
}
]
}
```
## 返回参数
| 参数 | 类型 | 说明 |
|------|------|------|
| id | string | 数据库记录ID |
| name | string | 数据库名称 |
| db_type | string | 数据库类型 |
| host | string | 主机 |
| port | int | 端口 |
| ... | ... | 其他字段 |
## 返回示例
```json
{
"id": "xxx-xxx-xxx",
"name": "学生数据库",
"description": "用于存储学生信息",
"db_type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "root",
"database": "students",
"table_count": 1,
"charset": "utf8mb4",
"created_at": "2026-03-06T15:00:00Z"
}
```
## 说明
- 创建时会自动连接数据库获取表结构 DDL
- 如果传入了 `fields`(字段映射),会自动生成带 COMMENT 的新 DDL 并存储

View File

@@ -0,0 +1,51 @@
# 获取数据库列表
## 接口地址
```
GET /database/list
```
## 请求参数
## 返回参数
| 参数 | 类型 | 说明 |
|------|------|------|
| list | array | 数据库列表 |
### list[] 详情
| 参数 | 类型 | 说明 |
|------|------|------|
| id | string | 数据库ID |
| name | string | 数据库名称 |
| description | string | 描述 |
| db_type | string | 数据库类型 |
| host | string | 主机 |
| port | int | 端口 |
| database | string | 数据库名 |
| table_count | int | 子表数量 |
| created_at | string | 创建时间 |
## 返回示例
```json
{
"list": [
{
"id": "xxx-xxx",
"name": "学生数据库",
"description": "用于存储学生信息",
"db_type": "mysql",
"host": "localhost",
"port": 3306,
"database": "students",
"table_count": 5,
"created_at": "2026-03-06T15:00:00Z"
}
]
}
```

268
teams/api/knowledge-api.md Normal file
View File

@@ -0,0 +1,268 @@
# 知识库 API
## 基础信息
| 项目 | 说明 |
|------|------|
| 基础URL | `http://localhost:8082` |
## 接口列表
### 1. 创建知识库
**请求**
```
POST /api/knowledge/create
Content-Type: application/json
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | String | 是 | 知识库名称 |
| description | String | 否 | 知识库描述 |
| llm_model_id | String | 是 | LLM 模型 ID |
| embedding_model_id | String | 是 | Embedding 模型 ID |
| parsing_config | Object | 是 | 解析配置 |
| - engine | String | 是 | 解析引擎markitdown / docling |
| - docling_url | String | 条件 | Docling URLengine=docling 时必填) |
| - enable_pdf | Boolean | 否 | 是否启用 PDF 解析 |
| - pandoc | Boolean | 否 | 是否启用 Pandoc |
| storage_config | Object | 否 | 存储配置,不传则使用全局配置 |
| - type | String | 否 | 存储模式local / minio |
| - endpoint | String | 否 | MinIO endpoint |
| - bucket | String | 否 | MinIO bucket |
| - access_key | String | 否 | MinIO access key |
| - secret_key | String | 否 | MinIO secret key |
| - use_ssl | Boolean | 否 | MinIO 是否使用 SSL |
**响应**
```json
{
"success": true,
"id": "kb_xxx",
"message": "Knowledge base created successfully"
}
```
---
### 2. 获取知识库列表
**请求**
```
GET /api/knowledge/list
```
**响应**
```json
{
"success": true,
"data": [
{
"id": "kb_001",
"name": "产品文档知识库",
"description": "用于存储产品手册",
"llm_model_id": "model_001",
"embedding_model_id": "model_002",
"status": "active",
"document_count": 15,
"chunk_count": 156,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
]
}
```
---
### 3. 获取知识库详情
**请求**
```
GET /api/knowledge/:id
```
**响应**
```json
{
"success": true,
"data": {
"id": "kb_001",
"name": "产品文档知识库",
"description": "用于存储产品手册",
"llm_model_id": "model_001",
"embedding_model_id": "model_002",
"parsing_config": {
"engine": "markitdown",
"enable_pdf": true,
"pandoc": true
},
"status": "active",
"document_count": 15,
"chunk_count": 156,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}
```
---
### 4. 删除知识库
**请求**
```
DELETE /api/knowledge/:id
```
**响应**
```json
{
"success": true,
"message": "Knowledge base deleted"
}
```
---
### 5. 获取知识库下的文档列表
**请求**
```
GET /api/knowledge/:id/documents
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| status | String | 否 | 过滤状态all / parsed / parsing / failed |
**响应**
```json
{
"success": true,
"data": [
{
"id": "doc_001",
"knowledge_base_id": "kb_001",
"name": "产品手册_v2.0.pdf",
"file_key": "abc123.pdf",
"file_url": "http://localhost:8082/files/abc123.pdf",
"file_size": 2516582,
"status": "parsed",
"chunk_count": 156,
"uploaded_at": "2024-01-15T10:30:00Z"
}
]
}
```
---
### 6. 上传文档到知识库
**请求**
```
POST /api/knowledge/:id/documents
Content-Type: multipart/form-data
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| file | File | 是 | 要上传的文件 |
**响应**
```json
{
"success": true,
"id": "doc_001",
"url": "http://localhost:8082/files/abc123.pdf",
"document": {
"id": "doc_001",
"knowledge_base_id": "kb_001",
"name": "产品手册_v2.0.pdf",
"file_size": 2516582,
"status": "parsing",
"chunk_count": 0,
"uploaded_at": "2024-01-15T10:30:00Z"
},
"message": "Document uploaded"
}
```
---
### 7. 删除知识库文档
**请求**
```
DELETE /api/knowledge/:id/documents/:doc_id
```
**响应**
```json
{
"success": true,
"message": "Document deleted"
}
```
---
### 8. 重新解析文档
**请求**
```
POST /api/knowledge/:id/documents/:doc_id/reparse
```
**响应**
```json
{
"success": true,
"message": "Document reparse started"
}
```
---
### 9. 获取文档预览内容
**请求**
```
GET /api/knowledge/:id/documents/:doc_id/preview
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | Number | 否 | 页码(默认 1 |
**响应**
```json
{
"success": true,
"data": {
"total_pages": 3,
"current_page": 1,
"content": "第一章 产品介绍..."
}
}
```

208
teams/api/model-api.md Normal file
View File

@@ -0,0 +1,208 @@
# Model Settings 接口文档
## 接口列表
### 1. 获取模型列表
**接口地址:** `GET /model/list`
**返回参数:**
```json
{
"list": [
{
"id": "xxx-xxx-xxx",
"name": "OpenAI",
"model_type": "chat",
"provider": "OpenAI",
"model": "gpt-4o",
"api_key": "sk-xxx",
"base_url": "https://api.openai.com",
"api_endpoint": "/v1/chat/completions",
"status": "active",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
]
}
```
---
### 2. 获取模型详情
**接口地址:** `GET /model/:id`
**返回参数:**
```json
{
"id": "xxx-xxx-xxx",
"name": "OpenAI",
"model_type": "chat",
"provider": "OpenAI",
"model": "gpt-4o",
"api_key": "sk-xxx",
"base_url": "https://api.openai.com",
"api_endpoint": "/v1/chat/completions",
"status": "active",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
```
---
### 3. 创建模型
**接口地址:** `POST /model/add`
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | string | 是 | 模型名称 |
| model_type | string | 是 | 模型类型chat/embedding/rerank/vlm |
| provider | string | 是 | 提供商OpenAI/Ollama |
| model | string | 是 | 模型标识,如 gpt-4o |
| api_key | string | 是 | API 密钥 |
| base_url | string | 是 | 基础 URL |
| api_endpoint | string | 否 | API 端点路径 |
**请求示例:**
```json
{
"name": "OpenAI",
"model_type": "chat",
"provider": "OpenAI",
"model": "gpt-4o",
"api_key": "sk-xxx",
"base_url": "https://api.openai.com",
"api_endpoint": "/v1/chat/completions"
}
```
**返回参数:**
```json
{
"id": "xxx-xxx-xxx",
"name": "OpenAI",
"model_type": "chat",
"provider": "OpenAI",
"model": "gpt-4o",
"api_key": "sk-xxx",
"base_url": "https://api.openai.com",
"api_endpoint": "/v1/chat/completions",
"status": "active",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
```
---
### 4. 更新模型
**接口地址:** `PUT /model/:id`
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | string | 否 | 模型名称 |
| model_type | string | 否 | 模型类型chat/embedding/rerank/vlm |
| provider | string | 否 | 提供商OpenAI/Ollama |
| model | string | 否 | 模型标识 |
| api_key | string | 否 | API 密钥 |
| base_url | string | 否 | 基础 URL |
| api_endpoint | string | 否 | API 端点路径 |
| status | string | 否 | 状态active/inactive |
**请求示例:**
```json
{
"name": "OpenAI Updated",
"model_type": "chat",
"provider": "OpenAI",
"model": "gpt-4o",
"api_key": "sk-xxx",
"base_url": "https://api.openai.com",
"api_endpoint": "/v1/chat/completions",
"status": "active"
}
```
---
### 5. 删除模型
**接口地址:** `DELETE /model/:id`
**返回参数:**
```json
{
"success": true
}
```
---
### 6. 测试连接
**接口地址:** `POST /model/test`
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| provider | string | 是 | 提供商OpenAI/Ollama |
| model | string | 是 | 模型标识 |
| api_key | string | 是 | API 密钥 |
| base_url | string | 是 | 基础 URL |
| api_endpoint | string | 否 | API 端点路径 |
**请求示例:**
```json
{
"provider": "OpenAI",
"model": "gpt-4o",
"api_key": "sk-xxx",
"base_url": "https://api.openai.com",
"api_endpoint": "/v1/chat/completions"
}
```
**返回参数:**
```json
{
"success": true,
"message": "Connection successful"
}
```
或失败时:
```json
{
"success": false,
"message": "HTTP 401: Unauthorized"
}
```
---
## 数据结构
### ModelInfo 模型信息
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 主键 UUID |
| name | string | 模型名称 |
| model_type | string | 模型类型chat/embedding/rerank/vlm |
| provider | string | 提供商OpenAI/Ollama |
| model | string | 模型标识 |
| api_key | string | API 密钥 |
| base_url | string | 基础 URL |
| api_endpoint | string | API 端点路径 |
| status | string | 状态active/inactive |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |

265
teams/api/neo4j-check.md Normal file
View File

@@ -0,0 +1,265 @@
# Neo4j 连接测试
## 接口地址
```
POST /neo4j/check
```
## 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| host | string | 是 | Neo4j 主机 |
| port | int | 是 | Neo4j 端口(默认 7687 |
| username | string | 是 | 用户名(默认 neo4j |
| password | string | 是 | 密码 |
| database | string | 否 | 数据库名(默认 neo4j |
| name | string | 否 | 数据库名称,用于保存记录 |
| uri | string | 否 | Neo4j 连接地址bolt://host:port |
| description | string | 否 | 数据库描述 |
## 请求示例
```json
{
"host": "localhost",
"port": 7687,
"username": "neo4j",
"password": "password",
"database": "neo4j",
"name": "My Neo4j Database"
}
```
## 返回参数
| 参数 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否连接成功 |
| message | string | 消息 |
| version | string | Neo4j 版本 |
| databases | array | 数据库列表 |
| databaseId | string | 数据库记录 ID新增 |
| name | string | 数据库名称(新增) |
| description | string | 数据库描述(新增) |
## 返回示例
```json
{
"success": true,
"message": "connection successful",
"version": "5.14.0",
"databases": ["neo4j", "system"],
"databaseId": "abc-123-def",
"name": "Neo4j-neo4j",
"description": "Neo4j neo4j@localhost:7687"
}
```
> **说明**:连接成功时,后端会自动检查数据库记录是否存在,不存在则创建并返回 `databaseId`、`name` 和 `description`。前端可使用这些信息进行后续保存图谱操作。
---
# Neo4j 获取图谱概览数据(核心接口)
获取所有标签(Labels)和关系类型(Relationship Types)的统计信息,用于前端图谱可视化。
## 接口地址
```
POST /neo4j/graphs
```
## 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| uri | string | 是 | Neo4j 连接地址,如 bolt://localhost:7687 |
| username | string | 是 | 用户名 |
| password | string | 是 | 密码 |
| database | string | 否 | 数据库名(默认 neo4j |
## 请求示例
```json
{
"uri": "bolt://localhost:7687",
"username": "neo4j",
"password": "password",
"database": "neo4j"
}
```
## 返回参数
| 参数 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| graphs | object | 图谱数据 |
### graphs 对象
| 参数 | 类型 | 说明 |
|------|
| labels------|------| | array | 标签列表 |
| relationshipTypes | array | 关系类型列表 |
### labels 数组项
| 参数 | 类型 | 说明 |
|------|------|------|
| name | string | 标签名称 |
| count | int | 该标签的节点数量 |
### relationshipTypes 数组项
| 参数 | 类型 | 说明 |
|------|------|------|
| name | string | 关系类型名称 |
| count | int | 该关系的数量 |
## 返回示例
```json
{
"success": true,
"graphs": {
"labels": [
{"name": "User", "count": 1523},
{"name": "Order", "count": 856},
{"name": "Product", "count": 2341},
{"name": "Category", "count": 45},
{"name": "Review", "count": 5678}
],
"relationshipTypes": [
{"name": "KNOWS", "count": 2341},
{"name": "BOUGHT", "count": 5678},
{"name": "BELONGS_TO", "count": 2341},
{"name": "HAS_REVIEW", "count": 5678},
{"name": "LOCATED_IN", "count": 1523}
]
}
}
```
## 前端使用说明
前端使用 ECharts 力导向图谱展示:
- `labels` 生成图谱节点count 决定节点大小
- `relationshipTypes` 生成图谱边
- 建议返回至少 5-10 个关系类型以便生成丰富图谱
---
# Neo4j 获取节点详情
## 接口地址
```
POST /neo4j/nodes
```
## 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| uri | string | 是 | Neo4j 连接地址,如 bolt://localhost:7687 |
| username | string | 是 | 用户名 |
| password | string | 是 | 密码 |
| database | string | 否 | 数据库名 |
| label | string | 是 | 节点标签名 |
| limit | int | 否 | 返回数量限制,默认 10 |
## 请求示例
```json
{
"uri": "bolt://localhost:7687",
"username": "neo4j",
"password": "password",
"label": "User",
"limit": 10
}
```
## 返回参数
| 参数 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| message | string | 消息 |
| nodes | array | 节点数据列表 |
## 返回示例
```json
{
"success": true,
"message": "success",
"nodes": [
{"id": "1", "name": "张三", "email": "zhangsan@example.com"},
{"id": "2", "name": "李四", "email": "lisi@example.com"}
]
}
```
---
# Neo4j 获取关系详情
## 接口地址
```
POST /neo4j/relationships
```
## 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| uri | string | 是 | Neo4j 连接地址 |
| username | string | 是 | 用户名 |
| password | string | 是 | 密码 |
| database | string | 否 | 数据库名 |
| relationship_type | string | 是 | 关系类型名 |
| limit | int | 否 | 返回数量限制,默认 10 |
## 请求示例
```json
{
"uri": "bolt://localhost:7687",
"username": "neo4j",
"password": "password",
"relationship_type": "KNOWS",
"limit": 10
}
```
## 返回参数
| 参数 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| message | string | 消息 |
| relationships | array | 关系数据列表 |
## 返回示例
```json
{
"success": true,
"message": "success",
"relationships": [
{
"startId": "1",
"endId": "2",
"startLabels": ["User"],
"endLabels": ["User"],
"since": "2020-01-01"
}
]
}
```

View File

@@ -0,0 +1,75 @@
# Neo4j 图谱保存接口需求
## 需求说明
前端需要保存 Neo4j 图谱的连接信息,以便后续快速加载和查看。
---
## 接口地址
```
POST /database/graph/save
```
## 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| databaseId | string | 是 | 数据库 ID |
| databaseName | string | 是 | 数据库名称 |
| uri | string | 是 | Neo4j 连接地址,如 bolt://localhost:7687 |
| username | string | 是 | 用户名 |
| labels | array | 是 | 标签列表 |
| relationshipTypes | array | 是 | 关系类型列表 |
| selectedLabel | string | 否 | 当前选中的标签 |
## 请求示例
```json
{
"databaseId": "123",
"databaseName": "neo4j",
"uri": "bolt://10.10.10.189:7687",
"username": "neo4j",
"labels": ["User", "Order", "Product"],
"relationshipTypes": ["KNOWS", "BOUGHT", "BELONGS_TO"],
"selectedLabel": "User"
}
```
## 返回参数
| 参数 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| message | string | 消息 |
## 返回示例
```json
{
"success": true,
"message": "保存成功"
}
```
---
## 前端调用示例
```javascript
fetch('/database/graph/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
databaseId: '123',
databaseName: 'neo4j',
uri: 'bolt://10.10.10.189:7687',
username: 'neo4j',
labels: ['User', 'Order', 'Product'],
relationshipTypes: ['KNOWS', 'BOUGHT', 'BELONGS_TO'],
selectedLabel: 'User',
}),
})
```

View File

@@ -0,0 +1,75 @@
# 获取子表列表
## 接口地址
```
GET /sub-table/database/:database_id
```
## 路径参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| database_id | string | 是 | 数据库ID |
## 返回参数
| 参数 | 类型 | 说明 |
|------|------|------|
| list | array | 子表列表 |
### list[] 详情
| 参数 | 类型 | 说明 |
|------|------|------|
| id | string | 子表ID |
| database_id | string | 关联的数据库ID |
| parent_table | string | 原始表名 |
| sub_table_name | string | 子表别名 |
| sub_table_comment | string | 子表注释 |
| mapping_type | string | 映射类型 |
| relation_field | string | 关联字段 |
| relation_type | string | 关联类型 |
| fields | array | 字段映射列表 |
| ddl | string | 建表 DDL带 COMMENT |
| created_at | string | 创建时间 |
### fields[] 详情
| 参数 | 类型 | 说明 |
|------|------|------|
| column_name | string | 列名 |
| mapped_name | string | 中文映射名 |
## 返回示例
```json
{
"list": [
{
"id": "xxx-xxx",
"database_id": "database-xxx",
"parent_table": "users",
"sub_table_name": "用户表",
"sub_table_comment": "用户信息",
"mapping_type": "horizontal",
"relation_field": "id",
"relation_type": "one_to_many",
"fields": [
{"column_name": "id", "mapped_name": "用户ID"},
{"column_name": "name", "mapped_name": "用户名"}
],
"ddl": "CREATE TABLE `users` (\n `id` int(10) unsigned NOT NULL COMMENT '用户ID'\n ...\n)",
"created_at": "2026-03-06T15:00:00Z"
}
]
}
```
## 使用场景
用于恢复映射状态:
1. 用户点击已存在的数据库的 "Map Tables" 按钮
2. 调用此接口获取已保存的子表信息
3. 根据 `parent_table` 勾选已选择的表
4. 根据 `fields` 恢复字段映射

96
teams/api/upload-api.md Normal file
View File

@@ -0,0 +1,96 @@
# 文件上传 API
## 基础信息
| 项目 | 说明 |
|------|------|
| 基础URL | `http://localhost:8082` |
| 上传模式 | local / minio配置决定 |
## 配置说明
```yaml
# config.yaml
upload_mode: "local" # 上传模式local 或 minio
upload_local_path: "resource/files" # 本地存储路径
server_base_url: "http://localhost:8082" # 服务器基础URL
# MinIO 配置upload_mode 为 minio 时需要)
minio_endpoint: "localhost:9000"
minio_access_key: "your-access-key"
minio_secret_key: "your-secret-key"
minio_bucket: "x-agents"
minio_use_ssl: false
```
## 接口列表
### 1. 上传文件
**请求**
```
POST /api/file_upload
Content-Type: multipart/form-data
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| file | File | 是 | 要上传的文件 |
**响应**
```json
{
"success": true,
"url": "http://localhost:8082/files/abc123.pdf",
"fileKey": "abc123",
"message": "Upload successful"
}
```
**错误响应**
```json
{
"success": false,
"message": "File too large (max 100MB)"
}
```
---
### 2. 删除文件
**请求**
```
DELETE /api/file_upload/:filename
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| filename | String | 是 | 文件名(不含路径) |
**响应**
```json
{
"success": true,
"message": "File deleted"
}
```
---
### 3. 访问文件(仅本地模式)
文件上传后,本地模式下可通过以下 URL 直接访问:
```
GET /files/{filename}
```
例如:`http://localhost:8082/files/abc123.pdf`
> 注意MinIO 模式返回的是预签名 URL有效期 24 小时。

View File

@@ -0,0 +1,874 @@
# 多智能体群聊系统实现计划
## 项目概述
实现类似"一人公司"的多智能体群聊系统,支持多个 Agent 在群聊中讨论问题,类似于人类的团队会议。
### 核心特性
- **三阶段流程**: 观点提出 → 讨论完善 → CEO 总结决策
- **智能轮数**: AI 判断讨论是否充分,自动决定轮数
- **用户插话**: 用户可以随时打断发表意见
- **角色扮演**: 可配置不同角色的 AgentCEO、CTO、Designer 等)
- **复用架构**: 复用现有的 LLM、ToolRegistry、SessionManager
---
## 一、系统架构
### 1.1 整体架构图
```
┌─────────────────────────────────────────────────────────────────┐
│ Agent Group Chat System │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ GroupChatManager ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││
│ │ │ Presenter │ │ Discusser │ │ Summarizer │ ││
│ │ │ Controller │ │ Controller │ │ Controller │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────┘ ││
│ │ │ │ │ ││
│ │ └────────────────┴────────────────┘ ││
│ │ │ ││
│ │ SmartRoundController ││
│ └─────────────────────────────────────────────────────────────┘│
│ │ │
│ ┌──────────────────┼──────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ CEO Agent │ │ CTO Agent │ │Designer Agent│ │
│ │ Participant │ │ Participant │ │ Participant │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ └──────────────────┴──────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │GroupContext │ │
│ │(共享上下文) │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 1.2 核心组件
| 组件 | 职责 | 文件位置 |
|------|------|----------|
| **GroupChatManager** | 群聊管理器,控制整体流程 | `group_chat/manager.py` |
| **Participant** | 参与群聊的 Agent 实例 | `group_chat/participant.py` |
| **StageController** | 阶段控制器,管理各阶段 | `group_chat/stages/controller.py` |
| **PresenterStage** | 观点提出阶段 | `group_chat/stages/presenter.py` |
| **DiscusserStage** | 讨论完善阶段 | `group_chat/stages/discusser.py` |
| **SummarizerStage** | 总结决策阶段 | `group_chat/stages/summarizer.py` |
| **SmartRoundController** | 智能轮数控制 | `group_chat/round_controller.py` |
| **GroupContext** | 共享讨论上下文 | `group_chat/context.py` |
| **GroupMessage** | 群聊消息 | `group_chat/message.py` |
---
## 二、数据结构设计
### 2.1 消息定义
```python
# group_chat/message.py
from typing import Optional
from pydantic import BaseModel, Field
from datetime import datetime
from enum import Enum
class MessageStage(str, Enum):
"""消息所属阶段"""
PRESENT = "present" # 观点提出
DISCUSS = "discuss" # 讨论完善
SUMMARIZE = "summarize" # 总结决策
class GroupMessage(BaseModel):
"""群聊消息"""
id: str = Field(..., description="消息唯一标识")
agent_id: str = Field(..., description="Agent ID")
agent_name: str = Field(..., description="Agent 名称")
agent_role: str = Field(..., description="Agent 角色")
content: str = Field(..., description="消息内容")
timestamp: datetime = Field(default_factory=datetime.now)
stage: MessageStage = Field(..., description="所属阶段")
round: int = Field(default=1, description="轮数")
replying_to: Optional[str] = Field(default=None, description="回复的消息ID")
class UserInterruption(BaseModel):
"""用户插话"""
id: str
content: str
timestamp: datetime = Field(default_factory=datetime.now)
stage: MessageStage
round: int
```
### 2.2 共享上下文
```python
# group_chat/context.py
from typing import Optional
from pydantic import BaseModel, Field
from .message import GroupMessage, UserInterruption
class GroupContext(BaseModel):
"""群聊共享上下文"""
topic: str = Field(..., description="讨论主题")
stage: str = Field(default="present", description="当前阶段")
round: int = Field(default=1, description="当前轮数")
messages: list[GroupMessage] = Field(default_factory=list)
user_interruptions: list[UserInterruption] = Field(default_factory=list)
final_decision: Optional[str] = Field(default=None, description="最终决策")
status: str = Field(default="running", description="running/completed/failed")
class GroupState(BaseModel):
"""群聊状态"""
context: GroupContext
participants: dict[str, dict] # {agent_id: config}
config: dict # 群聊配置
```
---
## 三、核心实现
### 3.1 Agent 角色定义
```python
# group_chat/roles.py
from typing import Optional
# 默认角色配置
DEFAULT_ROLES = {
"ceo": {
"id": "ceo",
"name": "CEO",
"role": "首席执行官",
"system_prompt": """你是一家公司的 CEO负责制定公司战略方向和重大决策。
你的特点是:高瞻远瞩、战略思维、注重长远利益。
在讨论中,你应该:
- 从公司整体战略角度分析问题
- 权衡短期和长期利益
- 做最终决策并承担责任
- 善于总结和归纳各方观点""",
"stages": ["present", "summarize"],
"is_final_decider": True,
"priority": 1
},
"cto": {
"id": "cto",
"name": "CTO",
"role": "首席技术官",
"system_prompt": """你是一家公司的 CTO负责技术决策和技术团队管理。
你的特点是:技术深厚、注重可行性、关注技术趋势。
在讨论中,你应该:
- 从技术角度分析方案的可行性和风险
- 提供技术实现建议
- 评估技术选型
- 关注系统的可扩展性和稳定性""",
"stages": ["present", "discuss"],
"is_final_decider": False,
"priority": 2
},
"designer": {
"id": "designer",
"name": "Designer",
"role": "产品设计师",
"system_prompt": """你是公司的产品设计师,负责用户体验和界面设计。
你的特点是:创意丰富、注重用户体验、审美在线。
在讨论中,你应该:
- 从用户角度分析问题
- 提出用户体验优化建议
- 关注产品细节
- 平衡美观和实用性""",
"stages": ["present", "discuss"],
"is_final_decider": False,
"priority": 3
},
"analyst": {
"id": "analyst",
"name": "Analyst",
"role": "数据分析师",
"system_prompt": """你是公司的数据分析师,负责数据分析和决策支持。
你的特点是:数据敏感、逻辑严谨、注重证据。
在讨论中,你应该:
- 用数据支撑观点
- 提供数据分析结果
- 指出数据驱动的机会和风险
- 关注关键指标""",
"stages": ["present", "discuss"],
"is_final_decider": False,
"priority": 4
}
}
```
### 3.2 Participant参与者 Agent
```python
# group_chat/participant.py
import uuid
from datetime import datetime
from typing import Optional
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import HumanMessage, SystemMessage
from .message import GroupMessage, MessageStage
from .context import GroupContext
class Participant:
"""参与群聊的 Agent"""
def __init__(
self,
llm: BaseChatModel,
config: dict,
stage: str = "present"
):
self.llm = llm
self.config = config
self.id = config["id"]
self.name = config["name"]
self.role = config["role"]
self.system_prompt = config["system_prompt"]
self.stages = config.get("stages", ["present", "discuss", "summarize"])
self.is_final_decider = config.get("is_final_decider", False)
async def generate_message(
self,
context: GroupContext,
stage: MessageStage,
round_num: int,
replying_to: Optional[str] = None
) -> GroupMessage:
"""生成消息"""
# 构建 prompt
prompt = self._build_prompt(context, stage, round_num)
# 调用 LLM
response = await self.llm.ainvoke([
SystemMessage(content=prompt),
HumanMessage(content=f"请就「{context.topic}」发表你的观点。")
])
content = response.content if hasattr(response, 'content') else str(response)
# 创建消息
message = GroupMessage(
id=str(uuid.uuid4()),
agent_id=self.id,
agent_name=self.name,
agent_role=self.role,
content=content,
timestamp=datetime.now(),
stage=stage,
round=round_num,
replying_to=replying_to
)
return message
def _build_prompt(self, context: GroupContext, stage: MessageStage, round_num: int) -> str:
"""构建 prompt"""
base_prompt = self.system_prompt
# 添加上下文
context_info = f"""
## 当前讨论
主题:{context.topic}
阶段:{stage.value}
轮数:{round_num}
## 历史消息
"""
# 添加历史消息(限制数量)
recent_messages = context.messages[-10:] if context.messages else []
for msg in recent_messages:
context_info += f"\n{msg.agent_name}{msg.content}\n"
# 添加用户插话
if context.user_interruptions:
context_info += "\n## 用户插话\n"
for interruption in context.user_interruptions[-3:]:
context_info += f"\n【用户】{interruption.content}\n"
# 阶段特定的指令
stage_instruction = {
"present": "请简洁地提出你的观点和建议。",
"discuss": "请回应其他人的观点,进行讨论和完善。",
"summarize": "请总结各方观点,给出最终决策建议。"
}
full_prompt = f"{base_prompt}\n\n{context_info}\n\n{stage_instruction.get(stage.value, '')}"
return full_prompt
def can_participate(self, stage: MessageStage) -> bool:
"""判断是否可以参与当前阶段"""
return stage.value in self.stages
```
### 3.3 智能轮数控制器
```python
# group_chat/round_controller.py
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import SystemMessage, HumanMessage
from .context import GroupContext
from .message import MessageStage
class SmartRoundController:
"""智能轮数控制器"""
def __init__(self, llm: BaseChatModel, max_rounds: int = 3):
self.llm = llm
self.max_rounds = max_rounds
async def should_continue(
self,
context: GroupContext,
stage: MessageStage
) -> tuple[bool, str]:
"""
判断是否继续下一轮
Returns:
(是否继续, 原因)
"""
# 达到最大轮数
if context.round >= self.max_rounds:
return False, "max_rounds_reached"
# 构建判断 prompt
prompt = self._build_judge_prompt(context, stage)
# 调用 LLM 判断
response = await self.llm.ainvoke([
SystemMessage(content=prompt),
HumanMessage(content="请判断讨论是否已经充分,是否需要更多轮数。")
])
content = response.content.lower() if hasattr(response, 'content') else str(response)
# 解析判断结果
if "充分" in content or "足够" in content or "不需要" in content:
return False, "discussion_sufficient"
elif "不充分" in content or "不够" in content or "需要" in content:
return True, "need_more_discussion"
# 默认继续
return True, "default_continue"
def _build_judge_prompt(self, context: GroupContext, stage: MessageStage) -> str:
"""构建判断 prompt"""
messages_summary = "\n".join([
f"{msg.agent_name}{msg.content[:200]}..."
for msg in context.messages[-5:]
])
return f"""你是一个讨论质量评估专家。请判断当前的讨论是否已经充分。
## 讨论信息
主题:{context.topic}
当前阶段:{stage.value}
当前轮数:{context.round}
最大轮数:{self.max_rounds}
## 最近的讨论内容
{messages_summary}
## 判断标准
- 各方观点是否已经充分表达?
- 是否有建设性的讨论和回应?
- 是否已经形成明确的结论或方向?
## 请输出
如果讨论已经充分,请输出"充分",并简要说明原因。
如果还需要更多讨论,请输出"不充分",并说明需要讨论哪些方面。
"""
```
### 3.4 群聊管理器
```python
# group_chat/manager.py
import uuid
from typing import Optional
from datetime import datetime
from langchain_core.language_models import BaseChatModel
from .context import GroupContext
from .message import GroupMessage, MessageStage, UserInterruption
from .participant import Participant
from .roles import DEFAULT_ROLES
from .round_controller import SmartRoundController
class GroupChatManager:
"""群聊管理器"""
def __init__(
self,
llm: BaseChatModel,
roles: dict = None,
max_rounds: int = 3,
enable_user_interrupt: bool = True
):
self.llm = llm
self.roles = roles or DEFAULT_ROLES
self.max_rounds = max_rounds
self.enable_user_interrupt = enable_user_interrupt
# 初始化组件
self.round_controller = SmartRoundController(llm, max_rounds)
# 运行时状态
self.context: Optional[GroupContext] = None
self.participants: dict[str, Participant] = {}
async def start_chat(self, topic: str) -> dict:
"""开始群聊"""
# 创建上下文
self.context = GroupContext(
topic=topic,
stage="present",
round=1,
messages=[],
user_interruptions=[],
status="running"
)
# 创建参与者
self.participants = {
role_id: Participant(self.llm, config)
for role_id, config in self.roles.items()
}
# 按优先级排序参与者
sorted_participants = sorted(
self.participants.values(),
key=lambda p: p.config.get("priority", 999)
)
# 开始第一阶段
result = await self._run_stage(MessageStage.PRESENT, sorted_participants)
return result
async def _run_stage(
self,
stage: MessageStage,
participants: list[Participant]
) -> dict:
"""运行指定阶段"""
# 更新上下文
self.context.stage = stage.value
# 按阶段获取参与者
stage_participants = [p for p in participants if p.can_participate(stage)]
# 执行多轮
for round_num in range(1, self.max_rounds + 1):
self.context.round = round_num
# 当前阶段参与者发言
messages = await self._run_round(stage, stage_participants, round_num)
# 智能判断是否继续
should_continue, reason = await self.round_controller.should_continue(
self.context, stage
)
if not should_continue:
break
# 阶段完成后的处理
if stage == MessageStage.PRESENT:
# 进入讨论阶段
if any(p.can_participate(MessageStage.DISCUSS) for p in participants):
return await self._run_stage(MessageStage.DISCUSS, participants)
elif stage == MessageStage.DISCUSS:
# 进入总结阶段
summarizer = next(
(p for p in participants if p.is_final_decider),
participants[0]
)
return await self._run_stage(MessageStage.SUMMARIZE, [summarizer])
elif stage == MessageStage.SUMMARIZE:
# 完成
self.context.status = "completed"
return self._build_result()
async def _run_round(
self,
stage: MessageStage,
participants: list[Participant],
round_num: int
) -> list[GroupMessage]:
"""运行一轮发言"""
messages = []
for participant in participants:
# 获取前一轮的最新消息(用于回复)
replying_to = None
if self.context.messages:
last_message = self.context.messages[-1]
if last_message.agent_id != participant.id:
replying_to = last_message.id
# 生成消息
message = await participant.generate_message(
context=self.context,
stage=stage,
round_num=round_num,
replying_to=replying_to
)
# 保存消息
self.context.messages.append(message)
messages.append(message)
return messages
async def add_interruption(self, content: str) -> dict:
"""添加用户插话"""
if not self.enable_user_interrupt:
return {"error": "用户插话已禁用"}
interruption = UserInterruption(
id=str(uuid.uuid4()),
content=content,
timestamp=datetime.now(),
stage=MessageStage(self.context.stage),
round=self.context.round
)
self.context.user_interruptions.append(interruption)
return {"success": True, "interruption_id": interruption.id}
def get_messages(self) -> list[GroupMessage]:
"""获取所有消息"""
return self.context.messages if self.context else []
def get_result(self) -> dict:
"""获取结果"""
return self._build_result()
def _build_result(self) -> dict:
"""构建结果"""
return {
"topic": self.context.topic,
"status": self.context.status,
"messages": [
{
"agent_name": m.agent_name,
"agent_role": m.agent_role,
"content": m.content,
"stage": m.stage.value,
"round": m.round
}
for m in self.context.messages
],
"final_decision": self.context.final_decision,
"user_interruptions": [
{"content": i.content, "timestamp": i.timestamp.isoformat()}
for i in self.context.user_interruptions
]
}
```
---
## 四、阶段实现
### 4.1 Presenter Stage观点提出
```python
# group_chat/stages/presenter.py
from typing import list
from ..participant import Participant
from ..message import GroupMessage
class PresenterStage:
"""观点提出阶段"""
async def execute(
self,
participants: list[Participant],
context
) -> list[GroupMessage]:
"""执行观点提出"""
messages = []
# 按优先级顺序发言
for participant in participants:
if not participant.can_participate("present"):
continue
message = await participant.generate_message(
context=context,
stage="present",
round=context.round
)
messages.append(message)
context.messages.append(message)
return messages
```
### 4.2 Discusser Stage讨论完善
```python
# group_chat/stages/discusser.py
from ..participant import Participant
from ..message import GroupMessage
class DiscusserStage:
"""讨论完善阶段"""
async def execute(
self,
participants: list[Participant],
context
) -> list[GroupMessage]:
"""执行讨论完善"""
messages = []
# 获取上一轮的消息
last_round_messages = [
m for m in context.messages
if m.stage == "present" and m.round == context.round - 1
]
for participant in participants:
if not participant.can_participate("discuss"):
continue
# 选择要回复的消息
replying_to = None
for msg in reversed(last_round_messages):
if msg.agent_id != participant.id:
replying_to = msg.id
break
message = await participant.generate_message(
context=context,
stage="discuss",
round=context.round,
replying_to=replying_to
)
messages.append(message)
context.messages.append(message)
return messages
```
### 4.3 Summarizer Stage总结决策
```python
# group_chat/stages/summarizer.py
from ..participant import Participant
from ..message import GroupMessage
class SummarizerStage:
"""总结决策阶段"""
async def execute(
self,
participants: list[Participant],
context
) -> GroupMessage:
"""执行总结决策"""
# CEO 总结
summarizer = next(
(p for p in participants if p.is_final_decider),
participants[0]
)
message = await summarizer.generate_message(
context=context,
stage="summarize",
round=context.round
)
# 保存最终决策
context.final_decision = message.content
context.messages.append(message)
return message
```
---
## 五、与现有系统集成
### 5.1 复用现有组件
```python
# group_chat/integration.py
from typing import Optional
from app.llm.factory import LLMFactory
from app.agent.tools.registry import ToolRegistry
from app.agent.memory.session import SessionManager
from .manager import GroupChatManager
class GroupChatSystem:
"""群聊系统 - 集成现有组件"""
def __init__(
self,
llm_provider: str = "openai",
openai_api_key: Optional[str] = None,
anthropic_api_key: Optional[str] = None,
roles: dict = None,
max_rounds: int = 3,
enable_user_interrupt: bool = True
):
# 初始化 LLM Factory
self.llm_factory = LLMFactory(
provider=llm_provider,
openai_api_key=openai_api_key,
anthropic_api_key=anthropic_api_key
)
# 初始化 Tool Registry
self.tool_registry = ToolRegistry()
# 初始化 Session Manager
self.session_manager = SessionManager()
# 配置
self.roles = roles
self.max_rounds = max_rounds
self.enable_user_interrupt = enable_user_interrupt
async def start_group_chat(
self,
topic: str,
session_id: str = None
) -> dict:
"""开始群聊"""
# 获取 LLM
llm = self.llm_factory.get_llm()
# 创建群聊管理器
manager = GroupChatManager(
llm=llm,
roles=self.roles,
max_rounds=self.max_rounds,
enable_user_interrupt=self.enable_user_interrupt
)
# 开始群聊
result = await manager.start_chat(topic)
# 保存到 session
if session_id:
self.session_manager.add_message(session_id, "user", topic)
self.session_manager.add_message(
session_id,
"assistant",
result.get("final_decision", str(result))
)
return result
async def add_message(
self,
message: str,
session_id: str
) -> dict:
"""添加用户消息(插话)"""
# 获取 session 中的 manager
# ... 实现获取逻辑
return manager.add_interruption(message)
```
---
## 六、文件结构
```
agent/app/agent/multi/
├── group_chat/
│ ├── __init__.py
│ ├── roles.py # Agent 角色定义
│ ├── message.py # 消息类型
│ ├── context.py # 共享上下文
│ ├── participant.py # 参与者 Agent
│ ├── manager.py # 群聊管理器
│ ├── round_controller.py # 智能轮数控制器
│ ├── stages/
│ │ ├── __init__.py
│ │ ├── controller.py # 阶段控制器
│ │ ├── presenter.py # 观点提出阶段
│ │ ├── discusser.py # 讨论完善阶段
│ │ └── summarizer.py # 总结决策阶段
│ └── integration.py # 与现有系统集成
```
---
## 七、实现顺序
1. **Phase 1: 基础架构**
- 定义数据类型 (message.py, context.py)
- 创建角色配置 (roles.py)
2. **Phase 2: 核心组件**
- 实现 Participant (participant.py)
- 实现 SmartRoundController (round_controller.py)
3. **Phase 3: 阶段实现**
- 实现 PresenterStage
- 实现 DiscusserStage
- 实现 SummarizerStage
4. **Phase 4: 集成**
- 实现 GroupChatManager
- 与现有系统集成
---
## 八、测试计划
1. **单元测试**: 测试各 Participant 的消息生成
2. **集成测试**: 测试完整的群聊流程
3. **轮数控制测试**: 测试智能轮数判断
4. **用户插话测试**: 测试插话机制
5. **端到端测试**: 模拟真实群聊场景

View File

@@ -0,0 +1,80 @@
# Notes: 多智能体群聊系统研究
## 核心概念
### 群聊系统 vs 之前的 Supervisor 系统
| 特性 | Supervisor 系统 | 群聊系统 |
|------|----------------|----------|
| 流程 | 线性:规划 → 执行 → 汇总 | 多阶段循环 |
| Agent 关系 | 层级Supervisor 管理 Workers | 平等协作 |
| 通信方式 | 单向:任务分发 | 多向:互相讨论 |
| 决策方式 | Supervisor 决定 | CEO 最终决策 |
| 用户参与 | 旁观 | 可插话 |
### 设计模式
#### 1. 流水线模式
```
Stage 1 (Presenter) → Stage 2 (Discusser) → Stage 3 (Summarizer)
```
#### 2. 消息传递
- 每个 Stage 维护一个消息队列
- Agent 的输出成为下一个 Agent 的输入
- 使用 Shared Context 存储共享状态
#### 3. 智能轮数
```python
class SmartRoundController:
def should_continue(self, stage, round_num, messages):
# 使用 LLM 判断是否继续
prompt = f"""
当前阶段: {stage}
当前轮数: {round_num}
讨论内容: {messages}
讨论是否已经充分?是否需要更多轮数?
"""
return llm.judge(prompt)
```
## 复用现有架构
### 可复用的组件
1. **LLM Factory** - 语言模型
2. **Tool Registry** - 工具注册
3. **Session Manager** - 会话管理
4. **Agent Executor** - Agent 执行逻辑(部分)
### 需要新增的组件
1. **GroupChatManager** - 群聊管理器
2. **Participant** - 参与者 Agent
3. **Stage Controller** - 阶段控制器
4. **SmartRoundController** - 智能轮数控制器
## 关键数据结构
### GroupMessage
```python
class GroupMessage(BaseModel):
id: str
agent_id: str
agent_name: str
content: str
timestamp: datetime
stage: str # presenter/discusser/summarizer
round: int
replying_to: Optional[str] # 回复的消息 ID
```
### GroupContext
```python
class GroupContext(BaseModel):
topic: str # 讨论主题
stage: str # 当前阶段
round: int # 当前轮数
messages: list[GroupMessage] # 所有消息
user_interruptions: list[str] # 用户插话
final_decision: Optional[str] # 最终决策
```

View File

@@ -0,0 +1,32 @@
# Task Plan: 多智能体群聊系统实现计划
## Goal
实现类似"一人公司"的多智能体群聊系统,支持头脑风暴、任务协作、角色扮演等多模式讨论。
## Phases
- [x] Phase 1: 基础架构设计和核心组件规划
- [ ] Phase 2: 群聊管理器 (GroupChatManager) 实现
- [ ] Phase 3: 参与 Agent (Participant) 实现
- [ ] Phase 4: 三个阶段实现 (Presenter/Discusser/Summarizer)
- [ ] Phase 5: 智能轮数控制实现
- [ ] Phase 6: 用户插话机制实现
- [ ] Phase 7: 与现有系统集成和 API 接口
## Key Questions
1. 如何复用现有的 Supervisor + Workers 架构?
2. 如何实现智能轮数控制?
3. 如何处理用户插话?
## Decisions Made
- 架构:任务流水线模式(观点提出 → 讨论完善 → 总结决策)
- 决策机制CEO Agent 最终决策
- 轮数控制AI 智能判断
- 用户参与:插话模式
- 复用策略:复用现有 LLM、ToolRegistry、SessionManager
## Status
**Currently in Phase 1** - 系统架构设计和核心组件规划已完成
## 实现计划文件
- `group_chat_implementation_plan.md` - 详细实现计划
- `group_chat_notes.md` - 研究笔记

View File

@@ -0,0 +1,709 @@
# 多智能体联动系统实现计划
## 项目概述
基于 LangGraph 实现类似 OpenClaw 的多智能体协作系统,采用 Supervisor + Workers 层级架构。
### 核心特性
- **任务规划**: Supervisor 分析任务并生成执行计划
- **动态分发**: LLM 自主决策调用哪个 Worker
- **并行执行**: 支持多个 Worker 同时处理任务
- **结果汇总**: Supervisor 汇总所有 Worker 结果
- **迭代优化**: 支持 Review 机制和迭代重试
---
## 一、系统架构
### 1.1 整体架构图
```
┌─────────────────────────────────────────────────────────────────┐
│ MultiAgentSystem │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Supervisor Agent │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Planner │ │ Dispatcher │ │ Aggregator │ │ │
│ │ │ (任务规划) │ │ (任务分发) │ │ (结果汇总) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────┼──────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Research │ │ Coder │ │ Review │ │
│ │ Worker │ │ Worker │ │ Worker │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ └──────────────────┴──────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Shared State │ │
│ │ (共享状态) │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 1.2 核心组件
| 组件 | 职责 | 文件位置 |
|------|------|----------|
| **SupervisorAgent** | 任务分析、规划、分发、汇总 | `agent/multi/supervisor.py` |
| **BaseWorker** | Worker 基类,定义执行接口 | `agent/multi/workers/base.py` |
| **ResearchWorker** | 信息搜索和调研 | `agent/multi/workers/research.py` |
| **CoderWorker** | 代码编写和修改 | `agent/multi/workers/coder.py` |
| **ReviewWorker** | 结果检查和评审 | `agent/multi/workers/review.py` |
| **SharedState** | 跨 Agent 共享状态 | `agent/multi/state.py` |
| **TaskQueue** | 任务队列管理 | `agent/multi/queue.py` |
| **MultiAgentGraph** | LangGraph 流程编排 | `agent/multi/graph.py` |
---
## 二、数据结构设计
### 2.1 Agent State 定义
```python
# agent/multi/types.py
from typing import TypedDict, Annotated, Optional
from operator import add
from pydantic import BaseModel
class TaskItem(BaseModel):
"""单个任务项"""
id: str
description: str
assigned_agent: str # research / coder / review
status: str # pending / running / completed / failed
result: Optional[dict] = None
error: Optional[str] = None
retry_count: int = 0
class AgentState(TypedDict):
"""贯穿整个图的 Agent 状态"""
# 用户输入
original_task: str # 原始任务描述
# 任务规划
task_plan: list[TaskItem] # 分解后的任务列表
current_task_index: int # 当前执行的任务索引
# 执行结果
results: dict # {task_id: result}
# 流程控制
iteration: int # 当前迭代次数
next_node: str # 下一个节点名称
# 共享上下文
shared_context: dict # Agent 间共享的数据
# 最终输出
final_output: str
status: str # running / completed / failed
```
### 2.2 Supervisor 输出结构
```python
# Supervisor 的结构化输出
class SupervisorDecision(BaseModel):
"""Supervisor 的决策"""
analysis: str # 任务分析
task_plan: list[TaskItem] # 任务计划
need_aggregation: bool # 是否需要汇总
next_worker: str # 下一个执行的 Worker
```
---
## 三、核心实现
### 3.1 Supervisor Agent
```python
# agent/multi/supervisor.py
from langchain_core.language_models import BaseChatModel
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel
from typing import Type
from .types import AgentState, TaskItem, SupervisorDecision
from .prompts import SUPERVISOR_SYSTEM_PROMPT
class SupervisorAgent:
"""Supervisor Agent - 负责任务规划和分发"""
def __init__(
self,
llm: BaseChatModel,
max_iterations: int = 3
):
self.llm = llm
self.max_iterations = max_iterations
self.output_parser = PydanticOutputParser(pydantic_object=SupervisorDecision)
def create_node(self):
"""创建 Supervisor 节点"""
return self._supervisor_node
async def _supervisor_node(self, state: AgentState) -> dict:
"""Supervisor 节点逻辑"""
# 首次调用:分析任务并生成计划
if not state.get("task_plan"):
decision = await self._plan_tasks(state["original_task"])
return {
"task_plan": decision.task_plan,
"next_node": decision.next_worker,
"current_task_index": 0,
"shared_context": {"task_analysis": decision.analysis}
}
# 检查是否需要继续
current_task = state["task_plan"][state["current_task_index"]]
if current_task["status"] == "completed":
# 当前任务完成,检查是否还有更多任务
if state["current_task_index"] + 1 < len(state["task_plan"]):
next_index = state["current_task_index"] + 1
next_task = state["task_plan"][next_index]
return {
"current_task_index": next_index,
"next_node": next_task["assigned_agent"]
}
else:
# 所有任务完成,进入汇总
return {"next_node": "aggregate"}
elif current_task["status"] == "failed":
# 任务失败,检查是否超过最大重试
if current_task["retry_count"] >= self.max_iterations:
return {"next_node": "aggregate", "status": "failed"}
else:
# 重试
return {"next_node": current_task["assigned_agent"]}
return {"next_node": state.get("next_node", "aggregate")}
async def _plan_tasks(self, task: str) -> SupervisorDecision:
"""调用 LLM 生成任务计划"""
prompt = SUPERVISOR_SYSTEM_PROMPT.format(task=task)
response = await self.llm.ainvoke([
{"role": "system", "content": prompt},
{"role": "user", "content": "请分析任务并制定执行计划。"}
])
# 解析 LLM 输出为结构化决策
# ... (实现解析逻辑)
return decision
```
### 3.2 Worker 基类
```python
# agent/multi/workers/base.py
from abc import ABC, abstractmethod
from typing import Any
from langchain_core.language_models import BaseChatModel
from ..types import AgentState
class BaseWorker(ABC):
"""Worker Agent 基类"""
def __init__(
self,
llm: BaseChatModel,
name: str,
system_prompt: str,
tools: list = None
):
self.llm = llm
self.name = name
self.system_prompt = system_prompt
self.tools = tools or []
@abstractmethod
async def execute(self, task: TaskItem, context: dict) -> dict:
"""执行任务"""
pass
def create_node(self):
"""创建 LangGraph 节点"""
async def node(state: AgentState) -> dict:
task = state["task_plan"][state["current_task_index"]]
result = await self.execute(task, state.get("shared_context", {}))
# 更新状态
return {
"results": {task.id: result},
"task_plan": self._update_task_status(
state["task_plan"],
task.id,
"completed" if result.get("success") else "failed"
),
"shared_context": {**state.get("shared_context", {}), **result.get("context", {})}
}
return node
def _update_task_status(self, tasks: list, task_id: str, status: str) -> list:
"""更新任务状态"""
return [
{**task, "status": status} if task["id"] == task_id else task
for task in tasks
]
```
### 3.3 任务队列(可选:支持并行执行)
```python
# agent/multi/queue.py
import asyncio
from typing import Any, Callable
from dataclasses import dataclass
from enum import Enum
class TaskStatus(Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
@dataclass
class QueuedTask:
id: str
agent_name: str
task_data: Any
status: TaskStatus = TaskStatus.PENDING
result: Any = None
error: str = None
class TaskQueue:
"""任务队列 - 支持并行执行多个 Worker"""
def __init__(self, max_concurrent: int = 3):
self.max_concurrent = max_concurrent
self.queue: asyncio.Queue = asyncio.Queue()
self.results: dict = {}
self._running = 0
async def add_task(self, task: QueuedTask):
"""添加任务到队列"""
await self.queue.put(task)
async def execute_all(self, worker_factory: Callable):
"""执行所有任务"""
async def worker():
while True:
try:
task = self.queue.get_nowait()
except asyncio.QueueEmpty:
break
self._running += 1
task.status = TaskStatus.Running
try:
worker_instance = worker_factory(task.agent_name)
task.result = await worker_instance.execute(task.task_data)
task.status = TaskStatus.COMPLETED
except Exception as e:
task.status = TaskStatus.FAILED
task.error = str(e)
finally:
self._running -= 1
self.results[task.id] = task
# 启动多个 worker 协程
workers = [asyncio.create_task(worker()) for _ in range(self.max_concurrent)]
await asyncio.gather(*workers)
return self.results
```
### 3.4 LangGraph 流程编排
```python
# agent/multi/graph.py
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from .types import AgentState
from .supervisor import SupervisorAgent
from .workers.research import ResearchWorker
from .workers.coder import CoderWorker
from .workers.review import ReviewWorker
from .aggregator import ResultAggregator
def create_multi_agent_graph(
llm,
tool_registry,
max_iterations: int = 3
) -> StateGraph:
"""创建多 Agent 流程图"""
# 初始化组件
supervisor = SupervisorAgent(llm, max_iterations)
research_worker = ResearchWorker(llm, tool_registry)
coder_worker = CoderWorker(llm, tool_registry)
review_worker = ReviewWorker(llm, tool_registry)
aggregator = ResultAggregator(llm)
# 创建图
graph = StateGraph(AgentState)
# 添加节点
graph.add_node("supervisor", supervisor.create_node())
graph.add_node("research", research_worker.create_node())
graph.add_node("coder", coder_worker.create_node())
graph.add_node("review", review_worker.create_node())
graph.add_node("aggregate", aggregator.create_node())
# 设置入口
graph.set_entry_point("supervisor")
# 添加边
graph.add_edge("supervisor", "research")
graph.add_edge("research", "review")
graph.add_edge("coder", "review")
# 条件边:从 review 回到 supervisor
def should_continue(state: AgentState) -> str:
if state.get("status") == "failed":
return "aggregate"
if state.get("iteration", 0) >= max_iterations:
return "aggregate"
if state.get("current_task_index", 0) >= len(state.get("task_plan", [])):
return "aggregate"
return "supervisor"
graph.add_conditional_edges(
"review",
should_continue,
{
"supervisor": "supervisor",
"aggregate": "aggregate"
}
)
# 结束节点
graph.add_edge("aggregate", END)
return graph.compile()
```
---
## 四、Prompt 设计
### 4.1 Supervisor System Prompt
```python
# agent/multi/prompts.py
SUPERVISOR_SYSTEM_PROMPT = """你是一个任务规划专家Supervisor。你的职责是将复杂任务分解为可执行的子任务并分配给合适的执行 Agent。
## 可用的 Worker Agent
- **research**: 信息搜索和调研
- **coder**: 代码编写、修改和调试
- **review**: 结果检查、质量评审
## 任务
{task}
## 请按以下步骤执行
### 步骤 1: 任务分析
分析任务的性质,确定需要哪些步骤来完成。
### 步骤 2: 任务分解
将任务分解为独立的子任务。每个子任务应该:
- 描述清晰
- 可以由单个 Agent 完成
- 有明确的完成标准
### 步骤 3: 分配 Agent
为每个子任务选择最合适的执行 Agent。
### 步骤 4: 确定执行顺序
如果有依赖关系,确定正确的执行顺序。
## 输出格式
请以 JSON 格式输出你的决策:
```json
{{
"analysis": "任务分析...",
"task_plan": [
{{
"id": "task_1",
"description": "子任务描述",
"assigned_agent": "research"
}},
{{
"id": "task_2",
"description": "子任务描述",
"assigned_agent": "coder"
}}
],
"need_aggregation": true,
"next_worker": "research"
}}
```
## 注意
- 如果任务很简单,可以只分配给一个 Agent
- 如果任务需要迭代优化,确保有 review 环节
- 考虑任务之间的依赖关系
"""
```
### 4.2 Review Worker Prompt
```python
REVIEW_SYSTEM_PROMPT = """你是一个代码和结果评审专家Reviewer。你的职责是检查任务执行结果是否符合要求。
## 任务描述
{task_description}
## 执行结果
{execution_result}
## 检查标准
1. 结果是否完整解决了原始任务?
2. 输出格式是否正确?
3. 是否存在明显的错误或遗漏?
4. 代码是否有潜在问题?
## 请输出评审结果
```json
{{
"passed": true/false,
"issues": [
{{"severity": "high/medium/low", "description": "问题描述"}}
],
"suggestions": ["改进建议"]
}}
```
"""
```
---
## 五、迭代控制
### 5.1 迭代逻辑
```python
# agent/multi/iteration.py
from typing import Optional
class IterationController:
"""迭代控制器"""
def __init__(self, max_iterations: int = 3):
self.max_iterations = max_iterations
def should_continue(
self,
iteration: int,
task_status: str,
review_result: dict
) -> tuple[bool, str]:
"""
判断是否继续迭代
Returns:
(是否继续, 原因)
"""
# 超过最大迭代次数
if iteration >= self.max_iterations:
return False, "max_iterations_reached"
# 任务成功完成
if task_status == "completed" and review_result.get("passed"):
return False, "task_completed"
# 任务失败且不可重试
if task_status == "failed" and not review_result.get("retryable"):
return False, "task_failed"
# 需要重试
if review_result.get("issues") and review_result.get("passed") is False:
return True, "needs_retry"
return True, "continue"
def get_next_action(
self,
review_result: dict,
current_worker: str
) -> str:
"""确定下一步动作"""
if review_result.get("passed"):
return "supervisor" # 返回 Supervisor
# 根据问题类型决定下一步
issues = review_result.get("issues", [])
high_severity = any(i.get("severity") == "high" for i in issues)
if high_severity:
# 严重问题,重新执行相同任务
return current_worker
else:
# 轻微问题,可以继续
return "supervisor"
```
---
## 六、与现有系统集成
### 6.1 复用现有组件
```python
# agent/multi/integration.py
from app.agent.core.agent import AgentManager
from app.agent.tools.registry import ToolRegistry
from app.agent.memory.session import SessionManager
from app.llm.factory import LLMFactory
class MultiAgentSystem:
"""多智能体系统 - 集成现有组件"""
def __init__(self, config: dict):
# 复用现有 LLM Factory
self.llm_factory = LLMFactory(
provider=config.get("llm_provider", "openai"),
openai_api_key=config.get("openai_api_key"),
anthropic_api_key=config.get("anthropic_api_key")
)
# 复用现有 Tool Registry
self.tool_registry = ToolRegistry()
self._register_default_tools()
# 复用现有 Session Manager
self.session_manager = SessionManager()
# 配置
self.max_iterations = config.get("max_iterations", 3)
def _register_default_tools(self):
"""注册默认工具"""
# 从现有 Agent 复制工具注册逻辑
from app.agent.tools.impl import search, calculator
self.tool_registry.register(
name="search",
func=search.search_web,
description="Search the web",
security_level="safe"
)
# ... 其他工具
async def execute(self, task: str, session_id: str = None) -> dict:
"""执行多 Agent 任务"""
# 创建 LangGraph
from .graph import create_multi_agent_graph
llm = self.llm_factory.get_llm()
graph = create_multi_agent_graph(
llm=llm,
tool_registry=self.tool_registry,
max_iterations=self.max_iterations
)
# 初始化状态
initial_state = {
"original_task": task,
"task_plan": [],
"current_task_index": 0,
"results": {},
"iteration": 0,
"next_node": "supervisor",
"shared_context": {},
"final_output": "",
"status": "running"
}
# 执行
result = await graph.ainvoke(initial_state)
# 保存到 session
if session_id:
self.session_manager.add_message(session_id, "user", task)
self.session_manager.add_message(session_id, "assistant", result["final_output"])
return result
```
---
## 七、文件结构
```
agent/
├── __init__.py
├── multi/
│ ├── __init__.py
│ ├── types.py # 数据类型定义
│ ├── prompts.py # Prompt 模板
│ ├── supervisor.py # Supervisor Agent
│ ├── graph.py # LangGraph 流程图
│ ├── iteration.py # 迭代控制
│ ├── integration.py # 与现有系统集成
│ ├── queue.py # 任务队列(可选)
│ └── workers/
│ ├── __init__.py
│ ├── base.py # Worker 基类
│ ├── research.py # Research Worker
│ ├── coder.py # Coder Worker
│ └── review.py # Review Worker
```
---
## 八、实现顺序
1. **Phase 1: 基础架构**
- 定义数据类型 (types.py)
- 创建 Prompt 模板 (prompts.py)
2. **Phase 2: Supervisor**
- 实现 SupervisorAgent
- 实现任务规划和分发逻辑
3. **Phase 3: Workers**
- 实现 BaseWorker
- 实现 ResearchWorker
- 实现 CoderWorker
- 实现 ReviewWorker
4. **Phase 4: 流程编排**
- 实现 LangGraph 流程图
- 添加条件边和迭代控制
5. **Phase 5: 集成**
- 与现有 Agent 系统集成
- 添加 API 接口
---
## 九、测试计划
1. **单元测试**: 测试各 Worker 的执行逻辑
2. **集成测试**: 测试完整的 Supervisor + Workers 流程
3. **迭代测试**: 测试重试和迭代逻辑
4. **端到端测试**: 模拟真实任务执行

View File

@@ -0,0 +1,107 @@
# Notes: LangGraph 多智能体研究
## 核心概念
### LangGraph 基础
- **StateGraph**: 有向无环图DAG节点是 Agent/函数,边是流转逻辑
- **State**: 贯穿整个图流动的状态对象
- **Node**: 执行单元(可以是 Agent、函数、条件判断
- **Edge**: 连接节点的边支持条件边conditional edges
### Supervisor + Workers 模式参考
#### 1. LangChain 官方 Supervisor 示例
```python
from langgraph.prebuilt import create_react_agent
from langgraph.graph import StateGraph, END
# 定义 Workers
research_agent = create_react_agent(llm, tools=[search])
coder_agent = create_react_agent(llm, tools=[write_file])
# 定义 Supervisor 节点
def supervisor_node(state):
# LLM 决定下一步调用哪个 Agent
response = llm.with_structured_output(SupervisorOutput).invoke(
[SystemMessage(content=SUPERVISOR_PROMPT)] + state["messages"]
)
return {"next": response.next_agent}
# 构建图
graph = StateGraph(AgentState)
graph.add_node("supervisor", supervisor_node)
graph.add_node("research", research_agent)
graph.add_node("code", coder_agent)
```
#### 2. 状态定义
```python
from typing import TypedDict, Annotated
import operator
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
task: str
plan: list
results: dict
iteration: int
next: str # 控制下一步流向
```
#### 3. 条件边实现
```python
def should_continue(state):
if state["iteration"] >= MAX_ITERATIONS:
return "end"
if state.get("task_complete"):
return "end"
return "continue"
graph.add_conditional_edges(
"review",
should_continue,
{
"continue": "supervisor",
"end": END
}
)
```
## 设计决策
### 架构优势
1. **清晰的分层**: Supervisor 负责任务规划Workers 负责执行
2. **可扩展**: 容易添加新的 Worker 类型
3. **可控**: 迭代次数全局配置
4. **灵活**: 支持条件分支和循环
### 需要解决的问题
1. **Supervisor 如何做规划**: 需要设计 prompt 让 LLM 生成任务列表
2. **任务队列**: 需要支持并行分发多个 Worker
3. **共享上下文**: 需要设计数据结构在 Agent 间共享状态
4. **Review 机制**: 需要定义检查标准和重试逻辑
## 关键 Prompt 设计
### Supervisor System Prompt
```
你是一个任务规划专家Supervisor。用户的任务是{task}
请按以下步骤执行:
1. 分析任务需求和约束
2. 将任务分解为可执行的子任务
3. 为每个子任务选择合适的执行 Agent
- research: 信息搜索和调研
- coder: 代码编写和修改
- review: 结果检查和评审
4. 确定执行顺序和依赖关系
当前任务进度:{progress}
共享上下文:{context}
请输出你的决策,格式如下:
- 需要执行的子任务列表
- 每个任务的执行 Agent
- 任务执行顺序
- 是否需要汇总结果
```

View File

@@ -0,0 +1,33 @@
# Task Plan: 多智能体联动系统实现计划
## Goal
基于 LangGraph 实现类似 OpenClaw 的多智能体联动系统,支持任务规划、动态分发、结果汇总和迭代优化。
## Phases
- [x] Phase 1: 系统架构设计和核心组件规划
- [ ] Phase 2: Supervisor Agent 实现
- [ ] Phase 3: Worker Agent 实现
- [ ] Phase 4: 任务队列和共享上下文实现
- [ ] Phase 5: State Machine 流程控制实现
- [ ] Phase 6: 迭代控制和 Review 机制实现
- [ ] Phase 7: 与现有 Agent 系统集成
## Key Questions
1. 如何用 LangGraph 实现 Supervisor + Workers 架构?
2. 如何设计任务队列支持并行执行?
3. 如何实现共享上下文在 Agent 间传递?
4. 如何控制迭代次数和流程分支?
## Decisions Made
- 架构Supervisor + Workers 层级模式
- 协作方式LLM 自主决策任务分配
- 通信共享内存Shared Context
- 迭代控制:全局最大迭代次数配置
- Workers 定义:复用现有 tool_registry
## Status
**Currently in Phase 1** - 系统架构设计和核心组件规划已完成
## 实现计划文件
- `implementation_plan.md` - 详细的实现计划
- `notes.md` - LangGraph 研究笔记

92
teams/web/columns-api.md Normal file
View File

@@ -0,0 +1,92 @@
# 后端需求 - 表结构返回 columns 数据
## 问题描述
前端在 Edit Mapping 页面需要展示表的列信息字段名、类型、COMMENT等但前端自行解析 DDL 存在困难。
## 需求
后端在获取表结构列表时,需要同时返回:
1. **DDL 语句**(已有的需求,继续保留)
2. **结构化的 columns 数据**(新增)
### 返回数据结构
```json
{
"success": true,
"tables": [
{
"table_name": "exam_scores",
"table_comment": "考试成绩表",
"ddl": "CREATE TABLE `exam_scores` (...)",
"columns": [
{
"column_name": "id",
"data_type": "int",
"column_type": "int(10) unsigned",
"is_nullable": "NO",
"default_value": null,
"column_key": "PRI",
"extra": "auto_increment",
"column_comment": ""
},
{
"column_name": "student_id",
"data_type": "int",
"column_type": "int(10) unsigned",
"is_nullable": "NO",
"default_value": null,
"column_key": "",
"extra": "",
"column_comment": ""
},
{
"column_name": "subject",
"data_type": "varchar",
"column_type": "varchar(50)",
"is_nullable": "NO",
"default_value": null,
"column_key": "",
"extra": "",
"column_comment": "科目"
},
{
"column_name": "score",
"data_type": "double",
"column_type": "double",
"is_nullable": "YES",
"default_value": null,
"column_key": "",
"extra": "",
"column_comment": "分数"
}
]
}
]
}
```
### 字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| column_name | string | 列名 |
| data_type | string | 数据类型(如 int, varchar, double |
| column_type | string | 完整列类型(如 int(10) unsigned |
| is_nullable | string | 是否可空YES/NO |
| default_value | string | 默认值 |
| column_key | string | 主键标识PRI/MUL/UNI |
| extra | string | 额外信息(如 auto_increment |
| column_comment | string | 列注释 |
## 影响范围
- 文件:`server/internal/service/database_service.go`
- 函数:`getMySQLTables`, `getPostgresTables`
- 数据模型:`server/internal/model/sub_table_info.go``ColumnInfo` 结构体
## 优先级
高 - 前端 Edit Mapping 页面字段映射功能依赖此数据

66
teams/web/ddl-edit.md Normal file
View File

@@ -0,0 +1,66 @@
# 后端需求 - DDL 编辑功能
## 问题描述
前端 Database 页面的 Table Mapping 功能已从"字段映射"改为"DDL 编辑"模式。后端需要支持保存和读取 DDL 数据。
## 需求
### 1. 保存 DDL
前端保存时发送 `ddl` 字段而非 `fields` 字段。
请求结构:
```json
{
"name": "数据库名",
"sub_tables": [
{
"parent_table": "users",
"sub_table_name": "用户表",
"sub_table_comment": "用户表",
"ddl": "CREATE TABLE users (...)"
}
]
}
```
### 2. 后端处理
- `CreateSubTableRequest` 已有 `DDL string` 字段(已添加)
- `UpdateSubTableRequest` 已有 `DDL string` 字段(已添加)
- `UpdateDatabaseRequest` 已有 `SubTables []CreateSubTableRequest` 字段(已添加)
### 3. Service 层修改
**sub_table_service.go**
1. `Create` 函数 - 添加 DDL 字段赋值:
```go
info := &model.SubTableInfo{
// ... 现有字段
DDL: req.DDL,
}
```
2. `Update` 函数 - 添加 DDL 更新逻辑:
```go
// 更新 DDL
info.DDL = req.DDL
```
**database_service.go 或 handler**
`Update` 方法中处理 `SubTables` 字段:
- 当前端传入 `sub_tables` 时,需要创建或更新对应的子表记录(包括 DDL
- 遍历 `sub_tables`,调用 `SubTableService.Create``SubTableService.Update`
## 影响范围
- `server/internal/service/sub_table_service.go` - Create/Update 方法
- `server/internal/service/database_service.go` 或 handler - Update 方法处理 SubTables
## 状态
- [x] 前端修改完成
- [x] 后端修改已完成

View File

@@ -0,0 +1,89 @@
# 后端需求 - 字段映射保存与读取
## 问题描述
前端 Edit Mapping 页面中用户输入的字段中文映射名mapped_name在保存后第二次打开时丢失了。
## 原因分析
1. **保存时**:前端只保存了表级别信息,没有保存字段的中文映射
2. **加载时**:前端每次都从 `/database/check` 重新获取表结构,没有读取已保存的映射数据
## 需求
### 1. 保存字段映射
前端保存时需要传递每个字段的中文映射名,后端需要存储这些数据。
请求结构:
```json
{
"name": "数据库名",
"sub_tables": [
{
"parent_table": "users",
"sub_table_name": "用户表",
"sub_table_comment": "用户表",
"fields": [
{
"column_name": "id",
"mapped_name": "编号"
},
{
"column_name": "username",
"mapped_name": "用户名"
}
]
}
]
}
```
### 2. 返回字段映射
后端在返回表结构时,需要同时返回已保存的字段映射信息。
返回结构:
```json
{
"success": true,
"tables": [
{
"table_name": "users",
"table_comment": "用户表",
"ddl": "...",
"columns": [
{
"column_name": "id",
"data_type": "int",
"column_type": "int(10)",
"column_comment": "",
"mapped_name": "编号"
},
{
"column_name": "username",
"data_type": "varchar",
"column_type": "varchar(50)",
"column_comment": "用户名",
"mapped_name": "用户名"
}
]
}
]
}
```
### 3. 数据存储
- 可以在 `sub_table_info` 表中增加 `fields` JSON 字段存储字段映射
- 或者创建新的关联表 `sub_table_fields`
## 影响范围
- `server/internal/service/database_service.go` - Create/Update 方法
- `server/internal/model/` - 数据模型修改
- 子表映射的数据存储结构
## 优先级
高 - 用户输入的映射数据丢失影响使用体验

View File

@@ -0,0 +1,375 @@
# 知识库创建 API
## 基础信息
| 项目 | 说明 |
|------|------|
| 基础URL | `http://localhost:8082` |
| 前端页面 | Knowledge Base 创建弹窗 |
## 接口列表
### 1. 创建知识库
**请求**
```
POST /api/knowledge/create
Content-Type: application/json
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | String | 是 | 知识库名称 |
| description | String | 否 | 知识库描述 |
| llm_model_id | String | 是 | LLM 模型 ID来自 model 表) |
| embedding_model_id | String | 是 | Embedding 模型 ID来自 model 表) |
| parsing_config | Object | 是 | 解析配置 |
| - engine | String | 是 | 解析引擎markitdown / docling |
| - docling_url | String | 条件必填 | Docling 服务 URLengine=docling 时必填) |
| - enable_pdf | Boolean | 否 | 是否启用 PDF 解析(默认 true |
| - pandoc | Boolean | 否 | 是否启用 Pandoc默认 true |
| storage_config | Object | 否 | 存储配置(默认 local |
| - type | String | 是 | 存储类型local / minio / s3 |
| - endpoint | String | 否 | MinIO Endpoint如 minio:9000 |
| - access_key_id | String | 否 | MinIO Access Key ID |
| - secret_access_key | String | 否 | MinIO Secret Access Key |
| - bucket | String | 否 | MinIO Bucket 名称 |
**请求示例**
本地存储:
```json
{
"name": "产品文档知识库",
"description": "用于存储产品手册和文档",
"llm_model_id": "model_001",
"embedding_model_id": "model_002",
"parsing_config": {
"engine": "markitdown",
"enable_pdf": true,
"pandoc": true
},
"storage_config": {
"type": "local"
}
}
```
使用 Docling + MinIO
```json
{
"name": "产品文档知识库",
"description": "用于存储产品手册和文档",
"llm_model_id": "model_001",
"embedding_model_id": "model_002",
"parsing_config": {
"engine": "docling",
"docling_url": "http://localhost:8501",
"enable_pdf": true,
"pandoc": true
},
"storage_config": {
"type": "minio",
"endpoint": "localhost:9000",
"access_key_id": "minioadmin",
"secret_access_key": "minioadmin",
"bucket": "x-agents"
}
}
```
**成功响应**
```json
{
"success": true,
"id": "kb_abc123",
"message": "Knowledge base created successfully"
}
```
**错误响应**
```json
{
"success": false,
"message": "LLM model not found"
}
```
---
### 2. 获取知识库列表
**请求**
```
GET /api/knowledge/list
```
**响应**
```json
{
"success": true,
"data": [
{
"id": "kb_001",
"name": "产品文档知识库",
"description": "用于存储产品手册",
"llm_model_id": "model_001",
"embedding_model_id": "model_002",
"status": "active",
"document_count": 15,
"chunk_count": 156,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
]
}
```
---
### 3. 获取知识库详情
**请求**
```
GET /api/knowledge/:id
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | String | 是 | 知识库 ID |
**响应**
```json
{
"success": true,
"data": {
"id": "kb_001",
"name": "产品文档知识库",
"description": "用于存储产品手册",
"llm_model_id": "model_001",
"embedding_model_id": "model_002",
"parsing_config": {
"engine": "markitdown",
"enable_pdf": true,
"pandoc": true
},
"status": "active",
"document_count": 15,
"chunk_count": 156,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}
```
---
### 4. 删除知识库
**请求**
```
DELETE /api/knowledge/:id
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | String | 是 | 知识库 ID |
**响应**
```json
{
"success": true,
"message": "Knowledge base deleted"
}
```
---
### 5. 获取知识库下的文档列表
**请求**
```
GET /api/knowledge/:id/documents
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | String | 是 | 知识库 ID |
**查询参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| status | String | 否 | 过滤状态all / parsed / parsing / failed |
**响应**
```json
{
"success": true,
"data": [
{
"id": "doc_001",
"knowledge_base_id": "kb_001",
"name": "产品手册_v2.0.pdf",
"file_key": "abc123.pdf",
"file_url": "http://localhost:8082/files/abc123.pdf",
"file_size": 2516582,
"status": "parsed",
"chunk_count": 156,
"uploaded_at": "2024-01-15T10:30:00Z"
}
]
}
```
---
### 6. 上传文档到知识库
**请求**
```
POST /api/knowledge/:id/documents
Content-Type: multipart/form-data
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | String | 是 | 知识库 ID |
| file | File | 是 | 要上传的文件 |
**响应**
```json
{
"success": true,
"dataid": "doc_001",
": {
" "name": "产品手册_v2.0.pdf",
"status": "parsing"
}
}
```
---
### 7. 删除知识库文档
**请求**
```
DELETE /api/knowledge/:id/documents/:doc_id
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | String | 是 | 知识库 ID |
| doc_id | String | 是 | 文档 ID |
**响应**
```json
{
"success": true,
"message": "Document deleted"
}
```
---
### 8. 重新解析文档
**请求**
```
POST /api/knowledge/:id/documents/:doc_id/reparse
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | String | 是 | 知识库 ID |
| doc_id | String | 是 | 文档 ID |
**响应**
```json
{
"success": true,
"message": "Document reparse started"
}
```
---
### 9. 获取文档预览内容
**请求**
```
GET /api/knowledge/:id/documents/:doc_id/preview
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | String | 是 | 知识库 ID |
| doc_id | String | 是 | 文档 ID |
| page | Number | 否 | 页码(默认 1 |
**响应**
```json
{
"success": true,
"data": {
"total_pages": 3,
"current_page": 1,
"content": "第一章 产品介绍\n\n欢迎使用我们的产品手册..."
}
}
```
---
## 数据库表设计(参考)
### knowledge_base 表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | String | 主键 |
| name | String | 知识库名称 |
| description | Text | 描述 |
| llm_model_id | String | LLM 模型 ID |
| embedding_model_id | String | Embedding 模型 ID |
| parsing_config | JSON | 解析配置 |
| storage_config | JSON | 存储配置(包含 type, endpoint, access_key_id, secret_access_key, bucket |
| status | String | 状态active / inactive |
| document_count | Integer | 文档数量 |
| chunk_count | Integer | 切片数量 |
| created_at | Timestamp | 创建时间 |
| updated_at | Timestamp | 更新时间 |
### knowledge_document 表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | String | 主键 |
| knowledge_base_id | String | 知识库 ID |
| name | String | 文档名称 |
| file_key | String | 文件存储 key |
| file_url | String | 文件访问 URL本地路径或 MinIO 预签名 URL |
| file_size | BigInteger | 文件大小 |
| status | String | 状态parsing / parsed / failed |
| chunk_count | Integer | 切片数量 |
| uploaded_at | Timestamp | 上传时间 |

View File

@@ -0,0 +1,43 @@
# 后端需求 - 保存和恢复映射状态
## 问题描述
用户第一次选择表并设置字段映射后,第二次点击 "Map Tables" 按钮进入界面时,之前选择的表和设置的字段映射都丢失了。
## 需求
前端打开已存在的数据库映射时,需要恢复以下状态:
### 1. 已选择的表列表
后端需要在数据库记录中保存用户选择了哪些表(不仅仅是子表信息),或者在查询时返回该数据库关联的所有子表。
### 2. 字段映射
每个子表保存的字段映射mapped_name需要在前端重新加载时显示。
## 期望的行为
1. 用户点击已存在的数据库的 "Map Tables" 按钮
2. 前端获取实时表结构
3. 同时加载该数据库已保存的子表信息(包括选择的表和字段映射)
4. 前端合并数据,显示:
- 已选择的表(勾选状态)
- 每个字段之前设置的 mapped_name
## 技术实现建议
在数据库表中增加或利用已有字段:
- `sub_table_info` 表已包含 `Fields` JSON 字段存储字段映射
- 需要在创建/更新数据库时保存选择的表列表
- 或者在查询时返回该数据库下所有已创建的子表
## 影响范围
- 数据库创建/更新接口
- 子表映射查询接口
## 优先级
高 - 影响用户体验,第二次进入无法看到之前的工作成果

193
teams/web/model-settings.md Normal file
View File

@@ -0,0 +1,193 @@
# Model Settings 需求文档
## 需求概述
Model Settings 页面用于管理 AI 模型配置,支持添加、编辑、删除和测试模型连接。
## 功能列表
### 1. 模型列表展示
展示已配置的模型列表,包含以下字段:
- Model Name模型名称
- Model Type模型类型Chat / Embedding / Rerank / VLM
- API EndpointAPI 端点)
- Status状态Active / Inactive
### 2. 添加新模型
点击 "Add New Model" 按钮,弹出表单包含以下字段:
- Model Name必填
- Model Type必选Chat / Embedding / Rerank / VLM
- Provider必选OpenAI / Ollama
- Model必填模型名称如 gpt-4o
- API Key必填
- Base URL必填
- API Endpoint可选
### 3. 测试连接
在添加模型表单中提供 "Test Connection" 按钮,用于验证模型连接是否可用。
### 4. 编辑模型
点击编辑按钮,弹出编辑表单,可修改模型信息。
### 5. 删除模型
点击删除按钮,确认后删除模型记录。
---
## 后端接口需求
### 1. 获取模型列表
**接口地址:** `GET /api/models``GET /model/list`
**返回参数:**
```json
{
"list": [
{
"id": "1",
"name": "OpenAI",
"model_type": "chat",
"provider": "OpenAI",
"model": "gpt-4o",
"api_key": "sk-xxx",
"base_url": "https://api.openai.com",
"api_endpoint": "/v1/chat/completions",
"status": "active",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
]
}
```
### 2. 添加模型
**接口地址:** `POST /api/models``POST /model/add`
**请求参数:**
```json
{
"name": "OpenAI",
"model_type": "chat",
"provider": "OpenAI",
"model": "gpt-4o",
"api_key": "sk-xxx",
"base_url": "https://api.openai.com",
"api_endpoint": "/v1/chat/completions"
}
```
**返回参数:**
```json
{
"id": "1",
"name": "OpenAI",
"model_type": "chat",
...
}
```
### 3. 更新模型
**接口地址:** `PUT /api/models/{id}``PUT /model/{id}`
**请求参数:**
```json
{
"name": "OpenAI Updated",
"model_type": "chat",
"provider": "OpenAI",
"model": "gpt-4o",
"api_key": "sk-xxx",
"base_url": "https://api.openai.com",
"api_endpoint": "/v1/chat/completions",
"status": "active"
}
```
### 4. 删除模型
**接口地址:** `DELETE /api/models/{id}``DELETE /model/{id}`
**返回参数:**
```json
{
"success": true
}
```
### 5. 测试连接
**接口地址:** `POST /api/models/test``POST /model/test`
**请求参数:**
```json
{
"provider": "OpenAI",
"model": "gpt-4o",
"api_key": "sk-xxx",
"base_url": "https://api.openai.com",
"api_endpoint": "/v1/chat/completions"
}
```
**返回参数:**
```json
{
"success": true,
"message": "Connection successful"
}
```
---
## 数据结构
### Model 表结构(参考)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 主键 UUID |
| name | string | 模型名称 |
| model_type | string | 模型类型chat/embedding/rerank/vlm |
| provider | string | 提供商OpenAI/Ollama |
| model | string | 模型标识 |
| api_key | string | API 密钥(加密存储) |
| base_url | string | 基础 URL |
| api_endpoint | string | API 端点路径 |
| status | string | 状态active/inactive |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
---
## 前端组件状态
### 表单数据 (newModelForm)
```typescript
{
name: string,
apiKey: string,
apiEndpoint: string,
baseUrl: string,
provider: string,
model: string,
modelType: string
}
```
### 模型类型选项 (modelTypeOptions)
- Chat
- Embedding
- Rerank
- VLM
### 提供商选项 (providerOptions)
- OpenAI
- Ollama

View File

@@ -0,0 +1,110 @@
# Neo4j 接口后端需求
## 需求说明
前端 Neo4j 图谱功能已完成,后端接口需要匹配前端调用。
---
## 1. 新增 `/neo4j/graphs` 接口
### 接口地址
```
POST /neo4j/graphs
```
### 请求参数
```json
{
"uri": "bolt://localhost:7687",
"username": "neo4j",
"password": "password",
"database": "neo4j"
}
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| uri | string | 是 | Neo4j 连接地址,如 bolt://localhost:7687 |
| username | string | 是 | 用户名 |
| password | string | 是 | 密码 |
| database | string | 否 | 数据库名(默认 neo4j |
### 返回参数
```json
{
"success": true,
"graphs": {
"labels": [
{"name": "User", "count": 1523},
{"name": "Order", "count": 856}
],
"relationshipTypes": [
{"name": "KNOWS", "count": 2341},
{"name": "BOUGHT", "count": 5678}
]
}
}
```
---
## 2. 修改路由路径
### 当前状态
- `/database/neo4j/nodes` → 需要改为 → `/neo4j/nodes`
- `/database/neo4j/relationships` → 需要改为 → `/neo4j/relationships`
---
## 总结
后端需要修改以下内容:
1. **新增** `/neo4j/graphs` 接口
2. **修改** `/database/neo4j/nodes``/neo4j/nodes`
3. **修改** `/database/neo4j/relationships``/neo4j/relationships`
---
## 附:前端 API 调用示例
```javascript
// 获取图谱概览
fetch('/neo4j/graphs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
uri: 'bolt://10.10.10.189:7687',
username: 'neo4j',
password: 'neo4j',
database: 'neo4j'
})
})
// 获取节点详情
fetch('/neo4j/nodes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
uri: 'bolt://10.10.10.189:7687',
username: 'neo4j',
password: 'neo4j',
label: 'User',
limit: 10
})
})
// 获取关系详情
fetch('/neo4j/relationships', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
uri: 'bolt://10.10.10.189:7687',
username: 'neo4j',
password: 'neo4j',
relationship_type: 'KNOWS',
limit: 10
})
})
```

View File

@@ -0,0 +1,99 @@
# Neo4j 连接成功后返回数据库 ID
## 需求说明
当前端 Connect 测试 Neo4j 连接成功后,后端需要返回数据库的 ID以便前端保存图谱配置。
---
## 问题
当前 `/neo4j/check` 接口返回:
```json
{
"success": true,
"message": "connection successful",
"version": "5.14.0",
"databases": ["neo4j", "system"]
}
```
**没有返回 `databaseId`**,导致后续保存图谱时缺少 `databaseId`
---
## 需求内容
修改 `/neo4j/check` 接口,在连接成功时:
1. **检查数据库是否已存在** - 根据 URIbolt://host:port、username、database 查询
2. **如果存在** - 返回已有的 `databaseId`
3. **如果不存在** - 自动创建一条数据库记录,并返回新的 `databaseId`
### 返回格式
```json
{
"success": true,
"message": "connection successful",
"version": "5.14.0",
"databases": ["neo4j", "system"],
"databaseId": "xxx-xxx-xxx",
"name": "Neo4j-neo4j",
"description": "Neo4j neo4j@10.10.10.189:7687"
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| message | string | 消息 |
| version | string | Neo4j 版本 |
| databases | array | 数据库列表 |
| databaseId | string | 数据库记录 ID |
| name | string | 数据库名称 |
| description | string | 数据库描述 |
### 请求参数
当前 `/neo4j/check` 请求:
```json
{
"db_type": "Neo4j",
"host": "10.10.10.189",
"port": 7687,
"username": "neo4j",
"password": "neo4j",
"database": "neo4j"
}
```
后端需要增加 `name` 字段用于数据库名称(可选):
```json
{
"name": "My Neo4j Database"
}
```
---
## 涉及文件
- `server/internal/service/neo4j_service.go`
- 函数:`Check()` - 第 81-128 行
- 函数:`ensureNeo4jDatabase()` - 第 131-175 行(已有代码但可能有问题)
- `server/internal/model/neo4j_info.go`
- 结构体:`Neo4jCheckResponse` - 需要确保 `databaseId` 字段正确返回
---
## 前端使用
前端代码已实现兼容处理:
```javascript
const dbId = result.databaseId || result.id || result.database_id || ''
```
所以后端返回 `databaseId``id``database_id` 都可以被正确识别。

196
teams/web/neo4j-graphs.md Normal file
View File

@@ -0,0 +1,196 @@
# 后端需求 - Neo4j 图谱数据获取(完善版)
## 需求描述
Neo4j 连接成功后,需要获取图谱数据供前端可视化展示。前端使用 ECharts 力导向图谱展示科幻风格效果。
## Neo4j 图谱核心概念
Neo4j 是图数据库,与关系型数据库概念不同:
- **Node节点** - 类似于表,但不需要固定结构
- **Label标签** - 类似于表的类型名(如 User, Order
- **Relationship关系** - 节点之间的边
- **Relationship Type关系类型** - 关系的类型(如 KNOWS, OWNS
## 后端需要提供的接口
### 1. 获取图谱概览数据(核心接口)
返回所有 Label标签和 Relationship Type关系类型的统计信息。这是前端图谱可视化的核心数据来源。
**接口地址:** `POST /database/check` (复用现有接口,在 db_type 为 Neo4j 时返回图谱数据)
**请求参数:**
```json
{
"db_type": "Neo4j",
"uri": "bolt://localhost:7687",
"username": "neo4j",
"password": "password",
"database": "neo4j"
}
```
**返回参数:**
```json
{
"success": true,
"graphs": {
"labels": [
{"name": "User", "count": 1523},
{"name": "Order", "count": 856},
{"name": "Product", "count": 2341},
{"name": "Category", "count": 45},
{"name": "Review", "count": 5678}
],
"relationshipTypes": [
{"name": "KNOWS", "count": 2341},
{"name": "BOUGHT", "count": 5678},
{"name": "BELONGS_TO", "count": 2341},
{"name": "HAS_REVIEW", "count": 5678},
{"name": "LOCATED_IN", "count": 1523}
]
}
}
```
### 2. 获取节点详情(可选,用于点击显示)
点击某个 Label 节点时,获取该类型节点的样本数据用于详情展示。
**接口地址:** `POST /database/neo4j/nodes`
**请求参数:**
```json
{
"uri": "bolt://localhost:7687",
"username": "neo4j",
"password": "password",
"database": "neo4j",
"label": "User",
"limit": 5
}
```
**返回参数:**
```json
{
"success": true,
"nodes": [
{"id": "1", "name": "张三", "email": "zhangsan@example.com", "created_at": "2024-01-01"},
{"id": "2", "name": "李四", "email": "lisi@example.com", "created_at": "2024-01-02"}
],
"properties": [
{"name": "id", "type": "string"},
{"name": "name", "type": "string"},
{"name": "email", "type": "string"},
{"name": "created_at", "type": "datetime"}
]
}
```
### 3. 获取关系详情(可选)
获取两个节点之间的关系数据。
**接口地址:** `POST /database/neo4j/relationships`
**请求参数:**
```json
{
"uri": "bolt://localhost:7687",
"username": "neo4j",
"password": "password",
"database": "neo4j",
"relationshipType": "KNOWS",
"limit": 10
}
```
**返回参数:**
```json
{
"success": true,
"relationships": [
{
"id": "rel-1",
"source": "1",
"target": "2",
"properties": {"since": "2020-01-01"}
}
]
}
```
## 数据结构说明
### graphs.labels[] - 标签列表(前端图谱节点)
| 字段 | 类型 | 说明 | 用途 |
|------|------|------|------|
| name | string | 标签名称(如 User, Order | 作为图谱节点显示 |
| count | int | 该标签的节点数量 | 计算节点大小 symbolSize |
### graphs.relationshipTypes[] - 关系类型列表(前端图谱边)
| 字段 | 类型 | 说明 | 用途 |
|------|------|------|------|
| name | string | 关系类型(如 KNOWS, OWNS | 作为图谱边的标签 |
| count | int | 该关系的数量 | 可能影响边的粗细 |
### nodes[] - 节点详情
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 节点唯一标识 |
| (其他) | any | 节点的其他属性 |
### relationships[] - 关系详情
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 关系唯一标识 |
| source | string | 起始节点ID |
| target | string | 目标节点ID |
| properties | object | 关系属性 |
## 前端图谱展示逻辑
前端使用 ECharts 力导向图谱force-directed graph展示方式如下
1. **节点生成**
- 根据 `graphs.labels` 数组生成节点
- `name` 作为节点显示名称
- `count` 决定节点大小symbolSize = log2(count+1) * 12
- 节点颜色按索引分配科幻配色(紫、蓝、绿、橙、粉、青)
- 节点带发光效果shadowBlur: 20
2. **边生成**
- 根据 `graphs.relationshipTypes` 生成边
- 边 label 显示关系类型名称
- 曲线连接curveness: 0.2
- 带箭头
3. **交互效果**
- 弹簧物理效果force layout
- 节点可拖拽
- 滚轮缩放
- hover 相邻节点高亮
## 优先级
高 - Neo4j 可视化的核心数据
## 注意事项
1. Neo4j 连接使用官方 Go 驱动:`github.com/neo4j/neo4j-go-driver`
2. 注意处理连接超时和认证失败的情况
3. 大数据量时需要限制返回数量limit 参数)
4. **建议返回足够多的关系类型**(建议至少 5-10 个),以便前端生成丰富的图谱连接
5. 如果关系类型少于节点数,可以创建额外连接让图谱更美观
## 前端缓存策略
### 方案设计
- **首次加载**:获取数据并缓存
- **第二次展示**:直接使用缓存,秒开
- **刷新按钮**:用户手动点击刷新获取最新数据
### 实现说明
前端会缓存图谱数据,第二次进入时直接展示缓存数据,提升用户体验。同时提供"刷新"按钮供用户手动刷新。

View File

@@ -0,0 +1,42 @@
# 后端需求 - 支持 Neo4j 图数据库
## 需求描述
添加 Neo4j 图数据库类型支持。
## Neo4j 连接参数
Neo4j 连接需要以下参数:
| 参数 | 类型 | 必填 | 说明 | 默认值 |
|------|------|------|------|--------|
| uri | string | 是 | 连接地址 | bolt://localhost:7687 |
| username | string | 是 | 用户名 | neo4j |
| password | string | 是 | 密码 | - |
| database | string | 否 | 数据库名 | neo4j默认数据库 |
### 连接示例
- `bolt://localhost:7687`
- `neo4j://localhost:7687`
- `bolt://192.168.1.100:7687`
## 需要修改的地方
### 1. 数据库类型列表
在前端和后端添加 "Neo4j" 选项
### 2. 连接表单
Neo4j 只需要 3-4 个字段:
- URI连接地址
- Username用户名
- Password密码
- Database数据库名可选
### 3. 数据库服务
- `server/internal/service/database_service.go`
- 新增 `connectNeo4j` 方法
- 新增 `getNeo4jTables` 方法
## 优先级
中 - 扩展数据库类型支持

View File

@@ -0,0 +1,35 @@
# 后端需求 - 编辑数据库时正确处理 sub_tables
## 问题描述
用户点击 Action 修改数据库,进入 Map Tables 页面后:
1. 初始显示 2 个已选中的表
2. 用户取消选中 1 个表,只保留 1 个
3. 点击 Save Mapping 保存
4. 再次点击 Map Tables 查看时,仍然显示 2 个表,而不是修改后的 1 个
## 原因
可能的问题:
1. 编辑时没有正确从数据库加载已保存的 sub_tables 数据
2. Save Mapping 时没有正确更新 sub_tables可能只是创建新的没有删除已取消的
## 需求
### 1. 加载数据库时返回 sub_tables 数据
在获取数据库详情时,需要返回该数据库已保存的子表映射信息(包括 parent_table 等),以便前端正确显示已选中的表。
### 2. 保存时正确处理子表
- 新增的子表:创建新记录
- 保留的子表:更新记录
- 取消的子表:删除对应记录
或者使用更简单的方案:
- 保存时删除该数据库所有的旧 sub_tables
- 重新创建新的 sub_tables 记录
## 状态
- [ ] 后端修改待实现

View File

@@ -0,0 +1,22 @@
# 后端需求 - 编辑数据库时更新 table_count
## 问题描述
用户点击 Action 修改数据库,从 2 个子表修改为 1 个子表并保存后,数据库列表中的 Tables 列没有更新为新的数量。
## 原因
编辑数据库并保存 sub_tables 时,后端没有更新 `table_count` 字段。
## 需求
在 UpdateDatabaseRequest 处理 `SubTables` 字段时,需要同步更新数据库记录的 `table_count` 字段为当前 sub_tables 的数量。
### 修改位置
- `server/internal/service/database_service.go` 或 handler
- 在 Update 方法中处理 SubTables 时更新 table_count
## 状态
- [x] 后端修改已完成

View File

@@ -0,0 +1,30 @@
# 后端需求 - 保存映射时更新 table_count
## 问题描述
用户在 Database 列表页面看到 Table Mapping 选中 2 个表并保存后,表格的 Tables 列仍然显示 1没有更新为实际选中的表数量。
## 原因
保存 Table Mapping 时,后端没有更新数据库的 `table_count` 字段。
## 需求
在保存子表映射时,需要同时更新数据库的 `table_count` 字段为实际保存的子表数量。
### 修改位置
- `server/internal/service/database_service.go` 或 handler
- 在处理 `SubTables` 保存逻辑后,更新 `database_info` 表的 `table_count` 字段
### 逻辑
```go
// 保存 sub_tables 后,更新 table_count
tableCount := len(subTables)
// 更新数据库记录的 table_count 字段
```
## 状态
- [x] 后端修改已完成

View File

@@ -0,0 +1,52 @@
# Web 前端需求 TODO
## 2026年3月
### 2026-03-06
- [x] **DDL 获取功能** - 后端需在获取表结构时返回 DDL 语句 ✔
- 相关文件:`server/internal/service/database_service.go`
- 函数:`getMySQLTables`, `getPostgresTables`
- 详细需求:[ddl-fetch.md](./ddl-fetch.md)
- [x] **返回结构化 columns 数据** - 后端需返回完整的列信息column_name, data_type, column_type, is_nullable, default_value, column_key, extra, column_comment
- 相关文件:`server/internal/service/database_service.go`
- 函数:`getMySQLTables`, `getPostgresTables`
- 详细需求:[columns-api.md](./columns-api.md)
- [x] **保存和读取字段映射** - 后端需支持保存/读取字段的中文映射名mapped_name
- 相关文件:`server/internal/service/database_service.go`, `server/internal/model/`
- 详细需求:[field-mapping.md](./field-mapping.md)
- [x] **保存和恢复映射状态** - 第二次进入 Map Tables 时需恢复之前选择的表和字段映射 ✔
- 相关文件:`server/internal/service/database_service.go`, `server/internal/model/`
- 详细需求:[mapping-state.md](./mapping-state.md)
- [x] **Neo4j 图谱数据获取** - 前端已完成 ECharts 科幻风格图谱,后端需提供图谱数据接口 ✔
- 前端:使用 ECharts force-directed graph力导向弹簧效果可拖拽hover 高亮
- 详细需求:[neo4j-graphs.md](./neo4j-graphs.md), [neo4j-support.md](./neo4j-support.md)
---
- [x] **Neo4j 接口路由修改** - 后端已完成 ✔
- 新增 `/neo4j/graphs` 接口
- 修改 `/database/neo4j/nodes``/neo4j/nodes`
- 修改 `/database/neo4j/relationships``/neo4j/relationships`
- 详细需求:[neo4j-api-requirement.md](./neo4j-api-requirement.md)
---
### 2026-03-07
- [x] **Neo4j 图谱保存接口** - 后端已完成 ✔
- 接口地址:`POST /database/graph/save`
- 详细需求:[neo4j-graph-save.md](./neo4j-graph-save.md)
- [x] **Neo4j 连接成功后返回 databaseId** - 后端已完成 ✔
- 问题Connect 测试连接成功后没有保存数据库记录,导致后续保存图谱时缺少 databaseId
- 解决方案:/neo4j/check 成功时检查数据库是否已存在,不存在则自动创建并返回 databaseId
- 详细需求:[neo4j-check-return-id.md](./neo4j-check-return-id.md)
---
> 需求完成后请完成者打 ✔

View File

@@ -0,0 +1,45 @@
# Web 前端需求 TODO
## 2026年3月
### 2026-03-08
- [x] **知识库Knowledge BaseAPI** - 后端已完成 ✔
- 创建知识库、获取列表、获取详情、删除
- 上传文档、删除文档、重新解析
- 获取文档预览内容
- 详细需求:[knowledge-base-api.md](./knowledge-base-api.md)
- [x] **编辑时正确处理 sub_tables** - 后端已完成 ✔
- 问题:取消选中 1 个表后保存,再次进入仍显示 2 个表
- 详细需求:[sub-tables-edit.md](./sub-tables-edit.md)
- [x] **知识库存储配置 (MinIO/S3)** - 后端已完成 ✔
- 前端已完成:添加 storage_config 参数传递
- 后端已完成KnowledgeBase 模型添加 storage_config 字段
- 上传文件时使用知识库的 storage_config而非全局配置
- 详细需求:[knowledge-base-api.md](./knowledge-base-api.md)
- [x] **文档列表返回 file_url** - 后端已完成 ✔
- 问题:重新进入知识库后 PDF 无法预览
- 已确认API 返回的 file_url 字段有值
---
### 2026-03-07
- [x] **DDL 编辑功能** - 后端已完成 ✔
- 前端只发送 ddl 字段,不再发送 fields 字段
- 详细需求:[ddl-edit.md](./ddl-edit.md)
- [x] **保存映射时更新 table_count** - 后端已完成 ✔
- 问题:用户保存 2 个子表后Tables 列仍显示 1
- 详细需求:[table-count-update.md](./table-count-update.md)
- [x] **编辑数据库时更新 table_count** - 后端已完成 ✔
- 问题:用户从 2 个子表修改为 1 个后Tables 列没有更新
- 详细需求:[table-count-update-edit.md](./table-count-update-edit.md)
---
> 需求完成后请完成者打 ✔