feat: localize logs, colorize streams, and fix draw pipeline params
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled
This commit is contained in:
@@ -14,7 +14,6 @@ from .config import (
|
||||
AUTO_QUOTE_WAIT_SECONDS,
|
||||
MESSAGE_DEBOUNCE_SECONDS,
|
||||
QINGJIAN_WS_URI,
|
||||
SHORT_REPLY_MAX_CHARS,
|
||||
)
|
||||
from .logger import setup_logger
|
||||
from .observability import activity_event, build_trace_id
|
||||
@@ -49,6 +48,48 @@ class QingjianClient:
|
||||
def _msg_text(data: dict) -> str:
|
||||
return str(data.get("msg", "") or "").strip()
|
||||
|
||||
@staticmethod
|
||||
def _extract_price_tokens(text: str) -> list[str]:
|
||||
s = str(text or "")
|
||||
if not s:
|
||||
return []
|
||||
out: list[str] = []
|
||||
out += re.findall(r"(?:¥|¥)\s*\d+(?:\.\d{1,2})?", s)
|
||||
out += re.findall(r"\d+(?:\.\d{1,2})?\s*元", s)
|
||||
# 去重保序
|
||||
seen = set()
|
||||
uniq: list[str] = []
|
||||
for x in out:
|
||||
k = x.strip()
|
||||
if k and k not in seen:
|
||||
seen.add(k)
|
||||
uniq.append(k)
|
||||
return uniq
|
||||
|
||||
@staticmethod
|
||||
def _route_cn(route: str) -> str:
|
||||
return {
|
||||
"pre_sales": "售前",
|
||||
"quote": "报价",
|
||||
"after_sales": "售后",
|
||||
"risk": "风控",
|
||||
}.get(str(route or ""), "未知")
|
||||
|
||||
@staticmethod
|
||||
def _status_text(route: str, action: str) -> str:
|
||||
a = str(action or "")
|
||||
if a == "quote":
|
||||
return "开始作图中"
|
||||
if a == "reply":
|
||||
return "已回复客户"
|
||||
if a == "transfer":
|
||||
return "已转人工"
|
||||
if a == "noop":
|
||||
return "仅监听中"
|
||||
if a == "update_state":
|
||||
return "状态已更新"
|
||||
return f"{QingjianClient._route_cn(route)}处理中"
|
||||
|
||||
def _append_dialogue(self, key: str, role: str, text: str) -> None:
|
||||
t = str(text or "").strip()
|
||||
if not t:
|
||||
@@ -148,21 +189,16 @@ class QingjianClient:
|
||||
return t
|
||||
|
||||
def _shorten_reply(self, text: str) -> str:
|
||||
max_len = max(8, int(SHORT_REPLY_MAX_CHARS))
|
||||
t = str(text or "").strip()
|
||||
t = self._humanize_reply(t)
|
||||
if len(t) <= max_len:
|
||||
return t
|
||||
# 优先按句号切,避免把一句话硬腰斩成“AI半句”
|
||||
# 只取首句,不做按字数硬截断,避免半句/残句
|
||||
parts = re.split(r"[。!?!?]", t)
|
||||
head = next((p.strip() for p in parts if p and p.strip()), "")
|
||||
if not head:
|
||||
# 无句号时按逗号切第一短分句
|
||||
# 无句号时按逗号切第一分句
|
||||
sub_parts = re.split(r"[,,;;::]", t)
|
||||
head = next((p.strip() for p in sub_parts if p and p.strip()), t)
|
||||
if len(head) > max_len:
|
||||
head = head[:max_len].rstrip(",,;;:: ")
|
||||
return head or t[:max_len]
|
||||
return head or t
|
||||
|
||||
@staticmethod
|
||||
def _humanize_reply(text: str) -> str:
|
||||
@@ -250,17 +286,27 @@ class QingjianClient:
|
||||
activity_event(self.logger, "agent_process_start", trace_id=trace_id, customer_id=context["customer_id"], acc_id=context["acc_id"], intent=context["intent"])
|
||||
route, decision, state = await self.orchestrator.decide(context)
|
||||
latency_ms = int((time.perf_counter() - t0) * 1000)
|
||||
status_text = self._status_text(route, decision.action)
|
||||
activity_event(
|
||||
self.logger,
|
||||
"agent_process_done",
|
||||
trace_id=trace_id,
|
||||
customer_id=context["customer_id"],
|
||||
route=route,
|
||||
route_cn=self._route_cn(route),
|
||||
action=decision.action,
|
||||
reason=decision.reason,
|
||||
reason=status_text,
|
||||
raw_reason=decision.reason,
|
||||
latency_ms=latency_ms,
|
||||
after_sales_stage=state.get("after_sales_stage", "new"),
|
||||
)
|
||||
# 价格日志(中文)
|
||||
price_hits = []
|
||||
price_hits.extend(self._extract_price_tokens(merged_msg))
|
||||
price_hits.extend(self._extract_price_tokens(decision.reply))
|
||||
price_hits.extend(self._extract_price_tokens(decision.reason))
|
||||
if price_hits:
|
||||
self.logger.info("[价格] 客户=%s 金额=%s", context["customer_id"], " | ".join(price_hits))
|
||||
|
||||
if decision.action == "transfer":
|
||||
text = (decision.transfer_msg or "").strip()
|
||||
@@ -281,6 +327,7 @@ class QingjianClient:
|
||||
customer_id=context["customer_id"],
|
||||
image_url=latest_image,
|
||||
)
|
||||
self.logger.info("[作图] 开始 customer=%s", context["customer_id"])
|
||||
draw_res = await auto_draw_preview(
|
||||
image_url=latest_image,
|
||||
customer_id=context["customer_id"],
|
||||
@@ -298,6 +345,7 @@ class QingjianClient:
|
||||
customer_id=context["customer_id"],
|
||||
preview_url=preview_url,
|
||||
)
|
||||
self.logger.info("[作图] 成功 customer=%s url=%s", context["customer_id"], preview_url)
|
||||
await post_tianwang_callback(
|
||||
"message_processed",
|
||||
data,
|
||||
@@ -311,6 +359,7 @@ class QingjianClient:
|
||||
customer_id=context["customer_id"],
|
||||
error=str(draw_res.get("error", "unknown")),
|
||||
)
|
||||
self.logger.error("[作图] 失败 customer=%s error=%s", context["customer_id"], draw_res.get("error", "unknown"))
|
||||
text = (decision.reply or "").strip()
|
||||
if self._is_invalid_ai_reply(text):
|
||||
text = self._fallback_reply("quote")
|
||||
|
||||
Reference in New Issue
Block a user