Files
ftb-project-management/docs/agent-spec.md
2026-07-02 18:19:34 +08:00

288 lines
16 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 输出必须有"对账报告"**
- 拆解任务前,先输出结构化对账:
- ✅ 完美对应(需求 ↔ 原型注释 ↔ 任务)
- ⚠️ 需求未见原型
- 无需求ID分组原型有明确功能但没有匹配到关联需求
- ❓ 注释含糊无法转化
- 对账报告先呈现给用户,用户审核后才执行写入
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原型拆解
**目的**:把"产品方案原型 + 关联需求"拆解成开发任务草案 + 测试用例草案。关联需求是正式范围锚点原型中明确可拆但没有匹配到关联需求的功能也可以拆成无需求ID分组草案。
**输入**
- 当前版本「产品方案」类型 + status=completed 的 VersionPlan取其 `resultUrl` 作为**原型链接**(约定:产品方案的成果即原型)
- 当前版本关联需求列表(已被加进 Version 的 Requirement[]
- 版本成员清单(带角色:前端/后端/UI/测试)
**调用入口**:版本详情页 → 产品方案 Tab → 计划状态 = completed 后按钮「AI 拆解开发任务」或「AI 拆解测试用例」
**触发条件**
- 当前版本至少有 1 条 type=product 的 VersionPlan 处于 `completed` 且 resultUrl 非空(提交了原型)
- 当前版本至少关联 1 条需求
**输出**
- 对账报告(结构化文本,含"完美对应/需求未见原型/无需求ID分组/含糊"
- DevTask 草案数组(每条带结构化 `description` + `taskTypeName` + `aiDraft: true` + `references[]` + `aiEstimateHours`,可选 `categoryCode` / `recommendedAssigneeName` / `recommendedAssigneeReason`
- TestCase 草案数组(每条带 `taskTypeName` + `aiDraft: true` + `references[]` + `aiEstimateHours`,可选 `categoryCode` / `recommendedAssigneeName` / `recommendedAssigneeReason`
- `target = dev_tasks``testCaseDrafts` 必须为空数组;`target = test_cases``devTaskDrafts` 必须为空数组
**原型与需求匹配规则**
- 优先按需求编号匹配:原型批注或文本出现 `requirement.id` / `requirement.code` 时,必须优先判定为该需求命中。
- 编号未出现时,再按需求标题和需求概述(`title` / `description`)做语义匹配。
- 有 QY 编号时,`prototype_note` 引用只能使用真实存在的 QY 编号。
- 没有 QY 编号但原型文本已命中需求编号或需求概述时,不应丢弃;可只引用 `requirement`,并在 `matched.noteIds` 返回空数组。
- 既匹配不到需求编号,也匹配不到需求概述语义,但能形成明确功能名称和任务范围的原型批注,进入 `prototypeOnly`,对应草案带 `requirementName` 且不带 `requirement` 引用。
- 只有无法形成稳定任务/用例的含糊批注才进入 `ambiguous`
- 原型批注只要包含标题、详细说明、字段、异常或验收标准之一,且能判断用户动作或系统行为,就不能进入 `ambiguous`;这类内容必须进入 `matched``prototypeOnly` 并继续拆解。
**开发任务与测试用例拆解粒度**
- Agent 先在内部把每条 QY 或命中的需求拆成交付切片,再生成 DevTask/TestCase交付切片维度按 UI、交互、接口、数据、异常、边界、状态流转、兼容和回归扫描。
- 每条业务规则、字段规则、交互规则、异常规则和验收标准都必须至少映射到 1 条开发任务或 1 条测试用例;本次 target 不包含的一侧可以不输出,但另一侧必须覆盖。
- DevTask 必须是一个可交付工程动作。前端、后端、接口、数据库、数据处理和集成支持要按职责拆开。
- DevTask `description` 必填,至少包含实现范围和验收点;如存在非目标范围或依赖,也必须写明。
**测试用例拆解粒度**
- TestCase 要按功能点、UI 交互、表单校验、接口、数据一致性、权限、异常、边界、状态流转、兼容性和回归点拆细。
- 不允许用一条“验证 XX 完整流程”覆盖多个交互、多个接口或多个规则。
- 一条 QY 若同时涉及 UI、接口、数据、异常和状态变化通常应拆出 3-8 条 TestCase。
- TestCase `description` 必填,至少包含前置条件、操作步骤和预期结果;涉及边界、异常或数据一致性时,必须写明测试数据或状态。
- DevTask / TestCase 必须输出 `taskTypeName`,但它必须是可复用的任务类型字典项,不是任务标题或任务概述。`categoryCode` 只作为可选的兼容映射字段;没有合适稳定码时可以省略,不能因此停止拆解。
**写入**
- 用户确认后,调用 `useDevTaskStore.createTask``useTestCaseStore.createTestCase`
- 打开采纳弹窗前,前端先过滤当前版本已采纳过的重复 DevTask/TestCase 草案
- 写入前,前端按 `taskTypeName` 检查 `TaskCategory` 字典;可复用开发类型不存在时自动追加非系统任务类型。测试用例未知类型不自动入库,优先按 `categoryCode` 或默认测试类型回退。
- 写入字段中 `aiDraft: true``aiDraftAt: ISO时间戳`
- 写入 `aiEstimateHours`,不写入执行人预估 `estimateHours`
- DevTask / TestCase 必须写入 `versionId` 作为执行归属;`requirementId` 可选
- 无正式需求 ID 的草案写入 `requirementName` 作为分组展示名,列表显示为 `无需求ID · {requirementName}`
- 无需求ID分组不创建 Requirement不写入需求池不加入版本关联需求列表
- 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小宝预警解读
**目的**:解释小宝预警规则引擎输出的版本发版风险结果,生成项目经理可读的风险原因、延期预测、建议发版窗口和处理动作。
**输入**
- 版本上下文:产品、项目、版本、期望发版日期。
- 规则风险结果:`riskScore``riskLevel``forecastReleaseDate``delayDays``confidence`、风险信号。
- 趋势和快照:风险分变化、连续上升/下降、关键 Bug、失败用例、阻塞和静默风险变化。
- 日报与工作活动证据:今日交付、今日进展、今日风险、进展备注、需要补充进展的事项。
**触发**
- `on_track` 不触发。
- `at_risk``likely_delayed``blocked` 自动触发。
- `attention` 在风险分明显上升、趋势连续上升、关键 Bug 增加、失败用例增加、阻塞增加、静默风险增加、置信度下降或预测发版日延后时触发。
**输出**`summary``why[]``forecast``recommendedReleaseWindow``suggestedActions[]``ownerHints[]`
**权限**:读规则结果和压缩证据;写 `xiaobao-risk-insights` 缓存。不修改 Version、Requirement、DevTask、TestCase、Bug、Member。
**失败回退**AI 不可用时保留规则预警前端显示“规则预警已生成AI 解读会在触发条件满足时自动补充”。AI 失败不影响快照保存和规则风险展示。
### 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 列表
prototypeOnly: Array<{ requirementName: string; noteIds: string[]; taskCount: number }>;
ambiguous: Array<{ noteId: string; reason: string }>;
};
devTaskDrafts: Array<{ title: string; description: string; taskTypeName: string; categoryCode?: string; priority: Priority; aiEstimateHours: number; requirementName?: string; recommendedAssigneeName?: string; recommendedAssigneeReason?: string; references: Reference[] }>;
testCaseDrafts: Array<{ title: string; description: string; taskTypeName: string; categoryCode?: string; priority: Priority; aiEstimateHours: number; requirementName?: string; recommendedAssigneeName?: string; recommendedAssigneeReason?: string; references: Reference[] }>;
}
```
要求AI 不输出数据库 `categoryId``taskTypeName` 是必填的可复用类型名,不能写成“排行榜测试”这类当前文档专属概述;`categoryCode` 只是可选映射提示,不限制 AI 拆解。前端确认写入时按 `taskTypeName` 解析 `TaskCategory`可复用开发类型缺失时才追加到任务类型字典测试用例未知类型回退到已有测试分类。AI 不输出 `estimateHours`、预计开始或预计截止。草案没有 `requirement` 引用时,必须有 `requirementName` 和至少一个 `prototype_note` 引用。
### 任务/用例分组契约
```ts
interface VersionScopedRequirementGroup {
versionId: string; // 执行归属,必填
requirementId?: string; // 正式需求 ID可选
requirementName?: string; // 无正式需求 ID 时的展示分组名
}
```
要求:版本级聚合使用 `versionId`;需求级进度只统计带 `requirementId` 的 DevTask。无需求ID分组只影响版本任务/用例视图,不影响需求池和关联需求列表。
## 视觉规范
AI 草案在 DevTask / TestCase 列表中的视觉区分:
- 整行加 `border-l-2 border-l-purple-400 bg-purple-50/30`
- 标题旁紫色徽章「AI 草案」(`bg-purple-100 text-purple-600`
- 用户在详情抽屉里编辑保存任意字段后,徽章和左边线自动消失
- 无需求ID分组不额外显示“原型发现”等标记只在分组标题中展示 `无需求ID · {requirementName}`