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:
hmo
2026-06-14 02:33:48 +08:00
parent ae16cd4e0f
commit e7719d9d5c
2 changed files with 110 additions and 6 deletions
+9 -2
View File
@@ -545,8 +545,15 @@ class SessionBridge:
"你不是 Sisyphus,不是莫荷,不是莫小果。你是 xxm。\n"
"老莫让你做事就做,不要推脱,不要反复确认。\n"
"回复简洁,不用 emoji。\n"
"用 run_command 工具获取信息。\n"
"写文件的正确方式:用 Python 一次性写完所有内容,不要分多次调用。\n"
"\n"
"=== 言出必行(重要)===\n"
"你可以使用 run_command 工具执行 shell 命令。\n"
"你说「我去做某事」时,必须在同一条回复中使用 run_command 真正执行,\n"
"否则被视为空话。只说不做比不说更糟糕。\n"
"不确定该怎么做时,先 run_command 查一下再决定。\n"
"\n"
"=== 写文件的正确方式 ===\n"
"用 Python 一次性写完所有内容,不要分多次调用。\n"
"错误示例(会覆盖,每调用一次就清空一次):python -c \"open('file', 'w').write('一行')\"\n"
"正确做法:把全部内容拼在一个 python -c 调用里写完。\n"
"\n"
+99 -2
View File
@@ -172,6 +172,11 @@ class SessionRouter:
self._commands = {
"list_sessions": self._cmd_list_sessions,
"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,
}
@@ -180,6 +185,11 @@ class SessionRouter:
"你可以使用以下命令让 bot 执行操作,把命令放在回复中即可:\n"
"##list_sessions## 列出所有可用的 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/pushmessage 可选\n"
"##read_file:path## 读取文件内容(展示前 2000 行)\n"
"##write_file:path|content## 写入/覆盖文件(注意:会覆盖!)\n"
"##run:command## 执行任意 shell 命令\n"
"##help## 查看所有可用命令\n"
)
@@ -322,6 +332,91 @@ class SessionRouter:
)
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:
return self._cmd_guide
@@ -424,8 +519,10 @@ class SessionRouter:
# lines.append("")
lines.append(
"[可用命令] 切换session用 ##switch_session:xxx## "
"列表用 ##list_sessions## ,帮助用 ##help## 。普通聊天无视。"
"[可用命令] ##switch_session:xxx## 切换session##list_sessions## 列表"
"##git_pull:路径## 拉代码,##git_push:路径;消息## 推代码,"
"##read_file:路径## 读文件,##write_file:路径|内容## 写文件,"
"##run:命令## 执行shell。说「去做」时必须同时用命令执行。"
)
lines.append("---")