133 lines
6.8 KiB
Python
133 lines
6.8 KiB
Python
"""User-bound scopes and result requirements, without model/platform calls."""
|
||
from copy import deepcopy
|
||
|
||
import pytest
|
||
|
||
from backend.prospecting.contracts import ProspectingBrief, TARGET_QUESTION
|
||
from backend.prospecting.dialogue import _ground_scope, reground_requirements
|
||
|
||
|
||
REQUEST = (
|
||
"我提供杭州装修服务,请在小红书找2位本人明确表达装修或设计服务需求的候选,优先查看评论和回复。"
|
||
"按人去重,整理需求原话、来源链接和主页入口;页面可见的小红书号能取得就记录,没有就说明。"
|
||
"最后导出名单,只做阅读和整理,不关注、不私信。遇到平台限制就停下来告诉我,保留已有成果。"
|
||
)
|
||
|
||
|
||
def ground(message, *, current=None, history=None, proposed=None):
|
||
return _ground_scope(proposed or ProspectingBrief(target_candidates=99,
|
||
required_result_fields=["csv", "xiaohongshu_id", "explicit_demand"]),
|
||
current or ProspectingBrief(), message, history or [])
|
||
|
||
|
||
def test_exact_real_request_keeps_count_and_explicit_demand_while_account_remains_optional():
|
||
actual = ground(REQUEST)
|
||
assert actual.target_candidates == 2 and TARGET_QUESTION not in actual.missing()
|
||
assert actual.required_result_fields == ["csv", "explicit_demand"]
|
||
assert actual.region is None # Merchant location is not a new geographic filter.
|
||
assert actual.max_notes is None and actual.max_comments_per_note is None
|
||
|
||
|
||
@pytest.mark.parametrize("message,count", [
|
||
("找3位正在准备装修的客户", 3),
|
||
("请寻找4名本人提出设计需求的潜在客户", 4),
|
||
("整理5个最近正在比较装修公司的真实用户", 5),
|
||
("收集6条已经明确表示要找设计服务的线索", 6),
|
||
("先找2位预算在10万元并且正在装修的客户", 2),
|
||
("找2位有需求的候选,改成3位", 3),
|
||
("先找3位客户,最后给我2位本人明确表达需求的候选", 2),
|
||
("例如找8位客户,实际先找2位有装修需求的候选", 2),
|
||
])
|
||
def test_quantity_binds_unit_and_described_person_object(message, count):
|
||
assert ground(message).target_candidates == count
|
||
|
||
|
||
@pytest.mark.parametrize("message", [
|
||
"预算300元,找本人有装修需求的客户",
|
||
"我做iPhone15配件,找本人明确需求的客户",
|
||
"目标是20人以上的公司,找有需求的客户",
|
||
"我们有2位同事,请找本人表达需求的候选",
|
||
"需要3位员工负责找客户,人数暂没定",
|
||
"找2个装修套餐并整理有需求的客户",
|
||
"找2位或3位有明确需求的候选",
|
||
"找2到3位有明确需求的候选",
|
||
"找2位有明确需求的候选到3位候选",
|
||
"不要帮我找2位有明确需求的候选,先讨论方案",
|
||
"比如找2位有明确需求的候选,实际数量还没决定",
|
||
"‘找2位本人明确表达需求的候选’是引用,帮我找客户",
|
||
"『找2位本人明确表达需求的候选』是示例,帮我找客户",
|
||
"`找2位本人明确表达需求的候选` 是代码示例,帮我找客户",
|
||
"```python\n找2位本人明确表达需求的候选\n```\n帮我找客户",
|
||
])
|
||
def test_unrelated_or_unconfirmed_quantities_cannot_be_used_as_target(message):
|
||
assert ground(message).target_candidates is None
|
||
|
||
|
||
@pytest.mark.parametrize("message", [
|
||
"页面可见的小红书号能取得就记录,没有就说明",
|
||
"小红书号有则填,无则空",
|
||
"取得小红书号就记录,获取不到则留空",
|
||
"小红书号最好取得,但拿不到也不影响交付",
|
||
"整理小红书号,找不到就标注未知",
|
||
"账号可选,名单继续保留",
|
||
"小红书号不是必须,没有也可以",
|
||
])
|
||
def test_conditional_account_recording_is_optional_and_removes_old_compiled_requirement(message):
|
||
current = ProspectingBrief(target_candidates=1, required_result_fields=["xiaohongshu_id"])
|
||
result = ground(message, current=current)
|
||
assert "xiaohongshu_id" not in result.required_result_fields
|
||
assert result.target_candidates == 1
|
||
|
||
|
||
@pytest.mark.parametrize("message", [
|
||
"必须取得小红书号,没有小红书号的人不计入",
|
||
"整理2个页面可见小红书号",
|
||
"需要提供正确归属的小红书号",
|
||
"有小红书号才算候选",
|
||
"只有取得小红书号才计入名单",
|
||
"必须有小红书号,拿不到也要继续找",
|
||
"必须有小红书号,没有就说明缺失",
|
||
"必须取得小红书号后再记录",
|
||
])
|
||
def test_explicit_account_requirement_is_not_weakened(message):
|
||
assert "xiaohongshu_id" in ground(message).required_result_fields
|
||
|
||
|
||
@pytest.mark.parametrize("message,fields", [
|
||
("找2位本人明确表达装修或设计服务需求的候选", {"explicit_demand"}),
|
||
("找2位候选,要求本人明确表达装修或设计服务需求", {"explicit_demand"}),
|
||
("目标客户:本人明确表达装修或设计服务需求的用户", {"explicit_demand"}),
|
||
("优先找有明确需求的人", set()),
|
||
("不一定有明确需求,相关信号也保留", set()),
|
||
("告诉我用户有没有明确需求", set()),
|
||
("小红书号可选,但必须导出CSV", {"csv"}),
|
||
("CSV可选,但必须提供小红书号", {"xiaohongshu_id"}),
|
||
])
|
||
def test_requirement_subject_and_optional_mode_stay_with_their_own_field(message, fields):
|
||
assert set(ground(message).required_result_fields) == fields
|
||
|
||
|
||
def test_revisions_follow_conversation_order_and_assistant_text_cannot_add_requirements():
|
||
history = [{"role": "user", "text": "必须有小红书号,只接受本人明确需求"},
|
||
{"role": "assistant", "text": "必须导出CSV"},
|
||
{"role": "user", "text": "小红书号能取得就记录,没有就说明"}]
|
||
result = ground("明确需求也不作为必要条件", history=history)
|
||
assert result.required_result_fields == []
|
||
revised = ground("这次必须提供小红书号", history=history)
|
||
assert set(revised.required_result_fields) == {"explicit_demand", "xiaohongshu_id"}
|
||
|
||
|
||
def test_reground_on_continue_repairs_fields_without_overriding_confirmed_quantity_card():
|
||
current = ProspectingBrief(target_candidates=1, required_result_fields=["csv", "xiaohongshu_id"],
|
||
product="杭州装修服务", user_notes=REQUEST)
|
||
history = [{"role": "user", "text": REQUEST},
|
||
{"role": "assistant", "text": TARGET_QUESTION},
|
||
{"role": "user", "text": "补充当前任务的信息:\n这次先整理多少位候选?:1"}]
|
||
before, saved_history = current.model_dump(), deepcopy(history)
|
||
result = reground_requirements(current, history)
|
||
assert result.target_candidates == 1
|
||
assert result.required_result_fields == ["csv", "explicit_demand"]
|
||
assert result.product == current.product and result.user_notes == REQUEST
|
||
assert current.model_dump() == before and history == saved_history
|
||
assert reground_requirements(result, history) == result
|