From 8fa3d7a8d08f65406e8247ca512976f88136f0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Tue, 29 Sep 2026 15:19:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8F=90=E5=8F=96/=E8=AF=84=E4=BC=B0?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E8=AF=8D=E5=BB=BA=E6=9D=90=E5=8C=96(?= =?UTF-8?q?=E8=A7=84=E6=A0=BC=E8=87=AA=E7=94=B1=E6=96=87=E6=9C=AC+?= =?UTF-8?q?=E6=A0=87=E5=87=86=E5=8D=95=E5=88=97)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- app/prompts/evaluator.md | 19 +++-- app/prompts/extractor.md | 14 +++- app/prompts/system.md | 2 +- .../2026-09-29-building-materials-ai-kefu.md | 8 +-- tests/test_company_facts.py | 7 +- tests/test_extractor_evaluator_prompts.py | 69 +++++++++++++++++++ 6 files changed, 103 insertions(+), 16 deletions(-) create mode 100644 tests/test_extractor_evaluator_prompts.py diff --git a/app/prompts/evaluator.md b/app/prompts/evaluator.md index 74a6d84..1757fa3 100644 --- a/app/prompts/evaluator.md +++ b/app/prompts/evaluator.md @@ -1,30 +1,35 @@ -你是一个客户意向评估助手。根据对话中收集到的客户信息,判断这个客户的购买意向。 +你是一个客户意向评估助手。根据对话中收集到的客户信息,判断这个客户的管道采购意向。 ## 评估标准 ### interested(有意向) 必须满足以下所有条件: -- 明确表达了购车需求(有预算或目标车型/品牌) -- 明确了目标国家/地区或采购用途 +- 明确表达了管道采购需求(有明确意向品类/规格要求,或有采购数量、预算) +- 明确了目标国家或用途/项目类型 - 提供了至少一种联系方式(手机/WhatsApp/邮箱/微信) - 对话至少进行了3轮以上(客户说了3条以上消息) - 客户表现出真实采购意向而非简单咨询 -- 客户询问价格、优惠、运费或试驾/交付安排,应视为意向积极信号(有真实购买兴趣) +- 客户询问价格、优惠、运费或样品/交期安排,应视为意向积极信号(有真实采购兴趣) - 客户主动索要我方联系方式(WhatsApp/邮箱/电话),视为最强意向信号之一 ### not_interested(无意向) 客户表现出以下特征: -- 明确表示不买、只是随便看看 +- 明确表示不买、没有采购计划、只是随便看看 - 态度冷淡,不愿多聊 -- 表示已经从其他渠道购买了 +- 表示已经从其他渠道采购了 ### pending(待定) 以下情况都应标记为pending: - 对话轮次少于3轮 - 还未收集到联系方式 -- 客户信息不够充分(缺少预算、车型、数量等关键信息) +- 客户信息不够充分(仅泛泛浏览品类,缺少规格要求、采购数量等关键信息) - 客户还在比较或犹豫阶段 +## 快速示例 +- 客户说明确的品类+规格+数量(如 "HDPE PE100 DN110 SDR11, 5000 meters, water project in Kenya")并留了联系方式 → interested +- 客户仅泛泛浏览("what pipes do you have"),没给规格、数量或联系方式 → pending +- 客户明确无采购计划("just looking" / 已从别家采购)→ not_interested + ## 严格规则 1. info_sufficient 必须为 true 时,intent_level 才可以是 interested 或 not_interested 2. 如果对话轮次少于3轮,info_sufficient 必须为 false,intent_level 必须为 pending diff --git a/app/prompts/extractor.md b/app/prompts/extractor.md index b25d0cd..0a05e54 100644 --- a/app/prompts/extractor.md +++ b/app/prompts/extractor.md @@ -1,8 +1,18 @@ -你是一个信息提取助手。请从以下对话历史中提取客户相关信息,输出JSON格式。 +你是一个信息提取助手。请从以下对话历史中提取客户(管道采购)相关信息,输出JSON格式。 ## 输出格式 返回一个JSON对象,包含你能从对话中识别到的所有信息字段。字段名用中文。示例字段: -目标国家/地区、采购用途、预算、意向品牌、意向车型、采购数量、车况要求、年份要求、运输方式、联系方式、其他备注。 +目标国家、意向品类、规格要求、标准要求、采购数量、用途、项目类型、预算提及、联系方式、其他备注。 + +字段说明: +- 意向品类:客户想采购的管道品类(如 HDPE给水管、PVC排水管、PPR管、双壁波纹管、球墨铸铁管等) +- 规格要求:自由文本,保留客户原话(如 "DN110 PE100 SDR11"、"4 inch SCH40"、"PN16")——不拆分、不翻译、不过度结构化,客户怎么说就怎么记 +- 标准要求:客户提到的执行标准(如 ISO4427、ASTM、EN12201、SCH40/SCH80 系列)——客户提到才记,没提到不要猜 +- 采购数量:数量或工程量(如 "5000米"、"3x40HQ"、"20000条") +- 用途:管道用途(给水/燃气/排污/灌溉等) +- 项目类型:市政工程/农田灌溉/房建项目/经销商备货等 +- 目标国家:客户所在或项目所在的国家/地区 +- 预算提及:客户提到的预算或目标价(有才记,原样保留) 其中联系方式如果有的话格式为:{"手机": "xxx", "WhatsApp": "xxx", "邮箱": "xxx", "微信": "xxx"} - 如果对话中客户更正了此前提供的联系方式(如 wrong number / 号码错了 / use this / 换成 / 又给了一个新号),联系方式必须以客户最后提供的值为准覆盖旧值 diff --git a/app/prompts/system.md b/app/prompts/system.md index 8d0836f..c41a10a 100644 --- a/app/prompts/system.md +++ b/app/prompts/system.md @@ -16,7 +16,7 @@ 1. **产品信息**(品类/口径/压力等级/标准/材质/应用场景/管道知识):直接热情介绍。 - 公司情况(品牌/覆盖市场/出口经验/运作模式)按系统提供的事实卡简短作答建立信任——但不因此报价、承诺或跳过留资引导 - 认证(certificate)按事实卡简短作答(ISO 三体系);证书编号、完整认证清单、检测报告细节 → 转客户经理(同第2类话术) -2. **价格、优惠、折扣、MOQ(起订量)、交期 lead time、装柜方案 container loading、样品费 sample fee、运费、关税、质保 warranty/after-sales 类**:一律不直接回答,用迂回话术(解释一句 + 固定英文短句收尾): +2. **价格、优惠、折扣、MOQ(起订量)、交期 lead time、装柜方案 container loading、样品费 sample fee、运费、关税、质保 warranty/after-sales 类**:一律不直接回答,用迂回话术(解释一句 + 引导留资模板句收尾,见语言规则): - "Prices depend on the specification, quantity and destination — Please leave your contact and our manager will send the full quotation." - "Lead time depends on your order and the production schedule — Please leave your contact and our manager will send the full quotation." - "MOQ differs by product line — Please leave your contact and our manager will send the full quotation." diff --git a/docs/superpowers/plans/2026-09-29-building-materials-ai-kefu.md b/docs/superpowers/plans/2026-09-29-building-materials-ai-kefu.md index 6b93a51..048d512 100644 --- a/docs/superpowers/plans/2026-09-29-building-materials-ai-kefu.md +++ b/docs/superpowers/plans/2026-09-29-building-materials-ai-kefu.md @@ -152,7 +152,7 @@ git add -A && git commit -m "feat: HESUPIPE 公司事实卡(官网硬事实+防 **Files:** - Modify: `app/prompts/system.md`(整篇重写,框架保留) -- [ ] **Step 1: 重写 system.md** +- [x] **Step 1: 重写 system.md** 保留参考项目的六段结构,逐段建材化: @@ -163,20 +163,20 @@ git add -A && git commit -m "feat: HESUPIPE 公司事实卡(官网硬事实+防 5. **对话推进**:预算路径改为 `"Which application? Water supply, gas, drainage or irrigation?"`;规格追问(口径/压力/标准);围绕已说品类推进 6. **好差示例**:6-8 组管道场景(例:客户说 "PE100 water pipe DN110",差:泛问 what do you need;好:确认 SDR11/SDR17? + 询问工程量;中文话术示例同步改管道语境) -- [ ] **Step 2: 自查清单** +- [x] **Step 2: 自查清单** - [ ] 所有汽车词(car/vehicle/BYD/Toyota/test drive/提车/试驾/续航)已清零:`grep -in "car\b\|vehicle\|byd\|toyota\|test drive\|试驾\|提车\|续航" app/prompts/system.md` 预期无输出 - [ ] 红线覆盖:MOQ、lead time、container loading、sample fee、certificate、price range - [ ] 固定英文拒绝短句原样出现(与 boundary_filter 的 DEFLECTIONS 措辞协调但不冲突——prompt 是"含义",墙是"兜底") -- [ ] **Step 3: 跑全量测试(system.md 变更影响的测试)** +- [x] **Step 3: 跑全量测试(system.md 变更影响的测试)** ```bash python -m pytest -q ``` 预期:PASS(若有断言 system.md 内容的测试,同步更新) -- [ ] **Step 4: Commit** +- [x] **Step 4: Commit** ```bash git add -A && git commit -m "feat: 系统提示词建材化(管道外贸人设+多语言拒绝短句)" diff --git a/tests/test_company_facts.py b/tests/test_company_facts.py index f3c4349..3d1134c 100644 --- a/tests/test_company_facts.py +++ b/tests/test_company_facts.py @@ -33,11 +33,14 @@ def test_facts_concatenated_between_system_and_context(): def test_facts_absent_from_extractor_prompt(): # extractor/evaluator 不拼事实卡(它们有自己的 prompt,不该看到公司事实) + # Task 4 起 extractor/evaluator 含通用品类词(HDPE/PVC等字段示例)—— + # 隔离断言只针对品牌/公司事实标记(HESUPIPE),不再卡通用品类词 from app.services.ai_service import EVALUATOR_PROMPT, EXTRACTOR_PROMPT assert "HESUPIPE" not in EXTRACTOR_PROMPT - assert "HDPE" not in EXTRACTOR_PROMPT assert "HESUPIPE" not in EVALUATOR_PROMPT - assert "HDPE" not in EVALUATOR_PROMPT + # 河南和硕/和硕集团 等公司主体信息同样不得泄入 + assert "和硕" not in EXTRACTOR_PROMPT + assert "和硕" not in EVALUATOR_PROMPT def test_facts_no_auto_words(): diff --git a/tests/test_extractor_evaluator_prompts.py b/tests/test_extractor_evaluator_prompts.py new file mode 100644 index 0000000..1001de2 --- /dev/null +++ b/tests/test_extractor_evaluator_prompts.py @@ -0,0 +1,69 @@ +"""提取/评估提示词建材化: 字段与占位符静态断言(Task 4)。""" +from app.services.ai_service import EVALUATOR_PROMPT, EXTRACTOR_PROMPT + +# 建材字段清单(与 system.md "关注的信息"节口径一致) +PIPE_FIELDS = ( + "意向品类", # HDPE给水管/PVC排水管/PPR… + "规格要求", # 自由文本保留客户原话 + "标准要求", # ISO4427/ASTM/EN/SCH 单列 + "采购数量", + "用途", # 给水/燃气/排污/灌溉… + "项目类型", + "目标国家", # email_service.send_lead_email 按此键取国家 + "预算提及", + "联系方式", +) + +# 汽车项目遗留字段——建材化后不得残留 +AUTO_WORDS = ("车型", "意向品牌", "车况要求", "年份要求", "运输方式", "购车", "试驾") + + +def test_extractor_has_pipe_fields(): + for field in PIPE_FIELDS: + assert field in EXTRACTOR_PROMPT, field + + +def test_extractor_no_auto_fields(): + for word in AUTO_WORDS: + assert word not in EXTRACTOR_PROMPT, word + + +def test_extractor_spec_is_free_text(): + # 规格要求 = 自由文本保留客户原话,不拆分不过度结构化 + assert "原话" in EXTRACTOR_PROMPT + assert "不拆分" in EXTRACTOR_PROMPT + + +def test_extractor_standard_only_when_mentioned(): + # 标准要求客户提到才记,不猜测 + assert "客户提到才记" in EXTRACTOR_PROMPT + + +def test_extractor_placeholders_and_contact_contract_intact(): + # 占位符(ai_service.replace 依赖)与联系方式四层合并的 extractor 层契约 + assert "$existing_info" in EXTRACTOR_PROMPT + assert "$messages" in EXTRACTOR_PROMPT + assert '{"手机": "xxx", "WhatsApp": "xxx", "邮箱": "xxx", "微信": "xxx"}' in EXTRACTOR_PROMPT + assert "整条消息只有号码" in EXTRACTOR_PROMPT + + +def test_evaluator_keeps_three_levels_and_placeholders(): + for level in ("interested", "not_interested", "pending"): + assert level in EVALUATOR_PROMPT, level + assert "$profile" in EVALUATOR_PROMPT + assert "$recent_messages" in EVALUATOR_PROMPT + # _normalize_evaluation 消费的输出字段 + for key in ("intent_level", "reasoning", "contact_info", "info_sufficient"): + assert key in EVALUATOR_PROMPT, key + + +def test_evaluator_no_auto_fields(): + for word in AUTO_WORDS: + assert word not in EVALUATOR_PROMPT, word + + +def test_evaluator_pipe_procurement_examples(): + # 管道采购语义示例: 品类+规格+数量=interested; 泛泛浏览=pending; 无采购计划=not_interested + assert "采购需求" in EVALUATOR_PROMPT + assert "随便看看" in EVALUATOR_PROMPT # not_interested: 明确不买 + assert "泛泛" in EVALUATOR_PROMPT # pending: 仅泛泛浏览