92 lines
4.9 KiB
Python
92 lines
4.9 KiB
Python
"""就地更新「知你客服12号」:启用 system_info + 修订 LLM 提示词(工作区路径、工具反馈)。"""
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import os
|
||
import sys
|
||
|
||
BACKEND = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||
sys.path.insert(0, BACKEND)
|
||
|
||
from sqlalchemy.orm.attributes import flag_modified
|
||
|
||
from app.core.database import SessionLocal
|
||
from app.models.agent import Agent
|
||
|
||
# 与 create_zhini_kefu_12.py 保持一致的 TOOLS 与提示词
|
||
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_path、content;mode 用 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、无代码围栏;含 intent、reply、user_profile(对象)。
|
||
|
||
上下文:
|
||
用户输入:{{user_input}}
|
||
用户画像:{{memory.user_profile}}
|
||
助手对外称呼:{{memory.assistant_display_name}}
|
||
远期摘要:{{memory.conversation_summary}}
|
||
相关历史(检索):{{memory.relevant_from_retrieval}}
|
||
最近几轮:{{memory.recent_turns}}
|
||
"""
|
||
|
||
|
||
def main() -> int:
|
||
name = os.environ.get("PATCH_AGENT_NAME", "知你客服12号")
|
||
db = SessionLocal()
|
||
try:
|
||
a = db.query(Agent).filter(Agent.name == name).first()
|
||
if not a:
|
||
print("未找到", name, file=sys.stderr)
|
||
return 1
|
||
wf = dict(a.workflow_config) if a.workflow_config else {}
|
||
nodes = list(wf.get("nodes") or [])
|
||
done = False
|
||
for i, n in enumerate(nodes):
|
||
if n.get("id") != "llm-unified":
|
||
continue
|
||
d = dict(n.get("data") or {})
|
||
d["prompt"] = LLM_PROMPT_V12
|
||
d["enable_tools"] = True
|
||
d["tools"] = list(TOOLS_V12)
|
||
d["selected_tools"] = list(TOOLS_V12)
|
||
nodes[i] = {**n, "data": d}
|
||
done = True
|
||
break
|
||
if not done:
|
||
print("未找到 llm-unified", file=sys.stderr)
|
||
return 1
|
||
wf["nodes"] = nodes
|
||
a.workflow_config = wf
|
||
flag_modified(a, "workflow_config")
|
||
db.commit()
|
||
print("已更新", name, "llm-unified: tools=", TOOLS_V12)
|
||
print(json.dumps({"name": name, "id": str(a.id)}, ensure_ascii=False))
|
||
return 0
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|