feat: complete customer service safety and handover flow

This commit is contained in:
张胜宇
2026-09-11 16:11:30 +08:00
parent 0059701509
commit ef098e6a4b
30 changed files with 1868 additions and 50 deletions
@@ -51,6 +51,48 @@ async def test_accept_is_idempotent_and_persists_outbox() -> None:
await session.commit()
@pytest.mark.asyncio
async def test_customer_service_accept_redacts_sensitive_message_before_persistence() -> None:
"""客服原始凭据只能存在于请求瞬间,数据库会话中必须是脱敏文本。"""
session_id = f"privacy-{uuid4()}"
key = f"privacy-key-{uuid4()}"
raw_message = "验证码 123456,银行卡 6222021234567890123,登录密码: Secret123"
context = RequestContext(
user_id="1", trace_id=str(uuid4()), roles=("customer",), permissions=("agent:run",)
)
request = AgentRequest(
agent_type="customer_service", message=raw_message, session_id=session_id,
idempotency_key=key,
)
run_id = ""
async with SessionFactory() as session:
try:
accepted = await AgentRunApplicationService(session).accept(request, context)
run_id = accepted.run_id
message = await session.scalar(select(ConversationMessage).where(
ConversationMessage.session_id == session_id,
ConversationMessage.role == "user",
))
assert message is not None
assert "123456" not in message.content
assert "6222021234567890123" not in message.content
assert "Secret123" not in message.content
assert "验证码" in message.content
assert "银行卡号已隐藏" in message.content
finally:
if run_id:
run = await session.scalar(select(AgentRun).where(AgentRun.run_id == run_id))
if run is not None:
await session.execute(delete(AgentRun).where(AgentRun.id == run.id))
await session.execute(delete(RequestIdempotency).where(
RequestIdempotency.id == run.idempotency_id
))
await session.execute(delete(ConversationMessage).where(
ConversationMessage.session_id == session_id
))
await session.commit()
@pytest.mark.asyncio
async def test_concurrent_same_key_creates_at_most_one_run() -> None:
session_id = f"concurrent-{uuid4()}"