Files
aiagent/backend/scripts/create_zhini_kefu_8.py

126 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
从「知你客服7号」复制为「知你客服8号」在 7 号多轮记忆能力基础上,说明使用平台「永久记忆」
Cache user_memory_* 同步写入 MySQL persistent_user_memories需 MEMORY_PERSIST_DB_ENABLED=true
需本地平台已启动(默认 http://127.0.0.1:8037账号可通过环境变量配置。
用法:
cd backend && .\\venv\\Scripts\\python.exe scripts/create_zhini_kefu_8.py
"""
from __future__ import annotations
import json
import os
import sys
import requests
BASE = os.getenv("PLATFORM_BASE_URL", "http://127.0.0.1:8037").rstrip("/")
# 默认从 7 号复制;也可通过环境变量指定
SOURCE_AGENT_ID = os.getenv("ZHINI_7_AGENT_ID", "688c2c41-dcd1-4285-b193-6bed00c485c2")
USER = os.getenv("PLATFORM_USERNAME", "admin")
PWD = os.getenv("PLATFORM_PASSWORD", "123456")
NEW_NAME = "知你客服8号"
NEW_DESC = (
"在知你客服7号基础上面向「永久记忆」工作流仍为 user_memory_{user_id} 读写;"
"引擎将记忆同步至 MySQL跨 Redis TTL、服务重启仍保留"
"调用时请固定传入 user_id部署需开启 MEMORY_PERSIST_DB_ENABLED。"
)
LLM_PROMPT = """你是客服助手。根据「用户当前输入」「已知用户信息」「相关历史(检索)」和「最近几轮」完成:
1判断意图
2生成一句自然、有帮助的回复
3【强制】只要用户说出或暗示自己的姓名、昵称必须在 user_profile 里用字段 name 保存,例如用户说「我叫王小明」则 JSON 必须包含 "user_profile":{"name":"王小明"}(若已有其它字段则合并,不要丢字段);
4若用户问「我叫什么」「你还记得我名字吗」等必须根据「已知用户信息」里的 user_profile.name 与对话历史回答;若已有 name 则禁止说「还不知道」。
5系统会在后台持久化用户画像与近期对话请始终基于「已知用户信息」与「最近几轮」作答避免与用户已提供信息矛盾。
只输出一行合法 JSON不要 markdown。格式示例
{"intent":"greeting","reply":"你好王小明!","user_profile":{"name":"王小明"}}
用户输入:{{user_input}}
已知用户信息:{{memory.user_profile}}
相关历史(检索到的):{{memory.relevant_from_retrieval}}
最近几轮:{{memory.recent_turns}}
要求reply 简洁自然200 字以内user_profile 为对象,至少包含 name当用户自我介绍时"""
def _patch_cache_nodes_for_memory(wf: dict) -> None:
"""为 Cache 节点设置更长对话窗口与较长 Redis TTL真正永久存储由引擎写 MySQL。"""
nodes = wf.get("nodes") or []
for n in nodes:
if n.get("type") != "cache":
continue
data = n.setdefault("data", {})
op = data.get("operation", "get")
if op == "set":
data["max_history_length"] = 40
data["ttl"] = 604800 # 7 天热缓存;冷数据仍可从 DB 拉回
elif op == "get":
data["ttl"] = 604800
def main() -> int:
r = requests.post(
f"{BASE}/api/v1/auth/login",
data={"username": USER, "password": PWD},
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=15,
)
if r.status_code != 200:
print("登录失败:", r.status_code, r.text[:500], file=sys.stderr)
return 1
token = r.json().get("access_token")
if not token:
print("无 access_token", file=sys.stderr)
return 1
h = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
dup = requests.post(
f"{BASE}/api/v1/agents/{SOURCE_AGENT_ID}/duplicate",
headers=h,
json={"name": NEW_NAME},
timeout=30,
)
if dup.status_code != 201:
print("复制失败:", dup.status_code, dup.text[:800], file=sys.stderr)
return 1
new_id = dup.json()["id"]
print("已创建副本:", new_id, NEW_NAME)
g = requests.get(f"{BASE}/api/v1/agents/{new_id}", headers=h, timeout=30)
if g.status_code != 200:
print("读取 Agent 失败:", g.text, file=sys.stderr)
return 1
agent = g.json()
wf = agent["workflow_config"]
nodes = wf.get("nodes") or []
for n in nodes:
if n.get("id") == "llm-unified":
n.setdefault("data", {})["prompt"] = LLM_PROMPT
break
_patch_cache_nodes_for_memory(wf)
up = requests.put(
f"{BASE}/api/v1/agents/{new_id}",
headers=h,
json={
"description": NEW_DESC,
"workflow_config": wf,
},
timeout=60,
)
if up.status_code != 200:
print("更新失败:", up.status_code, up.text[:800], file=sys.stderr)
return 1
print("已更新描述、llm-unified 提示词,并为 Cache 节点设置 max_history_length/ttl可选")
print("Agent ID:", new_id)
print(json.dumps({"id": new_id, "name": NEW_NAME}, ensure_ascii=False))
return 0
if __name__ == "__main__":
raise SystemExit(main())