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,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"
|
||||
|
||||
@@ -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/push,message 可选\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("---")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user