/** * 金蝶产品线 skill 映射(显式表,与各 SKILL.md「适用产品线」对齐)。 * 不做关键词自动路由;仅用于展示会话中已加载的产品线 skill,并做只读一致性检查。 */ /** 互斥产品线族:同族内多个 skill 可并存;跨族同时加载需询问用户。 */ export type ProductLineGroup = "cosmic" | "enterprise" | "enterprise-openapi"; export interface SkillProductBinding { skillName: string; group: ProductLineGroup; /** 与产品画像对应的稳定 id;不由 skill 自动写入 */ productId: string; label: string; } /** 仅产品线绑定 skill 参与画像检查;通用 skill(sql/error-handling 等)不在此表 */ export const SKILL_PRODUCT_BINDINGS: readonly SkillProductBinding[] = [ { skillName: "ok-cosmic", group: "cosmic", productId: "cosmic-java", label: "金蝶AI苍穹/金蝶AI星瀚/金蝶AI套件 Cosmic Java", }, { skillName: "kd-cosmic-review", group: "cosmic", productId: "cosmic-java", label: "金蝶AI苍穹/金蝶AI星瀚/金蝶AI套件 Cosmic Java", }, { skillName: "cosmic-unittest", group: "cosmic", productId: "cosmic-java", label: "金蝶AI苍穹/金蝶AI星瀚/金蝶AI套件 Cosmic Java", }, { skillName: "kd-ksql", group: "cosmic", productId: "cosmic-java", label: "金蝶AI苍穹/金蝶AI星瀚/金蝶AI套件 Cosmic Java", }, { skillName: "kd-enterprise-csharp", group: "enterprise", productId: "enterprise-csharp", label: "金蝶AI星空企业版/标准版 C# BOS", }, { skillName: "kd-enterprise-python-plugin", group: "enterprise", productId: "enterprise-ironpython", label: "金蝶AI星空企业版/标准版 IronPython", }, { skillName: "kd-enterprise-openapi", group: "enterprise-openapi", productId: "enterprise-openapi", label: "金蝶AI星空企业版/标准版 OpenAPI 集成", }, ] as const; const bindingBySkill = new Map(SKILL_PRODUCT_BINDINGS.map(b => [b.skillName, b])); export function getSkillProductBinding(skillName: string): SkillProductBinding | undefined { return bindingBySkill.get(skillName); } export interface ResolvedProductLinesFromSkills { productIds: string[]; groups: ProductLineGroup[]; /** 跨族冲突(例如同时 ok-cosmic + kd-enterprise-csharp) */ conflict: boolean; labels: string[]; } export function resolveProductLinesFromSkillNames(skillNames: string[]): ResolvedProductLinesFromSkills { const seenIds = new Set(); const groups = new Set(); const labels: string[] = []; for (const name of skillNames) { const b = bindingBySkill.get(name); if (!b) continue; groups.add(b.group); if (!seenIds.has(b.productId)) { seenIds.add(b.productId); labels.push(b.label); } } const groupList = [...groups]; const conflict = groupList.length > 1; return { productIds: [...seenIds], groups: groupList, conflict, labels, }; } export interface ProductLineIntentFromText { productIds: string[]; skills: string[]; reason: string; confidence: "none" | "high"; source: "none" | "explicit-turn-text"; } const EMPTY_PRODUCT_LINE_INTENT: ProductLineIntentFromText = { productIds: [], skills: [], reason: "当前文本没有明确金蝶产品线实体", confidence: "none", source: "none", }; const ENTERPRISE_PRODUCT_TERMS = ["星空企业版", "星空", "企业版", "标准版", "K/3 Cloud", "K3Cloud", "K3 Cloud"] as const; const COSMIC_PRODUCT_TERMS = ["苍穹", "星瀚", "金蝶AI套件", "AI套件", "Cosmic"] as const; const IRONPYTHON_TERMS = ["IronPython", "Python插件", "Python 脚本", "Python脚本", "BOS Python"] as const; const REPORT_OR_PLUGIN_TERMS = [ "报表", "账表", "帐表", "取数", "数据源", "继承原有插件", "继承", "自定义字段", "字段", "单据类型", "表单插件", "列表插件", "操作插件", "报表插件", "插件", ] as const; const EXTERNAL_INTEGRATION_TERMS = [ "OpenAPI", "WebAPI", "HTTP", "REST", "第三方系统", "外部系统", "数据同步", "RPA", "API调用", "API 调用", "回调", ] as const; function textHasAny(text: string, terms: readonly string[]): boolean { const lower = text.toLowerCase(); return terms.some(term => lower.includes(term.toLowerCase())); } /** * Infer a turn-local product-line hint from explicit user wording. * This does not persist or overwrite the project profile; kcode init remains the writer. */ export function inferProductLineIntentFromText(text: string): ProductLineIntentFromText { const normalized = text.trim(); if (!normalized) return EMPTY_PRODUCT_LINE_INTENT; const hasEnterpriseProduct = textHasAny(normalized, ENTERPRISE_PRODUCT_TERMS); const hasCosmicProduct = textHasAny(normalized, COSMIC_PRODUCT_TERMS); const hasReportOrPluginContext = textHasAny(normalized, REPORT_OR_PLUGIN_TERMS); const hasExternalIntegrationContext = textHasAny(normalized, EXTERNAL_INTEGRATION_TERMS); if (hasEnterpriseProduct) { if (hasExternalIntegrationContext && !hasReportOrPluginContext) { return { productIds: ["enterprise-openapi"], skills: ["kd-enterprise-openapi"], reason: "本轮需求明确提到企业版外部系统/OpenAPI 对接", confidence: "high", source: "explicit-turn-text", }; } if (textHasAny(normalized, IRONPYTHON_TERMS)) { return { productIds: ["enterprise-ironpython"], skills: ["kd-enterprise-python-plugin"], reason: "本轮需求明确提到企业版 IronPython/Python 插件", confidence: "high", source: "explicit-turn-text", }; } return { productIds: ["enterprise-csharp"], skills: ["kd-enterprise-csharp"], reason: hasReportOrPluginContext ? "本轮需求明确提到企业版报表/插件/字段扩展场景" : "本轮需求明确提到企业版产品线", confidence: "high", source: "explicit-turn-text", }; } if (hasCosmicProduct) { return { productIds: ["cosmic-java"], skills: ["ok-cosmic"], reason: "本轮需求明确提到金蝶AI苍穹/星瀚/套件 Cosmic 产品线", confidence: "high", source: "explicit-turn-text", }; } return EMPTY_PRODUCT_LINE_INTENT; }