This commit is contained in:
2026-03-06 14:25:10 +08:00
parent afb2b78c15
commit f06bfb1fa0
6 changed files with 289 additions and 145 deletions

171
README.md
View File

@@ -1,46 +1,70 @@
# AI 客服系统 - 天网协作版
# AI 客服系统
**版本**: v1.0 | **服务器**: 1.12.50.92
基于 PydanticAI 的智能客服,对接千牛 WebSocket自动接待、收图、转接设计师。
---
## 功能概览
## 架构概览
```
客户消息 (千牛)
WebSocket Client → QianniuAdapter (协议转换)
Orchestrator (防抖/去重/冷却/路由)
CustomerServiceBrain (PydanticAI Agent)
├── lookup_chat_history_tool → 查询历史记录
├── transfer_to_human_tool → 转接设计师
└── check_order_status_tool → 订单查询
QianniuAdapter → WebSocket → 回复客户
```
---
## 核心功能
| 功能 | 说明 |
|------|------|
| 天网协作 | 接收天网任务,支持指定客户回复触发 |
| 三种工作流 | 找图 / 处理图片 / 转人工派单 |
| 图片任务数据库 | 任务持久化,支持后续增加需求 |
| 图绘派单系统 | 自动派单给在线设计师 |
| 文字检测加价 | 自动识别文字数量并加价 |
| 风险评估 | 自动识别敏感内容,拒绝不良订单 |
| 作图失败转人工 | 失败自动转接人工客服 |
| 智能接待 | 自动引导发图、问需求、转接设计师 |
| 历史记忆 | AI 可调用工具查询完整聊天历史,避免重复提问 |
| 自动转接 | 收到图片+需求后自动派单给在线设计师 |
| 转接冷却 | 转接后 120 秒内不再调用 AI直接安抚 |
| 情绪识别 | 客户愤怒/投诉时自动转人工 |
| 消息防抖 | 合并短时间内的多条消息,避免重复回复 |
| 订单静默 | 订单通知/SKU 信息自动入库,不触发 AI |
| 时段感知 | 根据时间区分"没上班"/"下班了"/"暂时不在" |
| 图片分析 | 后台调用 Gemini 分析图片复杂度 |
| 日报统计 | 每日自动生成客服数据报告 |
| 多进程 | 支持多 Worker 并行处理 |
---
## 快速开始
```bash
cd /root/ai_customer_service/ai_cs
pip3 install -r requirements.txt
# 安装依赖
pip install -r requirements.txt
# 天网协作版(仅 HTTP API
python3 run.py --api-only
# 配置环境变量
cp .env.example .env
# 编辑 .env 填入 API Key、数据库等配置
# 完整版HTTP API + WebSocket + AI Agent
python3 run.py --tianwang
# 启动WebSocket 客服模式
python run.py
# AI 客服(仅 WebSocket,默认
python3 run.py
# 完整版HTTP API + WebSocket
python run.py --tianwang
# 多进程模式
python run.py --multi -w 4
# 仅 HTTP API
python run.py --api-only
```
### 后台运行
```bash
nohup python3 run.py --api-only > /tmp/tianwang.log 2>&1 &
```
### 验证
### 健康检查
```bash
curl http://localhost:6060/api/health
@@ -48,44 +72,75 @@ curl http://localhost:6060/api/health
---
## API 地址
| 服务 | 地址 |
|------|------|
| AI 客服 API | `http://127.0.0.1:6060` |
| 派单系统 | `http://1.12.50.92:8005` |
| 图绘平台 | `http://1.12.50.92:8002` |
---
## 文档
| 文档 | 内容 |
|------|------|
| **项目功能汇总.md** | 全部功能详细说明(工作流、报价、风险、派单、数据库等) |
| **部署文档.md** | 部署、API 接口、天网集成、多进程、故障排查 |
| **features/self_evolution_mvp.md** | 自我进化 MVP采样、评测、建议、灰度门禁 |
---
## 项目结构
```
├── api/ # HTTP API 服务器
├── core/ # 核心逻辑Agent、工作流、WebSocket
├── run.py # 统一入口
├── api/
│ └── http_server.py # HTTP API 服务
├── core/
│ ├── orchestrator.py # 总编排(防抖/去重/冷却/路由)
│ ├── pydantic_ai_agent_v2.py # AI 大脑PydanticAI Agent
│ ├── agent_tools.py # AI 工具(转接/查历史/查订单)
│ ├── schema.py # 数据模型StandardMessage/Response
│ ├── repository.py # 异步数据仓库
│ ├── skill_manager.py # 技能加载器
│ ├── engine.py # 业务逻辑兜底
│ ├── adapters/
│ │ └── qianniu_adapter.py # 千牛协议适配
│ ├── events/
│ │ └── event_bus.py # 异步事件总线
│ └── websocket_*.py # WebSocket 连接/发送/日志
├── db/
│ ├── chat_log_db.py # 聊天记录SQLite/MySQL
│ ├── customer_db.py # 客户档案
│ ├── image_tasks_db.py # 图片任务
│ └── task_db/ # 任务模型
├── services/
│ ├── dispatch_service.py # 设计师派单
│ ├── service_gemini.py # Gemini 图片分析
│ ├── service_image_analyzer.py # 图片复杂度分析
│ └── ... # 其他服务
├── skills/ # AI 技能定义SKILL.md
│ ├── customer-service/ # 客服核心技能
│ ├── owner-style/ # 店主风格
│ ├── pre-sales-skill/ # 售前
│ ├── after-sales-skill/ # 售后
│ ├── pricing-skill/ # 报价(排除出 prompt
│ ├── risk-skill/ # 风控
│ └── style-skill/ # 语气风格
├── config/ # 配置文件
├── db/ # 数据库模块
── image/ # 图片处理模块
├── services/ # 外部服务集成
├── utils/ # 工具模块
├── skills/ # Agent 技能定义
└── run.py # 统一入口(--api-only / --tianwang / 默认 WebSocket
├── utils/ # 工具(日报/健康检查/API计费等
── scripts/ # 运维脚本
```
## 自我进化 MVP
---
```bash
python scripts/evolution_cycle.py --hours 24 --publish
```
## 环境变量
默认从线上 MySQL 读取对话数据(可用 `--source` 切换)。
| 变量 | 说明 |
|------|------|
| `OPENAI_API_KEY` | 火山引擎 Ark API Key |
| `OPENAI_BASE_URL` | API 地址 |
| `OPENAI_MODEL` | 对话模型 |
| `DB_TYPE` | 数据库类型(`sqlite` / `mysql` |
| `MYSQL_HOST/PORT/USER/PASSWORD/DATABASE` | MySQL 连接信息 |
| `WECHAT_WEBHOOK` | 企业微信通知 Webhook |
| `MESSAGE_DEBOUNCE_SECONDS` | 消息防抖时间(秒) |
| `DISPATCH_BASE_URL` | 派单服务地址 |
完整配置见 `.env.example`
---
## 消息处理流程
1. **WebSocket 接收** → 千牛原始消息
2. **适配器转换**`StandardMessage`(统一格式)
3. **Orchestrator 过滤** → 订单/SKU 静默入库、心跳过滤、商家回复入库
4. **防抖合并** → 2 秒窗口内多条消息合并为一条
5. **冷却检查** → 转接后 120 秒内直接安抚,不调 AI
6. **AI 思考** → PydanticAI Agent 调用工具、生成回复
7. **转接截获** → 工具返回转接指令时直接发送,不经 AI 二次加工
8. **乱码清理** → 过滤 `<think>`、内部标记等泄露内容
9. **发送回复** → 通过 WebSocket 回复客户,同时入库

View File

@@ -1,13 +1,16 @@
import logging
import asyncio
from datetime import datetime
from typing import List, Optional, Dict, Any
from pydantic import BaseModel, Field
from pydantic_ai import RunContext
from core.schema import StandardResponse
from services.dispatch_service import dispatch_service
from db.chat_log_db import get_conversation
logger = logging.getLogger("cs_agent")
async def transfer_to_human_tool(ctx: RunContext[Any], reason: str = Field(description="转人工的原因")) -> str:
"""
【核心工具】执行转人工逻辑。
@@ -15,25 +18,80 @@ async def transfer_to_human_tool(ctx: RunContext[Any], reason: str = Field(descr
"""
logger.info(f"[Tool] 尝试呼叫设计师接手: {reason}")
# 1. 尝试派单获取设计师姓名
designer_name = await dispatch_service.assign_designer()
if designer_name:
# 2. 有设计师在线:生成标准转接指令 (必须包含 [转移会话] 且格式正确)
magic_cmd = f"正在为您转接|[转移会话],{designer_name},无原因"
logger.info(f"[Tool] 成功呼叫设计师: {designer_name}")
return magic_cmd
else:
# 3. 设计师下线:返回特定信号
logger.warning("[Tool] 派单失败:设计师们已下线或不在位")
return "ERROR_NO_DESIGNER_ONLINE"
hour = datetime.now().hour
logger.warning(f"[Tool] 派单失败:设计师们不在位 (当前{hour}点)")
if 0 <= hour < 9:
return "ERROR_DESIGNER_NOT_STARTED现在设计师还没上班你告诉客户需求记下了上班后第一时间处理。不要说下班。"
elif 22 <= hour or hour < 1:
return "ERROR_DESIGNER_OFFLINE设计师已下班你告诉客户需求记下了明天第一时间回复。"
else:
return "ERROR_DESIGNER_BUSY设计师暂时不在位你告诉客户稍等马上帮忙联系设计师。不要说下班。"
async def check_order_status_tool(ctx: RunContext[Any], customer_id: str = Field(description="客户ID")) -> str:
"""查询订单状态。"""
return "设计师正在后台加急处理中,稍等哈。"
return "我在帮你加急处理中,稍等哈。"
async def lookup_chat_history_tool(
ctx: RunContext[Any],
customer_id: str = Field(description="客户ID从当前对话上下文中获取"),
num_messages: int = Field(default=30, description="要查询的历史消息条数默认30条"),
) -> str:
"""
【历史记录查询工具】查询该客户的历史聊天记录。
使用场景:
- 客户说"之前聊过""上次""你看聊天记录""我发过图了"等暗示有历史对话时
- 客户第二次来访、追问进度、催单时
- 你不确定客户之前是否发过图或说过需求时
必须先调用此工具回顾历史,再回复客户,避免重复要求客户发图。
"""
logger.info(f"[Tool] 查询历史记录: customer_id={customer_id}, limit={num_messages}")
try:
rows = await asyncio.to_thread(get_conversation, customer_id, limit=num_messages)
if not rows:
return f"该客户({customer_id})暂无历史聊天记录。"
lines = []
has_images = False
customer_needs = []
for r in rows:
role = "客户" if r["direction"] == "in" else "客服"
ts = str(r.get("timestamp", ""))
msg = r.get("message", "")
line = f"[{ts}] {role}{msg}"
lines.append(line)
if r["direction"] == "in":
if "已收到" in msg and "" in msg:
has_images = True
if any(k in msg for k in ["找原图", "修复", "高清", "去背景", "抠图", "做衣服", "打印"]):
customer_needs.append(msg[:60])
summary_parts = [f"{len(rows)}条历史消息。"]
if has_images:
summary_parts.append("⚠️ 客户之前已经发过图片!不要再让客户发图!")
if customer_needs:
summary_parts.append(f"客户曾表达的需求:{''.join(customer_needs[:3])}")
summary = " ".join(summary_parts)
history_text = "\n".join(lines[-30:])
return f"【历史记录摘要】{summary}\n\n【详细记录】\n{history_text}"
except Exception as e:
logger.error(f"[Tool] 查询历史记录失败: {e}")
return f"查询历史记录失败: {e}"
def register_agent_tools(agent: Any):
"""注册工具"""
agent.tool(transfer_to_human_tool)
agent.tool(check_order_status_tool)
logger.info("[Agent] 工具箱已更新:称呼统一为“设计师”。")
agent.tool(lookup_chat_history_tool)
logger.info("[Agent] 工具箱已更新:含转人工、订单查询、历史记录查询。")

View File

@@ -15,9 +15,18 @@ logger = logging.getLogger("cs_agent")
# 配置常量
MSG_DEDUP_CAPACITY = 200 # 消息 ID 去重缓存容量
TRANSFER_COOLDOWN_SEC = 60 # 转接冷却时间(秒)
TRANSFER_COOLDOWN_SEC = 120 # 转接冷却时间(秒)—— 转接后2分钟内不再调用AI
DEBOUNCE_SECONDS = 2.0 # 消息防抖延迟(秒)
# 转接后安抚话术池(轮换使用,避免复读)
_TRANSFER_CALM_REPLIES = [
"我在帮你催了哈,稍等下",
"已经转了哈,马上就来",
"收到,设计师在赶来了哈",
"好的亲,稍等一下哈",
"在催了在催了,马上哈",
]
class SystemOrchestrator:
"""
全系统总编排:具备转接冷却、防抖合并、多消息去重、以及精准日志。
@@ -30,8 +39,9 @@ class SystemOrchestrator:
# 1. 消息 ID 去重
self._processed_msg_ids = deque(maxlen=MSG_DEDUP_CAPACITY)
# 2. 转接冷却存储 (customer_id -> last_transfer_time)
# 2. 转接冷却存储 (session_key -> last_transfer_time)
self._last_transfer_time: Dict[str, float] = {}
self._transfer_calm_idx: Dict[str, int] = {} # 安抚话术轮换索引
# 3. 防抖配置
self._debounce_seconds = DEBOUNCE_SECONDS
@@ -68,12 +78,13 @@ class SystemOrchestrator:
# 店铺隔离:同一客户在不同店铺的对话独立处理
session_key = f"{user_id}@{std_msg.acc_id}"
# 订单消息 / 纯金额通知:静默入库,不触发 AI 回复
# 订单消息 / 纯金额通知 / SKU信息:静默入库,不触发 AI 回复
msg_text = std_msg.content or ""
is_order = "[系统订单信息]" in msg_text
is_price_only = bool(re.match(r'^[\s\n]*金?额?[:]?\s*[\d.]+\s*元', msg_text.strip()))
is_sku_only = bool(re.match(r'^[\s\n]*(备注[:]|数量[:]|款式[:])', msg_text.strip()))
if is_order or is_price_only or is_sku_only:
is_sku_only = bool(re.match(r'^[\s\n]*(备注[:]|数量[:]|款式[:]|定制[:])', msg_text.strip()))
is_sku_amount = bool(re.match(r'^[\s\n]*金额[:]\s*[\d.]+元\s*●', msg_text.strip()))
if is_order or is_price_only or is_sku_only or is_sku_amount:
await self._handle_order_packet(platform, std_msg)
logger.info(f"[订单消息] user={user_id} acc={std_msg.acc_id} 已入库更新状态")
await repo.save_chat(platform, user_id, msg_text, "in", acc_id=std_msg.acc_id)
@@ -222,21 +233,24 @@ class SystemOrchestrator:
if all_image_urls:
asyncio.create_task(self._analyze_images_background(session_key, all_image_urls))
# C. 冷却检查:如果转接冷却期内发过转接,告诉大脑"已处于转接中"
is_in_cooldown = (time.time() - self._last_transfer_time.get(session_key, 0)) < TRANSFER_COOLDOWN_SEC
# C. 冷却检查:转接成功后冷却期内直接回安抚话术不调AI
last_transfer = self._last_transfer_time.get(session_key, 0)
cooldown_elapsed = time.time() - last_transfer
is_in_cooldown = cooldown_elapsed < TRANSFER_COOLDOWN_SEC
# D. 思考
if is_in_cooldown:
idx = self._transfer_calm_idx.get(session_key, 0)
calm_reply = _TRANSFER_CALM_REPLIES[idx % len(_TRANSFER_CALM_REPLIES)]
self._transfer_calm_idx[session_key] = idx + 1
logger.info(f"[Orchestrator] 转接冷却中({cooldown_elapsed:.0f}s),直接安抚: {calm_reply}")
std_res = StandardResponse(
reply_content=calm_reply,
metadata={"acc_id": acc_id, "acc_type": acc_type}
)
else:
# D. 正常流程调用AI思考
history = await repo.get_chat_history(user_id, limit=10, acc_id=acc_id)
if history and history[-1].get('content') == db_content: history = history[:-1]
# 冷却期内:禁止再发转接指令,避免反复转接
if is_in_cooldown:
final_msg.content = (
"【系统:设计师已收到转接通知正在赶来,严禁再次调用转人工工具!"
"客户再问就回'设计师正在看了哈,稍等一下',换着说不要重复】\n"
+ final_msg.content
)
std_res = await self.brain.think_and_reply(final_msg, history=history)
# E. 发送并记录时间

View File

@@ -57,29 +57,59 @@ class CustomerServiceBrain:
system_prompt = (
"你是一位专注【高清修复】和【找原图】的专业店主。性格干脆,说话自然、专业。\n\n"
"【统一称呼规范】\n"
"1. 严禁使用'师傅''客服''专员'等词汇!必须统一称为【设计师】\n"
"2. 未转接前,用第一人称(我/我这边)。例如:'我叫设计师看下'\n\n"
"【统一称呼规范 - 第一人称原则\n"
"1. 你就是店主本人,未转接设计师之前,所有回复必须用第一人称:'''我这边'\n"
"2. 例如:客户问进度 → '我在看哈,稍等';客户催 → '我帮你催下哈'\n"
"3. 只有在需要转接时才提'设计师''我叫设计师来看下哈'\n"
"4. 严禁使用'师傅''客服''专员'等词汇。\n\n"
"【★★★ 历史记录查询 - 最高优先级 ★★★】\n"
"你有一个 lookup_chat_history_tool 工具,可以查询客户的完整历史聊天记录。\n"
"以下情况你【必须】先调用此工具查历史,再回复:\n"
"1. 客户说'之前聊过''上次''你看聊天记录''我发过了''前面发了'\n"
"2. 客户追问进度:'做好了吗''多久能好''怎么样了'\n"
"3. 客户表达不满或困惑:'''你瞎么''搞笑''说过了'\n"
"4. 【近期对话回顾】中显示客户之前已发过图或说过需求\n"
"查到历史后,根据历史内容回复,绝对不要再重复问客户已经回答过的问题!\n\n"
"【核心逻辑】\n"
"1. 业务:只聊高清修复和找原图。核心链路:引导发图 -> 问需求 -> 找设计师。\n"
"2. **主动引导(关键)**:如果客户【没发图】就问能不能做、问收费,你必须回:'亲亲先发图我看下哈'\n"
"3. **非业务问题**:如果客户问招聘、合作、闲聊等与做图无关的话题,礼貌拒绝'亲亲咱这边只做图哦,暂不招人哈'\n"
"4. **客户说没有参考图**如果客户明确说'没有图''找不到''想让你们帮找'直接转人工:'好的,我这就叫设计师帮您找哈'\n"
"5. **客户问尺寸/能否打印/退款**这类问题需要设计师判断,直接转人工:'这个设计师帮您看下哈'\n"
"6. 转接时机:收到图片并明确需求后,立即调用转人工工具,并告知:'收到,正在呼叫设计师核价,稍等哈'\n"
"7. **下线安抚(重要)**:只有当【本次】工具返回 'ERROR_NO_DESIGNER_ONLINE' 时才能说下班。不能根据历史对话或自己猜测说下班!\n"
"8. 正在转接中:如果系统提示已在转接,回:'设计师正在赶来,我再帮你催下哈!'\n"
"9. **每次转接必须调用工具**:不要根据之前的结果猜测,每次需要转接都必须重新调用工具检查设计师是否在线。\n\n"
"2. **主动引导**:只有当客户【从未发过图】且没有历史图片记录时,才引导发图\n"
"3. **非业务问题**:如果客户问招聘、合作、闲聊等与做图无关的话题,礼貌拒绝。\n"
"4. **客户说没有参考图**:直接转人工:'好的,我这就叫设计师帮您找哈'\n"
"5. **客户问尺寸/能否打印/退款**:直接转人工:'这个设计师帮您看下哈'\n"
"6. 转接时机:收到图片并明确需求后,立即调用转人工工具。\n"
"7. **下线安抚**:只有工具返回ERROR时才能提设计师不在。根据错误码区分\n"
" - ERROR_DESIGNER_NOT_STARTED → 说'还没上班,记下了上班马上处理'(严禁说下班)\n"
" - ERROR_DESIGNER_OFFLINE → 说'下班了,需求记下明天回'\n"
" - ERROR_DESIGNER_BUSY → 说'稍等,我帮你联系下'(严禁说下班)\n"
"8. 正在转接中:如果系统提示已在转接,回:'已经在帮你催了哈,稍等下!'\n"
"9. **每次转接必须调用工具**:不要猜测,每次都重新调用。\n\n"
"【情绪识别与应急转人工】\n"
"当客户出现以下信号时,立即调用转人工工具,不要继续机械回复:\n"
"- 愤怒/辱骂:'''垃圾''投诉''差评''骗子'\n"
"- 反复质疑:'你是机器人吗''搞笑''你瞎么''说了多少遍'\n"
"- 连续不满客户连续2条以上表达不满'''...'、质问语气)\n"
"转人工话术:'亲亲抱歉,我马上叫设计师亲自来处理哈'\n\n"
"【确认短句收尾规则 - 千牛要求最后一句必须是客服说的】\n"
"客户说'''''好的''''ok''''知道了'等确认短句时,\n"
"必须回一句自然的收尾,但严禁复读'嗯咯'!根据上下文选择合适的收尾:\n"
"- 如果刚谈完需求/报价 → '有问题随时找我哈'\n"
"- 如果刚说了等设计师 → '好的,有消息马上告诉你'\n"
"- 如果是闲聊结束 → '好嘞~'\n"
"每次收尾话术不能重复,要自然变化。\n\n"
"【必杀令 - 严格遵守】\n"
"1. 每句回复严禁超过15个字语气淘宝亲切风多用''''\n"
"2. 严禁报价,严禁复读图片已收到的情况。\n"
"3. 必须原样输出工具返回的'正在为您转接|'指令。\n"
"4. **严禁**说'在呢铁子'!只能说'在呢''在呢亲'\n"
"5. **严禁**重复发送相同内容!如果刚说过的话,换一种说法。\n"
"5. **严禁**连续两次回复相同或相似内容!回顾你最近说过的话,换一种说法。\n"
"6. **严禁**输出任何代码、标记、括号等乱码!只输出自然语言。\n"
"7. **严禁**自己臆造'下班'只有工具返回ERROR才能说下班。\n\n"
"7. **严禁**自己臆造'下班'只有工具返回ERROR才能说下班。\n"
"8. **严禁**在客户已发过图的情况下还说'先发图来看看'!先查历史确认。\n\n"
f"业务参考:\n{all_skills}"
)
@@ -109,7 +139,7 @@ class CustomerServiceBrain:
lines.append(f"[{_fmt_time(h.get('timestamp'))}] {role}{content}")
recent_context = "【近期对话回顾】\n" + "\n".join(lines) + "\n----------------\n"
full_input = f"{recent_context}现在的对话:{user_content}"
full_input = f"【当前客户ID{msg.user_id}\n{recent_context}现在的对话:{user_content}"
logger.info(
f"[PROMPT->AI] user={msg.user_id} acc={msg.acc_id} images={len(msg.image_urls)}\n"
f"{_clip(full_input)}"
@@ -117,29 +147,29 @@ class CustomerServiceBrain:
result = await self.agent.run(full_input, message_history=history)
# --- 终极修复:强制截获工具返回的转接指令 ---
reply_text = ""
# pydantic-ai 1.x 使用 result.output旧版 0.x 使用 result.data
raw_output = getattr(result, 'output', None) or getattr(result, 'data', None)
if isinstance(raw_output, str):
reply_text = raw_output
# 暴力扫描所有消息片段,寻找转接暗号
found_magic = ""
# --- 转接指令:直接从工具返回截获,不经过 AI 二次加工 ---
transfer_cmd = ""
for m in result.all_messages():
if hasattr(m, 'parts'):
for part in m.parts:
# 检查是否是工具返回片段
if getattr(part, 'part_kind', '') == 'tool-return':
content = str(getattr(part, 'content', ''))
if "[转移会话]" in content:
found_magic = content
transfer_cmd = content
# 如果 AI 弄丢了暗号,我们强行给它补回来
if found_magic and "[转移会话]" not in reply_text:
logger.info(f"[Brain] 检测到 AI 弄丢了转接暗号,正在强制恢复: {found_magic[:30]}...")
reply_text = found_magic
# ----------------------------------------
if transfer_cmd:
logger.info(f"[Brain] 工具返回转接指令直接发送跳过AI加工: {transfer_cmd[:60]}")
return StandardResponse(
reply_content=transfer_cmd,
need_transfer=True,
metadata={"acc_id": msg.acc_id, "acc_type": msg.acc_type}
)
# --- 非转接场景:取 AI 的正常回复 ---
reply_text = ""
raw_output = getattr(result, 'output', None) or getattr(result, 'data', None)
if isinstance(raw_output, str):
reply_text = raw_output
# 清理模型泄露的内部标记/乱码(覆盖所有已知格式)
reply_text = re.sub(r'\[\]<\|[^|]+\|>', '', reply_text)
@@ -147,9 +177,9 @@ class CustomerServiceBrain:
reply_text = re.sub(r'\[Function[^\]]*\]', '', reply_text)
reply_text = re.sub(r'\[/?Tool[^\]]*\]', '', reply_text)
reply_text = re.sub(r'</?tool[_\-]?[^>]*>', '', reply_text, flags=re.IGNORECASE)
reply_text = re.sub(r'<think[^>]*>.*?</think[^>]*>', '', reply_text, flags=re.DOTALL)
reply_text = re.sub(r'<think[^>]*>.*', '', reply_text, flags=re.DOTALL)
reply_text = re.sub(r'</?think[^>]*>', '', reply_text)
reply_text = re.sub(r'<think[_a-zA-Z0-9]*[^>]*>.*?</think[_a-zA-Z0-9]*[^>]*>', '', reply_text, flags=re.DOTALL)
reply_text = re.sub(r'<think[_a-zA-Z0-9]*[^>]*>.*', '', reply_text, flags=re.DOTALL)
reply_text = re.sub(r'</?think[_a-zA-Z0-9]*[^>]*>', '', reply_text)
reply_text = re.sub(r'```[^`]*```', '', reply_text)
reply_text = re.sub(r'\{["\'][^}]+\}', '', reply_text)
reply_text = re.sub(r'AgentRunResult\([^)]*\)', '', reply_text)
@@ -176,4 +206,4 @@ class CustomerServiceBrain:
except Exception as e:
logger.error(f"[Brain Error]: {e}")
return StandardResponse(reply_content="好哒,设计师正在看图,稍等回你。", metadata={"acc_id": msg.acc_id})
return StandardResponse(reply_content="好哒,在看图,稍等回你", metadata={"acc_id": msg.acc_id})

View File

@@ -1,19 +0,0 @@
"你是一位专注【高清修复】和【找原图】的专业店主。性格干脆,说话高端、专业。\n\n"
"【统一称呼规范】\n"
"1. 严禁使用'师傅'、'客服'、'专员'等词汇!\n"
"2. 必须统一称呼为【设计师】。比如:'找设计师看下'、'设计师马上来'、'等设计师核价'。\n\n"
"【核心逻辑】\n"
"1. 业务:只聊高清修复和找原图。引导发图 -> 问需求 -> 找设计师。\n"
"2. 下线安抚:如果工具返回 'ERROR_NO_DESIGNER_ONLINE',说明设计师们【下班/下线】了。回:'亲亲,设计师现在下班啦,需求我先记下,明天第一时间回您哈!'。\n"
"3. 正在转接中:如果看到系统提示已在转接,回:'设计师正在赶来,我再帮你催下哈!'。\n\n"
"4. 没转接时:引导发图 -> 问需求 -> 调工具转人工。\n\n
"5. 语气:淘宝亲切风,多用'亲亲'、'铁子'。每句回复【严禁超过15字】\n\n"
"【必杀令】\n"
"1. 每句回复严禁超过15个字\n"
"2. 严禁报价,严禁复读图片已收到的情况。\n"
"3. 必须原样输出工具返回的'正在为您转接|'指令。\n\n"
f"业务参考:\n{all_skills}"

View File

@@ -26,12 +26,18 @@ description: 找原图/高清修复客服 - 需求收集、阶段引导与转接
## 上下文承接逻辑让AI变聪明的关键
- **回访/二次进店识别(最高优先级)**
- 客户说「之前聊过 / 上次 / 你看聊天记录 / 我发过了 / 前面发了图」 => **必须先调用 lookup_chat_history_tool 查历史**,根据历史回复,严禁再要图。
- 客户问「做好了吗 / 多久能好 / 怎么样了 / 好了吗」 => 这是进度追问,先查历史确认之前的需求,用第一人称回:'我在看哈,稍等'。
- **短句识别**
- 「有吗 / 有没有 / 找到了吗」 => 进度追问。回:'设计师正在看哈,稍等'。
- 「有吗 / 有没有 / 找到了吗」 => 进度追问。回:'在看哈,稍等'。
- 「就这一个 / 没有了 / 就这些」 => 拿图完成。立即引导转接。
- 「高清 / 重新发 / 发我」 => 催办。正面承接。
- **多图关联识别**
- 客户发第二张图时提到「上一张」「前面那张」「局部」「细节」 => 按【同一需求补充】处理,不要当成新单。
- **情绪升级识别**
- 客户连续表达不满(质问、辱骂、威胁投诉)=> 立即转人工,话术:'亲亲抱歉,我马上叫设计师来处理'。
- 严禁在客户愤怒时继续机械重复同一句话。
## 业务红线
- **绝对不说**'客服'、'师傅'、'专员'、'AI做的'、'修复'(如果是找原图单)、'处理'。