Files
ftb-project-management/docs/agent-spec.md
2026-06-29 16:19:52 +08:00

244 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Agent 规范
本文件是 FTB 系统中所有 AI Agent 的**唯一权威定义**。任何对 agent 的能力、职责、权限、协作方式的修改,必须先在本文件落地,再同步到 architecture / decisions / workflow / roadmap / glossary。
## 何时更新本文件
1. **新增 agent**:新建一个独立 agent
2. **agent 职责变化**:输入/输出/调用条件变了
3. **agent 权限变化**:可读哪些数据、可写哪些数据变了
4. **多 agent 协作方式变化**:编排顺序、依赖关系、出错回退策略变了
不影响以上四类的 prompt 微调、模型版本切换、性能优化不需要更新本文件。
## 全局原则
1. **AI 不直接动数据,先生草案,用户确认**
- 所有写入实体DevTask / TestCase必须带 `aiDraft: true` 标记
- 用户编辑保存后,标记自动清除(在 `useDevTaskStore.updateTask` / `useTestCaseStore.updateTestCase` 里实现)
2. **每条产物强制带引用**
- DevTask / TestCase 必须有 `references: Reference[]`,至少 1 条
- 引用类型:`requirement` / `prototype_note` / `external`
- 不带引用的草案不允许写入
3. **AI 输出必须有"对账报告"**
- 拆解任务前,先输出三段对账:
- ✅ 完美对应(需求 ↔ 原型注释 ↔ 任务)
- ⚠️ 仅需求未见原型 / 仅原型未见需求
- ❓ 注释含糊无法转化
- 对账报告先呈现给用户,用户审核后才执行写入
4. **AI 预估不等于执行人预估**
- AI 只能输出 `aiEstimateHours`
- `estimateHours` 留给负责人/执行人确认后填写
- AI 不输出预计开始/截止时间,排期由负责人后续维护
5. **AI 可推荐负责人,但不自动分配**
- AI 只能从当前版本成员 `members[].name` 中输出可选的 `recommendedAssigneeName`
- 推荐只作为采纳弹窗里的辅助信息,默认不写入 `assigneeId`
- 用户勾选“采纳推荐负责人”后,前端再次校验推荐姓名属于当前版本成员,才写入 DevTask/TestCase
- 没有明确匹配成员时不输出推荐字段
6. **历史版本不强制存储**
- 系统不要求历史原型 URL 作为基准
- 拆偏问题靠"对账报告"暴露给用户,由人工补救
- 这条决定见 decisions.md #17
## Agent 列表
### Agent 1Prototype Decompose Agent原型拆解
**目的**:把"产品方案原型 + 关联需求"拆解成开发任务草案 + 测试用例草案。
**输入**
- 当前版本「产品方案」类型 + status=completed 的 VersionPlan取其 `resultUrl` 作为**原型链接**(约定:产品方案的成果即原型)
- 当前版本关联需求列表(已被加进 Version 的 Requirement[]
- 版本成员清单(带角色:前端/后端/UI/测试)
**调用入口**:版本详情页 → 产品方案 Tab → 计划状态 = completed 后按钮「AI 拆解开发任务」或「AI 拆解测试用例」
**触发条件**
- 当前版本至少有 1 条 type=product 的 VersionPlan 处于 `completed` 且 resultUrl 非空(提交了原型)
- 当前版本至少关联 1 条需求
**输出**
- 对账报告(结构化文本,含"完美对应/单边/含糊"三段)
- DevTask 草案数组(每条带 `aiDraft: true` + `references[]` + `aiEstimateHours`,可选 `recommendedAssigneeName` / `recommendedAssigneeReason`
- TestCase 草案数组(每条带 `aiDraft: true` + `references[]` + `aiEstimateHours`,可选 `recommendedAssigneeName` / `recommendedAssigneeReason`
- `target = dev_tasks``testCaseDrafts` 必须为空数组;`target = test_cases``devTaskDrafts` 必须为空数组
**原型与需求匹配规则**
- 优先按需求编号匹配:原型批注或文本出现 `requirement.id` / `requirement.code` 时,必须优先判定为该需求命中。
- 编号未出现时,再按需求标题和需求概述(`title` / `description`)做语义匹配。
- 有 QY 编号时,`prototype_note` 引用只能使用真实存在的 QY 编号。
- 没有 QY 编号但原型文本已命中需求编号或需求概述时,不应丢弃;可只引用 `requirement`,并在 `matched.noteIds` 返回空数组。
- 只有既匹配不到需求编号,也匹配不到需求概述语义的原型批注,才进入 `noteOnly``ambiguous`
**测试用例拆解粒度**
- TestCase 要按功能点、UI 交互、表单校验、接口、数据一致性、权限、异常、边界、状态流转、兼容性和回归点拆细。
- 不允许用一条“验证 XX 完整流程”覆盖多个交互、多个接口或多个规则。
- 一条 QY 若同时涉及 UI、接口、数据、异常和状态变化通常应拆出 3-8 条 TestCase。
- 测试用例 `categoryCode` 可使用:`test_functional``test_ui_interaction``test_form_validation``test_api``test_data_consistency``test_permission``test_exception``test_boundary``test_state_flow``test_compatibility``test_regression`
**写入**
- 用户确认后,调用 `useDevTaskStore.createTask``useTestCaseStore.createTestCase`
- 打开采纳弹窗前,前端先过滤当前版本已采纳过的重复 DevTask/TestCase 草案
- 写入字段中 `aiDraft: true``aiDraftAt: ISO时间戳`
- 写入 `aiEstimateHours`,不写入执行人预估 `estimateHours`
- DevTask 草案不写预计开始/截止时间,负责人后续排期时再填写
- 只有用户在采纳弹窗勾选“采纳推荐负责人”时,才把已校验的 `recommendedAssigneeName` 写入 `assigneeId`
**权限**
-Version, Requirement, VersionPlan, Member
-DevTask仅 aiDraft 草案、TestCase仅 aiDraft 草案)
- 不得修改Requirement、Version、VersionPlan、Member、Bug
**失败回退**
- 原型 URL 不可达 → 报告"原型无法访问",不写入任何数据
- 没有已完成的产品方案计划 → 报告"请先完成产品方案并提交原型成果",不写入
- 关联需求为空 → 报告"无关联需求,无法对照",不写入
- 对账报告全是 ❓ → 报告"原型注释含糊度过高,建议人工拆解",不写入
**MVP 阶段限制**V3.1
- 只生成 DevTask 和 TestCase 草案
- 不自动分配 assigneeAI 只提供可选推荐,用户确认采纳后才写入
- 不做"上一版基准 diff"
### Agent 2Risk Watch Agent风险预警— 待规划
仅占位,正式规划见 roadmap.md V3.2。
### Agent 3Schedule Suggest Agent排期建议— 待规划
仅占位,正式规划见 roadmap.md V3.2。
## 多 Agent 协作V3.2 规划)
当前 V3.1 只有 Prototype Decompose Agent 单独运行。
未来 Risk Watch / Schedule Suggest 引入后,编排策略另行设计。
## 实现位置
V3.1 起,所有 Agent **走 NestJS 后端**(不走 Next.js API Route原因
- 与现有 `product` / `requirement` 模块架构一致
- 共享 Prisma 实例便于后续 AiLog 持久化
- 多 Agent 引入时统一在 `AiGateway` 管理 prompt / 降级 / 重试
代码位置:
```
apps/server/src/modules/
├── ai/
│ ├── ai.module.ts
│ ├── ai.controller.ts # POST /api/v1/ai/decompose
│ ├── ai.service.ts # 抓原型 + 调 LLM + 解析
│ ├── ai-gateway.service.ts # Anthropic 客户端单例懒加载key 改了重建)
│ ├── prompts/decompose.ts # System Prompt + Tool Schema
│ └── dto/decompose.dto.ts # 入参校验
└── config/
├── config.module.ts
├── ai-config.controller.ts # GET/PATCH /api/v1/config/ai, POST /test
├── ai-config.service.ts # 读写 data/ai-config.json
└── dto/update-ai-config.dto.ts
```
共享类型:`packages/shared/src/agent.ts`(前后端通用)
## 配置管理
**多提供商支持V3.1.5 起)**:系统支持配置任意数量的 AI 提供商,每个独立的 baseURL / apiKey / model运行时切换激活。
**支持的 API 格式**
- `anthropic` — Anthropic 官方 + 兼容 Anthropic Messages API 格式的中转站(如 ikuncode 的 anthropic 端点)
- `openai` — OpenAI 官方 + 兼容 OpenAI Chat Completions 格式的中转站
**API Key 优先级**:当前激活提供商的 apiKey → 环境变量 `ANTHROPIC_API_KEY`(兜底,仅 anthropic 格式可用)
**前端配置入口**`/admin/ai-config`,仅超管(`role.permissions``'*'`)可见
**字段(每个 Provider**
- `id`:用户自定义唯一 ID`ikuncode`
- `name`:显示名
- `format``anthropic` / `openai`
- `baseURL`:完整 API endpoint
- `apiKey`:完整 Key 仅在服务端持有;前端只能拿到 mask 视图
- `model`:该提供商上使用的模型 ID
- `remark`:可选备注
**全局字段**
- `activeProviderId`:当前激活哪个 provider
- `updatedAt` / `updatedBy`:审计
**测试连接**`POST /api/v1/config/ai/test``{ id }`,用 16 token 极简调用验证目标 provider 可用性
**激活**`POST /api/v1/config/ai/activate``{ id }`,切换激活 providerAiGateway 缓存自动失效,下次调用重新构建客户端
**安全约束**
- 配置文件路径在 .gitignore 里(`apps/server/data/`),不进入版本控制
- 前端 GET 接口永远不返回完整 Key
- 修改 Key 只能从前端 PATCH 上传新值,不支持读出旧值
- 编辑提供商时若不传 apiKey则保留原值
**数据迁移**旧版V3.1)的 `{ anthropicApiKey, model }` 单 key 结构,自动迁移为多 providers 结构id 为 `anthropic-official`,自动激活)。
## 数据契约
### Reference 类型
```ts
interface Reference {
type: 'requirement' | 'prototype_note' | 'external';
id: string; // REQ-008 / QY0007 / 自由文本
label: string; // 展示用
url?: string; // 可选跳转链接
}
```
### AI 草案标记
```ts
// DevTask 和 TestCase 共有
aiDraft?: boolean;
aiDraftAt?: string; // ISO 时间戳
references?: Reference[];
aiEstimateHours?: number; // AI 预估耗时,单位小时
```
### Agent 输入预期
```ts
interface DecomposeInput {
version: VersionWithContext; // 上下文(不含 prototypeUrl原型从产品方案成果取
prototypeUrl: string; // 从产品方案 completed 计划的 resultUrl 派生
requirements: Requirement[]; // 当前版本所属项目下已采纳、可用于本次拆解的需求
members: VersionMember[]; // 该版本参与人员(带角色)
target?: 'all' | 'dev_tasks' | 'test_cases';
}
```
要求:`requirements` 不能从产品级全量需求池直接取,必须经过项目维度和已采纳状态过滤。
### Agent 输出预期
```ts
interface DecomposeOutput {
report: {
matched: Array<{ reqId: string; noteIds: string[]; taskCount: number }>;
reqOnly: string[]; // 需求 ID 列表
noteOnly: string[]; // 原型注释 ID 列表
ambiguous: Array<{ noteId: string; reason: string }>;
};
devTaskDrafts: Array<{ title: string; description?: string; categoryCode: string; priority: Priority; aiEstimateHours: number; recommendedAssigneeName?: string; recommendedAssigneeReason?: string; references: Reference[] }>;
testCaseDrafts: Array<{ title: string; description: string; categoryCode: string; priority: Priority; aiEstimateHours: number; recommendedAssigneeName?: string; recommendedAssigneeReason?: string; references: Reference[] }>;
}
```
要求AI 不输出数据库 `categoryId`,只输出稳定 `categoryCode`。前端确认写入时按 `TaskCategory.code` 映射成 `categoryId`映射失败时使用对应分组的默认类型兜底。AI 不输出 `estimateHours`、预计开始或预计截止。
## 视觉规范
AI 草案在 DevTask / TestCase 列表中的视觉区分:
- 整行加 `border-l-2 border-l-purple-400 bg-purple-50/30`
- 标题旁紫色徽章「AI 草案」(`bg-purple-100 text-purple-600`
- 用户在详情抽屉里编辑保存任意字段后,徽章和左边线自动消失