forked from xiaohei/taiji-AI-PAD
480 lines
16 KiB
Markdown
480 lines
16 KiB
Markdown
# 数据工具与自定义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<string, string>; // 环境变量配置(必填,根据模板 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<string, string>; // 额外环境变量(可选,会与工具配置合并)
|
||
|
||
// 资源配置
|
||
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"
|
||
}'
|
||
```
|
||
|
||
---
|
||
|
||
**如有问题,请联系大智开发团队。**
|
||
|