# CodingAgent 工具创建任务书编写指南

本文档说明如何为 CodingAgent 准备任务书,让它自动创建和封装工具。

## 目录
- [工作流程](#工作流程)
- [任务书必需参数](#任务书必需参数)
- [任务书模板](#任务书模板)
- [JSON Schema 编写规范](#json-schema-编写规范)
- [提交任务](#提交任务)
- [常见问题](#常见问题)

---

## 工作流程

CodingAgent 自动化工具创建流程分为5个阶段:

### 阶段1: 环境准备
1. 生成唯一任务ID: `task_{8位随机字符}`
2. 创建临时工作目录: `data/staging/{task_id}/`
3. 初始化开发环境

### 阶段2: 工具开发
1. **选择运行环境**:
   - 纯Python + 简单依赖 → 使用 `uv` (快速启动)
   - 需要端口/服务/GPU/系统库 → 使用 `Docker` + 卷挂载

2. **实现核心逻辑**:
   - 编写业务逻辑代码
   - 创建 FastAPI HTTP 接口封装
   - 添加依赖到 `pyproject.toml`

3. **测试核心功能**:
   - 在 `tests/` 目录编写单元测试
   - 测试输出保存到 `tests/output/`
   - **注意**: 只测试核心逻辑,不测试HTTP服务启动

### 阶段3: 服务启动与验证
1. 后台启动 HTTP 服务
2. 验证健康检查端点: `GET /health`
3. 测试业务逻辑端点

### 阶段4: 工具注册
调用 `register_tool` 将工具注册到系统:
- 保存元数据到 `data/registry.json`
- 保存路由信息到 Router 的 SourceStore

### 阶段5: 完成报告
返回工具创建摘要,包含:
- 工具名称和ID
- 功能描述
- 访问方法
- 注册状态

---

## 任务书必需参数

### 1. 工具标识 (必需)

```markdown
## 工具标识
- **tool_id**: `your_tool_name`
  格式: 小写字母+下划线,匹配正则 `^[a-z][a-z0-9_]*$`

- **工具名称**: 工具的显示名称 (中文或英文)

- **工具描述**: 简洁描述工具功能 (1-2句话)

- **分类** (可选): `cv` (计算机视觉), `ai` (AI生成), `nlp` (自然语言处理), `audio` (音频处理) 等
```

### 2. 功能需求 (必需)

```markdown
## 功能需求

### 核心功能1
- 功能描述
- 输入要求
- 输出格式

### 核心功能2
- ...
```

### 3. 输入Schema (必需)

使用 JSON Schema 格式定义输入参数:

```markdown
## 输入Schema

\`\`\`json
{
  "type": "object",
  "required": ["param1", "param2"],
  "properties": {
    "param1": {
      "type": "string",
      "description": "参数1的描述"
    },
    "param2": {
      "type": "integer",
      "default": 10,
      "minimum": 1,
      "maximum": 100,
      "description": "参数2的描述"
    }
  }
}
\`\`\`
```

### 4. 输出Schema (必需)

```markdown
## 输出Schema

\`\`\`json
{
  "type": "object",
  "required": ["result"],
  "properties": {
    "result": {
      "type": "string",
      "description": "处理结果"
    },
    "metadata": {
      "type": "object",
      "description": "元数据信息"
    }
  }
}
\`\`\`
```

### 5. 实现要求 (必需)

```markdown
## 实现要求

1. **技术栈**: Python 3.12+, FastAPI, uvicorn, httpx, pillow
2. **API端点**:
   - GET `/health` - 健康检查 (必需)
   - POST `/process` - 主要业务逻辑
3. **错误处理**: 参数验证、异常捕获、超时处理
4. **日志**: 记录关键操作和错误
5. **环境**: 使用 uv 或 Docker
6. **端口**: 默认8000,支持 `--port` 参数
```

### 6. 注册参数 (CodingAgent自动处理)

这些参数由 CodingAgent 在注册时自动填写:

| 参数 | 类型 | 说明 | 示例 |
|------|------|------|------|
| `tool_id` | string | 工具唯一标识 | `"image_stitcher"` |
| `name` | string | 工具显示名称 | `"图片拼接工具"` |
| `description` | string | 功能描述 | `"将多张图片拼接成一张"` |
| `category` | string | 工具分类 | `"cv"`, `"ai"`, `"nlp"` |
| `runtime_type` | string | 运行环境 | `"local"` 或 `"docker"` |
| `internal_port` | integer | 服务端口 | `8001` |
| `endpoint_path` | string | API路径 | `"/stitch"` |
| `http_method` | string | HTTP方法 | `"POST"` |
| `input_schema` | object | 输入JSON Schema | 见上文 |
| `output_schema` | object | 输出JSON Schema | 见上文 |
| `host_dir` | string | 本地目录 (local) | `"tools/local/image_stitcher"` |
| `container_id` | string | 容器ID (docker) | `"abc123..."` |
| `stream_support` | boolean | 是否支持流式输出 | `false` |
| `group_ids` | array | 所属工具组 | `["image_processing"]` |
| `tool_slug_ids` | array | 工具标签 | `["comfyui", "stable-diffusion"]` |

---

## 任务书模板

```markdown
# {工具名称}任务书

## 工具标识
- **tool_id**: `tool_name_here`
- **工具名称**: 工具显示名称
- **工具描述**: 一句话描述工具功能
- **分类**: cv / ai / nlp / audio

## 功能需求

### 功能1: 功能名称
- 详细描述功能1
- 输入要求
- 输出格式

### 功能2: 功能名称
- 详细描述功能2

## 输入Schema

\`\`\`json
{
  "type": "object",
  "required": ["必需参数1", "必需参数2"],
  "properties": {
    "param1": {
      "type": "string",
      "description": "参数描述"
    },
    "param2": {
      "type": "integer",
      "default": 10,
      "description": "参数描述"
    }
  }
}
\`\`\`

## 输出Schema

\`\`\`json
{
  "type": "object",
  "required": ["result"],
  "properties": {
    "result": {
      "type": "string",
      "description": "结果描述"
    }
  }
}
\`\`\`

## 实现要求

1. **技术栈**: Python 3.12+, FastAPI, uvicorn, {其他依赖}
2. **API端点**:
   - GET `/health` - 健康检查
   - POST `/{endpoint}` - 主要业务逻辑
3. **错误处理**: 参数验证、异常捕获
4. **日志**: 记录关键操作
5. **环境**: uv (简单项目) 或 Docker (复杂项目)
6. **端口**: 默认8000, 支持 `--port` 参数

## 参考资料 (可选)

- API文档链接
- GitHub仓库
- 示例代码

## 特殊要求 (可选)

- 性能要求
- 安全要求
- 其他约束
```

---

## JSON Schema 编写规范

### 基本类型

```json
{
  "string_param": {
    "type": "string",
    "description": "字符串参数"
  },
  "integer_param": {
    "type": "integer",
    "description": "整数参数"
  },
  "number_param": {
    "type": "number",
    "description": "数字参数(含小数)"
  },
  "boolean_param": {
    "type": "boolean",
    "description": "布尔参数"
  }
}
```

### 枚举类型

```json
{
  "direction": {
    "type": "string",
    "enum": ["horizontal", "vertical", "grid"],
    "default": "horizontal",
    "description": "拼接方向: horizontal(水平), vertical(垂直), grid(网格)"
  }
}
```

### 数值约束

```json
{
  "count": {
    "type": "integer",
    "default": 1,
    "minimum": 1,
    "maximum": 10,
    "description": "生成数量,范围1-10"
  },
  "temperature": {
    "type": "number",
    "default": 0.7,
    "minimum": 0.0,
    "maximum": 2.0,
    "description": "温度参数"
  }
}
```

### 数组类型

```json
{
  "images": {
    "type": "array",
    "items": {
      "type": "string"
    },
    "minItems": 2,
    "description": "图片列表,至少2张"
  },
  "tags": {
    "type": "array",
    "items": {
      "type": "string"
    },
    "description": "标签列表"
  }
}
```

### 对象数组

```json
{
  "files": {
    "type": "array",
    "items": {
      "type": "object",
      "required": ["filename", "content"],
      "properties": {
        "filename": {
          "type": "string",
          "description": "文件名"
        },
        "content": {
          "type": "string",
          "description": "文件内容(Base64编码)"
        },
        "type": {
          "type": "string",
          "description": "文件类型"
        }
      }
    },
    "description": "文件列表"
  }
}
```

### 嵌套对象

```json
{
  "config": {
    "type": "object",
    "properties": {
      "width": {
        "type": "integer",
        "default": 512
      },
      "height": {
        "type": "integer",
        "default": 512
      },
      "quality": {
        "type": "string",
        "enum": ["low", "medium", "high"],
        "default": "medium"
      }
    },
    "description": "配置参数"
  }
}
```

### 字符串格式验证

```json
{
  "email": {
    "type": "string",
    "format": "email",
    "description": "邮箱地址"
  },
  "url": {
    "type": "string",
    "format": "uri",
    "description": "URL地址"
  },
  "tool_id": {
    "type": "string",
    "pattern": "^[a-z][a-z0-9_]*$",
    "description": "工具ID,只能包含小写字母、数字和下划线"
  }
}
```

### 必需参数 vs 可选参数

```json
{
  "type": "object",
  "required": ["prompt", "model"],  // 只列出真正必需的参数
  "properties": {
    "prompt": {
      "type": "string",
      "description": "生成提示词(必需)"
    },
    "model": {
      "type": "string",
      "description": "模型名称(必需)"
    },
    "steps": {
      "type": "integer",
      "default": 20,
      "description": "生成步数(可选,默认20)"
    }
  }
}
```

---

## 提交任务

### 方式1: 使用提交脚本

1. 将任务书保存到 `task_specs/` 目录,例如 `task_specs/my_tool_task.md`

2. 创建提交脚本 `scripts/submit_my_tool.py`:

```python
#!/usr/bin/env python3
import asyncio
from pathlib import Path
from tool_agent.tool.agent import CodingAgent

async def main():
    # 读取任务书
    task_spec_path = Path(__file__).parent.parent / "task_specs" / "my_tool_task.md"
    task_spec = task_spec_path.read_text(encoding="utf-8")

    # 创建CodingAgent实例
    agent = CodingAgent()

    print("🚀 提交工具封装任务...")
    print(f"📄 任务书: {task_spec_path}")
    print("-" * 60)

    # 执行任务
    result = await agent.execute(
        task_spec=task_spec,
        reference_files=[]
    )

    print("-" * 60)
    print("✅ 任务完成!")
    print(f"📊 结果: {result}")

if __name__ == "__main__":
    asyncio.run(main())
```

3. 运行脚本:

```bash
cd C:\Users\11304\gitlab\cybertogether\tool_agent
python scripts/submit_my_tool.py
```

### 方式2: 通过 RouterAgent 提交

```python
from tool_agent.router.router_agent import RouterAgent

router = RouterAgent()
result = await router.submit_task(task_spec="任务书内容...")
```

### 方式3: 通过 ServiceAgent 提交

```python
from tool_agent.service.agent import ServiceAgent

service = ServiceAgent()
result = await service.submit_task(task_spec="任务书内容...")
```

---

## 常见问题

### Q1: tool_id 命名规则是什么?

**A**: 必须符合正则表达式 `^[a-z][a-z0-9_]*$`:
- ✅ 正确: `image_stitcher`, `comfyui_launcher`, `text2video`
- ❌ 错误: `ImageStitcher` (大写), `image-stitcher` (连字符), `123tool` (数字开头)

### Q2: 什么时候用 uv,什么时候用 Docker?

**A**:
- **使用 uv**: 纯Python项目 + 简单依赖(pip可安装)
- **使用 Docker**: 需要系统库、GPU、端口映射、复杂环境配置

### Q3: 输入/输出 Schema 必须完全匹配实际API吗?

**A**: 是的。Schema 用于:
1. 参数验证 (Pydantic模型)
2. 自动生成API文档
3. RouterAgent 理解工具能力

### Q4: 如何处理文件上传?

**A**: 推荐使用 Base64 编码:

```json
{
  "image": {
    "type": "string",
    "description": "图片内容(Base64编码)"
  }
}
```

或使用文件对象数组:

```json
{
  "files": {
    "type": "array",
    "items": {
      "type": "object",
      "properties": {
        "filename": {"type": "string"},
        "content": {"type": "string", "description": "Base64编码"}
      }
    }
  }
}
```

### Q5: 工具创建失败怎么办?

**A**: CodingAgent 会自动:
1. 诊断错误原因
2. 尝试修复代码
3. 重新测试

如果多次失败,检查:
- 任务书是否清晰明确
- 依赖是否可安装
- API文档是否正确

### Q6: 如何测试已注册的工具?

**A**: 工具注册后会自动启动HTTP服务,可以通过:

```bash
# 查看工具列表
curl http://localhost:8080/tools

# 调用工具
curl -X POST http://localhost:8080/invoke \
  -H "Content-Type: application/json" \
  -d '{
    "tool_id": "your_tool_id",
    "parameters": {...}
  }'
```

### Q7: 可以更新已存在的工具吗?

**A**: 可以。重新提交任务书,使用相同的 `tool_id`,CodingAgent 会:
1. 检测到已存在的工具
2. 更新代码和配置
3. 重新注册元数据

### Q8: 如何添加工具到工具组?

**A**: 在任务书中指定:

```markdown
## 工具标识
- **tool_id**: `my_tool`
- **工具组**: `image_processing`
- **工具标签**: `["stable-diffusion", "comfyui"]`
```

或在注册时指定:

```python
register_tool(
    tool_id="my_tool",
    group_ids=["image_processing"],
    tool_slug_ids=["stable-diffusion", "comfyui"]
)
```

---

## 项目结构说明

工具创建后的标准结构:

```
tools/local/{tool_id}/
├── pyproject.toml              # 项目元数据和依赖
├── main.py                     # FastAPI HTTP服务入口
│   ├── @app.get("/health")     # 健康检查(必需)
│   └── @app.post("/{endpoint}") # 业务逻辑端点
├── {core_module}.py            # 核心业务逻辑
└── tests/
    ├── test_basic.py           # 单元测试
    └── output/                 # 测试输出文件
```

### main.py 示例

```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import uvicorn
import argparse

app = FastAPI()

class InputModel(BaseModel):
    param1: str
    param2: int = 10

class OutputModel(BaseModel):
    result: str

@app.get("/health")
async def health():
    return {"status": "ok"}

@app.post("/process")
async def process(input_data: InputModel) -> OutputModel:
    try:
        # 调用核心业务逻辑
        result = do_something(input_data.param1, input_data.param2)
        return OutputModel(result=result)
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--port", type=int, default=8000)
    args = parser.parse_args()

    uvicorn.run(app, host="0.0.0.0", port=args.port)
```

---

## 示例任务书

参考 `task_specs/kuaishou_kling_task.md` 查看完整示例。

---

## 联系与支持

- 项目仓库: `tool_agent/`
- 任务书目录: `task_specs/`
- 提交脚本目录: `scripts/`
- 工具注册表: `data/registry.json`

