Commit 02e1e2ed by luoqi

feat(ai-script): 稳健/标准档也接入画像(精简版:安全 + 定语气)

原只有深度档喂画像。三档对齐:
- buildPersonaGuide 加 mode:essential(禁忌/治疗敏感/特别关注 + rfm/生命周期)/ full(再叠切入点)。
- 稳健 + 标准档 → essential:只灌'护栏'类(安全红线 + 语气),不灌切入点(折扣/转介/家庭),
  避免轻量填空档被诱导加推销、破坏填空纪律。安全类画像(种植禁忌/看牙恐惧)现三档都见得到。
- 深度档保留 full(default,输出不变 → 无需 bump)。
- bump promptVersion:standard v13 / stable v26(eval 可按版本对比)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent 1842fb02
......@@ -112,12 +112,17 @@ const SCRIPT_PERSONA_GUIDE: Record<string, { warn?: boolean; guide: string }> =
family_structure: { guide: '可切入家庭套餐 / 家人同诊' },
time_preference: { guide: '约时间时优先客户习惯时段(更易接通/到诊)' },
};
const GUIDE_ORDER = [
// 精简版(稳健/标准档):只给 ⚠安全 + 定语气 —— 都是"约束/护栏"类,不诱导加推销;
// 全量版(深度档):再叠 切入点(治疗史/权益/折扣/转介/家庭/时间)给"分析"步发挥。
const GUIDE_ORDER_ESSENTIAL = [
'contraindication',
'treatment_sensitivity',
'special_attention',
'rfm',
'lifecycle_stage',
];
const GUIDE_ORDER_FULL = [
...GUIDE_ORDER_ESSENTIAL,
'treatment_history',
'entitlement_status',
'discount_anchor',
......@@ -128,16 +133,23 @@ const GUIDE_ORDER = [
export function buildPersonaGuide(
highlights: Array<{ key: string; label: string; description: string }>,
mode: 'full' | 'essential' = 'full',
): string {
const order = mode === 'essential' ? GUIDE_ORDER_ESSENTIAL : GUIDE_ORDER_FULL;
const byKey = new Map(highlights.map((h) => [h.key, h]));
const lines: string[] = [];
for (const key of GUIDE_ORDER) {
for (const key of order) {
const h = byKey.get(key);
const g = SCRIPT_PERSONA_GUIDE[key];
if (!h || !g) continue;
lines.push(`- ${g.warn ? '⚠ ' : ''}${h.label}${h.description} ${g.guide}`);
}
return lines.length ? `## 客户画像(内部参考:⚠注意事项 + 定语气 + 切入点)\n${lines.join('\n')}` : '';
if (!lines.length) return '';
const title =
mode === 'essential'
? '## 客户画像(内部参考:⚠注意事项 + 定语气)'
: '## 客户画像(内部参考:⚠注意事项 + 定语气 + 切入点)';
return `${title}\n${lines.join('\n')}`;
}
/**
......
......@@ -2,7 +2,7 @@ import type { DraftPlanScriptInput } from '../../shared/input.types';
import { smartDateDisplay, toothFriendly } from '../../shared/script-facts';
import { resolveDisease } from './phrasing';
import { deidentifyDoctor } from '../../shared/pii';
import { renderTreatmentPlan } from '../../shared/fact-block';
import { renderTreatmentPlan, buildPersonaGuide } from '../../shared/fact-block';
/**
* Prompt 版本管理约定:
......@@ -110,6 +110,9 @@ export function buildDraftPlanScriptPrompt(input: DraftPlanScriptInput): string
? `我是${clinicName}${input.agent.roleTitle}${input.agent.name}`
: `我是${clinicName}的客服`;
// 画像精简版(⚠安全 + 定语气):稳健档只灌"护栏"类,不灌切入点(避免诱导加推销),保持填空纪律。
const persona = buildPersonaGuide(input.personaHighlights ?? [], 'essential');
// ⚠️ user prompt 只给"模板占位需要的值";用法说明全在 system 提示词,这里不重复、不放内部元数据。
return `# 本次回访患者信息(只能用以下事实,不要编造或推断额外信息)
......@@ -139,5 +142,5 @@ ${advLines}
- ${basics}
## 语气
- ${toneHint}${noXray ? '\n\n## 安全(硬约束)\n- 本患者未满 18 岁或年龄未知:**整篇严禁出现"拍片/拍个片/X光/牙片"等任何拍片表述**(删除模板里的拍片句)' : ''}`;
- ${toneHint}${noXray ? '\n\n## 安全(硬约束)\n- 本患者未满 18 岁或年龄未知:**整篇严禁出现"拍片/拍个片/X光/牙片"等任何拍片表述**(删除模板里的拍片句)' : ''}${persona ? `\n\n${persona}` : ''}`;
}
......@@ -65,7 +65,7 @@ export function stableTemplateFallback(input: DraftPlanScriptInput): DraftPlanSc
* 改 system/prompt 文本 → bump 字母;改 schema → bump 日期。
*/
const DRAFT_PLAN_SCRIPT_PROMPT_VERSION =
'draft_plan_script@2026-06-06-4module-v25'; // v24: 治疗计划补 plannedTreatments(treatment_record planned 结构化;原只读常空的 emr.treatment_plan → 话术缺治疗计划);v23: common.md + 人群共性 SKILL 去 brace(原 {应治未治项}/{诊断医生}/{智能称呼} 改朴素措辞;稳健自身句位模板的 {} 不变,填空机制照常,行为基本不变);v22: 新老客改'熟络度'(recency为主+次数为辅,去二分标签,交LLM);v21: 开场日期改锚【最近一次就诊】(原误用诊断日,患者后来又来过会错位)+'来过之后'/告知'之前那次'区分;v20: schema describe 收口(去与模板矛盾的开场顺序/'下周'/负面例,只留段用途+关键约束)+ prompt/兜底 软化'下周'+ 修陈旧注释;v19: stable format.md 精简(去 common/机器闸重复、自查砍到高风险3条)+ 成人模板优化(去重/换标题/软化结束语);v18: base-common 精简合并去重 + 软化治疗方案口径(可点名/不报价不定细化/落点复查);v17: 目录重组(shared/+tiers/stable/)— base 拆 common+format、人群拆共性知识+稳健句位、病种文案归 stable phrasing、安全单一源 safety-rules、composer tier-aware;修开场顺序冲突。v16: 儿童模板复查段修复(删写死"3个月常规涂氟检查"→对齐本次{应治未治项}+用{复查时长},涂氟降级顺带);child SKILL 1.4.0;v15: user prompt 加"医生那次交代"(医嘱/建议/治疗计划,来自聚焦病历,仅引用不演绎);medicalRecord 补 recommendations;v14: user prompt 加 {牙位}(FDI→俗称)+ 本次目标(plan.goal)+ ≤18 禁拍片 belt;占位收口 {牙位}(删 【缺失牙位】);adult/child 模板带牙位;v13: 撤 token,人名去名留称呼(徐女士/韩医生 直接给,非 token);开场白先称呼确认对方再自报家门;v12: user prompt 人名脱敏(称呼/诊断医生/客服 用 token,生成后回填;监护人全名不进 prompt);v11: 统一通话称呼(年龄+性别+监护人,修"9岁张先生");监护人触达提示;医生标签 最后一次就诊→诊断医生;v10: 病种知识走 disease-knowledge 单一访问源(subKey 优先+文本兜底),修 颌骨囊肿 拿不到风险/优势的 bug;v9: 自报家门用登录客服 岗位+姓名(agent);v8: 占位符统一({}=替换、【】=原样保留);v7: 清除 user prompt 污染;v6: 清 system 污染;v5: 还原原模板
'draft_plan_script@2026-06-07-4module-v26'; // v26: 加画像精简版(⚠禁忌/治疗敏感/特别关注 + rfm/生命周期定语气;切入点留深度档,不诱导推销);v24: 治疗计划补 plannedTreatments(treatment_record planned 结构化;原只读常空的 emr.treatment_plan → 话术缺治疗计划);v23: common.md + 人群共性 SKILL 去 brace(原 {应治未治项}/{诊断医生}/{智能称呼} 改朴素措辞;稳健自身句位模板的 {} 不变,填空机制照常,行为基本不变);v22: 新老客改'熟络度'(recency为主+次数为辅,去二分标签,交LLM);v21: 开场日期改锚【最近一次就诊】(原误用诊断日,患者后来又来过会错位)+'来过之后'/告知'之前那次'区分;v20: schema describe 收口(去与模板矛盾的开场顺序/'下周'/负面例,只留段用途+关键约束)+ prompt/兜底 软化'下周'+ 修陈旧注释;v19: stable format.md 精简(去 common/机器闸重复、自查砍到高风险3条)+ 成人模板优化(去重/换标题/软化结束语);v18: base-common 精简合并去重 + 软化治疗方案口径(可点名/不报价不定细化/落点复查);v17: 目录重组(shared/+tiers/stable/)— base 拆 common+format、人群拆共性知识+稳健句位、病种文案归 stable phrasing、安全单一源 safety-rules、composer tier-aware;修开场顺序冲突。v16: 儿童模板复查段修复(删写死"3个月常规涂氟检查"→对齐本次{应治未治项}+用{复查时长},涂氟降级顺带);child SKILL 1.4.0;v15: user prompt 加"医生那次交代"(医嘱/建议/治疗计划,来自聚焦病历,仅引用不演绎);medicalRecord 补 recommendations;v14: user prompt 加 {牙位}(FDI→俗称)+ 本次目标(plan.goal)+ ≤18 禁拍片 belt;占位收口 {牙位}(删 【缺失牙位】);adult/child 模板带牙位;v13: 撤 token,人名去名留称呼(徐女士/韩医生 直接给,非 token);开场白先称呼确认对方再自报家门;v12: user prompt 人名脱敏(称呼/诊断医生/客服 用 token,生成后回填;监护人全名不进 prompt);v11: 统一通话称呼(年龄+性别+监护人,修"9岁张先生");监护人触达提示;医生标签 最后一次就诊→诊断医生;v10: 病种知识走 disease-knowledge 单一访问源(subKey 优先+文本兜底),修 颌骨囊肿 拿不到风险/优势的 bug;v9: 自报家门用登录客服 岗位+姓名(agent);v8: 占位符统一({}=替换、【】=原样保留);v7: 清除 user prompt 污染;v6: 清 system 污染;v5: 还原原模板
@Injectable()
export class DraftPlanScriptCall
......
import type { DraftPlanScriptInput } from '../../shared/input.types';
import { buildRichFactBlock } from '../../shared/fact-block';
import { buildRichFactBlock, buildPersonaGuide } from '../../shared/fact-block';
/**
* 标准档 user prompt —— 厚事实块(共享 buildRichFactBlock):病历全 + 其他reason + 近期治疗 + 目标,
* 病种只给名(去文案模板)。输出指令(去模板·4段标题不定·自由编排)在 system(format.standard + schema)。
* + 画像精简版(⚠安全 + 定语气;切入点留给深度档,避免轻档加推销)。
*/
export function buildStandardScriptPrompt(input: DraftPlanScriptInput): string {
return buildRichFactBlock(input);
const persona = buildPersonaGuide(input.personaHighlights ?? [], 'essential');
return persona ? `${buildRichFactBlock(input)}\n\n${persona}` : buildRichFactBlock(input);
}
......@@ -22,7 +22,7 @@ import { type DeepDraft, draftOutputToDeep } from '../deep/types';
*
* callKey 仍用 'draft_plan_script'(同一逻辑调用),档位差异落 promptVersion → eval 可按版本切档对比。
*/
const STANDARD_PROMPT_VERSION = 'draft_plan_script@2026-06-06-standard-v12'; // v11: 病历补全(治疗计划=plannedTreatments 结构化 + 一般情况/处置/诊断说明,对齐页面 emr-soap);v10: format.md 去污染(删 tier 名/去模板对比/见 common.md 等解释性 meta,纯指令);流式输出(段数组 partial 边出边渲染);v9: 真去模板 — 4 固定角色字段 → 自由 sections[];v8: 去 {} 替换占位;v7: format 瘦身 + opening 放开 + closing 软化
const STANDARD_PROMPT_VERSION = 'draft_plan_script@2026-06-07-standard-v13'; // v13: 加画像精简版(⚠禁忌/治疗敏感/特别关注 + rfm/生命周期定语气;切入点留深度档);v11: 病历补全(治疗计划=plannedTreatments 结构化 + 一般情况/处置/诊断说明,对齐页面 emr-soap);v10: format.md 去污染(删 tier 名/去模板对比/见 common.md 等解释性 meta,纯指令);流式输出(段数组 partial 边出边渲染);v9: 真去模板 — 4 固定角色字段 → 自由 sections[];v8: 去 {} 替换占位;v7: format 瘦身 + opening 放开 + closing 软化
@Injectable()
export class StandardScriptCall implements AiCall<DraftPlanScriptInput, DeepDraft> {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment