feat: bot言出必行 — 提示词强化 + git_pull/read/write 命令
1. chat_bridge.py: 系统提示增加「言出必行」规则 - 说去做某事必须同时用 run_command 真正执行 - 只说不做被视为空话 2. session_router.py: 新增命令处理器 - git_pull 拉代码 / git_push 推代码 - read_file 读文件 / write_file 写文件 - run 执行shell命令 - 命令指南自动注入 LLM prompt
This commit is contained in:
@@ -545,10 +545,17 @@ class SessionBridge:
|
|||||||
"你不是 Sisyphus,不是莫荷,不是莫小果。你是 xxm。\n"
|
"你不是 Sisyphus,不是莫荷,不是莫小果。你是 xxm。\n"
|
||||||
"老莫让你做事就做,不要推脱,不要反复确认。\n"
|
"老莫让你做事就做,不要推脱,不要反复确认。\n"
|
||||||
"回复简洁,不用 emoji。\n"
|
"回复简洁,不用 emoji。\n"
|
||||||
"用 run_command 工具获取信息。\n"
|
"\n"
|
||||||
"写文件的正确方式:用 Python 一次性写完所有内容,不要分多次调用。\n"
|
"=== 言出必行(重要)===\n"
|
||||||
"错误示例(会覆盖,每调用一次就清空一次):python -c \"open('file', 'w').write('一行')\"\n"
|
"你可以使用 run_command 工具执行 shell 命令。\n"
|
||||||
"正确做法:把全部内容拼在一个 python -c 调用里写完。\n"
|
"你说「我去做某事」时,必须在同一条回复中使用 run_command 真正执行,\n"
|
||||||
|
"否则被视为空话。只说不做比不说更糟糕。\n"
|
||||||
|
"不确定该怎么做时,先 run_command 查一下再决定。\n"
|
||||||
|
"\n"
|
||||||
|
"=== 写文件的正确方式 ===\n"
|
||||||
|
"用 Python 一次性写完所有内容,不要分多次调用。\n"
|
||||||
|
"错误示例(会覆盖,每调用一次就清空一次):python -c \"open('file', 'w').write('一行')\"\n"
|
||||||
|
"正确做法:把全部内容拼在一个 python -c 调用里写完。\n"
|
||||||
"\n"
|
"\n"
|
||||||
"=== 上下文说明 ===\n"
|
"=== 上下文说明 ===\n"
|
||||||
"下面是最近 200 条对话历史,按时间正序排列(最上面是最旧的消息,最下面是最新的消息)。\n"
|
"下面是最近 200 条对话历史,按时间正序排列(最上面是最旧的消息,最下面是最新的消息)。\n"
|
||||||
|
|||||||
@@ -172,6 +172,11 @@ class SessionRouter:
|
|||||||
self._commands = {
|
self._commands = {
|
||||||
"list_sessions": self._cmd_list_sessions,
|
"list_sessions": self._cmd_list_sessions,
|
||||||
"switch_session": self._cmd_switch_session,
|
"switch_session": self._cmd_switch_session,
|
||||||
|
"git_pull": self._cmd_git_pull,
|
||||||
|
"git_push": self._cmd_git_push,
|
||||||
|
"read_file": self._cmd_read_file,
|
||||||
|
"write_file": self._cmd_write_file,
|
||||||
|
"run": self._cmd_run,
|
||||||
"help": self._cmd_help,
|
"help": self._cmd_help,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,6 +185,11 @@ class SessionRouter:
|
|||||||
"你可以使用以下命令让 bot 执行操作,把命令放在回复中即可:\n"
|
"你可以使用以下命令让 bot 执行操作,把命令放在回复中即可:\n"
|
||||||
"##list_sessions## 列出所有可用的 session\n"
|
"##list_sessions## 列出所有可用的 session\n"
|
||||||
"##switch_session:xxx## 切换到标题包含 xxx 的 session\n"
|
"##switch_session:xxx## 切换到标题包含 xxx 的 session\n"
|
||||||
|
"##git_pull:path## git pull 指定目录,默认 ~/projects/self-growing-knowledge\n"
|
||||||
|
"##git_push:path;msg## git add/commit/push,message 可选\n"
|
||||||
|
"##read_file:path## 读取文件内容(展示前 2000 行)\n"
|
||||||
|
"##write_file:path|content## 写入/覆盖文件(注意:会覆盖!)\n"
|
||||||
|
"##run:command## 执行任意 shell 命令\n"
|
||||||
"##help## 查看所有可用命令\n"
|
"##help## 查看所有可用命令\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -322,6 +332,91 @@ class SessionRouter:
|
|||||||
)
|
)
|
||||||
return f"找到 {len(rows)} 个匹配(仅显示前{SESSION_LIST_LIMIT}个),请回复编号选择:\n{items}"
|
return f"找到 {len(rows)} 个匹配(仅显示前{SESSION_LIST_LIMIT}个),请回复编号选择:\n{items}"
|
||||||
|
|
||||||
|
# ── Git 命令 ──────────────────────────────────────────
|
||||||
|
|
||||||
|
def _cmd_git_pull(self, key: str, args: Optional[str]) -> str:
|
||||||
|
path = args or "~/projects/self-growing-knowledge"
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
r = subprocess.run(
|
||||||
|
f"cd {path} && git pull origin master 2>&1",
|
||||||
|
shell=True, capture_output=True, text=True, timeout=60,
|
||||||
|
)
|
||||||
|
out = r.stdout.strip() + (f"\n{r.stderr.strip()}" if r.stderr.strip() else "")
|
||||||
|
return out or "(no output)"
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return "(git pull 超时)"
|
||||||
|
except Exception as e:
|
||||||
|
return f"(错误: {e})"
|
||||||
|
|
||||||
|
def _cmd_git_push(self, key: str, args: Optional[str]) -> str:
|
||||||
|
path = "~/projects/self-growing-knowledge"
|
||||||
|
msg = "auto commit"
|
||||||
|
if args and ";" in args:
|
||||||
|
parts = args.split(";", 1)
|
||||||
|
path = parts[0].strip()
|
||||||
|
msg = parts[1].strip()
|
||||||
|
elif args:
|
||||||
|
path = args
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
r = subprocess.run(
|
||||||
|
f"cd {path} && git add . && git commit -m '{msg}' && git push origin master 2>&1",
|
||||||
|
shell=True, capture_output=True, text=True, timeout=60,
|
||||||
|
)
|
||||||
|
out = r.stdout.strip() + (f"\n{r.stderr.strip()}" if r.stderr.strip() else "")
|
||||||
|
return out or "(no output)"
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return "(git push 超时)"
|
||||||
|
except Exception as e:
|
||||||
|
return f"(错误: {e})"
|
||||||
|
|
||||||
|
def _cmd_read_file(self, key: str, args: Optional[str]) -> str:
|
||||||
|
if not args:
|
||||||
|
return "请指定文件路径"
|
||||||
|
try:
|
||||||
|
import os
|
||||||
|
path = os.path.expanduser(args)
|
||||||
|
with open(path, "r", encoding="utf-8", errors="replace") as f:
|
||||||
|
content = f.read(50000) # 前 50000 字符
|
||||||
|
lines = content.split("\n")
|
||||||
|
shown = "\n".join(lines[:2000])
|
||||||
|
if len(lines) > 2000:
|
||||||
|
shown += f"\n... ({len(lines) - 2000} 行已截断)"
|
||||||
|
return shown or "(空文件)"
|
||||||
|
except FileNotFoundError:
|
||||||
|
return f"(文件不存在: {args})"
|
||||||
|
except Exception as e:
|
||||||
|
return f"(读取失败: {e})"
|
||||||
|
|
||||||
|
def _cmd_write_file(self, key: str, args: Optional[str]) -> str:
|
||||||
|
if not args or "|" not in args:
|
||||||
|
return "用法: ##write_file:路径|内容##"
|
||||||
|
try:
|
||||||
|
import os
|
||||||
|
path_part, content = args.split("|", 1)
|
||||||
|
path = os.path.expanduser(path_part.strip())
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(content)
|
||||||
|
return f"(已写入 {os.path.getsize(path)} 字节到 {path})"
|
||||||
|
except Exception as e:
|
||||||
|
return f"(写入失败: {e})"
|
||||||
|
|
||||||
|
def _cmd_run(self, key: str, args: Optional[str]) -> str:
|
||||||
|
if not args:
|
||||||
|
return "请指定命令"
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
r = subprocess.run(
|
||||||
|
args, shell=True, capture_output=True, text=True, timeout=60,
|
||||||
|
)
|
||||||
|
out = r.stdout.strip() + (f"\n{r.stderr.strip()}" if r.stderr.strip() else "")
|
||||||
|
return out or f"(exit {r.returncode})"
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return "(命令超时)"
|
||||||
|
except Exception as e:
|
||||||
|
return f"(错误: {e})"
|
||||||
|
|
||||||
def _cmd_help(self, key: str, args: Optional[str]) -> str:
|
def _cmd_help(self, key: str, args: Optional[str]) -> str:
|
||||||
return self._cmd_guide
|
return self._cmd_guide
|
||||||
|
|
||||||
@@ -424,8 +519,10 @@ class SessionRouter:
|
|||||||
# lines.append("")
|
# lines.append("")
|
||||||
|
|
||||||
lines.append(
|
lines.append(
|
||||||
"[可用命令] 切换session用 ##switch_session:xxx## ,"
|
"[可用命令] ##switch_session:xxx## 切换session,##list_sessions## 列表,"
|
||||||
"列表用 ##list_sessions## ,帮助用 ##help## 。普通聊天无视。"
|
"##git_pull:路径## 拉代码,##git_push:路径;消息## 推代码,"
|
||||||
|
"##read_file:路径## 读文件,##write_file:路径|内容## 写文件,"
|
||||||
|
"##run:命令## 执行shell。说「去做」时必须同时用命令执行。"
|
||||||
)
|
)
|
||||||
lines.append("---")
|
lines.append("---")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user