From 8249f6735184c33c10780d92fd1aff878120fb6b Mon Sep 17 00:00:00 2001 From: "DESKTOP-72TV0V4\\caoxiaozhu" Date: Wed, 11 Mar 2026 14:26:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20teams=20=E7=9B=AE?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- teams/ai/ai-core-api.md | 137 +++ teams/ai/todo.md | 20 + teams/api/README.md | 18 + teams/api/database-check.md | 200 ++++ teams/api/database-create.md | 104 +++ teams/api/database-list.md | 51 + teams/api/knowledge-api.md | 268 ++++++ teams/api/model-api.md | 208 +++++ teams/api/neo4j-check.md | 265 ++++++ teams/api/neo4j-graph-save.md | 75 ++ teams/api/subtable-list.md | 75 ++ teams/api/upload-api.md | 96 ++ .../group_chat_implementation_plan.md | 874 ++++++++++++++++++ teams/multi_agent_plan/group_chat_notes.md | 80 ++ .../multi_agent_plan/group_chat_task_plan.md | 32 + teams/multi_agent_plan/implementation_plan.md | 709 ++++++++++++++ teams/multi_agent_plan/notes.md | 107 +++ teams/multi_agent_plan/task_plan.md | 33 + teams/web/columns-api.md | 92 ++ teams/web/ddl-edit.md | 66 ++ teams/web/field-mapping.md | 89 ++ teams/web/knowledge-base-api.md | 375 ++++++++ teams/web/mapping-state.md | 43 + teams/web/model-settings.md | 193 ++++ teams/web/neo4j-api-requirement.md | 110 +++ teams/web/neo4j-check-return-id.md | 99 ++ teams/web/neo4j-graphs.md | 196 ++++ teams/web/neo4j-support.md | 42 + teams/web/sub-tables-edit.md | 35 + teams/web/table-count-update-edit.md | 22 + teams/web/table-count-update.md | 30 + teams/web/todo-2026-3-6.md | 52 ++ teams/web/todo-2026-3-8.md | 45 + 33 files changed, 4841 insertions(+) create mode 100644 teams/ai/ai-core-api.md create mode 100644 teams/ai/todo.md create mode 100644 teams/api/README.md create mode 100644 teams/api/database-check.md create mode 100644 teams/api/database-create.md create mode 100644 teams/api/database-list.md create mode 100644 teams/api/knowledge-api.md create mode 100644 teams/api/model-api.md create mode 100644 teams/api/neo4j-check.md create mode 100644 teams/api/neo4j-graph-save.md create mode 100644 teams/api/subtable-list.md create mode 100644 teams/api/upload-api.md create mode 100644 teams/multi_agent_plan/group_chat_implementation_plan.md create mode 100644 teams/multi_agent_plan/group_chat_notes.md create mode 100644 teams/multi_agent_plan/group_chat_task_plan.md create mode 100644 teams/multi_agent_plan/implementation_plan.md create mode 100644 teams/multi_agent_plan/notes.md create mode 100644 teams/multi_agent_plan/task_plan.md create mode 100644 teams/web/columns-api.md create mode 100644 teams/web/ddl-edit.md create mode 100644 teams/web/field-mapping.md create mode 100644 teams/web/knowledge-base-api.md create mode 100644 teams/web/mapping-state.md create mode 100644 teams/web/model-settings.md create mode 100644 teams/web/neo4j-api-requirement.md create mode 100644 teams/web/neo4j-check-return-id.md create mode 100644 teams/web/neo4j-graphs.md create mode 100644 teams/web/neo4j-support.md create mode 100644 teams/web/sub-tables-edit.md create mode 100644 teams/web/table-count-update-edit.md create mode 100644 teams/web/table-count-update.md create mode 100644 teams/web/todo-2026-3-6.md create mode 100644 teams/web/todo-2026-3-8.md diff --git a/teams/ai/ai-core-api.md b/teams/ai/ai-core-api.md new file mode 100644 index 0000000..bec446c --- /dev/null +++ b/teams/ai/ai-core-api.md @@ -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 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 格式文本 diff --git a/teams/ai/todo.md b/teams/ai/todo.md new file mode 100644 index 0000000..08f7396 --- /dev/null +++ b/teams/ai/todo.md @@ -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 解析 + +--- + +> 需求完成后请完成者打 ✔ diff --git a/teams/api/README.md b/teams/api/README.md new file mode 100644 index 0000000..bd76b18 --- /dev/null +++ b/teams/api/README.md @@ -0,0 +1,18 @@ +# API 接口文档 + +## 目录 + +### Database 相关 + +- [检查数据库连接并获取表结构](database-check.md) +- [创建数据库配置](database-create.md) +- [获取数据库列表](database-list.md) +- [获取子表列表](subtable-list.md) + +### Neo4j 相关 + +- [Neo4j 连接测试](neo4j-check.md) + +--- + +> 接口如有更新,请同步更新此文档 diff --git a/teams/api/database-check.md b/teams/api/database-check.md new file mode 100644 index 0000000..8370434 --- /dev/null +++ b/teams/api/database-check.md @@ -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:7687),Neo4j 类型必填 | + +## 请求示例(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 并获取图谱概览数据(标签、关系类型、属性定义) + - 用于前端图可视化展示 diff --git a/teams/api/database-create.md b/teams/api/database-create.md new file mode 100644 index 0000000..d1dc72d --- /dev/null +++ b/teams/api/database-create.md @@ -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 并存储 diff --git a/teams/api/database-list.md b/teams/api/database-list.md new file mode 100644 index 0000000..87b93b6 --- /dev/null +++ b/teams/api/database-list.md @@ -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" + } + ] +} +``` diff --git a/teams/api/knowledge-api.md b/teams/api/knowledge-api.md new file mode 100644 index 0000000..593351f --- /dev/null +++ b/teams/api/knowledge-api.md @@ -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 URL(engine=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": "第一章 产品介绍..." + } +} +``` diff --git a/teams/api/model-api.md b/teams/api/model-api.md new file mode 100644 index 0000000..3a35d8b --- /dev/null +++ b/teams/api/model-api.md @@ -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 | 更新时间 | diff --git a/teams/api/neo4j-check.md b/teams/api/neo4j-check.md new file mode 100644 index 0000000..a75f4c5 --- /dev/null +++ b/teams/api/neo4j-check.md @@ -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" + } + ] +} +``` diff --git a/teams/api/neo4j-graph-save.md b/teams/api/neo4j-graph-save.md new file mode 100644 index 0000000..89d69e8 --- /dev/null +++ b/teams/api/neo4j-graph-save.md @@ -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', + }), +}) +``` diff --git a/teams/api/subtable-list.md b/teams/api/subtable-list.md new file mode 100644 index 0000000..c916bcb --- /dev/null +++ b/teams/api/subtable-list.md @@ -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` 恢复字段映射 diff --git a/teams/api/upload-api.md b/teams/api/upload-api.md new file mode 100644 index 0000000..a01d0fc --- /dev/null +++ b/teams/api/upload-api.md @@ -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 小时。 diff --git a/teams/multi_agent_plan/group_chat_implementation_plan.md b/teams/multi_agent_plan/group_chat_implementation_plan.md new file mode 100644 index 0000000..75c3b8a --- /dev/null +++ b/teams/multi_agent_plan/group_chat_implementation_plan.md @@ -0,0 +1,874 @@ +# 多智能体群聊系统实现计划 + +## 项目概述 + +实现类似"一人公司"的多智能体群聊系统,支持多个 Agent 在群聊中讨论问题,类似于人类的团队会议。 + +### 核心特性 +- **三阶段流程**: 观点提出 → 讨论完善 → CEO 总结决策 +- **智能轮数**: AI 判断讨论是否充分,自动决定轮数 +- **用户插话**: 用户可以随时打断发表意见 +- **角色扮演**: 可配置不同角色的 Agent(CEO、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. **端到端测试**: 模拟真实群聊场景 diff --git a/teams/multi_agent_plan/group_chat_notes.md b/teams/multi_agent_plan/group_chat_notes.md new file mode 100644 index 0000000..1943954 --- /dev/null +++ b/teams/multi_agent_plan/group_chat_notes.md @@ -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] # 最终决策 +``` diff --git a/teams/multi_agent_plan/group_chat_task_plan.md b/teams/multi_agent_plan/group_chat_task_plan.md new file mode 100644 index 0000000..cac821b --- /dev/null +++ b/teams/multi_agent_plan/group_chat_task_plan.md @@ -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` - 研究笔记 diff --git a/teams/multi_agent_plan/implementation_plan.md b/teams/multi_agent_plan/implementation_plan.md new file mode 100644 index 0000000..94899ec --- /dev/null +++ b/teams/multi_agent_plan/implementation_plan.md @@ -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. **端到端测试**: 模拟真实任务执行 diff --git a/teams/multi_agent_plan/notes.md b/teams/multi_agent_plan/notes.md new file mode 100644 index 0000000..f133b9e --- /dev/null +++ b/teams/multi_agent_plan/notes.md @@ -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 +- 任务执行顺序 +- 是否需要汇总结果 +``` diff --git a/teams/multi_agent_plan/task_plan.md b/teams/multi_agent_plan/task_plan.md new file mode 100644 index 0000000..d567018 --- /dev/null +++ b/teams/multi_agent_plan/task_plan.md @@ -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 研究笔记 diff --git a/teams/web/columns-api.md b/teams/web/columns-api.md new file mode 100644 index 0000000..a893fb8 --- /dev/null +++ b/teams/web/columns-api.md @@ -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 页面字段映射功能依赖此数据 diff --git a/teams/web/ddl-edit.md b/teams/web/ddl-edit.md new file mode 100644 index 0000000..204b862 --- /dev/null +++ b/teams/web/ddl-edit.md @@ -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] 后端修改已完成 diff --git a/teams/web/field-mapping.md b/teams/web/field-mapping.md new file mode 100644 index 0000000..02afa5e --- /dev/null +++ b/teams/web/field-mapping.md @@ -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/` - 数据模型修改 +- 子表映射的数据存储结构 + +## 优先级 + +高 - 用户输入的映射数据丢失影响使用体验 diff --git a/teams/web/knowledge-base-api.md b/teams/web/knowledge-base-api.md new file mode 100644 index 0000000..e3306e7 --- /dev/null +++ b/teams/web/knowledge-base-api.md @@ -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 服务 URL(engine=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 | 上传时间 | diff --git a/teams/web/mapping-state.md b/teams/web/mapping-state.md new file mode 100644 index 0000000..6142913 --- /dev/null +++ b/teams/web/mapping-state.md @@ -0,0 +1,43 @@ +# 后端需求 - 保存和恢复映射状态 + +## 问题描述 + +用户第一次选择表并设置字段映射后,第二次点击 "Map Tables" 按钮进入界面时,之前选择的表和设置的字段映射都丢失了。 + +## 需求 + +前端打开已存在的数据库映射时,需要恢复以下状态: + +### 1. 已选择的表列表 + +后端需要在数据库记录中保存用户选择了哪些表(不仅仅是子表信息),或者在查询时返回该数据库关联的所有子表。 + +### 2. 字段映射 + +每个子表保存的字段映射(mapped_name)需要在前端重新加载时显示。 + +## 期望的行为 + +1. 用户点击已存在的数据库的 "Map Tables" 按钮 +2. 前端获取实时表结构 +3. 同时加载该数据库已保存的子表信息(包括选择的表和字段映射) +4. 前端合并数据,显示: + - 已选择的表(勾选状态) + - 每个字段之前设置的 mapped_name + +## 技术实现建议 + +在数据库表中增加或利用已有字段: + +- `sub_table_info` 表已包含 `Fields` JSON 字段存储字段映射 +- 需要在创建/更新数据库时保存选择的表列表 +- 或者在查询时返回该数据库下所有已创建的子表 + +## 影响范围 + +- 数据库创建/更新接口 +- 子表映射查询接口 + +## 优先级 + +高 - 影响用户体验,第二次进入无法看到之前的工作成果 diff --git a/teams/web/model-settings.md b/teams/web/model-settings.md new file mode 100644 index 0000000..33ce937 --- /dev/null +++ b/teams/web/model-settings.md @@ -0,0 +1,193 @@ +# Model Settings 需求文档 + +## 需求概述 + +Model Settings 页面用于管理 AI 模型配置,支持添加、编辑、删除和测试模型连接。 + +## 功能列表 + +### 1. 模型列表展示 + +展示已配置的模型列表,包含以下字段: +- Model Name(模型名称) +- Model Type(模型类型):Chat / Embedding / Rerank / VLM +- API Endpoint(API 端点) +- 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 diff --git a/teams/web/neo4j-api-requirement.md b/teams/web/neo4j-api-requirement.md new file mode 100644 index 0000000..44325a5 --- /dev/null +++ b/teams/web/neo4j-api-requirement.md @@ -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 + }) +}) +``` diff --git a/teams/web/neo4j-check-return-id.md b/teams/web/neo4j-check-return-id.md new file mode 100644 index 0000000..fab67a6 --- /dev/null +++ b/teams/web/neo4j-check-return-id.md @@ -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. **检查数据库是否已存在** - 根据 URI(bolt://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` 都可以被正确识别。 + diff --git a/teams/web/neo4j-graphs.md b/teams/web/neo4j-graphs.md new file mode 100644 index 0000000..17427e8 --- /dev/null +++ b/teams/web/neo4j-graphs.md @@ -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. 如果关系类型少于节点数,可以创建额外连接让图谱更美观 + +## 前端缓存策略 + +### 方案设计 +- **首次加载**:获取数据并缓存 +- **第二次展示**:直接使用缓存,秒开 +- **刷新按钮**:用户手动点击刷新获取最新数据 + +### 实现说明 +前端会缓存图谱数据,第二次进入时直接展示缓存数据,提升用户体验。同时提供"刷新"按钮供用户手动刷新。 diff --git a/teams/web/neo4j-support.md b/teams/web/neo4j-support.md new file mode 100644 index 0000000..ddec242 --- /dev/null +++ b/teams/web/neo4j-support.md @@ -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` 方法 + +## 优先级 + +中 - 扩展数据库类型支持 diff --git a/teams/web/sub-tables-edit.md b/teams/web/sub-tables-edit.md new file mode 100644 index 0000000..21fad67 --- /dev/null +++ b/teams/web/sub-tables-edit.md @@ -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 记录 + +## 状态 + +- [ ] 后端修改待实现 diff --git a/teams/web/table-count-update-edit.md b/teams/web/table-count-update-edit.md new file mode 100644 index 0000000..c99edc6 --- /dev/null +++ b/teams/web/table-count-update-edit.md @@ -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] 后端修改已完成 diff --git a/teams/web/table-count-update.md b/teams/web/table-count-update.md new file mode 100644 index 0000000..b40370f --- /dev/null +++ b/teams/web/table-count-update.md @@ -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] 后端修改已完成 diff --git a/teams/web/todo-2026-3-6.md b/teams/web/todo-2026-3-6.md new file mode 100644 index 0000000..428b3e3 --- /dev/null +++ b/teams/web/todo-2026-3-6.md @@ -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) + +--- + +> 需求完成后请完成者打 ✔ \ No newline at end of file diff --git a/teams/web/todo-2026-3-8.md b/teams/web/todo-2026-3-8.md new file mode 100644 index 0000000..7555832 --- /dev/null +++ b/teams/web/todo-2026-3-8.md @@ -0,0 +1,45 @@ +# Web 前端需求 TODO + +## 2026年3月 + +### 2026-03-08 + +- [x] **知识库(Knowledge Base)API** - 后端已完成 ✔ + - 创建知识库、获取列表、获取详情、删除 + - 上传文档、删除文档、重新解析 + - 获取文档预览内容 + - 详细需求:[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) + +--- + +> 需求完成后请完成者打 ✔