HelloAgents Code Agent CLI 补丁应用失败排查:从 “Patch must start with ‘*** Begin Patch‘“ 读懂 Codex 风格补丁解析机制

发布时间:2026/9/11 21:18:35

HelloAgents Code Agent CLI 补丁应用失败排查:从 “Patch must start with ‘*** Begin Patch‘“ 读懂 Codex 风格补丁解析机制 HelloAgents Code Agent CLI 补丁应用失败排查从 Patch must start with *** Begin Patch 读懂 Codex 风格补丁解析机制【免费下载链接】hello-agents 《从零开始构建智能体》——从零开始的智能体原理与实践教程项目地址: https://gitcode.com/GitHub_Trending/he/hello-agents导读本文以 HelloAgents Code Agent CLI 项目Co-creation-projects/YYHDBL-HelloCodeAgentCli中一张真实的阻塞型blocker笔记note_20251219_150917_17.md为切入点完整还原补丁应用失败这一高频问题的产生链路CLI 如何提取模型回复中的补丁、执行器如何解析 Codex 风格的*** Begin Patch ... *** End Patch格式、以及格式不合法时为何会抛出 Patch must start with *** Begin Patch。读完本文你将掌握该 CLI 的补丁格式规范、解析器的宽容与严格边界、失败笔记的落盘机制并获得一份可直接复用的补丁排查清单。一次真实的补丁失败blocker 笔记全文解读在仓库Co-creation-projects/YYHDBL-HelloCodeAgentCli/.helloagents/notes/note_20251219_150917_17.md中记录了一条由 Agent 自动生成的结构化笔记完整内容如下--- id: note_20251219_150917_17 title: Patch failed type: blocker tags: [hello_agents_forStudy, patch_failed] created_at: 2025-12-19T15:09:17.654597 updated_at: 2025-12-19T15:09:17.654601 --- # Patch failed Error: Patch must start with *** Begin Patch User input: 将testDemo文件夹下的html文件内容改成中 文的 并且 用js实现一些简易的效果 让页面开起来更生 动 Patch: text *** Begin Patch ... *** End Patch这条笔记暴露了两个关键信息 1. **用户任务**把 testDemo 文件夹下的 HTML 文件内容改成中文并用 JS 实现一些简易效果让页面更生动——这是一个典型的前端页面改造需求与 code_agent/prompts/system.md 中补丁正确示例testDemo/style.css和 code_agent/prompts/react.md 中的示例testDemo/hello.html高度吻合。 2. **失败原因**解析器抛出了 Patch must start with *** Begin Patch。而笔记中记录的 Patch 字段显示模型产出的补丁被压缩成了形如 *** Begin Patch ... *** End Patch 的单行文本既不是独立的 *** Begin Patch 开头也没有独立成行的 *** End Patch 结尾。 这条笔记本身不是答案而是一份需要结合源码才能彻底读懂的**故障快照**。下面我们从解析器与 CLI 的源码出发还原它为何发生、以及如何避免。 ## 补丁格式规范Codex 风格三操作 HelloAgents Code Agent CLI 采用与 Claude Code / Codex 一致的补丁协议。在 [system.md](https://link.gitcode.com/i/b10b986b7d5dcd4fa7d75022d8115302) 中系统提示词对补丁格式给出了完整定义 text *** 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关键规则来自 system.md规则说明第一行必须是*** Begin Patch前面不能有任何文字最后一行必须是*** End Patch操作行格式*** Add File: path/*** Update File: path/*** Delete File: path内容Add / Update 后面跟完整文件内容Delete 后面不需要内容包裹不要在补丁外包裹 markdown 代码块不要用 路径相对于仓库根目录system.md 同时给出了错误与正确的对照示例。错误示例*** Begin Patch前面带了这是一个补丁这样的文字会被直接判为非法。正确示例第一行就是*** Begin Patch其后紧跟*** Add File: testDemo/style.css和文件正文。在 react.md 中规则被进一步细化并配了更完整的示例Finish[ 已为 testDemo/hello.html 添加样式。 *** Begin Patch *** Update File: testDemo/hello.html !DOCTYPE html html head style body {{ background: #f0f0f0; }} /style /head body h1Hello World/h1 /body /html *** End Patch ]react.md 特别强调三条易错点说明文字和补丁之间要有空行分隔*** Begin Patch独占一行前面不能加任何字符冒号、文字都不行不要用 markdown 代码块包裹补丁。对照这组规范回看失败笔记模型产出的补丁是*** Begin Patch ... *** End Patch——三个要素全部违反Begin 标记前有内容反引号、Begin/End 未独立成行、补丁被压缩为单行。这正是解析器拒绝执行的直接原因。解析器源码级剖析_parse_patch 的宽容与严格补丁的解析与执行全部集中在 apply_patch_executor.py 的ApplyPatchExecutor._parse_patch方法约 L262-L341。理解这段代码就能精确回答什么情况下会报 Patch must start with *** Begin Patch。第一步跳过围栏宽容处理前置噪声# 宽容处理跳过前置空行/代码块围栏找到真正的开头 while lines and lines[0].strip() in {, , patch, diff, text}: lines lines[1:]解析器并非绝对苛刻——它允许补丁前存在空行和、patch、diff、text等代码块围栏会自动跳过。这说明用围栏包裹补丁在 CLI 侧的_extract_patch之后通常已被处理但执行器仍做了二次容错。第二步定位 Begin 标记否则直接报错# 如果仍未以标头开头尝试向下寻找标头并截取 if lines and lines[0].strip() ! *** Begin Patch: for idx, l in enumerate(lines): if l.strip() *** Begin Patch: lines lines[idx:] break if not lines or lines[0].strip() ! *** Begin Patch: raise PatchApplyError(Patch must start with *** Begin Patch)注意这里的关键逻辑strip()后必须与字符串*** Begin Patch完全相等。只要第一行被追加了任何内容——比如反引号*** Begin Patch、冒号、说明文字——strip()后就不会等于*** Begin Patch。随后解析器会尝试向下寻找独立成行的*** Begin Patch若整篇文本中都不存在完全匹配的行就抛出本次笔记中记录的错误。从笔记内容可以推断模型把整段补丁压缩成单行*** Begin Patch ... *** End Patch其中 Begin 标记带反引号后缀、End 标记与前文挤在同一行导致精确匹配两处全部落空最终在进入任何文件操作之前就被拒绝。这是格式层校验失败并非文件写入失败——补丁从未被执行。第三步校验 End 标记while lines and lines[-1].strip() in {, }: lines lines[:-1] if not lines or lines[-1].strip() ! *** End Patch: for idx in range(len(lines) - 1, -1, -1): if lines[idx].strip() *** End Patch: lines lines[: idx 1] break if not lines or lines[-1].strip() ! *** End Patch: raise PatchApplyError(Patch must end with *** End Patch)同样的精确匹配策略也作用于结尾允许尾部空行与围栏但最后必须有独立成行的*** End Patch否则抛出Patch must end with *** End Patch。这一对错误信息Begin / End构成了补丁格式最外层的两道校验闸门。第四步逐行解析操作通过头尾校验后解析器按行扫描操作指令L305-L339*** Add File: path收集后续行作为新文件内容兼容两种形式——规范形式每行以开头和宽松形式直接给出正文模型可能省略*** Delete File: path无需内容*** Update File: path收集后续行作为更新载荷其余非空行若不匹配任何操作头抛出Unexpected patch line。第五步Update 载荷的 hunk 应用Update 操作通过_apply_update_payload→_split_hunks→_apply_hunk实现L369-L494将载荷按分隔符或空行切分为多个 hunk每个 hunk 中的 上下文、-删除行、新增行被拆分为 before/after 两个块然后在原文件中做精确子序列匹配。若找不到匹配上下文抛出Patch hunk context not found; file changed?并附带rel_path:search:上下文前80字符的重新检查提示随后_apply_update_payload会尝试宽松兜底——把 hunk 的 after 部分直接拼成新文件内容_hunks_to_after。此外若 Update 载荷中没有任何/-/ 空格前缀行则被判定为整文件替换L374-L377直接返回原文——这是为了兼容模型偶尔直接给出完整新文件内容的场景。CLI 侧的提取、规范化与确认补丁进入执行器前还要过三关在 hello_code_cli.py 中模型回复到执行器之间还有一道完整的前置管线第一关提取_extract_patchL32-L43PATCH_RE re.compile(r\s*\*\*\* Begin Patch[\s\S]*?\*\*\* End Patch, re.MULTILINE) PATCH_FENCE_RE re.compile( r(?:patch|diff|text)?\s*(\*\*\* Begin Patch[\s\S]*?\*\*\* End Patch)\s*, re.MULTILINE, )先尝试从patch/diff/text围栏中提取补丁主体失败则退回宽松的正则匹配允许前导空白。注意PATCH_RE 要求文本中真实存在*** Begin Patch ... *** End Patch的字面序列。如果模型像失败笔记中那样把标记写坏如*** Begin Patch ... *** End Patch单行、带反引号正则无法命中_extract_patch返回None根本走不到执行器——此时用户只会看到模型回复但不会产生任何落盘。这也解释了为什么失败笔记中记录的补丁看起来还在它是note_tool记录的原始内容不代表执行器实际收到过合法补丁。第二关规范化_normalize_patchL46-L60if stripped.startswith((Add File:, Update File:, Delete File:)) and not stripped.startswith(*** ): out.append(*** stripped)宽容处理模型遗漏***前缀的情况将其补全为标准 Codex 风格。第三关空补丁忽略与高风险确认if patch_text.strip() *** Begin Patch\n*** End Patch: continue空补丁没有任何操作会被静默忽略。随后_patch_requires_confirmationL63-L81按三条策略判定是否需要人工确认触发条件阈值包含*** Delete File:操作任意删除即确认文件操作数量≥ 6 个变更行数/-开头行≥ 400 行命中任一条件时CLI 会打印⚠️ 检测到高风险补丁删除/大规模变更。是否应用(y/n)等待用户输入y/yes才继续输入其他值则取消应用。失败如何被记录NoteTool 的 blocker 笔记机制笔记不会凭空出现。在 hello_code_cli.py 中补丁应用抛出的PatchApplyError被显式捕获并落盘为笔记except PatchApplyError as e: print(\n c(f❌ Patch failed: {e}, ERROR)) agent.note_tool.run({ action: create, title: Patch failed, content: fError: {e}\n\nUser input:\n{user_in}\n\nPatch:\n\ntext\n{patch_text}\n\n, note_type: blocker, tags: [project, patch_failed], })title固定为Patch failednote_type为blocker阻塞项tags包含项目名与patch_failedcontent记录错误消息、用户原始输入和补丁原文。这正是本笔记note_20251219_150917_17.md的生成路径。与之对称补丁成功时也会写入Patch applied笔记L196-L204note_type为actiontags 含patch_applied。笔记的存储格式由 note_tool.py 定义Markdown 文件 YAML 前置元数据id/title/type/tags/created_at/updated_at索引保存在notes_index.json。支持create / read / update / delete / list / search / summary七种操作笔记类型包括task_state、conclusion、blocker、action、reference、general。默认工作区为repo/.helloagents/notes/与本次笔记所在目录完全一致。这套机制的工程价值在于失败即沉淀。blocker 笔记把错误消息、用户输入、原始补丁三者绑定保存后续无论是人工复盘还是 Agent 通过context_fetch/note[search]检索都能完整还原失败现场形成可检索的排错知识库。格式校验之外执行器的安全纵深为什么解析器宁可报错也不硬着头皮应用因为ApplyPatchExecutor的设计目标首先是安全。在 apply_patch_executor.py 的类注释与实现中可以看到完整的防护链安全机制实现位置说明路径逃逸防护_safe_pathL185-L207拒绝绝对路径与~开头路径resolve()后校验前缀必须位于 repo_root 内拒绝修改符号链接后缀白名单_enforce_suffixL209-L221默认仅允许.py .md .toml .json .yml .yaml .txt .html .htm .css .js防止误改二进制或敏感文件原子写入_atomic_writeL245-L260临时文件 os.fsyncos.replace避免写入中断导致文件损坏自动备份_backup_fileL223-L243每个被改/被删文件在修改前备份到repo/.helloagents/backups/时间戳/保留相对路径结构并加.bak后缀规模限制applyL113-L121单补丁最多 10 个文件max_files、最多 800 行变更max_total_changed_lines冲突检测_apply_hunk/_find_subsequenceL424-L494Update 上下文精确匹配失败即报错并附带检索提示行尾空白归一化二次匹配兜底因此格式校验Begin/End 标记、操作行、hunk 上下文只是第一层闸门其后的每次写入都经过路径、后缀、规模、备份、原子性的层层把关。格式不合法就被拒绝不是缺陷而是这套安全模型的有意设计——宁可失败留痕也不做不可控的写入。排查清单当再次看到 Patch must start with *** Begin Patch结合本笔记的失败场景与源码逻辑遇到该错误时按以下顺序排查检查第一行补丁文本第一行去掉空行后必须是裸的*** Begin Patch。前面不能有冒号、说明文字、反引号不能与 End 标记挤在同一行。react.md 中的错误对比小节错误1补丁前有冒号错误2补丁前有文字在同一行错误3没有空行分隔是复现该报错的最常见三种写法。检查围栏虽然解析器容忍/patch/text围栏但前提是围栏内的 Begin/End 标记本身完好。如果标记被压缩成*** Begin Patch ... *** End Patch这种单行残缺形式围栏容错也无能为力。最稳妥的做法是完全不使用代码块包裹补丁system.md 规则 5。检查操作行*** Add File:/*** Update File:/*** Delete File:之后必须跟仓库相对路径Add/Update后不能空操作Delete后不要跟内容。检查结尾最后一行必须是*** End Patch其后仅允许空行或围栏否则会看到配套错误Patch must end with *** End Patch。检查用户输入与目标文件本次用户需求是修改testDemo下的 HTML 为中文并添加 JS 效果。若补丁路径写错如把相对路径写成绝对路径、或指向仓库外即便格式合法也会在_safe_path/ 后缀白名单处被拦截——这两类错误消息分别是Path escapes repo_root与Disallowed file suffix。善用失败笔记每次失败都会在.helloagents/notes/下生成一条带patch_failed标签的 blocker 笔记。通过context_fetch[{sources:[notes], query:patch_failed}]或直接查看 notes 目录可以回溯所有失败现场避免同类问题反复发生。小结一张只有十几行的 blocker 笔记串联起了 HelloAgents Code Agent CLI 完整的补丁链路从 system.md 与 react.md 定义的格式规范到 hello_code_cli.py 的提取、规范化、确认三步前置管线再到 apply_patch_executor.py 中宽容开头、精确匹配、安全兜底的解析与执行策略最后落到 note_tool.py 的失败留痕机制。Patch must start with *** Begin Patch这一报错的可贵之处在于它把模型格式幻觉这类模糊问题转化为一个可精确解释、可稳定复现、可对照排查的确定性错误。理解它就等于理解了整个补丁协议的边界而这份边界正是代码智能体在仓库中安全落盘的最后防线。【免费下载链接】hello-agents 《从零开始构建智能体》——从零开始的智能体原理与实践教程项目地址: https://gitcode.com/GitHub_Trending/he/hello-agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/11 22:08:42

