Files
ai-kefu-jc/tests/test_company_facts.py
T

82 lines
3.0 KiB
Python
Raw Normal View History

"""公司事实卡: 拼接位置与墙安全性静态测试。"""
from pathlib import Path
from app.services.ai_service import AIService, COMPANY_FACTS, SYSTEM_PROMPT
FACTS_PATH = Path("app/prompts/company_facts.md")
class _CapturingProvider:
def __init__(self):
self.prompts = []
async def chat(self, messages, system_prompt):
self.prompts.append(system_prompt)
return "ok"
async def stream_chat(self, messages, system_prompt):
self.prompts.append(system_prompt)
for t in ("ok",):
yield t
def test_facts_concatenated_between_system_and_context():
# 顺序: 行为 prompt → 事实卡 → 当轮提醒(提醒必须最后,保持最新鲜)
svc = AIService(provider=_CapturingProvider())
svc.provider.prompts.clear()
import asyncio
asyncio.run(svc.chat([{"role": "user", "content": "hi"}], context="⚠️ 提醒"))
p = svc.provider.prompts[0]
assert "HESUPIPE" in p and "HDPE" in p # 建材事实在场
assert p.index(SYSTEM_PROMPT) < p.index(COMPANY_FACTS) < p.index("⚠️ 提醒")
def test_facts_absent_from_extractor_prompt():
# extractor/evaluator 不拼事实卡(它们有自己的 prompt,不该看到公司事实)
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
def test_facts_no_auto_words():
# 建材化红线: 事实卡不得残留汽车项目词汇
# (只查 COMPANY_FACTS——SYSTEM_PROMPT 属 Task 3,不在本测试管辖)
for word in ("BYD", "Hesu Auto", "Toyota"):
assert word not in COMPANY_FACTS, word
for word in ("vehicle", "sedan", "suv"):
assert word not in COMPANY_FACTS.lower(), word
from app.services.boundary_filter import (
no_contact_value, split_sentences, violates_boundary,
)
def _fact_sentences():
# 注: 事实卡是无句读的 markdown 列表,多行会合并成大句块——无害,
# 检测是 search 式,违规在任何块内都能命中(草案已实证过两墙全清)
complete, tail = split_sentences(COMPANY_FACTS)
return complete + ([tail] if tail else [])
def test_facts_pass_both_output_walls():
# AI 原样引用事实也必须安全: 每句过价格墙+零号码墙
# (陷阱实证: "五万"会被裸预算模式击杀、"累计出口五万台"→整句被替换成要号话术)
for s in _fact_sentences():
assert not violates_boundary(s), s[:60]
assert not no_contact_value(s), s[:60]
def test_facts_length_capped():
# 防营销手册化: 事实卡是卡片不是 brochure——超限=该精简或走检索方案
assert len(COMPANY_FACTS) < 2500
def test_facts_no_contact_channel():
# 我方联系方式绝不入卡(红线: web 指按钮/App 反向留资)
# 只查确定性标记: 邮箱@与 wa.me 链接("WhatsApp"品牌词是合法内容,不作标记)
assert "@" not in COMPANY_FACTS
assert "wa.me" not in COMPANY_FACTS.lower()