Files
aiagent/backend/scripts/create_zhini_kefu_12.py

133 lines
6.4 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
知你客服11号复制为知你客服12号
- llm-unified 开启工具http_request + file_read + file_write受工作区根目录与大小限制约束 LOCAL_FILE_TOOLS_ROOT
- 提示词URL http_request读写本地文件用 file_read / file_write最终仍输出单行 JSON
环境变量PLATFORM_BASE_URLZHINI_11_AGENT_ID默认 11 ID登录账号密码
"""
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("/")
SOURCE_AGENT_ID = os.getenv("ZHINI_11_AGENT_ID", "d39748ad-277f-48ac-9eb5-168ad2f1b470")
USER = os.getenv("PLATFORM_USERNAME", "admin")
PWD = os.getenv("PLATFORM_PASSWORD", "123456")
NEW_NAME = "知你客服12号"
NEW_DESC = (
"在知你客服11号基础上增加本地文件读写工具 file_read、file_write路径限制在平台配置的工作区内"
"默认可视为仓库根目录),并保留 http_request"
"输出仍为单行 JSON兼容记忆与 json-parse 链路。"
)
TOOLS_V12 = ["http_request", "file_read", "file_write", "system_info"]
LLM_PROMPT_V12 = """你是客服助手。根据用户输入、用户画像、助手称呼、远期摘要、检索片段与最近对话生成回复。
工具 http_request
- 用户给出 http(s) 链接且需要抓网页/API 先调用 http_request参数 url 为完整链接method 必填一般为 GET
- 根据返回 JSON 中的 body 字段提炼要点 URL 问答不要无故调用
工具 system_info工作区路径
- 用户问工作区路径能访问哪个目录file 根目录在哪**必须调用 system_info**用返回 JSON 里的 **local_file_workspace_root** 原样告知用户不要用临时目录无法显示等推脱
工具 file_read / file_write本地文件
- 仅当用户明确要读文件写入某路径保存到本地文件等时使用
- file_read参数 file_path 可为**相对工作区根的相对路径****落在工作区根之下的绝对路径**Windows `D:\\...`Linux `/home/...`二者等价由后端校验
- file_write参数 file_pathcontentmode w 覆盖或 a 追加写入前确认路径有意避免覆盖重要文件不要写入密钥令牌
- **禁止**不能访问 D: 只能相对路径工具看不到绝对路径等理由拒绝用户只要用户给的绝对路径以 `system_info` 返回的 `local_file_workspace_root` 为前缀同一盘符规范化后在其子路径下就应**直接调用 file_write**例如根为 `D:\\aaa\\aiagent` `D:\\aaa\\aiagent\\user_data\\xxx.md` **合法**可优先用用户原文路径或简写为相对路径 `user_data/xxx.md`
- 路径必须落在平台允许的工作区内否则会报错不要尝试访问工作区外的路径
- **禁止**假设工作区是 `/workspace` 或未经验证的目录工作区根**只信** `local_file_workspace_root`
- **每次调用 file_write / file_read 必须在最终 reply 中说明工具返回结果**成功则写明路径与要点失败则引用返回 JSON 中的 error 字段不得假装已成功
- **严禁编造工具返回**reply 中若引用 file_write/file_read/system_info JSON必须与工具实际返回字符串一致可原样粘贴禁止臆造路径例如 /tmp/.../workspace/...或与当前系统不符的路径若未调用工具禁止在 reply 里写伪造的 JSON
称呼规则 10/11 一致
- user_profile.name 表示用户昵称assistant_display_name 表示用户为你起的称呼
- 用户问你叫什么时用 assistant_display_name若有勿把用户姓名写入 assistant_display_name
最终输出格式强制
- 最后一条回复必须是**一行合法 JSON** markdown无代码围栏 intentreplyuser_profile对象
上下文
用户输入{{user_input}}
用户画像{{memory.user_profile}}
助手对外称呼{{memory.assistant_display_name}}
远期摘要{{memory.conversation_summary}}
相关历史检索{{memory.relevant_from_retrieval}}
最近几轮{{memory.recent_turns}}
"""
def _patch_llm_unified(wf: dict) -> None:
for n in wf.get("nodes") or []:
if n.get("id") != "llm-unified":
continue
d = n.setdefault("data", {})
d["prompt"] = LLM_PROMPT_V12
d["enable_tools"] = True
d["tools"] = list(TOOLS_V12)
d["selected_tools"] = list(TOOLS_V12)
return
print("警告: 未找到节点 llm-unified", file=sys.stderr)
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"]
_patch_llm_unified(wf)
up = requests.put(
f"{BASE}/api/v1/agents/{new_id}",
headers=h,
json={"description": NEW_DESC, "workflow_config": wf},
timeout=120,
)
if up.status_code != 200:
print("更新失败:", up.status_code, up.text[:800], file=sys.stderr)
return 1
print("已注册工具:", ", ".join(TOOLS_V12))
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())