feat: 工作流记忆与内置工具、知你客服脚本、Agent管理技能展示与能力配置、文档与Windows启动脚本;忽略 redis_temp 二进制目录

Made-with: Cursor
This commit is contained in:
renjianbo
2026-04-08 11:44:24 +08:00
parent 599b8f2851
commit bd3f8be781
66 changed files with 10104 additions and 469 deletions

View File

@@ -0,0 +1,91 @@
"""就地更新「知你客服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、contentmode 用 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())