dieya_risk_prediction_backend/tests/test_conversations.py
kx_guo e8c122e542 完善风险评分、LLM兜底与会话历史加载
- 接入数据库驱动的七维风险评分第一阶段逻辑\n- 增加主模型失败后的 DeepSeek 语义、流式和预防建议兜底\n- 将 AI 预防建议改为基于评分、历史异常和标准规则动态生成\n- 增加无历史案例时的标准库兜底校验和通用规则匹配\n- 修复未知供应商识别及测试占位名称过滤\n- 增加后端滚动日志文件配置和 LLM 请求日志\n- 历史会话直接读取已保存结果,避免重新调用模型和重算风险\n- 补充评分、供应商识别和 LLM 协议测试
2026-09-04 18:02:39 +08:00

181 lines
6.1 KiB
Python
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.

from datetime import UTC, datetime
import pytest
from app.models.entities import Conversation, ConversationMessage
from app.services.conversations import ConversationService
def test_conversation_message_data_reads_persisted_risk_result() -> None:
message = ConversationMessage(
id=10,
conversation_id=3,
role="assistant",
content="已完成风险预估。",
structured_data={
"riskResult": {
"subjectId": "软管新品",
"score": 82,
"level": "low",
"modelVersion": "baseline-safety-v2",
"factors": [],
}
},
created_at=datetime.now(UTC),
)
result = ConversationService._message(message)
assert result.role == "assistant"
assert result.risk_result is not None
assert result.risk_result.score == 82
assert result.risk_result.level.value == "low"
def test_conversation_data_includes_all_messages() -> None:
conversation = Conversation(
id=3,
user_id=1,
title="软管新品评估",
created_at=datetime.now(UTC),
updated_at=datetime.now(UTC),
messages=[
ConversationMessage(
id=1,
conversation_id=3,
role="user",
content="评估软管新品",
created_at=datetime.now(UTC),
),
ConversationMessage(
id=2,
conversation_id=3,
role="assistant",
content="已完成分析",
created_at=datetime.now(UTC),
),
],
)
result = ConversationService._data(conversation)
assert result.message_count == 2
assert [message.role for message in result.messages] == ["user", "assistant"]
def test_only_explicit_risk_requests_generate_risk_cards() -> None:
assert ConversationService._is_risk_request(
"你好",
{"intent": "greet", "risk_requested": False},
) is False
assert ConversationService._is_risk_request(
"请只回复:AI 连通成功。",
{"intent": "general", "risk_requested": False},
) is False
assert ConversationService._is_risk_request(
"请评估这个产品的风险",
{"intent": "assessment", "risk_requested": True},
) is True
def test_legacy_general_semantics_hide_old_risk_card() -> None:
messages = [
ConversationMessage(
id=1,
conversation_id=1,
role="user",
content="你好",
created_at=datetime.now(UTC),
),
ConversationMessage(
id=2,
conversation_id=1,
role="assistant",
content="你好,我可以帮你做什么?",
structured_data={
"semantic": {"intent": "general"},
"riskResult": {
"subjectId": "误判",
"score": 61,
"level": "moderately_low",
"modelVersion": "baseline-safety-v2",
"factors": [],
},
},
created_at=datetime.now(UTC),
),
]
assert ConversationService._risk_result_for_message(messages, 1) is None
assert ConversationService._is_risk_request(
"我要做一款脱毛膏新品,100g,PE软管包装,加工厂准备使用瑞沣",
{"intent": "general", "risk_requested": False},
) is True
def test_product_assessment_semantics_override_nlu_miss() -> None:
normalized = ConversationService._normalize_semantics(
"我要做一款脱毛膏新品,100g,PE软管包装,加工厂准备使用瑞沣",
{"intent": "general", "risk_requested": False, "category": "脱毛膏"},
)
assert normalized["intent"] == "assessment"
assert normalized["risk_requested"] is True
def test_short_known_supplier_follow_up_is_routed_to_supplier_card() -> None:
assert ConversationService._infer_supplier_follow_up(
"佳亿",
["佳亿", "祥印", "瑞沣"],
) == "佳亿"
assert ConversationService._infer_supplier_follow_up(
"这个供应商",
["佳亿", "祥印"],
) is None
def test_unknown_standalone_name_is_treated_as_new_supplier() -> None:
assert ConversationService._infer_unknown_supplier_follow_up("德盛") == "德盛"
assert ConversationService._infer_unknown_supplier_follow_up("新供应商A") is None
assert ConversationService._infer_unknown_supplier_follow_up("测试供应商") is None
assert ConversationService._infer_unknown_supplier_follow_up("新供应商B") is None
assert ConversationService._infer_unknown_supplier_follow_up("洗发水") is None
assert ConversationService._infer_unknown_supplier_follow_up("帮我查德盛") is None
assert ConversationService._response_type("测试供应商", {"intent": "supplier"}) == "general"
assert ConversationService._response_type("新供应商A", {"intent": "supplier"}) == "general"
@pytest.mark.asyncio
async def test_any_product_without_history_uses_solution_card(monkeypatch: pytest.MonkeyPatch) -> None:
service = object.__new__(ConversationService)
async def no_history(_: str) -> int:
return 0
monkeypatch.setattr(service, "_count_category_anomalies", no_history)
response_type = ConversationService._response_type(
"评估防晒霜新品,50g,软管包装",
{"intent": "assessment", "risk_requested": True, "category": "防晒霜"},
)
result = await service._use_solution_card_without_history(
response_type,
"评估防晒霜新品,50g,软管包装",
{"intent": "assessment", "risk_requested": True, "category": "防晒霜"},
)
assert response_type == "risk_assessment"
assert result == "solution"
@pytest.mark.asyncio
async def test_unknown_product_without_category_uses_solution_card() -> None:
service = object.__new__(ConversationService)
result = await service._use_solution_card_without_history(
"risk_assessment",
"评估这个新产品的风险",
{"intent": "assessment", "risk_requested": True},
)
assert result == "solution"