首页
/ 仓库内编程助手的系统提示词设计:HelloAgents Code Agent CLI 的角色边界、安全准则与补丁协议全解析

仓库内编程助手的系统提示词设计:HelloAgents Code Agent CLI 的角色边界、安全准则与补丁协议全解析

2026-09-11 14:28:13作者:虞亚竹Luna

导读

本文以 HelloAgents Code Agent CLI 的全局系统提示词 system.md 为主线,逐条拆解这个类似 Claude Code/Codex 的仓库内编程助手如何通过提示词定义「角色定位、路径安全边界、按需探索策略、补丁写盘协议与对话历史隔离规则」,并结合 code_agent.pyhello_code_cli.pyapply_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):

  1. 拒绝绝对路径(以 /~ 开头);
  2. (repo_root / rel_path).resolve() 解析出最终路径;
  3. 校验解析结果必须以 repo_root 前缀开头,否则抛出 Path escapes repo_root
  4. 拒绝修改符号链接(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=8000reserve_ratio=0.15enable_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

关键规则六条:

  1. 第一行必须是 *** Begin Patch(前面不要有任何文字);
  2. 最后一行必须是 *** End Patch
  3. 操作行格式:*** Add File: <path> / *** Update File: <path> / *** Delete File: <path>
  4. Add/Update 后面跟完整文件内容,Delete 后面不需要内容;
  5. 不要在补丁外包裹 markdown 代码块(不要用 ```);
  6. 路径相对于仓库根目录。

提示词还专门给出了错误与正确示例,防止模型把说明文字和补丁挤在同一行——例如 *** 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/nhello_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_suffixapply_patch_executor.py);
  • 自动备份:每次应用前把将被修改的文件备份到 <repo>/.helloagents/backups/<timestamp>/_backup_fileapply_patch_executor.py);
  • 原子写入:先写临时文件并 fsync,再用 os.replace 原子替换目标,避免写盘中断导致文件损坏(_atomic_writeapply_patch_executor.py);
  • 冲突检测:Update 操作按 hunk 在原文中做精确子序列匹配,找不到上下文时抛出 PatchApplyError 并给出 path:search:'关键字' 形式的复查提示;同时提供「整文件替换」与「宽松匹配(忽略行尾空白)」两级容错(_apply_update_payload / _find_subsequenceapply_patch_executor.py);
  • 宽容解析_parse_patch 会跳过前置/结尾的空行与代码围栏,容忍模型常见的格式漂移(apply_patch_executor.py)。

此外,补丁应用成功或失败后,CLI 都会通过 NoteTool 写入结构化笔记(note_type 分别为 actionblocker),把「发生过什么」沉淀下来,供后续轮次检索(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__ 中被逐一注册到 ToolRegistrycode_agent.py),其中 TerminalToolconfirm_dangerous=Truedefault_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 中,这一节奏被细化为可执行规则:

  • 每次回复必须同时包含 ThoughtAction,缺一不可;
  • 已有足够信息时必须用 Finish[答案] 结束,不要为了「更全面」反复调用工具;
  • 一旦证据足够(rg 命中、关键文件片段、错误栈、配置项),必须 Finish
  • 如果发现自己准备重复执行相同工具调用,说明没有新信息,应立即 Finish 给出结论 + 最小化下一步建议;
  • 多步骤任务(≥2 个子步骤、需用户确认、跨回合)先 todo add 再行动,结尾 todo list 汇总。

CodeAgent.run_turn 还在用户输入命中「分步/步骤/计划/改造/完成后/多步」等词汇时,向系统提示追加一行轻量提示,引导模型先用 todo 跟踪(code_agent.py)。该提示不强制,只提高倾向。

九、实际运行:环境配置与 CLI 命令

要让上述全部规则生效,需要按 code_agent/README.md 的快速开始配置并启动:

  1. 安装依赖(根目录 requirements-mvp.txt),并在仓库根目录创建 .env(可参考 .env.example),至少包含:

    • DEEPSEEK_API_KEY=...(或其他 OpenAI 兼容 provider 的 key)
    • 可选:LLM_MODEL_ID=deepseek-chatLLM_BASE_URL=https://api.deepseek.com
  2. 启动 CLI(工作区默认 .):

python3 -m code_agent.hello_code_cli --repo .
  1. 内置命令:
    • :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,它的设计价值可以归纳为四个层次:

  1. 身份层:明确「仓库内编程助手」而非闲聊机器人,输出风格极简;
  2. 边界层:路径必须留在 repo_root 内,写盘只能走补丁通道,危险操作必须确认;
  3. 效率层:按需探索、先保底上下文后取证、工具输出即时摘要,控制上下文预算;
  4. 可审计层:补丁应用有备份、有冲突检测、有成功/失败笔记,每一次修改都可追溯。

更重要的是,这些提示词规则并非「纸上谈兵」——ApplyPatchExecutor 的路径校验与原子写、hello_code_cli.py 的补丁提取与确认策略、CodeAgent 的闲聊/历史查询拦截,共同保证了提示词约束在代码层的强制执行。对于任何想构建「在真实仓库里安全自主工作」的 Agent 的开发者,这份 system.md 连同 react.mdtools.md 与执行器源码,是一套可以直接对照复用的完整参考实现。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.16 K
2.78 K
kernelkernel
deepin linux kernel
C
34
18
docsdocs
暂无描述
Markdown
904
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
932
1.86 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
862
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.95 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.38 K
1.47 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
535
606
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
549
398
leetcodeleetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
Markdown
77
23