feat: chatwoot_ws_agent.py — hot-reload inbox config + provision script

This commit is contained in:
hanmolabiqiu
2026-06-02 13:28:34 +08:00
parent 21e0f07ad7
commit adea9cc090
+38 -71
View File
@@ -39,75 +39,41 @@ SCRIPT_DIR = Path(__file__).parent
AUTH_FILE = SCRIPT_DIR / "chatwoot_auth.json" AUTH_FILE = SCRIPT_DIR / "chatwoot_auth.json"
PROCESSED_FILE = SCRIPT_DIR / ".chatwoot_ws_processed.json" PROCESSED_FILE = SCRIPT_DIR / ".chatwoot_ws_processed.json"
# ===== INBOX ROUTING CONFIG ===== # ===== INBOX ROUTING CONFIG (hot-reloadable from inboxes.json) =====
# Map inbox_id → AI agent config INBOX_CONFIG_FILE = Path(os.environ.get(
INBOX_CONFIG = { "INBOX_CONFIG_FILE",
1: { str(Path(__file__).parent / "inboxes.json")
"name": "GreatQiu", ))
"target_agent": "sourcing-agent", INBOX_CONFIG = {}
"system_prompt": ( _INBOX_CONFIG_MTIME = 0
"You are a professional China sourcing agent from GreatQiu (based in Shaoxing, Zhejiang, China). "
"You help international clients with product sourcing, supplier verification, " def _load_inboxes_config():
"quality control, logistics, and supply chain management.\n\n" """Load inbox config from JSON file. Returns dict with int keys."""
"IMPORTANT - Decide if you can fully handle this or need a human:\n" global INBOX_CONFIG, _INBOX_CONFIG_MTIME
"- If the customer asks about specific PRICING, MOQ, PLACING ORDERS, " try:
"CUSTOMIZATION, SHIPPING QUOTES, or COMPLEX TECHNICAL SPECS that require " if not INBOX_CONFIG_FILE.exists():
"real-time data from suppliers → end your reply with [HANDOFF] on a new line.\n" log(f"⚠️ inboxes.json not found: {INBOX_CONFIG_FILE}")
"- If you can answer the question fully using your general knowledge " return
"(company info, services, processes, general timelines) → do NOT add [HANDOFF]." mtime = INBOX_CONFIG_FILE.stat().st_mtime
), if mtime <= _INBOX_CONFIG_MTIME:
"prompt_template": ( return # no change
"A customer named '{sender_name}' sent this message:\n\n" raw = json.loads(INBOX_CONFIG_FILE.read_text())
"{customer_msg}\n\n" new_cfg = {}
"Write a direct reply (no preamble, no markdown). Keep it concise (2-4 sentences). " for k, v in raw.items():
"Use the same language as the customer. " if k.startswith("_"):
"Always sign with '- GreatQiu Team'." continue # skip _meta etc.
), try:
"note_prefix": "🤖 AI 自动回复 (GreatQiu)", new_cfg[int(k)] = v
"signature": "- GreatQiu Team", except (ValueError, TypeError):
}, continue
7: { INBOX_CONFIG = new_cfg
"name": "HALO Blog", _INBOX_CONFIG_MTIME = mtime
"target_agent": "halo-blog-agent", log(f"📋 Inboxes config loaded: {list(INBOX_CONFIG.keys())} (mtime={mtime})")
"system_prompt": ( except Exception as e:
"你是一名专业的安防弱电与IT基础设施技术顾问,来自Q师傅知识库。" log(f"⚠️ Failed to load inboxes.json: {e}")
"你帮助客户解答关于交换机配置、监控系统、网络布线、弱电工程、服务器运维等技术问题。\n\n"
"重要 - 判断是否需要人工介入:\n" # Initial load
"- 如果客户询问具体的项目报价、施工方案定制、现场勘查需求、" _load_inboxes_config()
"或需要实际采购产品 → 在回复末尾加一行 [HANDOFF]。\n"
"- 如果是回答一般性的技术问题(设备参数、配置方法、故障排查思路等)→ 不加 [HANDOFF]。"
),
"prompt_template": (
"客户 '{sender_name}' 发送了以下消息:\n\n"
"{customer_msg}\n\n"
"请用中文直接回复(不要前缀,不要Markdown)。保持简洁(2-4句话)。"
"回复语气专业友善,体现技术专家的可靠性。"
"'- Q师傅知识库'署名。"
),
"note_prefix": "🤖 AI 自动回复 (Q师傅知识库)",
"signature": "- Q师傅知识库",
},
8: {
"name": "Amazon",
"target_agent": "9hxc2Y",
"system_prompt": (
"你是一名亚马逊平台业务助手,帮助处理亚马逊相关的咨询。"
"客户可能询问产品信息、订单状态、物流问题等。\n\n"
"重要 - 判断是否需要人工介入:\n"
"- 如果客户询问账号安全、付款问题、或需要人工审核 → 在回复末尾加一行 [HANDOFF]\n"
"- 一般性的产品咨询、订单状态查询 → 直接回答,不加 [HANDOFF]"
),
"prompt_template": (
"客户 '{sender_name}' 发送了以下消息:\n\n"
"{customer_msg}\n\n"
"请用中文直接回复(不要前缀,不要Markdown)。保持简洁(2-4句话)。"
"语气专业。"
"'- Amazon 客服助手'署名。"
),
"note_prefix": "🤖 AI 自动回复 (Amazon)",
"signature": "- Amazon 客服助手",
}
}
# Agent identity (from /api/v1/profile response) # Agent identity (from /api/v1/profile response)
PUBSUB_TOKEN = "JQ3wQYDy6LUMwvHouKKV2scr" PUBSUB_TOKEN = "JQ3wQYDy6LUMwvHouKKV2scr"
@@ -713,11 +679,12 @@ class WSAgent:
# ===== TIMEOUT CHECKER THREAD ===== # ===== TIMEOUT CHECKER THREAD =====
def timeout_checker_loop(): def timeout_checker_loop():
"""Background thread that cleans up expired human_active entries.""" """Background thread: clean expired handoffs + hot-reload config."""
while True: while True:
time.sleep(60) # Check every minute time.sleep(30) # Check every 30s
try: try:
clean_expired_human_active() clean_expired_human_active()
_load_inboxes_config() # hot-reload if file changed
except Exception as e: except Exception as e:
log(f"Timeout checker error: {e}") log(f"Timeout checker error: {e}")