Windkessel模型参数估计:频域分析与数值优化方法详解

简介:这份 MATLAB 代码包聚焦 Windkessel 模型参数估计方法的分析与比较,面向生物医学工程、心血管动力学及数据分析方向的高校本科生、研究生和教研人员,旨在帮助读者从血压、血流等时序数据中辨识模型参数,并对比不同估计方法的…

2026/9/11 22:08:42

MATLAB LSTM时间序列预测实战:从数据准备到滚动验证

简介:这是面向MATLAB用户的LSTM时间序列预测示例资源,适合需要借助深度学习工具箱完成历史序列建模与趋势预测的开发者,也适用于机器学习初学者理解循环神经网络的实际用法。脚本lstm_yuce.m演示了从数据预处理(归一化&#xff09…

2026/9/11 22:08:42

Bloom Filter 原理详解

在海量数据场景中,我们经常需要快速判断一个元素是否存在于集合中。传统的数据结构如哈希表、平衡树虽然能精确判断,但会随着数据量增长线性消耗内存,在亿级、十亿级数据下空间成本极高。布隆过滤器(Bloom Filter)正是…

2026/9/11 22:08:42

开源扫地机器人完全复刻指南:从硬件选型到SLAM建图导航

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/11 22:08:42

Ubuntu 22.04安装MySQL 8.0全指南与性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/11 22:03:41

演进式c++网络库

阶段 1:实现阻塞式 TCP Echo Server一、学习目标从最基础的 Socket 编程开始,理解 TCP 服务器建立连接、接收数据、发送数据的完整过程,并独立实现一个简单的 Echo Server。二、TCP 服务器基本流程• socket():创建 Socket • bin…

2026/9/10 16:39:38

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码