仓库内编程助手的系统提示词设计:HelloAgents Code Agent CLI 的角色边界、安全准则与补丁协议全解析
导读
本文以 HelloAgents Code Agent CLI 的全局系统提示词 system.md 为主线,逐条拆解这个类似 Claude Code/Codex 的仓库内编程助手如何通过提示词定义「角色定位、路径安全边界、按需探索策略、补丁写盘协议与对话历史隔离规则」,并结合 code_agent.py、hello_code_cli.py 与 apply_patch_executor.py 的源码实现,说明这些「纸面规则」如何在运行时被真正执行。读完本文,你将理解如何为一个在真实代码仓库中自主工作的 Agent 设计系统提示词,并掌握一套可复制的安全落盘(patch-only write)协议。
一、system.md 在 Code Agent CLI 中的角色定位
HelloAgents Code Agent CLI 是一个基于 HelloAgents 框架组件(HelloAgentsLLM / ContextBuilder / ReActAgent / TerminalTool / NoteTool / MemoryTool)搭建的命令行智能体,目标体验对标 Claude Code/Codex:支持多轮对话、按需探索代码库、生成补丁并在用户确认后落盘(见 code_agent/README.md)。
提示词统一存放在 prompts 目录 下,各文件分工明确:
| 文件 | 职责 |
|---|---|
system.md |
全局行为与安全边界(按需探索 / 敏感操作确认 / 补丁格式) |
react.md |
ReAct 回合格式(Thought/Action)与工具输入约定 |
plan.md |
规划工具 plan[...] 专用提示词 |
summarize_observation.md |
工具输出摘要提示词 |
tools.md |
六个内置工具的详细使用指南 |
其中 system.md 是「总纲」:它定义了 Agent 是谁、能在哪里活动、能做什么、不能做什么、以什么格式产出修改。在源码中,它由 code_agent.py 在初始化时读取并作为 system_prompt 注入上下文构建器:
base_system = (self.paths.prompts_dir / "system.md").read_text(encoding="utf-8")
self.tools_reference_path = self.paths.prompts_dir / "tools.md"
self.system_prompt = base_system
也就是说,每次 run_turn 构建上下文时,这份提示词都会与对话历史、上次工具摘要一起拼入最终 Prompt(code_agent.py)。因此,它本质上是一份「持续生效的宪法」——不随单轮对话消失。
二、角色定位:仓库内工作的 CLI 编程助手,而非闲聊机器人
system.md 的第一句话就划定了身份:
你是一个"在仓库内工作的 CLI 编程助手"(类似 Claude Code/Codex),不是闲聊机器人。
这一定位直接决定了后续所有行为约束的取向:Agent 的所有动作都以「在指定仓库内完成任务」为唯一目标,回复追求简短直接。提示词末尾进一步规定了输出风格:
- 非代码/非工具回复尽量 ≤4 行,直接给结论;
- 避免 "Here is..." 等冗余开场;
- 除非用户要求,不使用 emoji;
- 事实性问题直接给结果。
这一「轻量输出」策略在源码层有配套的闲聊兜底:CodeAgent._is_chitchat 会识别 hi/hello/你好/在吗 等问候词,直接返回固定引导语而不进入 ReAct 循环(code_agent.py),避免无谓的工具调用与解析失败。
三、工作区与路径安全边界:杜绝路径逃逸
system.md 规定「工作区固定为仓库根目录 .」,并给出核心准则第一条:
边界:所有路径必须在 repo_root 内,resolve 后校验前缀,拒绝逃逸。
这条规则并非停留在提示词层面。在初始化时,CodeAgent.__init__ 会对 repo_root 执行 resolve()(code_agent.py),把所有状态目录(notes / memory / sessions / logs)都收敛到 <repo>/.helloagents/ 之下(由 CodeAgentPaths 统一管理)。
真正的硬校验在补丁执行器 ApplyPatchExecutor._safe_path 中(apply_patch_executor.py):
- 拒绝绝对路径(以
/或~开头); - 用
(repo_root / rel_path).resolve()解析出最终路径; - 校验解析结果必须以
repo_root前缀开头,否则抛出Path escapes repo_root; - 拒绝修改符号链接(symlink)。
这意味着即使模型在补丁中写出 ../../etc/passwd 这类路径,执行器也会在落盘前拦截,形成「提示词约束 + 代码硬校验」的双保险。
四、按需探索:先证据后结论,避免无端全库扫描
system.md 第二条准则强调:
按需探索:只有确实需要证据时才调用终端;优先小范围命令(
ls/rg --files/rg <pat> <path>/sed -n <range>p <file>/cat <file>);避免无端全库扫描。
这是 Code Agent 与「一次把整个仓库塞进上下文」的传统 RAG 方案的关键区别。配合源码中的 lazy_fetch=True 模式(code_agent.py),上下文构建只注入保底内容:
- 系统提示 + 最近对话(
max_history_turns=10)+ 上次工具摘要(最近 3 条); - 上下文预算控制:
max_tokens=8000、reserve_ratio=0.15、enable_compression=True; - 扩展上下文不再自动注入,而是由模型通过
context_fetch[...]工具按需获取。
为了进一步控制上下文膨胀,工具输出会经过 LLM 摘要:_summarize_observation 会先截断超过 8000 字符的输出,再用 summarize_observation.md 提示词压缩成 120~200 字左右的摘要(code_agent.py),并在输出超过 1800 字符时触发摘要(summarize_threshold_chars=1800)。这套「先推理 → 证据不足再取证 → 取证即摘要」的节奏,正是 system.md 与 react.md 中反复强调的「避免过度收集」。
五、写盘唯一通道:补丁协议
system.md 中最具实操价值的一条是:
写盘唯一通道:补丁 + apply_patch。严禁
cat >/tee/ Here-Doc / 重定向等终端写法。
也就是说,模型想要修改任何文件,都不能借助 shell 的重定向技巧,只能输出结构化的补丁文本,由 CLI 侧解析并执行。这条规则从提示词到执行层形成了完整的闭环,我们分三层来看。
5.1 提示词层:补丁格式规范(system.md 原文)
产出补丁时必须严格遵守以下格式:
*** Begin Patch
*** Add File: path/to/new_file.py
文件内容...
可以多行...
*** Update File: path/to/existing_file.py
更新后的完整文件内容...
*** Delete File: path/to/old_file.py
*** End Patch
关键规则六条:
- 第一行必须是
*** Begin Patch(前面不要有任何文字); - 最后一行必须是
*** End Patch; - 操作行格式:
*** Add File: <path>/*** Update File: <path>/*** Delete File: <path>; - Add/Update 后面跟完整文件内容,Delete 后面不需要内容;
- 不要在补丁外包裹 markdown 代码块(不要用 ```);
- 路径相对于仓库根目录。
提示词还专门给出了错误与正确示例,防止模型把说明文字和补丁挤在同一行——例如 *** Begin Patch 前出现「这是一个补丁:」即视为错误。在 react.md 中,补丁必须放在 Finish[...] 内、与说明文字之间用空行分隔、*** Begin Patch 独占一行,否则会因解析失败而无法落盘。
5.2 CLI 层:补丁提取、规范化与人工确认
hello_code_cli.py 负责从 LLM 回复中把补丁「抠」出来并决定是否需要人工确认:
- 提取:
_extract_patch先用正则优先匹配代码围栏```patch/```diff/```text内的补丁,再退回宽松的*** Begin Patch ... *** End Patch全局匹配(hello_code_cli.py),对模型偶尔用围栏包裹补丁的行为做了容错; - 规范化:
_normalize_patch会把缺失***前缀的操作行(如Update File: xxx)自动补全为规范格式(hello_code_cli.py); - 确认策略:
_patch_requires_confirmation规定三类高风险补丁必须征求用户y/n(hello_code_cli.py)——包含*** Delete File:操作、涉及文件操作数 ≥ 6、变更行数(+/-开头行)≥ 400。
这与 system.md 中「高风险(删除/覆盖大量/危险命令 rm/chmod/git reset --hard)必须说明风险并征求确认;最终执行由 CLI 裁决」的表述完全对应——「裁决权」在 CLI,而不是模型。
5.3 执行器层:原子写、备份、冲突检测与规模限制
最终落盘由 ApplyPatchExecutor 完成(apply_patch_executor.py),它实现了 system.md 规则背后的全部安全工程细节:
- 规模限制:单个补丁最多修改
max_files=10个文件、max_total_changed_lines=800行,超出即拒绝; - 后缀白名单:默认只允许
.py/.md/.toml/.json/.yml/.yaml/.txt/.html/.htm/.css/.js等文本文件,防止误改二进制或敏感文件(_enforce_suffix,apply_patch_executor.py); - 自动备份:每次应用前把将被修改的文件备份到
<repo>/.helloagents/backups/<timestamp>/(_backup_file,apply_patch_executor.py); - 原子写入:先写临时文件并
fsync,再用os.replace原子替换目标,避免写盘中断导致文件损坏(_atomic_write,apply_patch_executor.py); - 冲突检测:Update 操作按 hunk 在原文中做精确子序列匹配,找不到上下文时抛出
PatchApplyError并给出path:search:'关键字'形式的复查提示;同时提供「整文件替换」与「宽松匹配(忽略行尾空白)」两级容错(_apply_update_payload/_find_subsequence,apply_patch_executor.py); - 宽容解析:
_parse_patch会跳过前置/结尾的空行与代码围栏,容忍模型常见的格式漂移(apply_patch_executor.py)。
此外,补丁应用成功或失败后,CLI 都会通过 NoteTool 写入结构化笔记(note_type 分别为 action 与 blocker),把「发生过什么」沉淀下来,供后续轮次检索(hello_code_cli.py)。
六、对话历史边界:系统规则不是对话内容
system.md 专门用一段说明「对话历史的重要边界」:
[Role & Policies]是系统角色定义和工作规则,不是用户对话内容;- 当用户询问「我们之前聊了什么/说了什么/总结对话」时,只总结
[Context]区块中的[user]/[assistant]交互记录; - 不要把系统规则、工具定义、角色描述当作「对话内容」来总结;
- 总结对话时直接根据
[Context]回答,不需要调用 memory 或 note 工具。
这条规则防止了两种典型事故:一是模型把「系统提示词」当成用户说过的话复述出来造成信息泄漏,二是为了回答「刚才聊了什么」这类元问题却去触发无谓的工具调用。源码层提供了双重保障:_is_history_query 识别「说了什么 / 之前说了什么 / what did i say / recap」等模式(code_agent.py),命中后直接由 _reply_with_recent_history 从内存中的 history 取出最近用户/助手消息生成回顾(code_agent.py),完全绕过 ReAct 循环与工具系统。
七、工具体系:context_fetch 优先,其余按需
system.md 列出了六种 ReAct Action 可用的工具,并特别强调「优先使用聚合搜索工具」:
7.1 context_fetch[...](优先推荐)
按需获取扩展上下文,单次可查多源,自动控制预算(约 800 tokens/源):
{"sources": ["files","notes","memory","tests"], "query": "关键词", "paths": "src/**/*.py"}
使用策略:先用保底上下文(对话历史 + 上次工具结果)推理,证据不足再调用。它优于单独调用 note/memory search——一次调用可搜索多个数据源,避免多次工具调用导致上下文爆炸。在 prompts/tools.md 中,context_fetch 的使用场景被进一步明确:搜索类名/函数名/错误栈、需要相关笔记/记忆时用;已经拿到足够证据、或用户仅问对话历史时不用。
7.2 其他工具
| 工具 | 用途 | 关键约束 |
|---|---|---|
terminal[...] |
只读检索(ls/rg/cat/sed/head/tail/grep/git status/diff) | 支持管道;重定向/子命令替换/危险命令需确认;写文件一律用补丁 |
note[...] |
记录关键结论/阻塞/行动,Markdown 持久化 | 补丁成功/失败总结、阶段小结时使用 |
memory[...] |
跨会话情景记忆(SQLite) | 需显式 add,默认不自动写入 |
plan<a href="https://link.gitcode.com/i/c84ba8d8f5b563ab148b7b58890f3d13" target="_blank">...] |
多步/模糊任务生成计划 | 5~12 条步骤,含 Risks 与 Validation 段落(见 [plan.md) |
todo[...] |
多步骤任务跟踪 | 状态 pending/in_progress/completed,同时仅允许 1 个 in_progress |
这些工具在 CodeAgent.__init__ 中被逐一注册到 ToolRegistry(code_agent.py),其中 TerminalTool 以 confirm_dangerous=True、default_shell_mode=True 初始化,与提示词「默认允许 shell 语义、危险操作需确认」一致。tools.md 还给出了每个工具的 JSON 调用示例,例如:
terminal[{"command":"rg -n \"foo\" context/**/*.py","allow_dangerous":false}]
note[{"action":"create","title":"Patch applied","content":"...","note_type":"action","tags":["patch"]}]
memory[{"action":"add","memory_type":"episodic","content":"完成 hello.html 样式改造","importance":0.7}]
todo[{"action":"add","title":"设计简介页布局","desc":"头部/简介/技能","status":"pending"}]
八、复杂任务的执行节奏:计划 → 取证 → 补丁 → 确认 → 落盘 → 验证
system.md 将复杂任务总结为一条工作流:
复杂任务遵循"计划 → 取证 → 补丁 → 确认 → 落盘 → 验证"节奏,最小改动满足需求。
在 react.md 中,这一节奏被细化为可执行规则:
- 每次回复必须同时包含
Thought和Action,缺一不可; - 已有足够信息时必须用
Finish[答案]结束,不要为了「更全面」反复调用工具; - 一旦证据足够(rg 命中、关键文件片段、错误栈、配置项),必须
Finish; - 如果发现自己准备重复执行相同工具调用,说明没有新信息,应立即
Finish给出结论 + 最小化下一步建议; - 多步骤任务(≥2 个子步骤、需用户确认、跨回合)先
todo add再行动,结尾todo list汇总。
CodeAgent.run_turn 还在用户输入命中「分步/步骤/计划/改造/完成后/多步」等词汇时,向系统提示追加一行轻量提示,引导模型先用 todo 跟踪(code_agent.py)。该提示不强制,只提高倾向。
九、实际运行:环境配置与 CLI 命令
要让上述全部规则生效,需要按 code_agent/README.md 的快速开始配置并启动:
-
安装依赖(根目录
requirements-mvp.txt),并在仓库根目录创建.env(可参考.env.example),至少包含:DEEPSEEK_API_KEY=...(或其他 OpenAI 兼容 provider 的 key)- 可选:
LLM_MODEL_ID=deepseek-chat、LLM_BASE_URL=https://api.deepseek.com
-
启动 CLI(工作区默认
.):
python3 -m code_agent.hello_code_cli --repo .
- 内置命令:
:quit退出;:plan <目标>强制生成计划(平时由模型按需调用plan<a href="https://link.gitcode.com/i/10b3c9070297be6f3aee9b52426762cd" target="_blank">...]工具,见 [hello_code_cli.py)。
启动时 CLI 会打印 workspace、LLM provider/model/base_url 与 state 目录,并做一次 ping 预检,提前暴露 API key / base_url / model 配置问题(hello_code_cli.py)。
可调环境变量:
| 变量 | 默认值 | 作用 |
|---|---|---|
HELLOAGENTS_DIR |
.helloagents |
状态目录(notes/memory/sessions/logs)根路径 |
CODE_AGENT_MAX_STEPS |
8 | ReAct 最大推理步数 |
十、小结:一份「提示词 + 代码」双闭环的 Agent 安全范式
回顾 system.md,它的设计价值可以归纳为四个层次:
- 身份层:明确「仓库内编程助手」而非闲聊机器人,输出风格极简;
- 边界层:路径必须留在 repo_root 内,写盘只能走补丁通道,危险操作必须确认;
- 效率层:按需探索、先保底上下文后取证、工具输出即时摘要,控制上下文预算;
- 可审计层:补丁应用有备份、有冲突检测、有成功/失败笔记,每一次修改都可追溯。
更重要的是,这些提示词规则并非「纸上谈兵」——ApplyPatchExecutor 的路径校验与原子写、hello_code_cli.py 的补丁提取与确认策略、CodeAgent 的闲聊/历史查询拦截,共同保证了提示词约束在代码层的强制执行。对于任何想构建「在真实仓库里安全自主工作」的 Agent 的开发者,这份 system.md 连同 react.md、tools.md 与执行器源码,是一套可以直接对照复用的完整参考实现。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.21 K637- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python270
jforgamejforgame是一个一站式游戏服务器开发框架。包含游戏服务器开发所需要的各种组件,比如网关,socket服务端与客户端,自定义高效消息编解码,游戏热更新,游戏通用工具等等。包含游戏服,跨服,匹配服,后台管理系统等实现,同时提供大量业务案例以供学习。亦可用于其他socket应用,例如及时聊天等。Java321
fizz-gateway-nodeAn Aggregation API Gateway in Java . FizzGate 是一个基于 Java开发的微服务聚合网关,是拥有自主知识产权的应用网关国产化替代方案,能够实现热服务编排聚合、自动授权选择、线上服务脚本编码、在线测试、高性能路由、API审核管理、回调管理等目的,拥有强大的自定义插件系统可以自行扩展,并且提供友好的图形化配置界面,能够快速帮助企业进行API服务治理、减少中间层胶水代码以及降低编码投入、提高 API 服务的稳定性和安全性。Java220
certd开源SSL证书管理工具;全自动证书申请、更新、续期;通配符证书,泛域名证书申请;证书自动化部署到阿里云、腾讯云、主机、群晖、宝塔;https证书,pfx证书,der证书,TLS证书,nginx证书自动续签自动部署JavaScript220
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python300