- Added `status` query parameter to `list_sessions_api` for filtering sessions by their status (active/closed). - Introduced `close_all_sessions_api` endpoint to allow users to close all active sessions for the current actor. - Updated `SessionRepository` to support status filtering in session listing and implemented logic for closing active sessions. - Improved Redis connection settings for better performance and reliability. This update enhances the chat functionality by providing more control over session management, improving user experience and system efficiency.
94 lines
3.2 KiB
Python
94 lines
3.2 KiB
Python
"""游客 FAQ:开户类问题意图与 Redis/LLM 降级。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import app.service.visitor_service as vs
|
|
from app.model.schemas import AuthContext
|
|
from app.service.visitor_service import (
|
|
VisitorState,
|
|
generate,
|
|
intent_classify,
|
|
run_visitor_chat,
|
|
)
|
|
|
|
|
|
def _base_state(message: str) -> VisitorState:
|
|
return {
|
|
"session_id": "sess-faq",
|
|
"trace_id": "tr-faq",
|
|
"message": message,
|
|
"intent": "",
|
|
"chitchat_memory": [],
|
|
"consult_memory": [],
|
|
"rag_context": "",
|
|
"rag_sources": [],
|
|
"reply": "",
|
|
"has_disclaimer": False,
|
|
"transfer_to_human": False,
|
|
}
|
|
|
|
|
|
def test_open_account_intent_uses_faq_keyword_without_llm(monkeypatch):
|
|
"""LLM 不可用时,开户类问题仍应走 faq 分支(不依赖 DeepSeek 分类)。"""
|
|
monkeypatch.setattr(vs, "_build_llm", lambda: (_ for _ in ()).throw(RuntimeError("no llm")))
|
|
|
|
out = intent_classify(_base_state("开户需要哪些资料"))
|
|
assert out["intent"] == "faq"
|
|
|
|
|
|
def test_generate_uses_rag_when_llm_fails(monkeypatch):
|
|
"""有 RAG 上下文但 LLM 失败时,用知识片段降级回复,而非整段 fallback。"""
|
|
monkeypatch.setattr(vs, "_build_llm", lambda: (_ for _ in ()).throw(RuntimeError("no llm")))
|
|
monkeypatch.setattr(
|
|
vs.VisitorMemoryService,
|
|
"as_prompt_text",
|
|
lambda self, sid, kind: (_ for _ in ()).throw(ConnectionError("redis down")),
|
|
)
|
|
|
|
state = _base_state("开户需要哪些资料")
|
|
state["intent"] = "faq"
|
|
state["rag_context"] = (
|
|
"[来源: faq-open-account | 片段 1]\n"
|
|
"开户需要有效身份证件、本人银行卡,并完成实名认证与风险测评问卷。"
|
|
)
|
|
|
|
out = generate(state)
|
|
assert "身份证" in out["reply"]
|
|
assert "银行卡" in out["reply"]
|
|
assert "暂时无法回答" not in out["reply"]
|
|
|
|
|
|
def test_run_visitor_open_account_with_mocks(monkeypatch):
|
|
"""端到端:mock RAG + 降级生成,应回答开户材料要点。"""
|
|
class _FakeRag:
|
|
def retrieve(self, intent, query, top_k=5):
|
|
return (
|
|
"[来源: faq-open-account | 片段 1]\n"
|
|
"开户需要有效身份证件、本人银行卡,并完成实名认证与风险测评问卷。",
|
|
[{"source_doc": "faq-open-account"}],
|
|
)
|
|
|
|
monkeypatch.setattr(vs, "VisitorRagService", _FakeRag)
|
|
monkeypatch.setattr(vs, "_build_llm", lambda: (_ for _ in ()).throw(RuntimeError("no llm")))
|
|
monkeypatch.setattr(vs.VisitorMemoryService, "recall", lambda self, sid, kind: [])
|
|
monkeypatch.setattr(
|
|
vs.VisitorMemoryService,
|
|
"as_prompt_text",
|
|
lambda self, sid, kind: "",
|
|
)
|
|
monkeypatch.setattr(vs.VisitorMemoryService, "append", lambda self, *a, **k: None)
|
|
|
|
ctx = AuthContext(
|
|
sub="visitor",
|
|
token_type="visitor",
|
|
roles=[],
|
|
actor_id="visitor",
|
|
agent_type="visitor",
|
|
trace_id="t",
|
|
jti="jti-faq",
|
|
)
|
|
reply, _, intent, transfer = run_visitor_chat(ctx, "开户需要哪些资料", "e2e-faq")
|
|
assert intent == "faq"
|
|
assert not transfer
|
|
assert "身份证" in reply
|