Files
Agent-System/web/src/engine/templateMatch.ts
T
weichenandCursor 0c5256c60c fix(web): bind task API to resolved intake key when URS-only pool
When deliverables only expose user_requirements templates, requirement phrases still must POST intake on V0.1. Re-resolve task_key on retry and skip auto-retry for stage gate errors.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 03:30:13 +00:00

89 lines
3.4 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.
import { DeliverableItem } from '../types/workbench';
function templatePool(deliverables: DeliverableItem[]): DeliverableItem[] {
return deliverables.filter((d) => d.isTemplateOnly);
}
/** 从文案判定 intake(V0.1)还是 user_requirements(V0.5),与交付物池无关。 */
export function resolveRequirementsTaskKey(text: string): 'intake' | 'user_requirements' {
const wantsUrs =
text.includes('用户需求表') ||
text.includes('URS') ||
(text.includes('用户需求') && !text.includes('初始'));
if (wantsUrs) return 'user_requirements';
const wantsIntake =
text.includes('初始需求') ||
text.includes('需求摸底') ||
text.includes('摸底') ||
text.includes('初始需求表');
if (wantsIntake) return 'intake';
// 含糊表述默认 V0.1 摸底表,避免误触 V0.5 用户需求表导致阶段门禁 403
return 'intake';
}
function findTemplateByTaskKey(
pool: DeliverableItem[],
taskKey: string,
): DeliverableItem | undefined {
return pool.find((d) => d.taskKey === taskKey);
}
/** 在 intake / user_requirements 等同语候选里按文案选择任务键,再匹配模板交付物。 */
function pickRequirementsTask(text: string, pool: DeliverableItem[]): DeliverableItem | undefined {
const taskKey = resolveRequirementsTaskKey(text);
const hit = findTemplateByTaskKey(pool, taskKey);
if (hit) return hit;
// 池里可能只有 URS 模板;仍返回虚拟条目以便上层使用正确 taskKey
const fallback = findTemplateByTaskKey(pool, taskKey === 'intake' ? 'user_requirements' : 'intake');
if (fallback) {
return { ...fallback, taskKey };
}
return undefined;
}
/** 根据用户输入解析应触发的文档任务键(不依赖交付物池顺序)。 */
export function resolveTaskKeyFromMessage(text: string): string | undefined {
if (text.includes('需求') || text.includes('摸底') || text.includes('用户')) {
return resolveRequirementsTaskKey(text);
}
if (text.includes('调研') || text.includes('竞品')) return 'research';
if (text.includes('可行性') || text.includes('注册') || text.includes('报告')) {
if (text.includes('研发') || text.includes('dev')) return 'feasibility_dev';
return 'feasibility_reg';
}
if (text.includes('设计') || text.includes('规格') || text.includes('架构')) return 'prelim_design';
if (text.includes('报价') || text.includes('工时') || text.includes('预算')) return 'cdo_quote';
return undefined;
}
/** 根据用户输入在模板交付物中解析应触发的文档任务。 */
export function matchTemplateDeliverable(
text: string,
deliverables: DeliverableItem[],
): DeliverableItem | undefined {
const pool = templatePool(deliverables);
const taskKey = resolveTaskKeyFromMessage(text);
if (!taskKey) return undefined;
const hit = findTemplateByTaskKey(pool, taskKey);
if (hit) return hit;
if (taskKey === 'intake' || taskKey === 'user_requirements') {
return pickRequirementsTask(text, pool);
}
return undefined;
}
/** 合并文案解析与模板匹配,保证 API 使用正确 task_key。 */
export function resolveTaskTemplateForMessage(
text: string,
deliverables: DeliverableItem[],
): { taskKey: string; template?: DeliverableItem } | undefined {
const taskKey = resolveTaskKeyFromMessage(text);
if (!taskKey) return undefined;
const template = matchTemplateDeliverable(text, deliverables);
return { taskKey, template };
}