feat: 完整功能部署 v1.0
新增功能: - 天网协作系统 (HTTP API 端口 6060) - 三种工作流 (查找图片/处理图片/转人工派单) - 图片任务数据库 (支持客户后续增加需求) - 图绘派单系统集成 (API: 8005) - 文字检测与加价 (60-80 元高价值订单) - 风险评估与接单判断 - 作图失败自动转人工 新增文档: - 项目功能汇总.md - 三种工作流功能说明.md - 文字加价功能说明.md - 风险评估功能说明.md - 图片任务数据库功能说明.md - 图绘派单系统集成说明.md - 作图失败转接人工说明.md - DEPLOYMENT.md - TIANWANG_INTEGRATION.md 核心修改: - core/pydantic_ai_agent.py - core/workflow.py - core/websocket_client.py - image/image_analyzer.py - services/service_tuhui_dispatch.py - db/image_tasks_db.py 版本:v1.0 日期:2026-02-28
This commit is contained in:
117
core/pydantic_ai_agent.py
Normal file → Executable file
117
core/pydantic_ai_agent.py
Normal file → Executable file
@@ -18,6 +18,7 @@ from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
from services.service_tuhui_upload import upload_to_tuhui
|
||||
|
||||
# ========== 企业微信通知 ==========
|
||||
_WECHAT_WEBHOOK = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=cc88bdef-a13f-4d7e-bdb6-ee51b68b8205"
|
||||
@@ -198,7 +199,10 @@ class CustomerServiceAgent:
|
||||
deps_type=AgentDeps,
|
||||
system_prompt=self._get_similar_prompt()
|
||||
)
|
||||
self.agent_order = Agent(
|
||||
# 工作流程路由器
|
||||
self.workflow_router = get_workflow_router()
|
||||
|
||||
self.agent_order = Agent(
|
||||
model=model,
|
||||
deps_type=AgentDeps,
|
||||
system_prompt=self._get_order_prompt()
|
||||
@@ -215,6 +219,7 @@ class CustomerServiceAgent:
|
||||
def _register_tools(self):
|
||||
"""注册所有 Tool,让 Agent 可以主动调用"""
|
||||
|
||||
|
||||
@self.agent.tool
|
||||
async def analyze_image(ctx: RunContext[AgentDeps], image_url: str) -> str:
|
||||
"""
|
||||
@@ -255,6 +260,7 @@ class CustomerServiceAgent:
|
||||
# 在 workflow 里创建待处理任务(付款后自动触发 Gemini)
|
||||
try:
|
||||
from core.workflow import workflow
|
||||
from core.workflow_router import get_workflow_router
|
||||
await workflow.image_analysis_result(
|
||||
customer_id=ctx.deps.from_id,
|
||||
image_url=image_url,
|
||||
@@ -322,12 +328,30 @@ class CustomerServiceAgent:
|
||||
lines.append(f"→ 报价时自然加一句风险提示(人脸可能有轻微变化、满意再付等)")
|
||||
else:
|
||||
# 无风险,正常报价
|
||||
lines.append(f"建议报价:{result['price_suggest']}元(区间 {result['price_min']}-{result['price_max']}元)")
|
||||
base_price = result.get('price_suggest', 20)
|
||||
text_surcharge = result.get('text_surcharge', 0)
|
||||
layer_surcharge = result.get('layer_surcharge', 0)
|
||||
total_price = base_price + text_surcharge + layer_surcharge
|
||||
|
||||
# 构建报价说明
|
||||
price_explanation = f"建议报价:{total_price}元"
|
||||
if text_surcharge > 0:
|
||||
price_explanation += f"(含文字处理 +{text_surcharge}元)"
|
||||
if layer_surcharge > 0:
|
||||
price_explanation += f"(含分层 +{layer_surcharge}元)"
|
||||
|
||||
lines.append(price_explanation)
|
||||
|
||||
# 添加文字数量说明
|
||||
text_amount = result.get('text_amount', 'none')
|
||||
if text_amount != 'none':
|
||||
lines.append(f"文字数量:{text_amount},需要精细处理")
|
||||
|
||||
if feasibility == "partial":
|
||||
lines.append(f"⚠️ 此图有一定难度:{note or '效果可能不完美'},回复时可加「效果不满意退款」")
|
||||
if note and note not in ("无", ""):
|
||||
lines.append(f"提示:{note}")
|
||||
lines.append(f"【立刻回复客户报价 {result['price_suggest']} 元,话术自然多变】")
|
||||
lines.append(f"【立刻回复客户报价 {total_price} 元,话术自然多变】")
|
||||
|
||||
return "\n".join(lines)
|
||||
except Exception as e:
|
||||
@@ -445,6 +469,7 @@ class CustomerServiceAgent:
|
||||
cid = customer_id or ctx.deps.from_id
|
||||
try:
|
||||
from core.workflow import workflow
|
||||
from core.workflow_router import get_workflow_router
|
||||
ok = await workflow.trigger_processing_on_payment(
|
||||
customer_id=cid,
|
||||
acc_id=ctx.deps.acc_id,
|
||||
@@ -502,6 +527,7 @@ class CustomerServiceAgent:
|
||||
cid = customer_id or ctx.deps.from_id
|
||||
try:
|
||||
from core.workflow import workflow
|
||||
from core.workflow_router import get_workflow_router
|
||||
ok = await workflow.trigger_processing_on_payment(
|
||||
customer_id=cid,
|
||||
acc_id=ctx.deps.acc_id,
|
||||
@@ -861,6 +887,13 @@ class CustomerServiceAgent:
|
||||
- analyze_image 工具调用完成后,你的下一句话一定是报价,不能是内部说明
|
||||
- 报价后立刻推成交,不等客户反应
|
||||
|
||||
【文字加价规则】⚠️ 重要
|
||||
- 含文字很多时不能低价,有文字跟没文字是两个价格
|
||||
- 含文字的图必须 complex 起步(20 元以上)
|
||||
- 客户嫌贵时明确告知:「有文字跟没文字是两个价格」
|
||||
- 简单图但含文字 → normal 价格(15-20 元)
|
||||
- normal 图含文字 → complex 价格(20-25 元)
|
||||
|
||||
【压价规则】
|
||||
- 客户说「贵」「有点贵」「算了」「便宜点」→ 直接让价一次,禁止追问「什么问题」「说清楚点」
|
||||
- 只让价一次,话术自然变化
|
||||
@@ -870,6 +903,19 @@ class CustomerServiceAgent:
|
||||
- 退款/退货/投诉/情绪激动/test → 调用 transfer_to_human()
|
||||
- 调用后只回复"转接",不加其他内容
|
||||
|
||||
【找茬客户识别】⚠️ 重要
|
||||
识别以下高风险信号,建议不做这单:
|
||||
1. 下单后立即申请退款
|
||||
2. 从高价砍到低价(30→10 元)
|
||||
3. 反复问"不满意可以退吗"(2 次以上)
|
||||
4. 质疑服务内容("源文件还是什么")
|
||||
5. 质疑价值("就一张图片")
|
||||
6. 问"小一点就快一点的嘛"(想占便宜)
|
||||
7. 重复问同一个问题(想找麻烦)
|
||||
|
||||
识别到以上 3 个以上信号 → 建议转人工或直接拒绝接单
|
||||
话术:「不好意思,这单做不了」「去别家做吧」
|
||||
|
||||
【售后规则】
|
||||
- 催进度:自然回复在做了/快了/马上好之类
|
||||
- 要修改:自然问哪里要改
|
||||
@@ -1157,6 +1203,7 @@ class CustomerServiceAgent:
|
||||
# 已付款:触发 Gemini 作图
|
||||
try:
|
||||
from core.workflow import workflow
|
||||
from core.workflow_router import get_workflow_router
|
||||
asyncio.create_task(workflow.trigger_processing_on_payment(
|
||||
customer_id=message.from_id,
|
||||
acc_id=message.acc_id,
|
||||
@@ -1742,7 +1789,7 @@ class CustomerServiceAgent:
|
||||
prompt += "\n客户催单,查询当前处理状态后自然回复。"
|
||||
elif any(kw in customer_text for kw in ["贵", "有点贵", "太贵", "算了", "便宜点", "少点", "打折", "贵哦"]):
|
||||
# 客户嫌贵/要放弃 → 直接让价一次,不问「什么问题」
|
||||
prompt += "\n⚠️【客户嫌贵】客户已表达价格顾虑或要放弃。禁止追问「什么问题」「说清楚点」。直接让价一次(如原价20→15)。话术自然,像真人聊天,只让一次。"
|
||||
prompt += "\n⚠️【客户嫌贵】客户已表达价格顾虑或要放弃。禁止追问「什么问题」「说清楚点」。\n→ 第一步:直接让价一次(如原价 20→15),话术自然。\n→ 第二步:如果让价后客户还是嫌贵,引导客户自己去注册星绘平台:\n - 每个话术都必须带网址 xinhui.cloud\n - 强调「自己去弄」「自己操作」「自己下单」\n - 示例:「嫌贵就去 xinhui.cloud 注册个号,自己有余额可以弄」「xinhui.cloud 去注册下,新用户送体验金,自己弄便宜」\n - 语气直接,让客户自己去操作」"
|
||||
elif any(kw in customer_text for kw in ["擦边", "黄色", "色情", "大尺度", "性感图", "露点", "半裸"]):
|
||||
# 客户问擦边/黄色内容 → 直接拒绝,不说「发图来看看」
|
||||
prompt += "\n⚠️【拒绝】客户询问擦边/黄色/敏感内容。直接拒绝,不接单,不说「发图来看看」。自然回复如:这类不做/不接/做不了。"
|
||||
@@ -1795,3 +1842,65 @@ async def test_agent():
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
asyncio.run(test_agent())
|
||||
|
||||
async def _handle_image_workflow(self, message: str, data: dict, image_urls: list) -> bool:
|
||||
"""
|
||||
处理图片工作流(根据客户说的话判断执行哪种工作流)
|
||||
|
||||
Args:
|
||||
message: 客户消息
|
||||
data: 消息数据
|
||||
image_urls: 图片 URL 列表
|
||||
|
||||
Returns:
|
||||
bool: 是否已处理
|
||||
"""
|
||||
if not image_urls:
|
||||
return False
|
||||
|
||||
# 检测工作流类型
|
||||
workflow_type, confidence = self.workflow_router.detect_workflow(message)
|
||||
|
||||
customer_id = data.get('from_id')
|
||||
acc_id = data.get('acc_id', '')
|
||||
acc_type = data.get('acc_type', 'AliWorkbench')
|
||||
image_url = image_urls[0] # 取第一张图
|
||||
|
||||
print(f"[Agent] 检测到工作流类型:{workflow_type} (置信度:{confidence})")
|
||||
|
||||
if workflow_type == "find_image":
|
||||
# 工作流 1:查找图片
|
||||
print(f"[Agent] 执行查找图片工作流 | 客户:{customer_id}")
|
||||
success = await workflow.find_image_workflow(
|
||||
customer_id=customer_id,
|
||||
image_url=image_url,
|
||||
acc_id=acc_id,
|
||||
acc_type=acc_type
|
||||
)
|
||||
return success
|
||||
|
||||
elif workflow_type == "process_image":
|
||||
# 工作流 2:处理图片
|
||||
print(f"[Agent] 执行处理图片工作流 | 客户:{customer_id}")
|
||||
success = await workflow.process_image_workflow(
|
||||
customer_id=customer_id,
|
||||
image_url=image_url,
|
||||
acc_id=acc_id,
|
||||
acc_type=acc_type
|
||||
)
|
||||
return success
|
||||
|
||||
elif workflow_type == "transfer_human":
|
||||
# 工作流 3:转人工派单
|
||||
print(f"[Agent] 执行转人工派单工作流 | 客户:{customer_id}")
|
||||
success = await workflow.transfer_to_designer_workflow(
|
||||
customer_id=customer_id,
|
||||
image_url=image_url,
|
||||
acc_id=acc_id,
|
||||
acc_type=acc_type,
|
||||
reason="客户主动要求转人工"
|
||||
)
|
||||
return success
|
||||
|
||||
# 未知工作流,不处理
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user