generated from kgod/ai-review-template
自内部仓库剥离深度优化与 RAG 知识库后的交付版本: - Builder 对话式简历生成(FSM + 意图路由 LLM 兜底增强) - 条目级轻度优化:事实覆盖门禁 + STAR/bullet 修复链,功能/简介/成果与技术栈同级保护 - 简历导入:DOCX/PDF 解析、结构归一、手机号脱敏 - PostgreSQL 运行时 + Alembic 迁移链 Co-Authored-By: Claude <noreply@anthropic.com>
109 lines
4.5 KiB
Python
109 lines
4.5 KiB
Python
"""Detail-path guards: skip intents never pollute drafts; LLM gate routes before fact-merge."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
from typing import Any
|
|
|
|
from app.builder_conversation.candidate import _candidate_rewrite
|
|
from app.builder_conversation.predicates import _is_no_information_reply
|
|
from app.builder_conversation.rescue import llm_detail_route
|
|
from app.chat_intents import ChatTurnClassification
|
|
from builder_flow_helpers import create_builder_session, send_message, start_education
|
|
from test_api import active_component
|
|
|
|
|
|
class _StubClassifier:
|
|
def __init__(self, result: ChatTurnClassification) -> None:
|
|
self.result = result
|
|
|
|
def classify(self, message: str, *, state_summary: dict[str, Any]) -> ChatTurnClassification:
|
|
return self.result
|
|
|
|
|
|
def _agent(classifier: Any) -> Any:
|
|
return SimpleNamespace(
|
|
expander=SimpleNamespace(expand=lambda entry, *, context: {}),
|
|
_chat_intent_classifier=classifier,
|
|
)
|
|
|
|
|
|
def _profile_with_draft() -> dict[str, Any]:
|
|
return {
|
|
"job_type": "campus",
|
|
"builder": {
|
|
"active_section": "education",
|
|
"identity_draft": {"school": "X 大学", "description": "完成数据库课程项目。"},
|
|
"gap_state": {"asked": ["academic_result"], "skipped": [], "rounds": 1},
|
|
},
|
|
}
|
|
|
|
|
|
def test_skip_words_count_as_no_information() -> None:
|
|
for word in ("跳过", "先跳过", "跳过吧", "不用了", "不需要", "以后再说", "没有了"):
|
|
assert _is_no_information_reply(word), word
|
|
|
|
|
|
def test_skip_reply_does_not_pollute_description(client: Any) -> None:
|
|
session_id, body = create_builder_session(client)
|
|
start_education(client, session_id, body)
|
|
send_message(client, session_id, "完成数据库课程项目。") # triggers the gap prompt
|
|
reply = send_message(client, session_id, "跳过")
|
|
card = active_component(reply)["data"]
|
|
assert card["component"] == "experience_confirm_card"
|
|
assert "跳过" not in str(card["value"].get("description") or "")
|
|
|
|
|
|
def test_detail_gate_no_info_skips_gap_without_merging() -> None:
|
|
agent = _agent(_StubClassifier(ChatTurnClassification(intent="no_info", confidence=0.9)))
|
|
transition = llm_detail_route(agent, _profile_with_draft(), "先跳过这个")
|
|
|
|
assert transition is not None
|
|
state = transition.profile["builder"]
|
|
assert "先跳过这个" not in state["identity_draft"]["description"]
|
|
assert "academic_result" in state["gap_state"]["skipped"]
|
|
|
|
|
|
def test_detail_gate_revise_regenerates_candidate() -> None:
|
|
agent = _agent(_StubClassifier(ChatTurnClassification(
|
|
intent="revise_proposal", confidence=0.9, revision_instruction="再简洁一点",
|
|
)))
|
|
transition = llm_detail_route(agent, _profile_with_draft(), "帮我再精简下")
|
|
|
|
assert transition is not None
|
|
assert transition.profile["builder"]["pending_entry"]["_proposal"] is not None
|
|
assert "帮我再精简下" not in transition.profile["builder"]["identity_draft"]["description"]
|
|
|
|
|
|
def test_detail_gate_chitchat_does_not_merge() -> None:
|
|
agent = _agent(_StubClassifier(ChatTurnClassification(intent="chitchat", confidence=0.9)))
|
|
transition = llm_detail_route(agent, _profile_with_draft(), "好的谢谢")
|
|
|
|
assert transition is not None
|
|
assert transition.profile["builder"]["identity_draft"]["description"] == "完成数据库课程项目。"
|
|
|
|
|
|
def test_detail_gate_passes_facts_and_low_confidence_through() -> None:
|
|
facts = _agent(_StubClassifier(ChatTurnClassification(intent="provide_facts", confidence=0.9)))
|
|
assert llm_detail_route(facts, _profile_with_draft(), "GPA 4.3") is None
|
|
shaky = _agent(_StubClassifier(ChatTurnClassification(intent="no_info", confidence=0.4)))
|
|
assert llm_detail_route(shaky, _profile_with_draft(), "跳过") is None
|
|
|
|
|
|
def test_candidate_rewrite_ensure_facts_appends_missing() -> None:
|
|
class _Expander:
|
|
def expand(self, entry: dict[str, Any], *, context: dict[str, Any]) -> dict[str, Any]:
|
|
return {"optimized_description": "主修课程:数据结构、计算机视觉。", "source": "test"}
|
|
|
|
agent = SimpleNamespace(expander=_Expander())
|
|
proposal = _candidate_rewrite(
|
|
agent,
|
|
{"job_type": "campus"},
|
|
{"description": "学习数据结构、计算机视觉课程。GPA: 4.3/5.0,排名前百分之10。"},
|
|
"education",
|
|
ensure_facts=True,
|
|
)
|
|
assert "GPA: 4.3/5.0" in proposal["optimized_description"]
|
|
assert "排名前百分之10" in proposal["optimized_description"]
|
|
assert proposal["uncovered_facts"] == []
|