diff --git a/app/service/agent/implementations/customer_service.py b/app/service/agent/implementations/customer_service.py index fc7dfa0..3bb641f 100644 --- a/app/service/agent/implementations/customer_service.py +++ b/app/service/agent/implementations/customer_service.py @@ -402,6 +402,13 @@ class CustomerServiceAgent(BaseAgent): """ first = next((line.strip() for line in answer.splitlines() if line.strip()), "") first = first.lstrip("#").strip() + # 适当性回答的首行是「{产品名}为 R2(中低风险),与您当前的风险测评结果不匹配…」, + # 产品名在"为 R"之前。这一条必须在冒号判断**之前**:那一行里没有冒号。 + for level in range(1, 6): + index = first.find(f"为 R{level}") + if index >= 0: + candidate = first[:index].strip() + return candidate if 0 < len(candidate) <= 20 else "" topic = first.split(":", 1)[0].strip() if ":" in first else first # FAQ 型回答的首行是"问:C1 客户能买什么产品?",冒号前只有一个"问"字; # 拿它当主语只会把检索词污染成"问 那它风险高吗"。 diff --git a/tests/unit/service/test_customer_service_search_query.py b/tests/unit/service/test_customer_service_search_query.py index 745ec71..f9b307c 100644 --- a/tests/unit/service/test_customer_service_search_query.py +++ b/tests/unit/service/test_customer_service_search_query.py @@ -51,6 +51,23 @@ def test_fallback_answer_yields_no_topic() -> None: assert CustomerServiceAgent._topic_of("") == "" +def test_topic_extracted_from_suitability_answer() -> None: + """适当性回答的首行是「{产品名}为 R2(中低风险),…」,产品名在"为 R"之前。 + + 这一行既没有冒号也不是标题行。漏掉这种格式的后果是实测到的:客户问 + 「那我能买它吗」时取不出主语,整句被当成没有指代对象,直接转人工。 + """ + assert CustomerServiceAgent._topic_of( + "南方季季盈90天为 R2(中低风险),与您当前的风险测评结果不匹配," + "按照投资者适当性管理规定暂时无法购买。" + ) == "南方季季盈90天" + + +def test_topic_from_suitability_answer_requires_a_name_before_the_marker() -> None: + """以"为 R2"开头说明前面没有产品名,不能把空串或等级本身当成主语。""" + assert CustomerServiceAgent._topic_of("为 R2(中低风险),与您的测评结果不匹配。") == "" + + def test_faq_style_answer_yields_no_topic() -> None: """FAQ 型回答首行是"问:…",冒号前只有一个"问"字,不能当主语。""" assert CustomerServiceAgent._topic_of(