| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- """qna 引擎单测:用 fake LLM 返回固定 JSON,验证图节点输出"""
- import json
- import sys
- from pathlib import Path
- sys.path.insert(0, str(Path(__file__).resolve().parents[2])) # cfc-langgraph 根
- from src.qna import schemas, graph, prompts # noqa: E402
- class FakeLLM:
- """返回固定决策 JSON 的假 LLM"""
- def __init__(self, decisions):
- self.decisions = list(decisions)
- self.calls = []
- def invoke(self, messages):
- self.calls.append(messages)
- d = self.decisions.pop(0) if len(self.decisions) > 1 else self.decisions[0]
- return type("R", (), {"content": json.dumps(d, ensure_ascii=False)})()
- def make_scene(**kw):
- base = {
- "scene_key": "microbiome",
- "opening_prompt": "了解您的肠道健康状况",
- "dimensions_json": {"user": ["肠道状态"], "need": ["营养需求"]},
- "kb_scope": ["microbiome"],
- "max_questions": 12,
- }
- base.update(kw)
- return base
- def test_decide_next_ask():
- llm = FakeLLM([{"action": "ask", "question": {
- "id": "q1", "type": "single", "text": "您多久吃一次蔬菜?",
- "options": [{"id": "a", "label": "每天"}]}, "reason": "了解饮食"}])
- state = graph.decide_next(llm, make_scene(), [], prompt_fn=prompts.build_decide_prompt)
- assert state["action"] == "ask"
- assert state["question"]["text"].startswith("您多久")
- def test_decide_next_force_finish_when_max_reached():
- llm = FakeLLM([{"action": "ask", "question": {"id": "q9", "type": "text", "text": "x"}}])
- history = [{"question": {"text": f"q{i}"}, "answer": "a"} for i in range(12)]
- state = graph.decide_next(llm, make_scene(max_questions=12), history, prompt_fn=prompts.build_decide_prompt)
- assert state["action"] == "finish"
- def test_decide_next_invalid_json_retries_once():
- llm = FakeLLM(["not json", {"action": "finish", "reason": "信息足够"}])
- state = graph.decide_next(llm, make_scene(), [], prompt_fn=prompts.build_decide_prompt)
- assert state["action"] == "finish"
- assert len(llm.calls) == 2 # 重试了一次
- def test_generate_profile_structure():
- llm = FakeLLM([{"user_profile": [{"dimension": "肠道状态", "score": 70, "description": "偏健康",
- "evidence": ["答1"]}],
- "need_profile": [{"dimension": "营养需求", "description": "补纤维",
- "evidence": ["答1"], "suggestion": "多吃粗粮"}]}])
- history = [{"question": {"text": "q1"}, "answer": "a"}]
- profile, kb_used = graph.generate_profile(llm, make_scene(), history, kb_context=[{"content": "菌属知识"}],
- prompt_fn=prompts.build_profile_prompt)
- assert "user_profile" in profile and "need_profile" in profile
- assert profile["user_profile"][0]["dimension"] == "肠道状态"
- assert 0 <= profile["user_profile"][0]["score"] <= 100
- assert kb_used is True
|