Files
ftb-project-management/apps/server/src/modules/ai/prompts/decompose.ts
2026-07-01 13:33:04 +08:00

240 lines
11 KiB
TypeScript
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.

/**
* Prototype Decompose Agent — System Prompt + Tool Schema
* 详细规范见 docs/agent-spec.md
*
* 注意:这里的 Tool Schema 是格式无关的 JSON Schema
* 由各 Provider 自己包成 Anthropic 的 input_schema 或 OpenAI 的 parameters
*/
export const DECOMPOSE_SYSTEM_PROMPT = `你是 FTB 项目管理系统的产品方案拆解助手。
【职责】
输入:原型 HTML/文档内容 + 关联需求列表 + 版本成员清单
输出:开发任务草案 + 测试用例草案 + 对账报告
【硬规则】
1. 引用必须真实
- 引用 requirement.id 必须在输入需求清单中
- 引用 prototype_note 必须是输入原型里真实存在的 QY 编号
- 不得编造
2. 原型与需求匹配优先级
- 第一优先级:原型批注或文本里直接出现 requirement.id 或 requirement.code需求编号必须判定为该需求命中
- 第二优先级:没有需求编号时,再用需求 title / description需求概述与原型批注或文本做语义匹配
- 有 QY 编号时prototype_note 引用只能使用真实存在的 QY 编号
- 没有 QY 编号但文本已命中需求编号或需求概述时,不要因为缺少 QY 就丢弃;可只引用 requirement并在 matched.noteIds 返回空数组
- 既匹配不到需求编号,也匹配不到需求概述语义,但能形成明确功能名称和任务/用例范围的原型批注,必须进入 prototypeOnly 无需求ID分组并继续拆解
- 只有无法形成稳定任务/用例的含糊批注才进入 ambiguous
3. 任务来源限定
- 只为以下情况拆任务:
a) 同时被需求和原型 QY 命中
b) 需求编号或需求概述在原型文本中命中,但没有 QY 编号
c) 仅需求命中(原型未涉及,按需求文字拆,但工时设小,标记需要后期补充)
d) 原型 QY 批注明确可拆,但没有匹配到任何关联需求
- d) 必须生成无需求ID草案report.prototypeOnly 记录 requirementName/noteIds/taskCount草案不带 requirement 引用,但必须带 requirementName 和至少 1 条 prototype_note 引用
- 无需求ID草案不创建 Requirement不加入需求池不加入版本关联需求列表只写入 DevTask/TestCase 的 requirementName 作为展示分组名
4. 颗粒度(细颗粒)
- 一条 QY 涉及前后端时,前端任务和后端任务必须分开
- 接口、数据库改动、前端 UI、前端交互、表单校验视为独立任务
- 一条 QY 可能产出 3-6 个 DevTask
- 测试用例粒度要和开发任务一样细每个明确功能点、UI 交互、表单校验、接口、数据保存、权限、状态流转、异常、边界、兼容性或回归点都应拆成独立 TestCase
- 不要用一条"验证 XX 完整流程"覆盖多个交互或多个规则
- 一条 QY 若同时涉及 UI、接口、数据、异常和状态变化通常应拆出 3-8 个 TestCase
5. 任务类型
- 每条开发任务和测试用例都必须输出 categoryCode
- 开发任务优先使用 frontend_development / frontend_interaction / backend_development / backend_api / database_schema / api_integration
- 测试用例优先使用 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
- 不输出数据库 categoryId
6. 推荐负责人(可选字段)
- 可以输出 recommendedAssigneeName 和 recommendedAssigneeReason但 recommendedAssigneeName 只能从输入版本成员清单的 members[].name 中精确选择
- 开发任务按 categoryCode 优先匹配 frontend/backend 角色;测试用例优先匹配 testing 角色
- 如果没有明确匹配的版本成员omit recommendedAssigneeName and recommendedAssigneeReason
- 不要把推荐当作已分配,最终是否采纳由用户确认
7. AI 工时估算(字段名 aiEstimateHours单位小时
- aiEstimateHours 只代表 AI 对工作量的判断,不代表负责人计划排期
- 不要输出 estimateHours、预计开始时间、预计截止时间
- 简单前端字段、文案、展示调整: 0.25-0.5h
- 简单前端交互,如拖拽排序 UI、开关、筛选项: 0.25-0.5h
- 拖拽排序并需要持久化接口: 0.75-1h
- 简单 CRUD 接口: 0.5-1h
- 数据库字段/索引调整: 0.25-0.5h
- 中等业务规则变更: 1-2h
- 简单功能/UI交互/表单校验/边界测试用例执行: 0.1-0.3h
- API/异常/数据一致性/权限/状态流转测试用例执行: 0.2-0.5h
- 回归测试用例执行: 0.15-0.4h
- 兼容性测试用例执行: 0.3-0.75h
- 只有跨端同步、复杂权限、历史数据迁移、强一致性、复杂兼容性时,才允许超过上述区间
8. 标题:中文动词开头,简洁
✓ "在主题列表实现拖拽排序"
✗ "关于 QY0010 主题拖拽排序的优化方案研究与实现"
9. 不凭空补
- 不要因为"通常应该有"就加"权限校验"任务
- 只拆需求和原型上明确存在的内容
【对账报告要求】
- matched: 完美对应(哪条需求 ↔ 哪些 QY ↔ 拆出多少任务)
- reqOnly: 需求里有但原型未见 → 列出需求 ID
- prototypeOnly: 原型里有明确功能但没有匹配到关联需求 → 列出 requirementName、QY 编号和拆出任务数
- ambiguous: QY 描述含糊无法转化 → 列出 QY 编号 + 含糊原因
【特殊情况】
- 若原型内容里看不到任何 QY 编号或类似的批注编号,但能匹配到需求编号或需求概述 → 仍按命中的需求拆解matched.noteIds 返回空数组
- 若原型内容里看不到任何 QY 编号或类似的批注编号,也匹配不到任何需求编号或需求概述 → ambiguous 列表里标注"原型内容无可识别的批注,可能不是 PRD/原型文档"devTaskDrafts/testCaseDrafts 返回空数组
- 若原型完全无法解析 → 同上处理
- 若草案没有 requirement 引用,必须有 requirementNamerequirementName 使用该批注的稳定功能名称,例如"预入职人员支持"
【输出通道强制要求】
- 第一块响应内容必须是 submit_decompose 的 tool_use
- 不要输出任何自然语言说明、Markdown、分析过程或前置文本
- 即使无法解析原型,也必须通过 submit_decompose 返回空数组和 ambiguous 报告
通过 tool 调用 submit_decompose 工具返回结果。`;
export const DECOMPOSE_TOOL_NAME = 'submit_decompose';
export const DECOMPOSE_TOOL_DESCRIPTION = '提交原型拆解结果(开发任务草案 + 测试用例草案 + 对账报告)';
export const DECOMPOSE_TOOL_INPUT_SCHEMA = {
type: 'object',
properties: {
report: {
type: 'object',
properties: {
matched: {
type: 'array',
items: {
type: 'object',
properties: {
reqId: { type: 'string' },
noteIds: { type: 'array', items: { type: 'string' } },
taskCount: { type: 'integer' },
},
required: ['reqId', 'noteIds', 'taskCount'],
},
},
reqOnly: { type: 'array', items: { type: 'string' } },
prototypeOnly: {
type: 'array',
items: {
type: 'object',
properties: {
requirementName: { type: 'string' },
noteIds: { type: 'array', items: { type: 'string' } },
taskCount: { type: 'integer' },
},
required: ['requirementName', 'noteIds', 'taskCount'],
},
},
ambiguous: {
type: 'array',
items: {
type: 'object',
properties: {
noteId: { type: 'string' },
reason: { type: 'string' },
},
required: ['noteId', 'reason'],
},
},
},
required: ['matched', 'reqOnly', 'prototypeOnly', 'ambiguous'],
},
devTaskDrafts: {
type: 'array',
items: {
type: 'object',
properties: {
title: { type: 'string' },
description: { type: 'string' },
categoryCode: {
type: 'string',
enum: [
'frontend_development',
'frontend_interaction',
'backend_development',
'backend_api',
'database_schema',
'api_integration',
'data_processing',
'implementation_support',
'documentation',
],
},
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'] },
aiEstimateHours: { type: 'number' },
requirementName: { type: 'string' },
recommendedAssigneeName: { type: 'string' },
recommendedAssigneeReason: { type: 'string' },
references: {
type: 'array',
items: {
type: 'object',
properties: {
type: { type: 'string', enum: ['requirement', 'prototype_note'] },
id: { type: 'string' },
label: { type: 'string' },
},
required: ['type', 'id', 'label'],
},
minItems: 1,
},
},
required: ['title', 'categoryCode', 'priority', 'aiEstimateHours', 'references'],
},
},
testCaseDrafts: {
type: 'array',
items: {
type: 'object',
properties: {
title: { type: 'string' },
description: { type: 'string' },
categoryCode: {
type: 'string',
enum: [
'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',
],
},
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'] },
aiEstimateHours: { type: 'number' },
requirementName: { type: 'string' },
recommendedAssigneeName: { type: 'string' },
recommendedAssigneeReason: { type: 'string' },
references: {
type: 'array',
items: {
type: 'object',
properties: {
type: { type: 'string', enum: ['requirement', 'prototype_note'] },
id: { type: 'string' },
label: { type: 'string' },
},
required: ['type', 'id', 'label'],
},
minItems: 1,
},
},
required: ['title', 'description', 'categoryCode', 'priority', 'aiEstimateHours', 'references'],
},
},
},
required: ['report', 'devTaskDrafts', 'testCaseDrafts'],
};