Files
XingHuo/tests/test_agent_service.py
T

93 lines
3.2 KiB
Python

"""T-07 agent_service StateGraph 骨架(mock LLM,不依赖外网/真 key)。
FakeLLM 捕获收到的 messages 供断言(system 边界 + 历史窗口 + 本轮消息);
降级路径(无 key)与免责声明护栏单独覆盖。图/LLM 缓存经 reset_cache 隔离。
"""
from __future__ import annotations
import pytest
from langchain_core.messages import AIMessage
from app.config.settings import settings
from app.service import agent_service
class FakeLLM:
def __init__(self, reply: str = "模拟回复"):
self.reply = reply
self.calls: list[list] = []
def invoke(self, messages):
self.calls.append(list(messages))
return AIMessage(content=self.reply)
@pytest.fixture()
def fake_llm(monkeypatch):
llm = FakeLLM()
monkeypatch.setattr(agent_service, "_llm", llm)
monkeypatch.setattr(settings, "deepseek_api_key", "test-key")
yield llm
agent_service.reset_cache()
HISTORY = [
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "您好,有什么可以帮您?"},
]
def test_chat_customer_appends_disclaimer(fake_llm):
out = agent_service.chat("customer", HISTORY, "查一下我的持仓")
assert out["has_disclaimer"] is True
assert out["reply"].endswith(agent_service.CHAT_DISCLAIMER)
assert "模拟回复" in out["reply"]
def test_chat_risk_also_disclaimer(fake_llm):
out = agent_service.chat("risk", [], "今天有多少待审预警")
assert out["has_disclaimer"] is True
def test_chat_advisor_no_disclaimer(fake_llm):
out = agent_service.chat("advisor", [], "总结客户需求")
assert out["has_disclaimer"] is False
assert agent_service.CHAT_DISCLAIMER not in out["reply"]
def test_messages_compose_system_history_and_user(fake_llm):
agent_service.chat("customer", HISTORY, "最新问题")
msgs = fake_llm.calls[0]
assert msgs[0].__class__.__name__ == "SystemMessage"
assert "不得提供投资建议" in msgs[0].content # customer 边界注入
assert [m.__class__.__name__ for m in msgs[1:]] == ["HumanMessage", "AIMessage", "HumanMessage"]
assert msgs[-1].content == "最新问题"
def test_role_boundary_per_agent_type(fake_llm):
agent_service.chat("analyst", [], "聚合一下")
system = fake_llm.calls[0][0]
assert "只读" in system.content
def test_degraded_reply_without_api_key(monkeypatch):
"""key 缺失 → 降级固定提示(不抛异常,演示链路不断)。"""
monkeypatch.setattr(settings, "deepseek_api_key", "")
agent_service.reset_cache()
out = agent_service.chat("customer", [], "在吗")
assert "LLM 未配置" in out["reply"] and "在吗" in out["reply"]
assert out["has_disclaimer"] is True # 降级回复同样经 guard 护栏
def test_graph_skeleton_nodes():
graph = agent_service.build_graph()
assert {"llm", "guard"} <= set(graph.nodes) # __start__ 为 LangGraph 内部节点
def test_unknown_agent_type_falls_back_customer_boundary(fake_llm):
"""未知 agent_type 兜底 customer 边界(fail-safe:最严声明口径)。"""
out = agent_service.chat("unknown", [], "hi")
assert out["has_disclaimer"] is True
assert "不得提供投资建议" in fake_llm.calls[0][0].content