forked from xiaohei/taiji-AI-PAD
更新外部数据工具和工具集
This commit is contained in:
@@ -1,479 +0,0 @@
|
||||
# 数据工具与自定义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"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**如有问题,请联系大智开发团队。**
|
||||
|
||||
@@ -0,0 +1,843 @@
|
||||
# 外部数据工具 - 接口文档
|
||||
|
||||
> **版本**: 2026-01-23 v1.0
|
||||
> **基础路径**: `/api/user/external-tools`
|
||||
> **认证方式**: Bearer Token(在请求头添加 `Authorization: Bearer <JWT Token>`)
|
||||
|
||||
---
|
||||
|
||||
## 📊 业务流程
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 外部数据工具流程 │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ① 创建外部工具 ② Agent Manager 生成 ③ 创建自定义 Agent │
|
||||
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
|
||||
│ │ POST │ ───→ │ 生成 Pydantic │ ───→ │ POST │ │
|
||||
│ │ /external-tools│ │ 工具代码文件 │ │ /custom-agents │ │
|
||||
│ └───────────────┘ └───────────────┘ │ +externalTools │ │
|
||||
│ │ │ └───────────────┘ │
|
||||
│ ↓ ↓ │ │
|
||||
│ 保存基本信息 返回 tool_ref_id 传递 tool_ref_ids │
|
||||
│ 到 PostgreSQL 给 Agent Manager │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 核心概念
|
||||
|
||||
| 概念 | 说明 |
|
||||
|------|------|
|
||||
| **外部数据工具** | 用户创建的连接外部 API 的工具配置 |
|
||||
| **tool_ref_id** | Agent Manager 生成工具后返回的标识,部署 Agent 时传递 |
|
||||
| **工具状态** | pending(等待生成), active(可用), error(生成失败) |
|
||||
|
||||
### 存储职责划分
|
||||
|
||||
| 存储位置 | 存储内容 |
|
||||
|---------|---------|
|
||||
| **MCP-Server (PostgreSQL)** | 工具基本信息(名称、URL、方法)、tool_ref_id、状态 |
|
||||
| **Agent Manager** | Pydantic 工具代码文件、完整配置(含敏感信息) |
|
||||
|
||||
---
|
||||
|
||||
## 🔐 通用请求头
|
||||
|
||||
```http
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer <JWT Token>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📑 接口列表
|
||||
|
||||
### 外部数据工具接口
|
||||
|
||||
| 序号 | 接口 | 方法 | 说明 |
|
||||
|:---:|------|------|------|
|
||||
| 1 | `/api/user/external-tools` | POST | 创建外部数据工具 |
|
||||
| 2 | `/api/user/external-tools/upload` | POST | 上传 JSON 文件创建工具 |
|
||||
| 3 | `/api/user/external-tools` | GET | 获取工具列表 |
|
||||
| 4 | `/api/user/external-tools/{tool_id}` | GET | 获取工具详情 |
|
||||
| 5 | `/api/user/external-tools/{tool_id}` | PUT | 更新工具配置 |
|
||||
| 6 | `/api/user/external-tools/{tool_id}` | DELETE | 删除工具 |
|
||||
| 7 | `/api/user/external-tools/{tool_id}/test` | POST | 测试工具连接 |
|
||||
|
||||
### 工具集接口
|
||||
|
||||
| 序号 | 接口 | 方法 | 说明 |
|
||||
|:---:|------|------|------|
|
||||
| 9 | `/api/user/toolkits` | POST | 创建工具集(最多 8 个工具) |
|
||||
| 10 | `/api/user/toolkits` | GET | 获取工具集列表 |
|
||||
| 11 | `/api/user/toolkits/{toolkit_id}` | GET | 获取工具集详情 |
|
||||
| 12 | `/api/user/toolkits/{toolkit_id}` | PUT | 更新工具集 |
|
||||
| 13 | `/api/user/toolkits/{toolkit_id}` | DELETE | 删除工具集 |
|
||||
|
||||
### 自定义 Agent 接口
|
||||
|
||||
| 序号 | 接口 | 方法 | 说明 |
|
||||
|:---:|------|------|------|
|
||||
| 8 | `/api/user/custom-agents` | POST | 创建自定义 Agent(支持外部工具/工具集) |
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ 创建外部数据工具
|
||||
|
||||
### 接口
|
||||
|
||||
```
|
||||
POST /api/user/external-tools
|
||||
```
|
||||
|
||||
### 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|-----|------|:---:|------|
|
||||
| `name` | string | ✅ | 工具名称(1-100字符) |
|
||||
| `description` | string | ❌ | 工具描述 |
|
||||
| `url` | string | ✅ | API 端点 URL |
|
||||
| `method` | string | ❌ | HTTP 方法,默认 POST |
|
||||
| `headers` | object | ❌ | 自定义请求头 |
|
||||
| `auth` | object | ❌ | 认证配置 |
|
||||
| `request_params` | object | ❌ | URL 查询参数定义(JSON Schema) |
|
||||
| `request_body` | object | ❌ | 请求体定义(JSON Schema) |
|
||||
| `response_mapping` | object | ❌ | 响应字段映射 |
|
||||
| `timeout` | integer | ❌ | 超时时间(秒),默认 30 |
|
||||
| `retry` | object | ❌ | 重试配置 |
|
||||
|
||||
### 认证配置 (auth)
|
||||
|
||||
#### API Key 认证
|
||||
```json
|
||||
{
|
||||
"auth": {
|
||||
"type": "api_key",
|
||||
"key": "sk-xxxxxxxxxxxx",
|
||||
"in": "header",
|
||||
"name": "X-API-Key"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Bearer Token 认证
|
||||
```json
|
||||
{
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Basic Auth 认证
|
||||
```json
|
||||
{
|
||||
"auth": {
|
||||
"type": "basic",
|
||||
"username": "admin",
|
||||
"password": "password123"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "weather-query-tool",
|
||||
"description": "查询天气信息的外部数据工具",
|
||||
"url": "https://api.weather.com/v1/forecast",
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"auth": {
|
||||
"type": "api_key",
|
||||
"key": "sk-xxxxxxxxxxxx",
|
||||
"in": "header",
|
||||
"name": "X-API-Key"
|
||||
},
|
||||
"request_params": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {
|
||||
"type": "string",
|
||||
"description": "城市名称",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": 30
|
||||
}
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "weather-query-tool",
|
||||
"tool_ref_id": "tool-weather-abc123",
|
||||
"status": "active",
|
||||
"created_at": "2026-01-23T10:00:00Z"
|
||||
},
|
||||
"message": "外部数据工具创建成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 上传 JSON 文件创建工具
|
||||
|
||||
### 接口
|
||||
|
||||
```
|
||||
POST /api/user/external-tools/upload
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
### 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|:---:|------|
|
||||
| `file` | File | ✅ | JSON 配置文件(.json) |
|
||||
|
||||
### JSON 文件格式
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "weather-api",
|
||||
"description": "查询城市天气信息",
|
||||
"url": "https://api.weather.com/v1/forecast",
|
||||
"method": "GET",
|
||||
"auth": {
|
||||
"type": "api_key",
|
||||
"key": "your-weather-api-key",
|
||||
"in": "query",
|
||||
"name": "apikey"
|
||||
},
|
||||
"request_params": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {
|
||||
"type": "string",
|
||||
"description": "城市名称",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": 10
|
||||
}
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
同创建接口
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 获取工具列表
|
||||
|
||||
### 接口
|
||||
|
||||
```
|
||||
GET /api/user/external-tools
|
||||
```
|
||||
|
||||
### 查询参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|:---:|------|
|
||||
| `status` | string | ❌ | 过滤状态:active/pending/error |
|
||||
| `page` | integer | ❌ | 页码,默认 1 |
|
||||
| `page_size` | integer | ❌ | 每页数量,默认 20 |
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"tools": [
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "weather-query-tool",
|
||||
"description": "查询天气信息的外部数据工具",
|
||||
"url": "https://api.weather.com/v1/forecast",
|
||||
"method": "POST",
|
||||
"auth_type": "api_key",
|
||||
"status": "active",
|
||||
"usage_count": 15,
|
||||
"created_at": "2026-01-23T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"page": 1,
|
||||
"page_size": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4️⃣ 获取工具详情
|
||||
|
||||
### 接口
|
||||
|
||||
```
|
||||
GET /api/user/external-tools/{tool_id}
|
||||
```
|
||||
|
||||
### 路径参数
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|-----|------|------|
|
||||
| `tool_id` | string | 工具ID(UUID) |
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "weather-query-tool",
|
||||
"description": "查询天气信息的外部数据工具",
|
||||
"url": "https://api.weather.com/v1/forecast",
|
||||
"method": "POST",
|
||||
"auth_type": "api_key",
|
||||
"tool_ref_id": "tool-weather-abc123",
|
||||
"status": "active",
|
||||
"usage_count": 15,
|
||||
"created_at": "2026-01-23T10:00:00Z",
|
||||
"updated_at": "2026-01-23T10:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**:MCP-Server 只存储基本展示信息,不存储完整配置和敏感信息。
|
||||
|
||||
---
|
||||
|
||||
## 5️⃣ 更新工具配置
|
||||
|
||||
### 接口
|
||||
|
||||
```
|
||||
PUT /api/user/external-tools/{tool_id}
|
||||
```
|
||||
|
||||
### 请求参数
|
||||
|
||||
与创建接口相同,需要传递完整配置(因为 MCP-Server 不存储完整配置)。
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "weather-query-tool-v2",
|
||||
"tool_ref_id": "tool-weather-abc123-v2",
|
||||
"status": "active",
|
||||
"updated_at": "2026-01-23T11:00:00Z"
|
||||
},
|
||||
"message": "外部数据工具更新成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6️⃣ 删除工具
|
||||
|
||||
### 接口
|
||||
|
||||
```
|
||||
DELETE /api/user/external-tools/{tool_id}
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
},
|
||||
"message": "外部数据工具删除成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7️⃣ 测试工具连接
|
||||
|
||||
### 接口
|
||||
|
||||
```
|
||||
POST /api/user/external-tools/{tool_id}/test
|
||||
```
|
||||
|
||||
### 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|:---:|------|
|
||||
| `test_params` | object | ❌ | 测试参数 |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"test_params": {
|
||||
"city": "北京"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"connected": true,
|
||||
"response_time_ms": 156,
|
||||
"status_code": 200,
|
||||
"sample_response": {
|
||||
"status": "ok",
|
||||
"data": {
|
||||
"city": "北京",
|
||||
"temperature": "15°C"
|
||||
}
|
||||
}
|
||||
},
|
||||
"message": "工具连接测试成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8️⃣ 创建带有外部工具的自定义 Agent
|
||||
|
||||
> **注意**: 此功能已整合到原有的自定义 Agent 创建接口中
|
||||
|
||||
### 接口
|
||||
|
||||
```
|
||||
POST /api/user/custom-agents
|
||||
```
|
||||
|
||||
### 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|-----|------|:---:|------|
|
||||
| `name` | string | ✅ | Agent 名称(1-63字符) |
|
||||
| `template` | string | ✅ | Agent 模板名称(从 Agent Manager 获取) |
|
||||
| `frameworkTemplate` | string | ❌ | 框架模板类型(A2A/langchain/MCP),默认 MCP |
|
||||
| `description` | string | ❌ | Agent 描述 |
|
||||
| `externalTools` | string[] | ❌ | **外部数据工具 ID 列表**(使用新的外部工具) |
|
||||
| `tools` | string[] | ❌ | 内置工具 ID 列表 |
|
||||
| `cpuRequest` | string | ❌ | CPU 请求量,默认 "100m" |
|
||||
| `cpuLimit` | string | ❌ | CPU 限制量 |
|
||||
| `memoryRequest` | string | ❌ | 内存请求量,默认 "128Mi" |
|
||||
| `memoryLimit` | string | ❌ | 内存限制量 |
|
||||
| `model` | string | ❌ | 使用的模型名称(会自动注入 LiteLLM 配置) |
|
||||
| `envConfig` | object | ❌ | 自定义环境变量 |
|
||||
|
||||
### 请求示例(使用外部数据工具)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-data-agent",
|
||||
"template": "custom_agent",
|
||||
"description": "我的数据处理 Agent",
|
||||
"externalTools": [
|
||||
"550e8400-e29b-41d4-a716-446655440000",
|
||||
"550e8400-e29b-41d4-a716-446655440001"
|
||||
],
|
||||
"cpuRequest": "500m",
|
||||
"cpuLimit": "1000m",
|
||||
"memoryRequest": "512Mi",
|
||||
"memoryLimit": "1Gi",
|
||||
"model": "gpt-4"
|
||||
}
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"name": "my-data-agent",
|
||||
"namespace": "ai-agents",
|
||||
"status": "Pending",
|
||||
"tools_attached": 2,
|
||||
"servicePort": 8080,
|
||||
"accessInfo": {
|
||||
"domain": "my-data-agent.example.com",
|
||||
"domain_url": "https://my-data-agent.example.com"
|
||||
},
|
||||
"modelInjected": true,
|
||||
"quotaRemaining": {
|
||||
"cpu": 3.5,
|
||||
"memory": 7.0
|
||||
}
|
||||
},
|
||||
"message": "自定义 Agent my-data-agent 创建成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ❌ 错误响应
|
||||
|
||||
### 通用格式
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": {
|
||||
"error": "错误代码",
|
||||
"message": "错误信息"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 常见错误码
|
||||
|
||||
| HTTP状态码 | 错误代码 | 说明 |
|
||||
|-----------|---------|------|
|
||||
| 400 | `invalid_config` | 工具配置格式无效 |
|
||||
| 400 | `invalid_url` | URL 格式无效 |
|
||||
| 400 | `invalid_tool_id` | 无效的工具 ID 格式 |
|
||||
| 400 | `tool_not_active` | 工具尚未就绪 |
|
||||
| 400 | `missing_tools` | 必须选择至少一个工具 |
|
||||
| 400 | `quota_insufficient` | CPU/内存配额不足 |
|
||||
| 403 | `no_quota` | 没有自定义Agent配额 |
|
||||
| 403 | `no_model_permission` | 没有指定模型的使用权限 |
|
||||
| 404 | `tool_not_found` | 工具不存在 |
|
||||
| 409 | `tool_name_exists` | 工具名称已存在 |
|
||||
| 500 | `am_generate_failed` | Agent Manager 生成工具失败 |
|
||||
|
||||
---
|
||||
|
||||
## 📊 Agent Manager 接口(内部使用)
|
||||
|
||||
以下接口由 MCP-Server 内部调用,前端无需关注:
|
||||
|
||||
| 接口 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `POST /tools/generate` | 生成 Pydantic 工具文件 |
|
||||
| `PUT /tools/{tool_ref_id}` | 更新工具文件 |
|
||||
| `DELETE /tools/{tool_ref_id}` | 删除工具文件 |
|
||||
| `POST /tools/{tool_ref_id}/test` | 测试工具连接 |
|
||||
| `POST /agents` | 创建 Agent(支持 tool_refs 参数) |
|
||||
|
||||
---
|
||||
|
||||
## 📋 JSON 配置文件示例
|
||||
|
||||
### 示例 1:天气查询工具
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "weather-api",
|
||||
"description": "查询城市天气信息",
|
||||
"url": "https://api.weather.com/v1/forecast",
|
||||
"method": "GET",
|
||||
"auth": {
|
||||
"type": "api_key",
|
||||
"key": "your-weather-api-key",
|
||||
"in": "query",
|
||||
"name": "apikey"
|
||||
},
|
||||
"request_params": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {
|
||||
"type": "string",
|
||||
"description": "城市名称",
|
||||
"required": true
|
||||
},
|
||||
"units": {
|
||||
"type": "string",
|
||||
"description": "温度单位",
|
||||
"enum": ["metric", "imperial"],
|
||||
"default": "metric"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": 10
|
||||
}
|
||||
```
|
||||
|
||||
### 示例 2:企业内部 API
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "internal-crm-api",
|
||||
"description": "查询客户信息",
|
||||
"url": "https://internal.company.com/api/v2/customers",
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
},
|
||||
"request_body": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"customer_id": {
|
||||
"type": "string",
|
||||
"description": "客户ID",
|
||||
"required": true
|
||||
},
|
||||
"include_orders": {
|
||||
"type": "boolean",
|
||||
"description": "是否包含订单信息",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"response_mapping": {
|
||||
"success_field": "code",
|
||||
"success_value": 0,
|
||||
"data_field": "data",
|
||||
"error_field": "message"
|
||||
},
|
||||
"timeout": 30
|
||||
}
|
||||
```
|
||||
|
||||
### 示例 3:数据库查询服务
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "db-query-service",
|
||||
"description": "执行 SQL 查询",
|
||||
"url": "https://db-gateway.company.com/query",
|
||||
"method": "POST",
|
||||
"auth": {
|
||||
"type": "basic",
|
||||
"username": "readonly",
|
||||
"password": "secure-password-123"
|
||||
},
|
||||
"request_body": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"database": {
|
||||
"type": "string",
|
||||
"description": "数据库名称",
|
||||
"required": true
|
||||
},
|
||||
"sql": {
|
||||
"type": "string",
|
||||
"description": "SQL 查询语句",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": 60
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧰 工具集接口
|
||||
|
||||
工具集允许用户将多个外部数据工具组合在一起,方便部署自定义 Agent。
|
||||
|
||||
### 9️⃣ 创建工具集
|
||||
|
||||
```
|
||||
POST /api/user/toolkits
|
||||
```
|
||||
|
||||
#### 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|-----|------|:---:|------|
|
||||
| `name` | string | ✅ | 工具集名称(1-100字符) |
|
||||
| `description` | string | ❌ | 工具集描述 |
|
||||
| `tool_ids` | string[] | ✅ | 外部数据工具 ID 列表(1-8 个) |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "数据分析工具集",
|
||||
"description": "包含数据查询和分析相关工具",
|
||||
"tool_ids": [
|
||||
"550e8400-e29b-41d4-a716-446655440000",
|
||||
"550e8400-e29b-41d4-a716-446655440001"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "660e8400-e29b-41d4-a716-446655440002",
|
||||
"name": "数据分析工具集",
|
||||
"description": "包含数据查询和分析相关工具",
|
||||
"tool_count": 2,
|
||||
"created_at": "2026-01-23T10:00:00Z"
|
||||
},
|
||||
"message": "工具集创建成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🔟 获取工具集列表
|
||||
|
||||
```
|
||||
GET /api/user/toolkits
|
||||
```
|
||||
|
||||
#### 查询参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|-----|------|:---:|------|
|
||||
| `page` | integer | ❌ | 页码,默认 1 |
|
||||
| `page_size` | integer | ❌ | 每页数量,默认 20,最大 100 |
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"toolkits": [
|
||||
{
|
||||
"id": "660e8400-e29b-41d4-a716-446655440002",
|
||||
"name": "数据分析工具集",
|
||||
"description": "包含数据查询和分析相关工具",
|
||||
"tool_count": 2,
|
||||
"usage_count": 5,
|
||||
"created_at": "2026-01-23T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"page": 1,
|
||||
"page_size": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣1️⃣ 获取工具集详情
|
||||
|
||||
```
|
||||
GET /api/user/toolkits/{toolkit_id}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "660e8400-e29b-41d4-a716-446655440002",
|
||||
"name": "数据分析工具集",
|
||||
"description": "包含数据查询和分析相关工具",
|
||||
"tool_ids": [
|
||||
"550e8400-e29b-41d4-a716-446655440000",
|
||||
"550e8400-e29b-41d4-a716-446655440001"
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "weather-api",
|
||||
"description": "天气查询 API",
|
||||
"url": "https://api.weather.com/current",
|
||||
"method": "GET",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"name": "stock-api",
|
||||
"description": "股票查询 API",
|
||||
"url": "https://api.stock.com/price",
|
||||
"method": "GET",
|
||||
"status": "active"
|
||||
}
|
||||
],
|
||||
"usage_count": 5,
|
||||
"created_at": "2026-01-23T10:00:00Z",
|
||||
"updated_at": "2026-01-23T12:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣2️⃣ 更新工具集
|
||||
|
||||
```
|
||||
PUT /api/user/toolkits/{toolkit_id}
|
||||
```
|
||||
|
||||
#### 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|-----|------|:---:|------|
|
||||
| `name` | string | ❌ | 工具集名称 |
|
||||
| `description` | string | ❌ | 工具集描述 |
|
||||
| `tool_ids` | string[] | ❌ | 工具 ID 列表(1-8 个) |
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣3️⃣ 删除工具集
|
||||
|
||||
```
|
||||
DELETE /api/user/toolkits/{toolkit_id}
|
||||
```
|
||||
|
||||
> ⚠️ **注意**:删除工具集不会删除其中的工具,只是解除组合关系。
|
||||
|
||||
---
|
||||
|
||||
## 📌 在自定义 Agent 中使用工具集
|
||||
|
||||
创建自定义 Agent 时,可以通过 `toolkit` 字段指定工具集:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-data-agent",
|
||||
"template": "custom_agent",
|
||||
"toolkit": "660e8400-e29b-41d4-a716-446655440002",
|
||||
"cpuRequest": "500m",
|
||||
"memoryRequest": "512Mi"
|
||||
}
|
||||
```
|
||||
|
||||
也可以同时使用工具集和单独的工具(会自动合并去重):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-data-agent",
|
||||
"template": "custom_agent",
|
||||
"toolkit": "660e8400-e29b-41d4-a716-446655440002",
|
||||
"externalTools": ["770e8400-e29b-41d4-a716-446655440003"],
|
||||
"cpuRequest": "500m",
|
||||
"memoryRequest": "512Mi"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**如有问题,请联系开发团队。**
|
||||
|
||||
@@ -1,501 +0,0 @@
|
||||
# Agent 域名访问改动计划
|
||||
|
||||
> **版本**: 2026-01-14 v1
|
||||
> **状态**: ✅ 已完成
|
||||
> **相关服务**: mcp-server, agent-manager
|
||||
> **完成时间**: 2026-01-14
|
||||
|
||||
---
|
||||
|
||||
## 📋 背景与需求
|
||||
|
||||
### 业务变更说明
|
||||
|
||||
1. **Agent Manager 服务升级**:部署好的每个 Pod 现在会自动绑定域名和外网 IP
|
||||
2. **访问方式变更**:租户后续将使用域名访问属于自己的平台 Agent 和自定义 Agent
|
||||
3. **数据存储需求**:需要记录 Agent Manager 返回的访问信息(domain、external_ip 等)
|
||||
|
||||
### Agent Manager 返回的 access_info 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"access_info": {
|
||||
"external_ip": "135.171.210.24",
|
||||
"ip_url": "http://135.171.210.24:80",
|
||||
"domain": "my-agent.taijiagent.com",
|
||||
"domain_url": "http://my-agent.taijiagent.com",
|
||||
"recommended": "http://my-agent.taijiagent.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 当前代码分析
|
||||
|
||||
### 1. 数据库模型现状
|
||||
|
||||
#### Agent 模型 (`models.py:125`)
|
||||
|
||||
```python
|
||||
class Agent(BaseModel, Base):
|
||||
# ... 已有字段
|
||||
access_url = Column(String(500)) # 访问 URL(单个字段)
|
||||
endpoints = Column(JSON, default=dict) # 端点信息
|
||||
# ❌ 缺少 domain、external_ip 等字段
|
||||
```
|
||||
|
||||
#### AgentBillingRecord 模型 (`models.py:1157`)
|
||||
|
||||
```python
|
||||
class AgentBillingRecord(BaseModel, Base):
|
||||
# ... 已有字段
|
||||
agent_name = Column(String(100), nullable=False)
|
||||
# ❌ 缺少 access_info 相关字段(domain、external_ip、access_url)
|
||||
```
|
||||
|
||||
### 2. Agent Manager Client 现状
|
||||
|
||||
#### AgentCreateResult (`agent_manager_client.py:97`)
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class AgentCreateResult:
|
||||
name: str
|
||||
namespace: str
|
||||
status: str
|
||||
access_info: Optional[Dict[str, Any]] = None # ✅ 已有,但未完整使用
|
||||
```
|
||||
|
||||
#### AgentStatusResult (`agent_manager_client.py:119`)
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class AgentStatusResult:
|
||||
# ❌ 缺少 domain、external_ip 等字段
|
||||
access_url: Optional[str] = None # 只有单个 access_url
|
||||
endpoints: Optional[List[str]] = None
|
||||
```
|
||||
|
||||
### 3. 创建 Agent 代码现状
|
||||
|
||||
#### 平台 Agent 创建 (`user.py:1427-1439`)
|
||||
|
||||
```python
|
||||
result = await client.create_agent(...)
|
||||
# 创建 Agent 记录时
|
||||
agent = Agent(
|
||||
name=instance_name,
|
||||
# ❌ 未保存 result.access_info 中的 domain、external_ip
|
||||
)
|
||||
```
|
||||
|
||||
#### 自定义 Agent 创建 (`user.py:3146-3157`)
|
||||
|
||||
```python
|
||||
result = await client.create_custom_agent(...)
|
||||
# ❌ 响应中只返回了 accessInfo,未持久化 domain 到数据库
|
||||
return SuccessResponse(
|
||||
data={
|
||||
"accessInfo": result.access_info, # 只是透传,未存储
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### 4. 查询 Agent 列表代码现状
|
||||
|
||||
#### 用户 Agent 资源查询 (`user.py:3904-3999`)
|
||||
|
||||
```python
|
||||
@router.get("/resources/agents")
|
||||
async def get_user_agents_info(...):
|
||||
# 从 Agent Manager 获取状态
|
||||
agent_status = await client.get_agent_status(record.agent_name)
|
||||
agent_info["accessUrl"] = agent_status.access_url # ❌ 只返回 access_url
|
||||
# ❌ 缺少 domain、external_ip 等字段
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 改动方案
|
||||
|
||||
### 阶段一:数据库模型改动
|
||||
|
||||
#### 1.1 修改 AgentBillingRecord 模型
|
||||
|
||||
**文件**: `services/mcp-server/models.py`
|
||||
|
||||
```python
|
||||
class AgentBillingRecord(BaseModel, Base):
|
||||
# ... 已有字段
|
||||
|
||||
# ========== 新增:访问信息字段 ==========
|
||||
external_ip = Column(String(45), nullable=True) # 外网 IP 地址
|
||||
domain = Column(String(255), nullable=True) # 域名
|
||||
domain_url = Column(String(500), nullable=True) # 域名访问地址
|
||||
access_url = Column(String(500), nullable=True) # 推荐访问地址
|
||||
service_port = Column(Integer, nullable=True) # 服务端口
|
||||
namespace = Column(String(100), nullable=True) # K8s 命名空间
|
||||
```
|
||||
|
||||
#### 1.2 创建数据库迁移脚本
|
||||
|
||||
**文件**: `services/mcp-server/alembic/versions/xxxx_add_agent_access_info.py`
|
||||
|
||||
```python
|
||||
"""Add agent access info fields
|
||||
|
||||
Revision ID: xxxx
|
||||
"""
|
||||
|
||||
def upgrade():
|
||||
op.add_column('agent_billing_records',
|
||||
sa.Column('external_ip', sa.String(45), nullable=True))
|
||||
op.add_column('agent_billing_records',
|
||||
sa.Column('domain', sa.String(255), nullable=True))
|
||||
op.add_column('agent_billing_records',
|
||||
sa.Column('domain_url', sa.String(500), nullable=True))
|
||||
op.add_column('agent_billing_records',
|
||||
sa.Column('access_url', sa.String(500), nullable=True))
|
||||
op.add_column('agent_billing_records',
|
||||
sa.Column('service_port', sa.Integer, nullable=True))
|
||||
op.add_column('agent_billing_records',
|
||||
sa.Column('namespace', sa.String(100), nullable=True))
|
||||
|
||||
# 添加索引(可选,用于按域名查询)
|
||||
op.create_index('idx_agent_billing_domain', 'agent_billing_records', ['domain'])
|
||||
|
||||
def downgrade():
|
||||
op.drop_index('idx_agent_billing_domain', 'agent_billing_records')
|
||||
op.drop_column('agent_billing_records', 'namespace')
|
||||
op.drop_column('agent_billing_records', 'service_port')
|
||||
op.drop_column('agent_billing_records', 'access_url')
|
||||
op.drop_column('agent_billing_records', 'domain_url')
|
||||
op.drop_column('agent_billing_records', 'domain')
|
||||
op.drop_column('agent_billing_records', 'external_ip')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 阶段二:Agent Manager Client 改动
|
||||
|
||||
#### 2.1 修改 AgentStatusResult
|
||||
|
||||
**文件**: `services/mcp-server/app/agent_manager_client.py`
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class AgentStatusResult:
|
||||
# ... 已有字段
|
||||
|
||||
# ========== 新增:访问信息字段 ==========
|
||||
external_ip: Optional[str] = None # 外网 IP
|
||||
domain: Optional[str] = None # 域名
|
||||
domain_url: Optional[str] = None # 域名访问地址
|
||||
```
|
||||
|
||||
#### 2.2 修改 get_agent_status 方法解析逻辑
|
||||
|
||||
**文件**: `services/mcp-server/app/agent_manager_client.py`
|
||||
|
||||
在 `get_agent_status` 方法中,需要解析 Agent Manager 返回的 access_info:
|
||||
|
||||
```python
|
||||
async def get_agent_status(self, name: str) -> AgentStatusResult:
|
||||
# ... 现有逻辑
|
||||
|
||||
# 解析 access_info
|
||||
access_info = data.get("access_info", {})
|
||||
|
||||
return AgentStatusResult(
|
||||
# ... 现有字段
|
||||
external_ip=access_info.get("external_ip"),
|
||||
domain=access_info.get("domain"),
|
||||
domain_url=access_info.get("domain_url"),
|
||||
access_url=access_info.get("recommended") or access_info.get("domain_url"),
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 阶段三:创建 Agent 代码改动
|
||||
|
||||
#### 3.1 平台 Agent 创建改动
|
||||
|
||||
**文件**: `services/mcp-server/app/routes/user.py`
|
||||
|
||||
**位置**: `list_platform_agents` / `deploy_platform_agent` 函数
|
||||
|
||||
```python
|
||||
# 在创建 billing_record 时保存 access_info
|
||||
billing_record = AgentBillingRecord(
|
||||
# ... 已有字段
|
||||
|
||||
# ========== 新增:保存访问信息 ==========
|
||||
external_ip=result.access_info.get("external_ip") if result.access_info else None,
|
||||
domain=result.access_info.get("domain") if result.access_info else None,
|
||||
domain_url=result.access_info.get("domain_url") if result.access_info else None,
|
||||
access_url=result.access_info.get("recommended") if result.access_info else None,
|
||||
service_port=result.service_port,
|
||||
namespace=result.namespace,
|
||||
)
|
||||
```
|
||||
|
||||
#### 3.2 自定义 Agent 创建改动
|
||||
|
||||
**文件**: `services/mcp-server/app/routes/user.py`
|
||||
|
||||
**位置**: `create_custom_agent` 函数(约第 3146-3213 行)
|
||||
|
||||
```python
|
||||
# 在创建 billing_record 时保存 access_info
|
||||
billing_record = AgentBillingRecord(
|
||||
# ... 已有字段
|
||||
|
||||
# ========== 新增:保存访问信息 ==========
|
||||
external_ip=result.access_info.get("external_ip") if result.access_info else None,
|
||||
domain=result.access_info.get("domain") if result.access_info else None,
|
||||
domain_url=result.access_info.get("domain_url") if result.access_info else None,
|
||||
access_url=result.access_info.get("recommended") if result.access_info else None,
|
||||
service_port=result.service_port,
|
||||
namespace=result.namespace,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 阶段四:查询 Agent 列表改动
|
||||
|
||||
#### 4.1 用户 Agent 资源查询改动
|
||||
|
||||
**文件**: `services/mcp-server/app/routes/user.py`
|
||||
|
||||
**位置**: `get_user_agents_info` 函数(约第 3904-3999 行)
|
||||
|
||||
```python
|
||||
@router.get("/resources/agents", response_model=SuccessResponse)
|
||||
async def get_user_agents_info(...):
|
||||
for record in billing_records:
|
||||
agent_info = {
|
||||
# ... 已有字段
|
||||
|
||||
# ========== 新增:访问信息字段(优先使用数据库存储的值) ==========
|
||||
"externalIp": record.external_ip,
|
||||
"domain": record.domain,
|
||||
"domainUrl": record.domain_url,
|
||||
"accessUrl": record.access_url, # 推荐访问地址
|
||||
}
|
||||
|
||||
# 从 Agent Manager 获取最新状态(实时更新 IP 等信息)
|
||||
if agent_manager_available and client:
|
||||
try:
|
||||
agent_status = await client.get_agent_status(record.agent_name)
|
||||
# 更新实时状态
|
||||
agent_info["status"] = agent_status.status
|
||||
agent_info["healthStatus"] = agent_status.health_status
|
||||
agent_info["podIp"] = agent_status.pod_ip
|
||||
|
||||
# 更新访问信息(如果 Agent Manager 返回了新的值)
|
||||
if agent_status.external_ip:
|
||||
agent_info["externalIp"] = agent_status.external_ip
|
||||
if agent_status.domain:
|
||||
agent_info["domain"] = agent_status.domain
|
||||
if agent_status.domain_url:
|
||||
agent_info["domainUrl"] = agent_status.domain_url
|
||||
if agent_status.access_url:
|
||||
agent_info["accessUrl"] = agent_status.access_url
|
||||
except Exception as e:
|
||||
logger.warning(f"获取 Agent {record.agent_name} 状态失败: {e}")
|
||||
```
|
||||
|
||||
#### 4.2 自定义 Agent 列表查询改动
|
||||
|
||||
**文件**: `services/mcp-server/app/routes/user.py`
|
||||
|
||||
**位置**: `list_my_custom_agents` 函数(约第 3462-3516 行)
|
||||
|
||||
同样需要添加 domain 等字段的返回。
|
||||
|
||||
---
|
||||
|
||||
### 阶段五:响应模型改动
|
||||
|
||||
#### 5.1 添加/修改 Schema
|
||||
|
||||
**文件**: `services/mcp-server/app/schemas.py` 或 `services/mcp-server/schemas.py`
|
||||
|
||||
```python
|
||||
class AgentAccessInfo(BaseModel):
|
||||
"""Agent 访问信息"""
|
||||
external_ip: Optional[str] = Field(None, description="外网 IP 地址")
|
||||
domain: Optional[str] = Field(None, description="域名")
|
||||
domain_url: Optional[str] = Field(None, description="域名访问地址")
|
||||
ip_url: Optional[str] = Field(None, description="IP 访问地址")
|
||||
recommended: Optional[str] = Field(None, description="推荐访问地址")
|
||||
|
||||
|
||||
class AgentResourceInfo(BaseModel):
|
||||
"""用户 Agent 资源信息"""
|
||||
name: str
|
||||
template: str
|
||||
templateName: Optional[str] = None
|
||||
status: str
|
||||
healthStatus: str
|
||||
|
||||
# Pod 信息
|
||||
podIp: Optional[str] = None
|
||||
hostIp: Optional[str] = None
|
||||
nodeName: Optional[str] = None
|
||||
|
||||
# ========== 新增:访问信息 ==========
|
||||
externalIp: Optional[str] = Field(None, description="外网 IP 地址")
|
||||
domain: Optional[str] = Field(None, description="域名")
|
||||
domainUrl: Optional[str] = Field(None, description="域名访问地址")
|
||||
accessUrl: Optional[str] = Field(None, description="推荐访问地址(域名优先)")
|
||||
|
||||
# 资源配置
|
||||
servicePort: Optional[int] = None
|
||||
namespace: str = "ai-agents"
|
||||
cpu: Optional[str] = None
|
||||
memory: Optional[str] = None
|
||||
replicas: int = 1
|
||||
|
||||
# 运行信息
|
||||
startTime: Optional[str] = None
|
||||
runningSeconds: int = 0
|
||||
endpoints: Optional[List[str]] = None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📄 接口文档更新
|
||||
|
||||
### GET /api/user/resources/agents 响应更新
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"platformAgents": [
|
||||
{
|
||||
"name": "echo-agent-b00a7b8e-fa5a66",
|
||||
"template": "echo_agent",
|
||||
"templateName": "echo_agent",
|
||||
"status": "Running",
|
||||
"healthStatus": "healthy",
|
||||
"podIp": "10.244.2.103",
|
||||
"externalIp": "135.171.210.24",
|
||||
"domain": "echo-agent-b00a7b8e-fa5a66.taijiagent.com",
|
||||
"domainUrl": "http://echo-agent-b00a7b8e-fa5a66.taijiagent.com",
|
||||
"accessUrl": "http://echo-agent-b00a7b8e-fa5a66.taijiagent.com",
|
||||
"servicePort": 80,
|
||||
"namespace": "agent-echo-agent-b00a7b8e-fa5a66",
|
||||
"cpu": "100m",
|
||||
"memory": "256Mi",
|
||||
"replicas": 1,
|
||||
"startTime": "2026-01-11T15:17:17.579421",
|
||||
"runningSeconds": 227937
|
||||
}
|
||||
],
|
||||
"customAgents": [
|
||||
{
|
||||
"name": "my-mysql-agent",
|
||||
"template": "mysql_agent",
|
||||
"templateName": "MCP",
|
||||
"status": "Running",
|
||||
"healthStatus": "healthy",
|
||||
"podIp": "10.244.1.61",
|
||||
"externalIp": "135.171.210.25",
|
||||
"domain": "my-mysql-agent.taijiagent.com",
|
||||
"domainUrl": "http://my-mysql-agent.taijiagent.com",
|
||||
"accessUrl": "http://my-mysql-agent.taijiagent.com",
|
||||
"servicePort": 80,
|
||||
"namespace": "agent-my-mysql-agent",
|
||||
"cpu": "500m",
|
||||
"memory": "1Gi",
|
||||
"replicas": 1,
|
||||
"startTime": "2026-01-13T07:32:52.290231",
|
||||
"runningSeconds": 83003
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"totalPlatformAgents": 1,
|
||||
"totalCustomAgents": 1
|
||||
}
|
||||
},
|
||||
"message": "Agent 列表获取成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 改动文件清单
|
||||
|
||||
| 序号 | 文件路径 | 改动类型 | 改动说明 |
|
||||
|:---:|---------|---------|---------|
|
||||
| 1 | `services/mcp-server/models.py` | 修改 | AgentBillingRecord 添加访问信息字段 |
|
||||
| 2 | `services/mcp-server/alembic/versions/xxx.py` | 新增 | 数据库迁移脚本 |
|
||||
| 3 | `services/mcp-server/app/agent_manager_client.py` | 修改 | AgentStatusResult 添加 domain 等字段 |
|
||||
| 4 | `services/mcp-server/app/routes/user.py` | 修改 | 创建 Agent 时保存 access_info |
|
||||
| 5 | `services/mcp-server/app/routes/user.py` | 修改 | 查询 Agent 列表返回 domain 等字段 |
|
||||
| 6 | `services/mcp-server/app/schemas.py` | 修改 | 添加/修改响应模型 |
|
||||
| 7 | `Docs/用户资源信息查询接口文档.md` | 修改 | 更新接口文档 |
|
||||
| 8 | `Docs/数据工具与自定义Agent-前端接口文档.md` | 修改 | 更新接口文档 |
|
||||
|
||||
---
|
||||
|
||||
## 🔄 实施步骤
|
||||
|
||||
### Step 1: 数据库改动(需要停机)
|
||||
|
||||
1. 备份数据库
|
||||
2. 执行数据库迁移脚本
|
||||
3. 验证迁移结果
|
||||
|
||||
### Step 2: 代码改动
|
||||
|
||||
1. 修改 `models.py`
|
||||
2. 修改 `agent_manager_client.py`
|
||||
3. 修改 `user.py` 中的创建 Agent 逻辑
|
||||
4. 修改 `user.py` 中的查询 Agent 逻辑
|
||||
5. 修改响应模型
|
||||
|
||||
### Step 3: 测试验证
|
||||
|
||||
1. 单元测试
|
||||
2. 集成测试(创建 Agent → 查询列表 → 访问域名)
|
||||
3. 前端联调
|
||||
|
||||
### Step 4: 文档更新
|
||||
|
||||
1. 更新 API 接口文档
|
||||
2. 更新前端接口文档
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **向后兼容**:新增字段均为可选(nullable=True),不影响现有数据
|
||||
2. **域名生效时间**:域名 DNS 解析可能有延迟(通常 1-5 分钟)
|
||||
3. **访问优先级**:推荐使用 `accessUrl`(域名优先),Pod IP 会随重启变化
|
||||
4. **安全考虑**:域名访问可能需要配置 HTTPS(后续考虑)
|
||||
|
||||
---
|
||||
|
||||
## 📊 预估工时
|
||||
|
||||
| 阶段 | 工时估算 |
|
||||
|-----|---------|
|
||||
| 数据库改动 | 0.5 天 |
|
||||
| Agent Manager Client 改动 | 0.5 天 |
|
||||
| 创建 Agent 代码改动 | 1 天 |
|
||||
| 查询 Agent 列表改动 | 0.5 天 |
|
||||
| 测试与联调 | 1 天 |
|
||||
| 文档更新 | 0.5 天 |
|
||||
| **总计** | **4 天** |
|
||||
|
||||
---
|
||||
|
||||
**文档编写**: AI Assistant
|
||||
**最后更新**: 2026-01-14
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1156,6 +1156,282 @@ class AgentManagerClient:
|
||||
count=data.get("count", 0)
|
||||
)
|
||||
|
||||
# ==================== External Data Tool Management ====================
|
||||
# 外部数据工具管理接口
|
||||
# Agent Manager 负责生成和存储 Pydantic 工具文件,返回 tool_ref_id 给 MCP-Server
|
||||
|
||||
async def generate_external_tool(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
url: str,
|
||||
method: str,
|
||||
user_id: str,
|
||||
tenant_id: Optional[str] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Dict[str, Any]] = None,
|
||||
request_params: Optional[Dict[str, Any]] = None,
|
||||
request_body: Optional[Dict[str, Any]] = None,
|
||||
response_mapping: Optional[Dict[str, Any]] = None,
|
||||
timeout: int = 30,
|
||||
retry: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
生成外部数据工具
|
||||
|
||||
调用: POST /tools/generate
|
||||
|
||||
将工具配置发送给 Agent Manager,AM 会:
|
||||
1. 根据配置生成 Pydantic 工具代码文件
|
||||
2. 存储工具文件和配置
|
||||
3. 返回 tool_ref_id(工具标识)
|
||||
|
||||
Args:
|
||||
name: 工具名称
|
||||
description: 工具描述
|
||||
url: API 端点 URL
|
||||
method: HTTP 方法(GET/POST/PUT/DELETE/PATCH)
|
||||
user_id: 用户 ID
|
||||
tenant_id: 租户 ID(可选)
|
||||
headers: 自定义请求头
|
||||
auth: 认证配置(type, key, in, name 等)
|
||||
request_params: URL 查询参数定义(JSON Schema 格式)
|
||||
request_body: 请求体定义(JSON Schema 格式)
|
||||
response_mapping: 响应字段映射
|
||||
timeout: 超时时间(秒)
|
||||
retry: 重试配置
|
||||
|
||||
Returns:
|
||||
{
|
||||
"success": true,
|
||||
"tool_ref_id": "tool-xxx-123",
|
||||
"tool_name": "weather_query_tool",
|
||||
"status": "active",
|
||||
"message": "工具生成成功"
|
||||
}
|
||||
"""
|
||||
payload: Dict[str, Any] = {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"url": url,
|
||||
"method": method,
|
||||
"user_id": user_id,
|
||||
}
|
||||
|
||||
if tenant_id:
|
||||
payload["tenant_id"] = tenant_id
|
||||
if headers:
|
||||
payload["headers"] = headers
|
||||
if auth:
|
||||
payload["auth"] = auth
|
||||
if request_params:
|
||||
payload["request_params"] = request_params
|
||||
if request_body:
|
||||
payload["request_body"] = request_body
|
||||
if response_mapping:
|
||||
payload["response_mapping"] = response_mapping
|
||||
if timeout:
|
||||
payload["timeout"] = timeout
|
||||
if retry:
|
||||
payload["retry"] = retry
|
||||
|
||||
logger.info(
|
||||
"generating_external_tool",
|
||||
name=name,
|
||||
url=url,
|
||||
method=method,
|
||||
user_id=user_id
|
||||
)
|
||||
|
||||
return await self._request("POST", "/tools/generate", json=payload)
|
||||
|
||||
async def update_external_tool(
|
||||
self,
|
||||
tool_ref_id: str,
|
||||
name: str,
|
||||
description: str,
|
||||
url: str,
|
||||
method: str,
|
||||
user_id: str,
|
||||
tenant_id: Optional[str] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Dict[str, Any]] = None,
|
||||
request_params: Optional[Dict[str, Any]] = None,
|
||||
request_body: Optional[Dict[str, Any]] = None,
|
||||
response_mapping: Optional[Dict[str, Any]] = None,
|
||||
timeout: int = 30,
|
||||
retry: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
更新外部数据工具
|
||||
|
||||
调用: PUT /tools/{tool_ref_id}
|
||||
|
||||
Agent Manager 会重新生成工具文件,可能返回新的 tool_ref_id。
|
||||
|
||||
Args:
|
||||
tool_ref_id: 原工具标识
|
||||
其他参数同 generate_external_tool
|
||||
|
||||
Returns:
|
||||
{
|
||||
"success": true,
|
||||
"tool_ref_id": "tool-xxx-123-v2",
|
||||
"status": "active",
|
||||
"message": "工具更新成功"
|
||||
}
|
||||
"""
|
||||
payload: Dict[str, Any] = {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"url": url,
|
||||
"method": method,
|
||||
"user_id": user_id,
|
||||
}
|
||||
|
||||
if tenant_id:
|
||||
payload["tenant_id"] = tenant_id
|
||||
if headers:
|
||||
payload["headers"] = headers
|
||||
if auth:
|
||||
payload["auth"] = auth
|
||||
if request_params:
|
||||
payload["request_params"] = request_params
|
||||
if request_body:
|
||||
payload["request_body"] = request_body
|
||||
if response_mapping:
|
||||
payload["response_mapping"] = response_mapping
|
||||
if timeout:
|
||||
payload["timeout"] = timeout
|
||||
if retry:
|
||||
payload["retry"] = retry
|
||||
|
||||
logger.info(
|
||||
"updating_external_tool",
|
||||
tool_ref_id=tool_ref_id,
|
||||
name=name,
|
||||
user_id=user_id
|
||||
)
|
||||
|
||||
return await self._request("PUT", f"/tools/{tool_ref_id}", json=payload)
|
||||
|
||||
async def delete_external_tool(self, tool_ref_id: str) -> Dict[str, Any]:
|
||||
"""
|
||||
删除外部数据工具
|
||||
|
||||
调用: DELETE /tools/{tool_ref_id}
|
||||
|
||||
Agent Manager 会删除对应的工具文件和配置。
|
||||
|
||||
Args:
|
||||
tool_ref_id: 工具标识
|
||||
|
||||
Returns:
|
||||
{
|
||||
"success": true,
|
||||
"message": "工具删除成功"
|
||||
}
|
||||
"""
|
||||
logger.info("deleting_external_tool", tool_ref_id=tool_ref_id)
|
||||
return await self._request("DELETE", f"/tools/{tool_ref_id}")
|
||||
|
||||
async def test_external_tool(
|
||||
self,
|
||||
tool_ref_id: str,
|
||||
test_params: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
测试外部数据工具连接
|
||||
|
||||
调用: POST /tools/{tool_ref_id}/test
|
||||
|
||||
Agent Manager 会尝试调用工具的 API 并返回测试结果。
|
||||
|
||||
Args:
|
||||
tool_ref_id: 工具标识
|
||||
test_params: 测试参数(可选)
|
||||
|
||||
Returns:
|
||||
{
|
||||
"success": true,
|
||||
"connected": true,
|
||||
"response_time_ms": 156,
|
||||
"status_code": 200,
|
||||
"sample_response": {...}
|
||||
}
|
||||
"""
|
||||
payload = {}
|
||||
if test_params:
|
||||
payload["test_params"] = test_params
|
||||
|
||||
logger.info("testing_external_tool", tool_ref_id=tool_ref_id)
|
||||
return await self._request("POST", f"/tools/{tool_ref_id}/test", json=payload)
|
||||
|
||||
async def create_agent_with_tools(
|
||||
self,
|
||||
name: str,
|
||||
template: str,
|
||||
tool_refs: List[str],
|
||||
config: Optional[AgentConfig] = None,
|
||||
env: Optional[Dict[str, str]] = None
|
||||
) -> AgentCreateResult:
|
||||
"""
|
||||
创建带有外部数据工具的 Agent
|
||||
|
||||
调用: POST /agents(新增 tool_refs 字段)
|
||||
|
||||
Agent Manager 会根据 tool_refs 加载对应的工具文件,部署到 AKS。
|
||||
|
||||
Args:
|
||||
name: Agent 名称
|
||||
template: Agent 模板(通常为 "custom_agent")
|
||||
tool_refs: 外部数据工具标识列表
|
||||
config: 资源配置
|
||||
env: 环境变量
|
||||
|
||||
Returns:
|
||||
创建结果
|
||||
"""
|
||||
payload: Dict[str, Any] = {
|
||||
"name": name,
|
||||
"template": template,
|
||||
"tool_refs": tool_refs
|
||||
}
|
||||
|
||||
if config:
|
||||
payload["config"] = config.to_dict()
|
||||
|
||||
# 构建环境变量
|
||||
final_env = {"LLM_BASE_URL": LLM_BASE_URL}
|
||||
if env:
|
||||
final_env.update(env)
|
||||
payload["env"] = final_env
|
||||
|
||||
logger.info(
|
||||
"creating_agent_with_tools",
|
||||
name=name,
|
||||
template=template,
|
||||
tool_refs=tool_refs,
|
||||
config=config.to_dict() if config else None
|
||||
)
|
||||
|
||||
data = await self._request("POST", "/agents", json=payload)
|
||||
|
||||
return AgentCreateResult(
|
||||
name=data["name"],
|
||||
namespace=data["namespace"],
|
||||
status=data["status"],
|
||||
created_at=data["created_at"],
|
||||
template=data["template"],
|
||||
service_port=data.get("service_port"),
|
||||
access_info=data.get("access_info"),
|
||||
pod_id=data.get("pod_id"),
|
||||
pod_ip=data.get("pod_ip"),
|
||||
host_ip=data.get("host_ip"),
|
||||
node_name=data.get("node_name"),
|
||||
owner_info=data.get("owner_info")
|
||||
)
|
||||
|
||||
# ==================== Unimplemented Interfaces (Not yet provided by Agent Manager) ====================
|
||||
# The interfaces called by the following methods are not yet implemented in Agent Manager
|
||||
# Method signatures are retained for future extension, but will raise NotImplementedError when called
|
||||
|
||||
@@ -11,6 +11,7 @@ from . import (
|
||||
provider_health_management, # 阶段四:供应商健康检查管理
|
||||
platform_agent_quota, # 平台 Agent 配额管理
|
||||
billing_webhook, # LiteLLM Token计费webhook
|
||||
external_tools, # 外部数据工具管理
|
||||
)
|
||||
|
||||
|
||||
@@ -35,6 +36,8 @@ def register_routes(app: FastAPI) -> None:
|
||||
agents.router,
|
||||
sessions.router, # 会话管理路由
|
||||
tools.router,
|
||||
external_tools.router, # 外部数据工具管理路由
|
||||
external_tools.toolkit_router, # 外部数据工具集管理路由
|
||||
monitoring.router,
|
||||
metrics.router,
|
||||
websocket.router,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3144,6 +3144,105 @@ async def create_custom_agent(
|
||||
logger.warning(f"查询工具详情失败,仅传递工具 ID 列表: {str(e)}")
|
||||
# ================================================
|
||||
|
||||
# ========== 外部数据工具配置传递给 Agent Manager ==========
|
||||
from models import ExternalDataTool, ExternalToolkit
|
||||
|
||||
external_tool_refs = [] # 存储 tool_ref_id 列表
|
||||
external_tool_ids_to_process = [] # 需要处理的工具 ID 列表
|
||||
|
||||
# 优先处理工具集(如果指定了 toolkit)
|
||||
if req.toolkit:
|
||||
logger.info(f"处理工具集: user_id={user_id}, toolkit={req.toolkit}")
|
||||
try:
|
||||
toolkit_result = await db.execute(
|
||||
select(ExternalToolkit).where(
|
||||
ExternalToolkit.id == PyUUID(req.toolkit),
|
||||
ExternalToolkit.owner_id == PyUUID(user_id)
|
||||
)
|
||||
)
|
||||
toolkit = toolkit_result.scalar_one_or_none()
|
||||
|
||||
if not toolkit:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"工具集不存在或无权限: {req.toolkit}"
|
||||
)
|
||||
|
||||
# 获取工具集中的工具 ID
|
||||
if toolkit.tool_ids:
|
||||
external_tool_ids_to_process.extend(toolkit.tool_ids)
|
||||
logger.info(f"从工具集获取工具: toolkit={toolkit.name}, tools={toolkit.tool_ids}")
|
||||
|
||||
# 更新工具集使用次数
|
||||
toolkit.usage_count = (toolkit.usage_count or 0) + 1
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.warning(f"查询工具集失败: {req.toolkit}, error={str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"无效的工具集 ID: {req.toolkit}"
|
||||
)
|
||||
|
||||
# 合并直接指定的外部工具(如果同时指定了 externalTools)
|
||||
if req.externalTools and len(req.externalTools) > 0:
|
||||
for ext_id in req.externalTools:
|
||||
if ext_id not in external_tool_ids_to_process:
|
||||
external_tool_ids_to_process.append(ext_id)
|
||||
|
||||
# 处理所有需要使用的外部数据工具
|
||||
if external_tool_ids_to_process:
|
||||
logger.info(f"处理外部数据工具: user_id={user_id}, tools={external_tool_ids_to_process}")
|
||||
|
||||
for ext_tool_id in external_tool_ids_to_process:
|
||||
try:
|
||||
ext_tool_result = await db.execute(
|
||||
select(ExternalDataTool).where(
|
||||
ExternalDataTool.id == PyUUID(ext_tool_id),
|
||||
ExternalDataTool.owner_id == PyUUID(user_id)
|
||||
)
|
||||
)
|
||||
ext_tool = ext_tool_result.scalar_one_or_none()
|
||||
|
||||
if not ext_tool:
|
||||
logger.warning(f"外部数据工具不存在或无权限: {ext_tool_id}")
|
||||
continue
|
||||
|
||||
if ext_tool.status != "active":
|
||||
logger.warning(f"外部数据工具未就绪: {ext_tool_id}, status={ext_tool.status}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"外部数据工具 '{ext_tool.name}' 尚未就绪,状态: {ext_tool.status}"
|
||||
)
|
||||
|
||||
if not ext_tool.tool_ref_id:
|
||||
logger.warning(f"外部数据工具缺少 tool_ref_id: {ext_tool_id}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"外部数据工具 '{ext_tool.name}' 缺少关联标识,请重新创建"
|
||||
)
|
||||
|
||||
external_tool_refs.append(ext_tool.tool_ref_id)
|
||||
|
||||
# 更新工具使用次数
|
||||
ext_tool.usage_count = (ext_tool.usage_count or 0) + 1
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.warning(f"查询外部数据工具失败: {ext_tool_id}, error={str(e)}")
|
||||
|
||||
if external_tool_refs:
|
||||
# 传递外部工具的 tool_ref_id 列表给 Agent Manager
|
||||
env_vars["EXTERNAL_TOOL_REFS"] = json.dumps(external_tool_refs)
|
||||
logger.info(
|
||||
f"外部数据工具配置已准备: user_id={user_id}, "
|
||||
f"tool_count={len(external_tool_refs)}, "
|
||||
f"tool_refs={external_tool_refs}"
|
||||
)
|
||||
# ================================================
|
||||
|
||||
# ========== A2A 框架配置 ==========
|
||||
if framework_template == "A2A":
|
||||
import time as time_module
|
||||
|
||||
@@ -837,6 +837,12 @@ class CreateCustomAgentRequest(BaseModel):
|
||||
# 工具配置(第一个工具为主工具,可决定 Agent 类型)
|
||||
tools: Optional[List[str]] = Field(None, description="选择的工具ID列表,第一个工具为主工具,可从中获取模板和环境变量配置")
|
||||
|
||||
# 外部数据工具配置(用户创建的自定义外部 API 工具)
|
||||
externalTools: Optional[List[str]] = Field(None, description="外部数据工具 ID 列表,部署时会将 tool_ref_id 传给 Agent Manager")
|
||||
|
||||
# 工具集配置(用户创建的工具组合,最多 8 个工具)
|
||||
toolkit: Optional[str] = Field(None, description="工具集 ID,使用工具集中的所有工具")
|
||||
|
||||
# 用户配置
|
||||
endpoint: Optional[str] = Field(None, description="用户终结点")
|
||||
apiKey: Optional[str] = Field(None, description="用户 API 密钥")
|
||||
@@ -962,3 +968,182 @@ class AgentManagerCallbackResponse(BaseModel):
|
||||
message: str
|
||||
recordId: Optional[str] = None
|
||||
|
||||
|
||||
# ============= 外部数据工具 =============
|
||||
|
||||
class ExternalToolAuthConfig(BaseModel):
|
||||
"""外部工具认证配置"""
|
||||
type: str = Field(..., pattern="^(api_key|bearer|basic|none)$", description="认证类型")
|
||||
key: Optional[str] = Field(None, description="API Key 或 Bearer Token")
|
||||
in_location: Optional[str] = Field(None, alias="in", description="Key 位置: header/query/body")
|
||||
name: Optional[str] = Field(None, description="Key 名称(如 X-API-Key)")
|
||||
username: Optional[str] = Field(None, description="Basic Auth 用户名")
|
||||
password: Optional[str] = Field(None, description="Basic Auth 密码")
|
||||
|
||||
class Config:
|
||||
populate_by_name = True
|
||||
|
||||
|
||||
class ExternalToolRetryConfig(BaseModel):
|
||||
"""外部工具重试配置"""
|
||||
max_attempts: int = Field(3, ge=1, le=10, description="最大重试次数")
|
||||
delay_seconds: int = Field(1, ge=0, le=60, description="重试延迟(秒)")
|
||||
|
||||
|
||||
class CreateExternalToolRequest(BaseModel):
|
||||
"""创建外部数据工具请求
|
||||
|
||||
用户上传的外部数据工具配置,将发送给 Agent Manager 生成 Pydantic 工具文件。
|
||||
"""
|
||||
name: str = Field(..., min_length=1, max_length=100, description="工具名称")
|
||||
description: Optional[str] = Field(None, description="工具描述")
|
||||
url: str = Field(..., min_length=1, max_length=500, description="API 端点 URL")
|
||||
method: str = Field("POST", pattern="^(GET|POST|PUT|DELETE|PATCH)$", description="HTTP 方法")
|
||||
|
||||
# 请求配置
|
||||
headers: Optional[Dict[str, str]] = Field(None, description="自定义请求头")
|
||||
auth: Optional[ExternalToolAuthConfig] = Field(None, description="认证配置")
|
||||
|
||||
# 参数定义(JSON Schema 格式)
|
||||
request_params: Optional[Dict[str, Any]] = Field(None, description="URL 查询参数定义")
|
||||
request_body: Optional[Dict[str, Any]] = Field(None, description="请求体定义")
|
||||
|
||||
# 响应配置
|
||||
response_mapping: Optional[Dict[str, Any]] = Field(None, description="响应字段映射")
|
||||
|
||||
# 运行配置
|
||||
timeout: int = Field(30, ge=1, le=300, description="超时时间(秒)")
|
||||
retry: Optional[ExternalToolRetryConfig] = Field(None, description="重试配置")
|
||||
|
||||
|
||||
class UpdateExternalToolRequest(BaseModel):
|
||||
"""更新外部数据工具请求
|
||||
|
||||
需要传递完整配置,因为 MCP-Server 不存储完整配置。
|
||||
"""
|
||||
name: str = Field(..., min_length=1, max_length=100, description="工具名称")
|
||||
description: Optional[str] = Field(None, description="工具描述")
|
||||
url: str = Field(..., min_length=1, max_length=500, description="API 端点 URL")
|
||||
method: str = Field("POST", pattern="^(GET|POST|PUT|DELETE|PATCH)$", description="HTTP 方法")
|
||||
|
||||
headers: Optional[Dict[str, str]] = Field(None, description="自定义请求头")
|
||||
auth: Optional[ExternalToolAuthConfig] = Field(None, description="认证配置")
|
||||
request_params: Optional[Dict[str, Any]] = Field(None, description="URL 查询参数定义")
|
||||
request_body: Optional[Dict[str, Any]] = Field(None, description="请求体定义")
|
||||
response_mapping: Optional[Dict[str, Any]] = Field(None, description="响应字段映射")
|
||||
timeout: int = Field(30, ge=1, le=300, description="超时时间(秒)")
|
||||
retry: Optional[ExternalToolRetryConfig] = Field(None, description="重试配置")
|
||||
|
||||
|
||||
class ExternalToolInfo(BaseModel):
|
||||
"""外部数据工具信息(列表响应)"""
|
||||
id: str
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
url: str
|
||||
method: str
|
||||
auth_type: str
|
||||
status: str # pending, active, error
|
||||
usage_count: int = 0
|
||||
created_at: str
|
||||
updated_at: Optional[str] = None
|
||||
|
||||
|
||||
class ExternalToolDetail(BaseModel):
|
||||
"""外部数据工具详情"""
|
||||
id: str
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
url: str
|
||||
method: str
|
||||
auth_type: str
|
||||
tool_ref_id: Optional[str] = None
|
||||
status: str
|
||||
error_message: Optional[str] = None
|
||||
usage_count: int = 0
|
||||
created_at: str
|
||||
updated_at: Optional[str] = None
|
||||
|
||||
|
||||
class TestExternalToolRequest(BaseModel):
|
||||
"""测试外部工具连接请求"""
|
||||
test_params: Optional[Dict[str, Any]] = Field(None, description="测试参数")
|
||||
|
||||
|
||||
class TestExternalToolResponse(BaseModel):
|
||||
"""测试外部工具连接响应"""
|
||||
connected: bool
|
||||
response_time_ms: Optional[int] = None
|
||||
status_code: Optional[int] = None
|
||||
sample_response: Optional[Dict[str, Any]] = None
|
||||
error_message: Optional[str] = None
|
||||
|
||||
|
||||
class CreateCustomAgentWithToolsRequest(BaseModel):
|
||||
"""创建带有外部数据工具的自定义 Agent 请求
|
||||
|
||||
新版自定义 Agent 创建接口,使用外部数据工具(通过 tool_ref_id 关联)。
|
||||
"""
|
||||
name: str = Field(..., min_length=1, max_length=63, description="Agent 名称")
|
||||
description: Optional[str] = Field(None, description="描述")
|
||||
|
||||
# 外部数据工具(核心字段)
|
||||
external_tools: List[str] = Field(..., min_length=1, description="外部数据工具 ID 列表")
|
||||
|
||||
# 资源配置
|
||||
cpuRequest: str = Field("100m", description="CPU 请求量")
|
||||
cpuLimit: Optional[str] = Field(None, description="CPU 限制量")
|
||||
memoryRequest: str = Field("128Mi", description="内存请求量")
|
||||
memoryLimit: Optional[str] = Field(None, description="内存限制量")
|
||||
|
||||
# 模型配置
|
||||
model: Optional[str] = Field(None, description="使用的模型名称")
|
||||
|
||||
|
||||
# ==================== 外部数据工具集 ====================
|
||||
|
||||
class CreateToolkitRequest(BaseModel):
|
||||
"""创建工具集请求"""
|
||||
name: str = Field(..., min_length=1, max_length=100, description="工具集名称")
|
||||
description: Optional[str] = Field(None, description="工具集描述")
|
||||
tool_ids: List[str] = Field(
|
||||
...,
|
||||
min_length=1,
|
||||
max_length=8,
|
||||
description="外部数据工具 ID 列表(1-8 个)"
|
||||
)
|
||||
|
||||
|
||||
class UpdateToolkitRequest(BaseModel):
|
||||
"""更新工具集请求"""
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=100, description="工具集名称")
|
||||
description: Optional[str] = Field(None, description="工具集描述")
|
||||
tool_ids: Optional[List[str]] = Field(
|
||||
None,
|
||||
min_length=1,
|
||||
max_length=8,
|
||||
description="外部数据工具 ID 列表(1-8 个)"
|
||||
)
|
||||
|
||||
|
||||
class ToolkitInfo(BaseModel):
|
||||
"""工具集基本信息"""
|
||||
id: str
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
tool_count: int
|
||||
usage_count: int = 0
|
||||
created_at: str
|
||||
|
||||
|
||||
class ToolkitDetail(BaseModel):
|
||||
"""工具集详情"""
|
||||
id: str
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
tool_ids: List[str]
|
||||
tools: List[ExternalToolInfo] = [] # 包含的工具信息
|
||||
usage_count: int = 0
|
||||
created_at: str
|
||||
updated_at: Optional[str] = None
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
-- 017: 添加外部数据工具表
|
||||
-- 用于存储用户创建的外部数据工具基本信息和 Agent Manager 关联标识
|
||||
|
||||
-- 创建外部数据工具表
|
||||
CREATE TABLE IF NOT EXISTS external_data_tools (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- 基本信息(用于前端展示)
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- API 配置(仅用于展示,实际配置存储在 Agent Manager)
|
||||
url VARCHAR(500) NOT NULL,
|
||||
method VARCHAR(10) NOT NULL DEFAULT 'POST',
|
||||
auth_type VARCHAR(20) DEFAULT 'none', -- api_key, bearer, basic, none
|
||||
|
||||
-- Agent Manager 关联(核心字段)
|
||||
tool_ref_id VARCHAR(100) UNIQUE, -- AM 返回的工具标识
|
||||
status VARCHAR(20) DEFAULT 'pending', -- pending, active, error
|
||||
error_message TEXT, -- 生成失败时的错误信息
|
||||
|
||||
-- 归属信息
|
||||
owner_id UUID NOT NULL REFERENCES users(id),
|
||||
tenant_id UUID REFERENCES users(id),
|
||||
channel_id UUID REFERENCES channels(id),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- 统计信息
|
||||
usage_count INTEGER DEFAULT 0,
|
||||
|
||||
-- 时间戳
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 创建索引
|
||||
CREATE INDEX IF NOT EXISTS idx_external_data_tool_owner ON external_data_tools(owner_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_external_data_tool_ref ON external_data_tools(tool_ref_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_external_data_tool_status ON external_data_tools(status);
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON TABLE external_data_tools IS '外部数据工具表 - 存储用户创建的外部 API 工具配置';
|
||||
COMMENT ON COLUMN external_data_tools.tool_ref_id IS 'Agent Manager 返回的工具标识,部署 Agent 时传递此标识';
|
||||
COMMENT ON COLUMN external_data_tools.status IS '工具状态:pending(等待生成), active(可用), error(生成失败)';
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
-- 018: 添加外部数据工具集表
|
||||
-- 用户可以将多个外部数据工具组合成一个工具集,方便部署自定义 Agent
|
||||
|
||||
-- 创建外部数据工具集表
|
||||
CREATE TABLE IF NOT EXISTS external_toolkits (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- 基本信息
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- 工具列表(存储工具 ID 的 JSON 数组,最多 8 个)
|
||||
tool_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
|
||||
-- 归属信息
|
||||
owner_id UUID NOT NULL REFERENCES users(id),
|
||||
tenant_id UUID REFERENCES users(id),
|
||||
channel_id UUID REFERENCES channels(id),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- 统计信息
|
||||
usage_count INTEGER DEFAULT 0,
|
||||
|
||||
-- 时间戳
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 约束:同一用户下工具集名称唯一
|
||||
CONSTRAINT uq_toolkit_name_owner UNIQUE (name, owner_id)
|
||||
);
|
||||
|
||||
-- 创建索引
|
||||
CREATE INDEX IF NOT EXISTS idx_external_toolkit_owner ON external_toolkits(owner_id);
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON TABLE external_toolkits IS '外部数据工具集表 - 存储用户创建的工具组合';
|
||||
COMMENT ON COLUMN external_toolkits.tool_ids IS '工具 ID 列表,JSON 数组格式,最多 8 个工具';
|
||||
COMMENT ON COLUMN external_toolkits.usage_count IS '工具集被 Agent 使用次数';
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
运行 017 迁移:添加外部数据工具表
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到 Python 路径
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
import asyncpg
|
||||
|
||||
|
||||
async def run_migration():
|
||||
"""执行迁移"""
|
||||
# 获取数据库连接信息
|
||||
database_url = os.getenv(
|
||||
"DATABASE_URL",
|
||||
"postgresql://postgres:postgres@localhost:5432/mcp_server"
|
||||
)
|
||||
|
||||
# 解析连接字符串
|
||||
if database_url.startswith("postgresql://"):
|
||||
# 转换为 asyncpg 格式
|
||||
database_url = database_url.replace("postgresql://", "postgres://")
|
||||
|
||||
print(f"连接数据库: {database_url.split('@')[1] if '@' in database_url else database_url}")
|
||||
|
||||
try:
|
||||
conn = await asyncpg.connect(database_url)
|
||||
print("数据库连接成功")
|
||||
|
||||
# 读取 SQL 文件
|
||||
sql_file = Path(__file__).parent / "017_add_external_data_tools.sql"
|
||||
with open(sql_file, "r", encoding="utf-8") as f:
|
||||
sql = f.read()
|
||||
|
||||
print("执行迁移 SQL...")
|
||||
await conn.execute(sql)
|
||||
print("迁移执行成功!")
|
||||
|
||||
# 验证表是否创建
|
||||
result = await conn.fetchval(
|
||||
"SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'external_data_tools'"
|
||||
)
|
||||
if result > 0:
|
||||
print("✅ external_data_tools 表已创建")
|
||||
else:
|
||||
print("❌ external_data_tools 表创建失败")
|
||||
|
||||
await conn.close()
|
||||
|
||||
except Exception as e:
|
||||
print(f"迁移失败: {e}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_migration())
|
||||
|
||||
@@ -1360,3 +1360,85 @@ class TenantModelKey(BaseModel, Base):
|
||||
Index("idx_tenant_model_key_status", status),
|
||||
UniqueConstraint("tenant_id", "model_name", name="uq_tenant_model"),
|
||||
)
|
||||
|
||||
|
||||
class ExternalDataTool(BaseModel, Base):
|
||||
"""外部数据工具模型
|
||||
|
||||
用户创建的外部数据工具,通过 Agent Manager 生成 Pydantic 工具文件。
|
||||
MCP-Server 只存储工具的基本信息和 Agent Manager 返回的标识,
|
||||
工具的实际代码文件由 Agent Manager 管理。
|
||||
|
||||
核心流程:
|
||||
1. 用户上传工具配置(表单或 JSON 文件)
|
||||
2. MCP-Server 发送配置给 Agent Manager 生成 Pydantic 工具文件
|
||||
3. Agent Manager 返回 tool_ref_id(工具标识)
|
||||
4. MCP-Server 存储基本信息 + tool_ref_id
|
||||
5. 部署 Agent 时,传递 tool_ref_id 列表给 Agent Manager
|
||||
"""
|
||||
__tablename__ = "external_data_tools"
|
||||
|
||||
# 基本信息(用于前端展示)
|
||||
name = Column(String(100), nullable=False)
|
||||
description = Column(Text)
|
||||
|
||||
# API 配置(仅用于展示,实际配置存储在 Agent Manager)
|
||||
url = Column(String(500), nullable=False)
|
||||
method = Column(String(10), nullable=False, default="POST")
|
||||
auth_type = Column(String(20), default="none") # api_key, bearer, basic, none
|
||||
|
||||
# Agent Manager 关联(核心字段)
|
||||
tool_ref_id = Column(String(100), unique=True) # AM 返回的工具标识,部署时传给 AM
|
||||
status = Column(String(20), default="pending") # pending, active, error
|
||||
error_message = Column(Text) # 生成失败时的错误信息
|
||||
|
||||
# 归属信息
|
||||
owner_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
|
||||
tenant_id = Column(GUID(), ForeignKey("users.id")) # 租户 ID(可选)
|
||||
channel_id = Column(GUID(), ForeignKey("channels.id")) # 渠道 ID(可选)
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
# 统计信息
|
||||
usage_count = Column(Integer, default=0) # 被 Agent 使用次数
|
||||
|
||||
# 关联关系
|
||||
owner = relationship("User", foreign_keys=[owner_id])
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_external_data_tool_owner", owner_id),
|
||||
Index("idx_external_data_tool_ref", tool_ref_id),
|
||||
Index("idx_external_data_tool_status", status),
|
||||
)
|
||||
|
||||
|
||||
class ExternalToolkit(BaseModel, Base):
|
||||
"""外部数据工具集模型
|
||||
|
||||
用户可以将多个外部数据工具组合成一个工具集,方便部署自定义 Agent。
|
||||
每个工具集最多包含 8 个工具。
|
||||
"""
|
||||
__tablename__ = "external_toolkits"
|
||||
|
||||
# 基本信息
|
||||
name = Column(String(100), nullable=False)
|
||||
description = Column(Text)
|
||||
|
||||
# 工具列表(存储工具 ID 的 JSON 数组,最多 8 个)
|
||||
tool_ids = Column(JSON, nullable=False, default=list)
|
||||
|
||||
# 归属信息
|
||||
owner_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
|
||||
tenant_id = Column(GUID(), ForeignKey("users.id"))
|
||||
channel_id = Column(GUID(), ForeignKey("channels.id"))
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
# 统计信息
|
||||
usage_count = Column(Integer, default=0) # 被 Agent 使用次数
|
||||
|
||||
# 关联关系
|
||||
owner = relationship("User", foreign_keys=[owner_id])
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_external_toolkit_owner", owner_id),
|
||||
UniqueConstraint("name", owner_id, name="uq_toolkit_name_owner"), # 同一用户下工具集名称唯一
|
||||
)
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 安装系统依赖与 Node.js (prisma需要)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
nodejs \
|
||||
npm && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 安装LiteLLM和prisma (使用国内源)
|
||||
# 注意:Prisma Python 客户端 0.12.0 需要 Prisma CLI 5.8.0
|
||||
# 需要同时安装 Prisma CLI 和 Python 客户端
|
||||
RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple \
|
||||
litellm[proxy]==1.17.0 \
|
||||
redis==5.0.1 \
|
||||
prometheus-client==0.19.0 \
|
||||
prisma==0.12.0 && \
|
||||
(rm -f /usr/local/bin/prisma || true) && \
|
||||
npm install -g prisma@5.8.0
|
||||
|
||||
# 复制配置文件
|
||||
COPY config/ ./config/
|
||||
|
||||
# 创建logs目录
|
||||
RUN mkdir -p logs
|
||||
|
||||
# 设置环境变量
|
||||
ENV LITELLM_MASTER_KEY=sk-taiji-master-key
|
||||
ENV LITELLM_CONFIG_PATH=/app/config/litellm.yaml
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 4000
|
||||
|
||||
# 健康检查(使用 API key,增加超时时间因为health端点需要检查所有模型)
|
||||
HEALTHCHECK --interval=60s --timeout=30s --start-period=30s --retries=3 \
|
||||
CMD curl -f -H "Authorization: Bearer sk-taiji-master-key" --max-time 25 http://localhost:4000/health || exit 1
|
||||
|
||||
# 启动LiteLLM代理
|
||||
CMD ["litellm", "--config", "/app/config/litellm_simple.yaml", "--port", "4000", "--host", "0.0.0.0"]
|
||||
|
||||
@@ -1,379 +0,0 @@
|
||||
# LiteLLM 网关配置
|
||||
# taiji-AI-PAD 模型治理层配置
|
||||
|
||||
# 基础设置
|
||||
general_settings:
|
||||
master_key: "os.environ/LITELLM_MASTER_KEY"
|
||||
database_url: "os.environ/DATABASE_URL"
|
||||
|
||||
# 日志设置
|
||||
set_verbose: true
|
||||
json_logs: true
|
||||
log_raw_request_response: false # 生产环境设为false
|
||||
|
||||
# 缓存设置
|
||||
redis_host: "redis"
|
||||
redis_port: 6379
|
||||
redis_password: null
|
||||
|
||||
# 速率限制
|
||||
max_budget: 1000.0 # 美元
|
||||
budget_duration: "30d"
|
||||
|
||||
# ✅ 回调和监控 - 添加webhook用于Token计费
|
||||
success_callback: ["langfuse", "webhook"]
|
||||
failure_callback: ["langfuse", "webhook"]
|
||||
|
||||
# Webhook配置 - 指向mcp-server的计费webhook端点
|
||||
webhook_url: "http://mcp-server:8002/api/v1/billing/litellm-callback"
|
||||
webhook_headers:
|
||||
Content-Type: "application/json"
|
||||
|
||||
# 安全设置
|
||||
allowed_ips: ["127.0.0.1", "172.20.0.0/16"] # Docker网络
|
||||
|
||||
# 模型配置
|
||||
model_list:
|
||||
# OpenAI 模型组
|
||||
- model_name: "gpt-3.5-turbo"
|
||||
litellm_params:
|
||||
model: "openai/gpt-3.5-turbo"
|
||||
api_key: "os.environ/OPENAI_API_KEY"
|
||||
max_tokens: 4000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: false
|
||||
max_input_tokens: 16385
|
||||
max_output_tokens: 4096
|
||||
input_cost_per_token: 0.0000015
|
||||
output_cost_per_token: 0.000002
|
||||
|
||||
- model_name: "gpt-4"
|
||||
litellm_params:
|
||||
model: "openai/gpt-4"
|
||||
api_key: "os.environ/OPENAI_API_KEY"
|
||||
max_tokens: 8000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: false
|
||||
max_input_tokens: 8192
|
||||
max_output_tokens: 8192
|
||||
input_cost_per_token: 0.00003
|
||||
output_cost_per_token: 0.00006
|
||||
|
||||
- model_name: "gpt-4-turbo"
|
||||
litellm_params:
|
||||
model: "openai/gpt-4-turbo-preview"
|
||||
api_key: "os.environ/OPENAI_API_KEY"
|
||||
max_tokens: 4000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: true
|
||||
max_input_tokens: 128000
|
||||
max_output_tokens: 4096
|
||||
input_cost_per_token: 0.00001
|
||||
output_cost_per_token: 0.00003
|
||||
|
||||
# Anthropic 模型组
|
||||
- model_name: "claude-3-haiku"
|
||||
litellm_params:
|
||||
model: "anthropic/claude-3-haiku-20240307"
|
||||
api_key: "os.environ/ANTHROPIC_API_KEY"
|
||||
max_tokens: 4000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: true
|
||||
max_input_tokens: 200000
|
||||
max_output_tokens: 4096
|
||||
input_cost_per_token: 0.00000025
|
||||
output_cost_per_token: 0.00000125
|
||||
|
||||
- model_name: "claude-3-sonnet"
|
||||
litellm_params:
|
||||
model: "anthropic/claude-3-sonnet-20240229"
|
||||
api_key: "os.environ/ANTHROPIC_API_KEY"
|
||||
max_tokens: 4000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: true
|
||||
max_input_tokens: 200000
|
||||
max_output_tokens: 4096
|
||||
input_cost_per_token: 0.000003
|
||||
output_cost_per_token: 0.000015
|
||||
|
||||
- model_name: "claude-3-opus"
|
||||
litellm_params:
|
||||
model: "anthropic/claude-3-opus-20240229"
|
||||
api_key: "os.environ/ANTHROPIC_API_KEY"
|
||||
max_tokens: 4000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: true
|
||||
max_input_tokens: 200000
|
||||
max_output_tokens: 4096
|
||||
input_cost_per_token: 0.000015
|
||||
output_cost_per_token: 0.000075
|
||||
|
||||
# 本地/开源模型(如果可用)
|
||||
- model_name: "llama-3-8b"
|
||||
litellm_params:
|
||||
model: "ollama/llama3"
|
||||
api_base: "http://ollama:11434"
|
||||
max_tokens: 2000
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: false
|
||||
supports_vision: false
|
||||
max_input_tokens: 8192
|
||||
max_output_tokens: 2048
|
||||
input_cost_per_token: 0.0 # 本地模型无成本
|
||||
output_cost_per_token: 0.0
|
||||
|
||||
# OpenRouter 模型组 - 通过 OpenRouter 访问多种模型
|
||||
# 注意: 使用 openrouter/ 前缀时,LiteLLM 会自动使用 OpenRouter API
|
||||
- model_name: "openrouter-gpt-4"
|
||||
litellm_params:
|
||||
model: "openrouter/openai/gpt-4"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
max_tokens: 8000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: false
|
||||
max_input_tokens: 8192
|
||||
max_output_tokens: 8192
|
||||
input_cost_per_token: 0.00003
|
||||
output_cost_per_token: 0.00006
|
||||
|
||||
- model_name: "openrouter-gpt-3.5-turbo"
|
||||
litellm_params:
|
||||
model: "openrouter/openai/gpt-3.5-turbo"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
max_tokens: 4000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: false
|
||||
max_input_tokens: 16385
|
||||
max_output_tokens: 4096
|
||||
input_cost_per_token: 0.0000015
|
||||
output_cost_per_token: 0.000002
|
||||
|
||||
- model_name: "openrouter-claude-3.5-sonnet"
|
||||
litellm_params:
|
||||
model: "openrouter/anthropic/claude-3.5-sonnet"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
max_tokens: 4000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: true
|
||||
max_input_tokens: 200000
|
||||
max_output_tokens: 4096
|
||||
input_cost_per_token: 0.000003
|
||||
output_cost_per_token: 0.000015
|
||||
|
||||
- model_name: "openrouter-claude-3-opus"
|
||||
litellm_params:
|
||||
model: "openrouter/anthropic/claude-3-opus"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
max_tokens: 4000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: true
|
||||
max_input_tokens: 200000
|
||||
max_output_tokens: 4096
|
||||
input_cost_per_token: 0.000015
|
||||
output_cost_per_token: 0.000075
|
||||
|
||||
# 路由器配置
|
||||
router_settings:
|
||||
routing_strategy: "least-busy" # 路由策略: least-busy, round-robin, latency-based
|
||||
allowed_fails: 3
|
||||
cooldown_time: 30
|
||||
retry_after: 10
|
||||
|
||||
# 模型组定义
|
||||
model_group_configs:
|
||||
- group_name: "gpt-3.5-group"
|
||||
models:
|
||||
- model_name: "gpt-3.5-turbo"
|
||||
weight: 1.0
|
||||
|
||||
- group_name: "gpt-4-group"
|
||||
models:
|
||||
- model_name: "gpt-4"
|
||||
weight: 0.7
|
||||
- model_name: "gpt-4-turbo"
|
||||
weight: 0.3
|
||||
|
||||
- group_name: "claude-group"
|
||||
models:
|
||||
- model_name: "claude-3-haiku"
|
||||
weight: 0.5
|
||||
- model_name: "claude-3-sonnet"
|
||||
weight: 0.3
|
||||
- model_name: "claude-3-opus"
|
||||
weight: 0.2
|
||||
|
||||
- group_name: "fast-models"
|
||||
models:
|
||||
- model_name: "gpt-3.5-turbo"
|
||||
weight: 0.4
|
||||
- model_name: "claude-3-haiku"
|
||||
weight: 0.4
|
||||
- model_name: "llama-3-8b"
|
||||
weight: 0.2
|
||||
|
||||
- group_name: "premium-models"
|
||||
models:
|
||||
- model_name: "gpt-4-turbo"
|
||||
weight: 0.4
|
||||
- model_name: "claude-3-opus"
|
||||
weight: 0.3
|
||||
- model_name: "claude-3-sonnet"
|
||||
weight: 0.3
|
||||
|
||||
- group_name: "openrouter-group"
|
||||
models:
|
||||
- model_name: "openrouter-gpt-4"
|
||||
weight: 0.3
|
||||
- model_name: "openrouter-gpt-3.5-turbo"
|
||||
weight: 0.3
|
||||
- model_name: "openrouter-claude-3.5-sonnet"
|
||||
weight: 0.25
|
||||
- model_name: "openrouter-claude-3-opus"
|
||||
weight: 0.15
|
||||
|
||||
# 用户和权限配置
|
||||
litellm_settings:
|
||||
# API密钥管理
|
||||
api_keys:
|
||||
- key: "sk-taiji-mcp-server"
|
||||
models: ["gpt-3.5-turbo", "gpt-4", "claude-3-haiku", "claude-3-sonnet", "openrouter-gpt-4", "openrouter-gpt-3.5-turbo", "openrouter-claude-3.5-sonnet"]
|
||||
max_budget: 100.0
|
||||
budget_duration: "1d"
|
||||
metadata:
|
||||
user_id: "mcp-server"
|
||||
service: "mcp-server"
|
||||
|
||||
- key: "sk-taiji-data-ingestion"
|
||||
models: ["gpt-3.5-turbo", "claude-3-haiku", "llama-3-8b", "openrouter-gpt-3.5-turbo", "openrouter-claude-3.5-sonnet"]
|
||||
max_budget: 50.0
|
||||
budget_duration: "1d"
|
||||
metadata:
|
||||
user_id: "data-ingestion"
|
||||
service: "data-ingestion"
|
||||
|
||||
- key: "sk-taiji-agent-dev"
|
||||
models: ["gpt-3.5-group", "claude-group", "fast-models", "openrouter-group"]
|
||||
max_budget: 20.0
|
||||
budget_duration: "1d"
|
||||
metadata:
|
||||
user_id: "agent-development"
|
||||
service: "agent-development"
|
||||
|
||||
- key: "sk-taiji-premium"
|
||||
models: ["premium-models", "gpt-4-group", "openrouter-group", "openrouter-gpt-4", "openrouter-claude-3-opus", "openrouter-claude-3.5-sonnet"]
|
||||
max_budget: 200.0
|
||||
budget_duration: "1d"
|
||||
metadata:
|
||||
user_id: "premium-user"
|
||||
service: "premium"
|
||||
|
||||
# 回调配置
|
||||
callbacks:
|
||||
# 成功回调
|
||||
success_callback:
|
||||
- callback_name: "langfuse"
|
||||
callback_type: "success"
|
||||
callback_vars:
|
||||
langfuse_public_key: "os.environ/LANGFUSE_PUBLIC_KEY"
|
||||
langfuse_secret_key: "os.environ/LANGFUSE_SECRET_KEY"
|
||||
langfuse_host: "os.environ/LANGFUSE_HOST"
|
||||
|
||||
# 失败回调
|
||||
failure_callback:
|
||||
- callback_name: "langfuse"
|
||||
callback_type: "failure"
|
||||
callback_vars:
|
||||
langfuse_public_key: "os.environ/LANGFUSE_PUBLIC_KEY"
|
||||
langfuse_secret_key: "os.environ/LANGFUSE_SECRET_KEY"
|
||||
langfuse_host: "os.environ/LANGFUSE_HOST"
|
||||
|
||||
# 监控和指标
|
||||
monitoring:
|
||||
prometheus_port: 4001
|
||||
health_check_interval: 30
|
||||
|
||||
# 自定义指标
|
||||
custom_metrics:
|
||||
- name: "taiji_model_requests_total"
|
||||
type: "counter"
|
||||
description: "Total model requests"
|
||||
labels: ["model", "user_id", "status"]
|
||||
|
||||
- name: "taiji_model_latency"
|
||||
type: "histogram"
|
||||
description: "Model response latency"
|
||||
labels: ["model", "user_id"]
|
||||
|
||||
- name: "taiji_model_cost"
|
||||
type: "gauge"
|
||||
description: "Model cost tracking"
|
||||
labels: ["model", "user_id"]
|
||||
|
||||
# 错误处理
|
||||
error_handling:
|
||||
# 重试配置
|
||||
retry_policy:
|
||||
max_retries: 3
|
||||
retry_delay: 1.0
|
||||
exponential_backoff: true
|
||||
|
||||
# 超时设置
|
||||
timeout:
|
||||
request_timeout: 60
|
||||
|
||||
# 回退策略
|
||||
fallback:
|
||||
enabled: true
|
||||
fallback_models:
|
||||
"gpt-4": ["openrouter-gpt-4", "gpt-4-turbo", "claude-3-sonnet", "openrouter-claude-3-sonnet"]
|
||||
"claude-3-opus": ["openrouter-claude-3-opus", "claude-3-sonnet", "openrouter-claude-3-sonnet", "gpt-4", "openrouter-gpt-4"]
|
||||
"gpt-3.5-turbo": ["openrouter-gpt-3.5-turbo", "claude-3-haiku", "llama-3-8b"]
|
||||
"openrouter-gpt-4": ["gpt-4", "gpt-4-turbo", "openrouter-claude-3.5-sonnet"]
|
||||
"openrouter-gpt-3.5-turbo": ["gpt-3.5-turbo", "claude-3-haiku"]
|
||||
"openrouter-claude-3.5-sonnet": ["claude-3-sonnet", "gpt-4", "openrouter-gpt-4"]
|
||||
"openrouter-claude-3-opus": ["claude-3-opus", "claude-3-sonnet", "openrouter-claude-3.5-sonnet"]
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level: "INFO"
|
||||
format: "json"
|
||||
|
||||
# 请求日志
|
||||
log_requests: true
|
||||
log_responses: false # 生产环境关闭
|
||||
|
||||
# 敏感信息过滤
|
||||
redact_messages_in_logs: true
|
||||
redact_user_api_key_info: true
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
# LiteLLM 简化配置 - 用于测试和开发
|
||||
# taiji-AI-PAD 模型治理层配置
|
||||
|
||||
# 基础设置
|
||||
general_settings:
|
||||
master_key: "sk-taiji-master-key"
|
||||
|
||||
# 日志设置
|
||||
set_verbose: true
|
||||
json_logs: false
|
||||
log_raw_request_response: false
|
||||
|
||||
# 安全设置
|
||||
allowed_ips: ["127.0.0.1", "172.20.0.0/16", "0.0.0.0/0"] # 允许所有IP用于测试
|
||||
|
||||
# 模型配置 - 使用测试模型和 OpenRouter
|
||||
model_list:
|
||||
# 测试模型 - 使用 OpenRouter 的 Qwen 模型
|
||||
- model_name: "test-model"
|
||||
litellm_params:
|
||||
model: "openrouter/qwen/qwen-2-7b-instruct"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
max_tokens: 500
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: false
|
||||
supports_vision: false
|
||||
|
||||
# OpenRouter 模型 - 通过 OpenRouter 访问
|
||||
# 注意: 使用 openrouter/ 前缀时,LiteLLM 会自动使用 OpenRouter API
|
||||
- model_name: "openrouter-gpt-4o-mini"
|
||||
litellm_params:
|
||||
model: "openrouter/openai/gpt-4o-mini"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
max_tokens: 1000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: false
|
||||
|
||||
- model_name: "openrouter-gpt-3.5-turbo"
|
||||
litellm_params:
|
||||
model: "openrouter/openai/gpt-3.5-turbo"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
max_tokens: 1000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: false
|
||||
|
||||
# gpt-3.5-turbo 别名 - 向后兼容
|
||||
- model_name: "gpt-3.5-turbo"
|
||||
litellm_params:
|
||||
model: "openrouter/openai/gpt-3.5-turbo"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
max_tokens: 4000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: false
|
||||
|
||||
- model_name: "openrouter-claude-3.5-sonnet"
|
||||
litellm_params:
|
||||
model: "openrouter/anthropic/claude-3.5-sonnet"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
max_tokens: 4000
|
||||
temperature: 0.7
|
||||
model_info:
|
||||
mode: "chat"
|
||||
supports_function_calling: true
|
||||
supports_vision: true
|
||||
|
||||
# 路由器配置
|
||||
router_settings:
|
||||
routing_strategy: "round-robin"
|
||||
allowed_fails: 1
|
||||
cooldown_time: 10
|
||||
|
||||
# 用户配置
|
||||
litellm_settings:
|
||||
api_keys:
|
||||
- key: "sk-test-key"
|
||||
models: ["test-model", "openrouter-gpt-4o-mini", "openrouter-gpt-3.5-turbo", "openrouter-claude-3.5-sonnet", "gpt-3.5-turbo"]
|
||||
metadata:
|
||||
user_id: "test-user"
|
||||
service: "testing"
|
||||
|
||||
- key: "sk-taiji-master-key"
|
||||
models: ["test-model", "openrouter-gpt-4o-mini", "openrouter-gpt-3.5-turbo", "openrouter-claude-3.5-sonnet", "gpt-3.5-turbo"]
|
||||
metadata:
|
||||
user_id: "master"
|
||||
service: "all"
|
||||
|
||||
# 监控
|
||||
monitoring:
|
||||
health_check_interval: 30
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level: "INFO"
|
||||
format: "text"
|
||||
log_requests: true
|
||||
log_responses: false
|
||||
Reference in New Issue
Block a user