"""Anonymous customer-service orchestration without private customer access.""" from __future__ import annotations import json import re from inspect import isawaitable from rag.intent import Intent class QueryTooLongError(ValueError): pass async def _config(config_getter, key: str, default: str): value = config_getter(key, default) if isawaitable(value): value = await value return value or default async def _maybe_await(value): return await value if isawaitable(value) else value class AnonymousCustomerAgent: def __init__( self, *, context, rag_retrieve, intent_recognize, generate_answer, audit_writer, config_getter, ): self.context = context self.rag_retrieve = rag_retrieve self.intent_recognize = intent_recognize self.generate_answer = generate_answer self.audit_writer = audit_writer self.config_getter = config_getter async def handle(self, session_id: str, query: str, *, trace_id: str) -> dict: if len(query) > 2000: raise QueryTooLongError("query长度不能超过2000字符") await self.context.append(session_id, "user", query) if self._contains_sensitive_input(query): await _maybe_await(self.audit_writer( action="anon_sensitive_input", trace_id=trace_id, session_id=session_id, )) intent = await _maybe_await(self.intent_recognize(query)) sources = [] if intent is Intent.GUIDE_PURCHASE: answer = await _config( self.config_getter, "agent.customer.template.guide_purchase", "请前往开户页面办理。", ) elif intent is Intent.WANT_ADVISOR: answer = await _config( self.config_getter, "agent.customer.template.guide_advisor", "如需基金推荐,请联系投资顾问。", ) elif intent is Intent.KNOWLEDGE_QA: try: sources = await _maybe_await(self.rag_retrieve(query, None)) except Exception: sources = [] if not sources: answer = await _config( self.config_getter, "agent.customer.template.fallback_human", "当前未找到匹配信息,请转人工客服。", ) else: messages = await self.context.get(session_id) prompt = messages + [ { "role": "system", "content": "仅根据提供的知识来源回答,不得编造基金推荐。", }, { "role": "system", "content": f"知识来源:{json.dumps(sources, ensure_ascii=False)}", }, ] try: answer = await _maybe_await(self.generate_answer(prompt)) except Exception: answer = await _config( self.config_getter, "agent.customer.template.fallback_human", "当前服务繁忙,请转人工客服。", ) else: answer = await _config( self.config_getter, "agent.customer.template.fallback_human", "当前未找到匹配信息,请转人工客服。", ) await self.context.append(session_id, "assistant", answer) return { "answer": answer, "sources": sources, "intent": intent.value, "trace_id": trace_id, } @staticmethod def _contains_sensitive_input(query: str) -> bool: return bool( re.search(r"(?