/** * 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 引用,必须有 requirementName;requirementName 使用该批注的稳定功能名称,例如"预入职人员支持" 【输出通道强制要求】 - 第一块响应内容必须是 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'], };