# 数据工具与自定义Agent - 功能修复文档 > **创建日期**: 2026-01-13 > **状态**: 待修复 > **优先级**: 高 --- ## 1. 问题概述 ### 当前实现与预期业务逻辑不一致 **预期业务逻辑**: 1. 前端获取 `dataTemplates`(Agent 模板列表,如 `mysql_agent`、`postgresql_agent`) 2. 用户选择模板,根据模板的 `env_info` 填写配置值(如 MySQL 连接信息),保存为**工具** 3. 用户创建自定义 Agent 时选择已创建的工具 4. MCP-Server 从工具中获取模板类型和配置,传递给 Agent Manager 创建对应的 Agent **当前实现问题**: - Tool 模型设计为通用 API 调用配置(endpoint、method、schema) - **缺少 `template` 字段**:无法关联 Agent 模板 - **缺少 `env_config` 字段**:无法存储模板所需的环境变量配置 - 创建 Agent 时,工具只作为附加的 API 能力,而不是决定 Agent 类型的关键信息 --- ## 2. 正确的业务流程 ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ 正确的业务流程 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ ① 获取模板列表 ② 创建工具(基于模板) ③ 创建Agent(选择工具) │ │ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ │ │ GET │ │ POST │ │ POST │ │ │ │ /templates │──────→│ /tools/create │───────────→│ /custom-agents│ │ │ └───────────────┘ └───────────────┘ └───────────────┘ │ │ │ │ │ │ │ ↓ ↓ ↓ │ │ 返回 dataTemplates 保存到 Tool 表 从工具获取 template │ │ [mysql_agent, - template: mysql_agent 和 envConfig, │ │ postgresql_agent] - envConfig: { 传递给 Agent Manager │ │ MYSQL_HOST: "...", │ │ 含 env_info: MYSQL_USER: "...", │ │ - required ... │ │ - optional } │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ### 关键概念 | 概念 | 说明 | |------|------| | **模板 (template)** | Agent Manager 提供的镜像类型,如 `mysql_agent`,包含 `env_info` 定义所需配置 | | **工具 (Tool)** | 用户基于模板创建的配置实例,包含 `template` + 填写好的 `envConfig` | | **自定义 Agent** | 根据工具的模板类型和配置创建的 K8s Pod | --- ## 3. 需要修改的内容 ### 3.1 数据库模型修改 **文件**: `services/mcp-server/models.py` **修改 Tool 模型,添加字段**: ```python class Tool(BaseModel, Base): """工具模型""" __tablename__ = "tools" name = Column(String(100), nullable=False) description = Column(Text) category = Column(String(50)) # database, api, function 等 # ========== 新增:模板相关字段 ========== template = Column(String(100)) # 模板名称,如 "mysql_agent", "postgresql_agent" env_config = Column(JSON, default=dict) # 环境变量配置,根据模板 env_info 填写 # ========================================= # 工具定义(保留,用于自定义 API 工具) schema = Column(JSON) # 改为可空,模板工具不需要 endpoint = Column(String(500)) method = Column(String(10), default="POST") # ... 其他字段保持不变 ``` ### 3.2 创建工具接口修改 **文件**: `services/mcp-server/app/routes/user.py` **修改 `POST /api/user/tools/create` 接口**: ```python @router.post("/tools/create", response_model=SuccessResponse) async def create_tool( req: dict, principal: dict = Depends(require_auth), db: AsyncSession = Depends(get_db) ): """ 创建工具(基于模板) 请求体示例(MySQL 工具): { "name": "my-mysql-tool", "description": "我的MySQL数据库连接工具", "template": "mysql_agent", "envConfig": { "MYSQL_HOST": "mysql.example.com", "MYSQL_USER": "root", "MYSQL_PASSWORD": "password123", "MYSQL_DATABASE": "mydb", "MYSQL_PORT": "3306", "OPENAI_API_KEY": "sk-xxx" } } """ user_id = principal.get("user_id") # 验证模板名称 template_name = req.get("template") if template_name: # 从 Agent Manager 获取模板列表并验证 try: from app.agent_manager_client import get_agent_manager_client, AgentManagerError client = get_agent_manager_client() custom_templates = await client.list_custom_templates() template_names = [t.template for t in custom_templates] if template_name not in template_names: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"无效的模板名称: {template_name}。支持的模板: {', '.join(template_names)}" ) except AgentManagerError: pass # Agent Manager 不可用时跳过验证 # 检查工具名称是否已存在 result = await db.execute( select(Tool).where( and_( Tool.owner_id == user_id, Tool.name == req.get("name") ) ) ) existing_tool = result.scalar_one_or_none() if existing_tool: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail=f"工具名称 '{req.get('name')}' 已存在" ) # 创建工具 tool = Tool( name=req.get("name"), description=req.get("description"), category="database" if template_name else req.get("type", "api"), template=template_name, # 新增 env_config=req.get("envConfig", {}), # 新增 schema=req.get("config", {}).get("schema") if not template_name else None, endpoint=req.get("config", {}).get("endpoint") if not template_name else None, method=req.get("config", {}).get("method", "GET") if not template_name else None, auth_config={"apiKey": req.get("config", {}).get("apiKey")} if req.get("config", {}).get("apiKey") else {}, owner_id=user_id, is_active=True, is_public=False, ) db.add(tool) await db.commit() await db.refresh(tool) return SuccessResponse( data={ "id": str(tool.id), "name": tool.name, "template": tool.template, }, message="工具创建成功" ) ``` ### 3.3 创建自定义 Agent 接口修改 **文件**: `services/mcp-server/app/routes/user.py` **修改 `POST /api/user/custom-agents` 接口的工具处理逻辑**: ```python @router.post("/custom-agents", response_model=SuccessResponse) async def create_custom_agent( req: CreateCustomAgentRequest, principal: dict = Depends(require_auth), db: AsyncSession = Depends(get_db) ): """ 创建自定义 Agent 新逻辑:从选中的工具获取模板类型和环境变量配置 """ # ... 配额检查等代码保持不变 ... # ========== 新增:从工具获取模板和配置 ========== template_name = req.template # 默认使用请求中的 template env_vars = req.envConfig or {} if req.tools and len(req.tools) > 0: # 查询第一个工具(主工具,决定 Agent 类型) primary_tool_id = req.tools[0] try: tool_result = await db.execute( select(Tool).where( Tool.id == PyUUID(primary_tool_id), or_( Tool.is_public == True, Tool.owner_id == PyUUID(user_id) ) ) ) primary_tool = tool_result.scalar_one_or_none() if primary_tool and primary_tool.template: # 使用工具的模板类型 template_name = primary_tool.template # 合并工具的环境变量配置 if primary_tool.env_config: env_vars = {**primary_tool.env_config, **env_vars} logger.info( f"从工具获取配置: tool_id={primary_tool_id}, " f"template={template_name}, env_keys={list(env_vars.keys())}" ) except Exception as e: logger.warning(f"查询工具失败: {str(e)}") # ================================================ # 验证模板 try: client = get_agent_manager_client() custom_templates = await client.list_custom_templates() template_names = [t.template for t in custom_templates] if template_name not in template_names: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"无效的模板名称: {template_name}。支持的模板: {', '.join(template_names)}" ) except AgentManagerError as e: logger.warning(f"无法获取模板列表,跳过验证: {str(e)}") # ... 后续代码使用 template_name 和 env_vars 创建 Agent ... ``` ### 3.4 获取用户工具列表接口修改 **文件**: `services/mcp-server/app/routes/user.py` **修改 `GET /api/user/tools` 响应格式**: ```python @router.get("/tools", response_model=SuccessResponse) async def get_user_tools( principal: dict = Depends(require_auth), db: AsyncSession = Depends(get_db) ): """获取用户创建的所有工具""" user_id = principal.get("user_id") result = await db.execute( select(Tool) .where(Tool.owner_id == user_id) .order_by(desc(Tool.created_at)) ) tools = result.scalars().all() tools_data = [] for tool in tools: tools_data.append({ "id": str(tool.id), "name": tool.name, "description": tool.description, "template": tool.template, # 新增 "category": tool.category, "envConfig": tool.env_config, # 新增(注意:敏感信息如密码应脱敏) "created_at": tool.created_at.isoformat() if tool.created_at else None, "updated_at": tool.updated_at.isoformat() if tool.updated_at else None, "is_active": tool.is_active, }) return SuccessResponse(data={"tools": tools_data}) ``` ### 3.5 数据库迁移 需要创建数据库迁移脚本,添加新字段: ```sql -- 添加 template 字段 ALTER TABLE tools ADD COLUMN template VARCHAR(100); -- 添加 env_config 字段 ALTER TABLE tools ADD COLUMN env_config JSON DEFAULT '{}'; -- 创建索引 CREATE INDEX idx_tool_template ON tools(template); ``` --- ## 4. 更新后的接口文档 ### 4.1 创建工具(基于模板) **接口**: `POST /api/user/tools/create` **请求参数**: ```typescript interface CreateToolRequest { name: string; // 工具名称(必填) description?: string; // 工具描述 template: string; // 模板名称(必填,来自 dataTemplates[].template) envConfig: Record; // 环境变量配置(必填,根据模板 env_info 填写) } ``` **请求示例(MySQL 工具)**: ```json { "name": "my-mysql-tool", "description": "我的MySQL数据库连接工具", "template": "mysql_agent", "envConfig": { "MYSQL_HOST": "mysql.example.com", "MYSQL_USER": "root", "MYSQL_PASSWORD": "password123", "MYSQL_DATABASE": "mydb", "MYSQL_PORT": "3306", "OPENAI_API_KEY": "sk-xxx" } } ``` **请求示例(PostgreSQL 工具)**: ```json { "name": "my-pgsql-tool", "description": "我的PostgreSQL数据库连接工具", "template": "postgresql_agent", "envConfig": { "POSTGRES_HOST": "postgres.example.com", "POSTGRES_USER": "postgres", "POSTGRES_PASSWORD": "password123", "POSTGRES_DATABASE": "mydb", "POSTGRES_PORT": "5432", "OPENAI_API_KEY": "sk-xxx" } } ``` **响应示例**: ```json { "success": true, "data": { "id": "d14cd898-e2cf-4150-a7f9-53b962c1c9a2", "name": "my-mysql-tool", "template": "mysql_agent" }, "message": "工具创建成功" } ``` ### 4.2 创建自定义 Agent(选择工具) **接口**: `POST /api/user/custom-agents` **请求参数变化**: ```typescript interface CreateCustomAgentRequest { name: string; // Agent名称(必填) tools: string[]; // 工具ID列表(必填,第一个工具决定 Agent 类型) // 以下参数变为可选(可从工具自动获取) template?: string; // 模板名称(可选,默认从工具获取) envConfig?: Record; // 额外环境变量(可选,会与工具配置合并) // 资源配置 cpuRequest?: string; // 默认 "100m" memoryRequest?: string; // 默认 "128Mi" // 其他可选参数 frameworkTemplate?: string; // 默认 "MCP" model?: string; } ``` **请求示例**: ```json { "name": "my-mysql-agent", "tools": ["d14cd898-e2cf-4150-a7f9-53b962c1c9a2"], "cpuRequest": "500m", "memoryRequest": "1Gi", "model": "gpt-4" } ``` **说明**: - `template` 和 `envConfig` 从工具自动获取 - 请求中的 `envConfig` 会与工具配置合并(请求中的优先) --- ## 5. 修改检查清单 - [ ] **models.py**: 添加 `template` 和 `env_config` 字段到 Tool 模型 - [ ] **数据库迁移**: 创建迁移脚本添加新字段 - [ ] **user.py - create_tool**: 修改创建工具接口,支持 template 和 envConfig - [ ] **user.py - create_custom_agent**: 修改创建 Agent 接口,从工具获取模板和配置 - [ ] **user.py - get_user_tools**: 修改响应格式,包含 template 和 envConfig - [ ] **schemas.py**: 更新请求/响应模型定义 - [ ] **接口文档**: 更新前端接口文档 --- ## 6. 测试用例 ### 6.1 创建工具 ```bash # 创建 MySQL 工具 curl -X POST /api/user/tools/create \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "my-mysql-tool", "template": "mysql_agent", "envConfig": { "MYSQL_HOST": "mysql.example.com", "MYSQL_USER": "root", "MYSQL_PASSWORD": "password123", "MYSQL_DATABASE": "mydb" } }' ``` ### 6.2 创建 Agent(选择工具) ```bash # 创建 Agent,选择已创建的工具 curl -X POST /api/user/custom-agents \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "my-mysql-agent", "tools": ["d14cd898-e2cf-4150-a7f9-53b962c1c9a2"], "cpuRequest": "500m", "memoryRequest": "1Gi" }' ``` --- **如有问题,请联系大智开发团队。**