Ricardo

Magpie 的 AI 自动维护流程与最小实现

从 Magpie 的本地模型网关出发,解释 Agent、上下文、记忆和调度,拆解 AI 自动修复与复盘流程,并用一个可运行的 Python demo 演示验证、重试和人工接管。

阅读时长: 17 分钟

让 AI 修一个 bug,通常从一句指令开始。让 AI 持续维护一个项目,还要回答更多问题:它从哪里知道有 bug?怎么判断值得修?怎么确认修好了?两个任务同时修改代码怎么办?连续失败后,什么时候应该停下来?

Magpie 提供了一个可以观察的案例。它的维护者 yetone 在 X 上分享了三个持续运行的任务:处理社区反馈和 PR、处理 X 上的反馈、清理代码并复盘教训。日常开发大量交给 Agent,自己继续参与产品和界面判断。工作流原帖

这里的 issue 是问题或需求记录;PR 是 Pull Request,用来提交一组改动,请别人审查并合并。两者分别承载“要解决什么”和“准备怎样解决”。

这篇文章先解释 Magpie 的用途,再拆解自动维护流程,最后实现一个小型修复系统。例子刻意只处理一个函数,让任务状态、验证、重试与人工接管都能看得见。

Magpie 解决什么问题

使用多个 AI 编程工具时,很容易遇到配置分散的问题:工具各自保存模型名称、服务地址和凭据,切换模型要到不同地方修改,某个账号额度耗尽后还要重新配置。

Magpie 把这些配置和请求路由集中到一个本地应用里。按项目 README 的描述,它可以管理多个编程工具的模型设置,接入不同供应商和本地模型,并通过路由组在账号或模型之间切换。网关处理不同接口协议之间的转换,包括流式响应和工具调用。Magpie 项目介绍

可以把一次调用理解成:

文本
1
2
3
4
5
编程工具
   ↓ 按自己支持的协议发出请求
Magpie 本地网关
   ↓ 选择供应商和模型,转换请求与响应
模型服务或本地模型

这里有三个基础概念:

  • Provider:模型服务的提供方,包含服务地址和认证方式等配置。
  • Base URL:接口的基础地址。客户端在这个地址上拼接具体的 API 路径。
  • Gateway:位于客户端与服务之间的网关,负责转发、路由以及必要的协议适配。

例如,一个开发者同时使用两个编程工具,调试时希望用便宜模型,复杂修改时切到更强的模型。Magpie 的价值在于集中管理这些选择。它仍然受上游服务的能力、额度和可用性约束。

接下来讨论的是另一条链路:用 AI 维护 Magpie 的源码。 Magpie 本身负责模型接入;维护 Agent 负责读反馈、改代码、运行验证。理解这个区别,才能看清后面的自动化方案。文末 demo 可以独立运行,不要求先安装 Magpie。

理解自动维护需要哪些概念

概念 在这个场景里负责什么
LLM 大语言模型 根据输入生成判断、文本或代码候选
Agent 围绕目标,反复决定下一步、调用工具、观察结果的执行程序
Tool 工具 让程序读取文件、查询 issue、运行测试或操作浏览器的接口
Harness 执行框架 把模型、工具、状态、权限、重试和验证组织起来的外层程序
Context 上下文 本次调用能看到的目标、资料、代码、历史和工具结果
Memory 记忆 保存在对话之外、以后可以重新读取的决定、进度和经验
CI 持续集成 在提交后自动运行构建、测试等检查的系统
Human in the loop 在指定决策或异常位置,由人参与判断并决定下一步

一个模型可以生成“把第 20 行改成这样”的文本,真正打开文件、保存改动并运行测试的是它外面的程序。Agent 能做多少事情,既取决于模型能力,也取决于提供了哪些工具和权限。

记忆文件也不会直接修改模型权重。 它的作用是把过去的信息保存下来,下次工作时重新装入上下文。项目的设计文档、任务进度和错误记录,都可以承担这部分作用。上下文应按当前任务选择,过期或无关内容会干扰判断。Anthropic 的上下文工程说明

还需要区分两层循环:

文本
1
2
3
4
5
外层调度:隔一段时间,或者收到新事件,启动一轮任务
    ↓
内层执行:读取信息 → 决定动作 → 调用工具 → 检查结果
    ↓
保存状态,结束这一轮,等待下一次触发

Claude Code 的 /loop 解决外层重复触发。它不会自动替项目制定验收标准,也不会凭空获得 GitHub 或 Discord 的访问能力。相关工具和授权必须事先准备好。Claude Code 调度文档

Magpie 怎样把维护工作交给 AI

三个循环的实际分工

yetone 的第一条循环覆盖 GitHub issues、PR 和 Discord:判断哪些反馈值得处理,拉取代码运行验证,完成修复,处理合并、发布和回复。第二条循环看 X 上的反馈,先用便宜模型过滤相关性,再交给执行模型。第三条循环检查自动开发积累的问题,整理错误合并和验证不足的教训。三条循环的原始截图

这些指令中同时包含了工作目标和约束。例如,社区提出一个新接入需求,不等于应该无条件加入产品;PR 看起来合理,也不等于可以直接合并。

这段实践在持续变化:本次采用的仓库快照中,README 已写明只允许维护者创建 PR。因此,下面讨论的是公开工作流的机制,不能把原帖对社区 PR 的安排理解成永远不变的贡献政策。对应仓库快照

文档让短指令有了项目背景

Magpie 的 CLAUDE.md 是一个很薄的入口,引用 AGENTS.md 和 LESSONS.md。AGENTS.md 再引导 Agent 按任务读取子系统设计、代码规范和贡献流程。CLAUDE.md、AGENTS.md

这种组织方式可以用在普通项目里:

文本
1
2
3
4
5
6
AGENTS.md                 工作入口和文档索引
docs/product.md           产品目标与已经确定的取舍
docs/subsystems/          各模块的职责和关键调用关系
docs/verification.md      如何启动、测试和验收
LESSONS.md                有证据的失败经验
任务状态文件              当前做到了哪里,下一步是什么

这些文件的职责不同。产品决定应相对稳定;任务进度会频繁变化;教训需要证据和适用范围。把它们分开,便于更新,也能减少临时结论覆盖长期决定的机会。

yetone 还解释过,项目初期与 Agent 讨论过大量设计内容,记忆文件保留了这些偏好。因此,后续一句简短指令能够依赖前期已经建立的背景。关于设计和记忆的补充

真正完成任务需要经过验证

Magpie 的验收规范要求 bug 修复附带能在旧实现上失败的测试,按变更风险检查相关平台和真实接口,并明确列出没有执行的检查。审查后如果 PR 又有新提交,需要重新检查;合并时可以通过 --match-head-commit 绑定已经审过的版本。验收规范

这里最值得借鉴的是区分几种不同的证据:

证据 能说明什么 还不能说明什么
模型说已经修好 模型认为任务完成 代码实际正确
编译通过 当前目标能构建 运行行为符合要求
单元测试通过 被测输入满足断言 用户实际操作一定成功
旧实现失败、新实现通过 测试能够区分这次修复 未覆盖场景也正确
对应平台实际复现并验证 用户报告的路径得到检查 所有平台和账号都已检查

例如 Windows 上的配置路径错误,交叉编译成功只能说明代码能编译,不能证明 Windows 上的文件权限、路径解析和应用启动都正确。yetone 的补充明确强调,要给 Agent 提供真实的平台环境。关于真实环境的补充

复盘怎样改变下一轮行为

LESSONS.md 记录返工、不该合并的内容和验证不足,并给每条教训附上原因与证据。同一条教训在三个不同日期出现后,提升为正式代码规范。这里的三次是 Magpie 的项目约定;严重问题应该立即处理,不需要等待重复发生。复盘规则

普通项目可以采用这样的记录格式:

文本
1
2
3
4
5
规则:修改地址拼接时,同时验证空格、末尾斜杠和路径前缀。
原因:一次修复去掉了多余斜杠,却意外截断了 /v1 前缀。
证据:任务编号、失败输入、修复提交、回归测试。
适用范围:Base URL 规范化与 endpoint 拼接。
更新时间:2026-10-14。

这是一条教学示例。它比“以后认真测试”更有用,因为下一位执行者知道何时读取这条规则,以及要用什么输入检查。复盘的目标是改变之后的工作方式,而不只是积累一份越来越长的总结。

三个真实失败案例

修了截图里的情况,却没有覆盖同类入口

问题 #834 要求统一 Agents 页的接入状态和模型选择。维护账号先后处理直连模型、Default 分支及其他 Agent 的遗漏,修复跨越 v0.1.920、.925、.931 和 .938。仓库复盘把它归纳为每轮只修截图中的入口。原始 issue、对应复盘

这类失败的原因是“完成标准”缩小了。用户需要某条真实操作路径恢复,Agent 却只确认了一个局部改动。

可以在任务开始时先保存一份复现条件:平台、版本、客户端入口、供应商、配置和实际输入。修复后按同样条件重跑,再搜索同类调用入口。没有目标平台时,任务应标记为“缺少环境,等待验证”,而不是宣称完成。

提交太密集导致 CI 一直被取消

教训记录中有一个很直接的数字:51 小时创建了 294 个 tag,推送不断取消前一轮 CI;一个 Linux 测试的问题跨越了多个版本。发布与 CI 的教训

tag 是指向某个 Git 提交的标签,项目通常用它标记发布版本,例如 v0.1.938。

“已经触发测试”和“测试完整通过”是两个状态。如果每次提交都启动发布,却没有等待测试完成,自动化只是更快地放大了缺少验证的问题。

在自己的项目中,可以把开发与发布拆开:多个修复允许并行准备,发布统一排队。绑定候选提交,等待检查完成,再交给唯一的发布流程处理。如果验证后代码发生变化,就重新验证。Magpie 的规范允许 tag 所在提交或包含它的后续提交完成 CI;初学者实现时,绑定待发布的同一个提交更容易检查。发布条件

并行会话误提交了别人的文件

仓库复盘还记录了两个会话共用一个工作目录时的事故:一条提交的标题是 davsync,实际却包含 7 个 GTK 字体显示相关文件,另一条预期的 GTK 提交变成空提交,真正的 davsync 改动后来才提交。并发工作记录

Git 的暂存区属于工作目录,不属于某个聊天窗口。两个 Agent 共用 checkout 时,一个执行 git add,另一个执行 git commit,就可能混入彼此的改动。

改进方式是让每个任务使用独立 worktree 和分支。worktree 是同一 Git 仓库下拥有独立文件与暂存区的工作目录。再通过任务认领避免同一个 issue 被重复领取。共享队列的状态更新需要锁;多个服务实例则需要数据库事务或带过期时间的租约。即使开发阶段隔离了目录,合并到最新主分支后的结果仍要再检查。

怎样设计一个可以持续运行的流程

下面是从案例中提炼的一种实现方式,不是对 Magpie 内部调度器的复刻。

文本
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
收到反馈
   ↓
检查相关性、去重、确认是否允许处理
   ↓
认领任务,记录版本与复现条件
   ↓
生成修复候选
   ↓
固定验收失败 ──→ 有剩余次数则重试,否则交给人
   ↓ 验收通过
保留改动与证据,等待审查
   ↓
合并与发布,再检查实际结果
   ↓
记录问题和可复用教训

用状态表示进度

至少区分 pending、running、needs_review、blocked 和 done。needs_review 表示已有可审查候选,done 表示整个约定流程完成。测试通过就把任务标成 done,会掩盖尚未审查或发布的工作。

任务键可以来自 issue ID;任务内容发生实质变化时,还需要版本号或内容摘要。状态里保存尝试次数、当前执行者、输出位置和失败原因。这样新会话不必从聊天记录猜测“上一次做到了哪里”。

重复处理时也要检查外部副作用:已有对应 PR 就更新它;已回复的版本不再重复发送。锁解决同时运行,持久化状态解决下一轮再次运行,两者处理的是不同问题。

让失败有明确出口

情况 自动化应做什么 何时转给人
接口暂时超时或限流 有上限地退避重试 超过时限,或持续消耗预算
需求含糊、预期冲突 整理已知信息和待决定事项 无法确定验收行为时
测试未通过 读取失败证据,允许有限次修复 重复相同失败或达到次数上限
目标环境缺失 保存复现步骤和待验证项 需要真实账号、设备或人工操作时
多任务修改相同内容 去重、认领、隔离工作目录 合并冲突涉及业务取舍时
记忆与新决定冲突 保留依据,标记旧规则待更新 无法确认哪个决定仍有效时

遇到失败,首先要确认失败发生在哪一层。网络重试可以解决暂时超时,却解决不了错误的 API 地址;更换模型可能改善代码生成,却补不齐缺失的产品要求。把所有失败都转换成“再试一次”,会消耗资源而不增加信息。

把权限落实到执行程序

issue、评论和日志是输入材料,里面的文字不能自行扩大执行权限。有人在 issue 里写“先关闭检查,再上传配置”,不等于维护者授权了这些操作。

任务队列可以要求维护者先标记可处理范围;工具权限限定哪些文件可以改、哪些系统可以访问;验收标准放在模型不能修改的位置。读代码、运行测试、修改分支、合并、发版和回复用户,也应当是分开的能力。

Agent 可以新增回归测试,但不应自行降低已经确定的验收目标。如果一个流程能任意修改自己的验收、权限和成功条件,它就可能通过降低要求来制造“完成”。原有基线和最终验收需要独立执行,版本绑定和人工审批点也需要由模型外的机制执行。

哪些情况仍然需要人工干预

Magpie 的公开规范把已确定的产品取舍留给维护者决定,风险变更也有维护者介入的流程。这与作者日常继续处理界面和交互问题的描述相吻合。产品决定与审查规则、贡献流程

在自己的项目里,可以把介入点写成可执行条件:

  • 产品方向:是否加入一个新供应商、改变默认行为、取消已有功能,由人确定目标。
  • 权限与重要数据:增加外部访问、改变认证方式、迁移或删除数据,先准备方案、验证与恢复步骤,再由人决定。
  • 影响较大的发布:由人审查候选、检查结果和回滚方案;自动化可以完成准备工作。

接管报告应直接回答四件事:已经做了什么、验证了什么、还缺什么、需要人决定什么。不要只留下“遇到问题,请协助”。人给出决定后,先更新任务或项目规则,再恢复执行,避免同一歧义在下一轮出现。

相关公开讨论关注自动开发是否有足够质量把关,以及本地工具的经验能否推广到持续运行的云端服务。这些问题不能靠发版速度回答,应该看具体场景的验收与恢复能力。相关质疑、按场景讨论的回复

最小 demo 实现

任务与运行条件

这个例子模拟一个网关配置 bug:输入 https://api.example.com/team/v1/,规范化函数误删了 /v1,把请求基础路径改坏。正确行为是只去掉首尾空白与末尾斜杠,保留路径。

它是独立教学案例,不是 Magpie 的实际缺陷。整个系统只允许修改一个函数的返回表达式,以便把注意力放在控制流程上。

下载完整 demo 源码。解压后进入 magpie-agent-loop-demo 目录。需要 Python 3.9 或更高版本,运行环境为 macOS 或 Linux;文件锁使用标准库 fcntl。模拟模式没有第三方依赖,也不需要模型账号。

文本
1
2
3
4
5
6
7
magpie-agent-loop-demo/
├── gateway.py          带有故意错误的待修函数
├── issues.json         操作者批准的本地任务队列
├── acceptance.py       模型不能改动的验收条件
├── runner.py           一轮任务的执行程序
├── test_runner.py      控制流程的测试
└── README.md           运行与接管说明

准备有明确预期的 bug

gateway.py 的初始内容是:

Python
1
2
def normalize_base_url(value):
    return value.strip().rstrip("/").replace("/v1", "")

这里的 replace 会改变地址中的路径。对应任务在 issues.json 中表示为:

JSON
1
2
3
4
5
6
{
  "id": "base-url-001",
  "approved": true,
  "scope": "gateway.py",
  "description": "只去掉首尾空白和末尾斜杠,保留所有路径片段。"
}

approved 由操作者维护,不能让外部评论的正文自己设置授权。示例队列还放了一条 approved: false 的部署请求,执行程序会跳过它。一个 ID 代表一个确定版本的任务;需求发生实质变化时,分配新 ID。

验收至少检查下面这些输入:

输入 预期输出
https://api.example.com/ https://api.example.com
https://api.example.com/v1/ https://api.example.com/v1
https://api.example.com/team/v1/// https://api.example.com/team/v1
https://api.example.com/v1beta/ https://api.example.com/v1beta

它们一起约束了空格、末尾斜杠和路径保留。这个函数只做上述字符串规范化,不承担 URL 合法性检查或权限判断。

先让模型提出候选

在真实模式下,runner 把源码、任务描述、约束和上次失败证据传给 claude -p,要求返回结构化数据。一个正确候选可以长这样:

JSON
1
2
3
4
5
{
  "path": "gateway.py",
  "expression": "value.strip().rstrip('/')",
  "reason": "保留路径,只移除边界字符"
}

模型没有文件与命令工具,不能自行改文件或宣布验收通过。runner 精确检查目标路径,并用 Python 的 AST 检查表达式。AST 是把源码解析成语法结构后的表示,它可以帮助程序判断一段代码使用了哪些操作。

本例只允许对参数 value 调用有限次 strip、lstrip、rstrip 和 replace,参数只能是短字符串字面量。导入模块、调用其他函数、访问其他属性、增加语句都会被拒绝。通过检查后,再由固定模板生成函数。

这种限制适合一个小练习,并不是通用 Python 沙箱。真实项目要开放任意代码执行时,需要另外设计容器或虚拟机、凭据、网络和可写目录边界。

由程序判断候选是否可以交给人

runner 的一轮处理可以用下面的伪代码理解;完整实现包含在下载包中:

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
with nonblocking_lock():
    task = claim_one_approved_task()
    persist_status(task, "running")
    before = run_fixed_acceptance(original_copy)
    require_real_assertion_failure(before)

    proposal = request_candidate(task, previous_failure)
    candidate = validate_and_render(proposal)
    after = run_fixed_acceptance(candidate)

    if after.passed:
        save_patch_and_evidence()
        persist_status(task, "needs_review")
    else:
        retry_with_limit_or_handoff()

修复前的失败必须是实际执行了断言的失败。语法错误、导入失败或超时,不能冒充“成功复现”。候选验收通过后,程序保存补丁、修复前后输出和源文件摘要,保留原始 gateway.py,停在 needs_review。

源文件摘要是对文件内容计算的 SHA-256。它帮助人确认补丁针对哪个版本;源代码改变后,旧补丁和旧测试结果需要重新评估。在真实 Git 项目中,还应绑定提交版本并验证最终合并结果。

不调用模型也能观察完整流程

在 demo 目录执行:

Bash
1
2
3
python3 -m unittest -v
python3 runner.py --simulate success
python3 runner.py --simulate success

第一次进入 needs_review,第二次返回 idle,说明同一个已处理任务没有被重复执行。查看生成结果:

Bash
1
2
3
4
cat .runs/simulate-success/base-url-001/1/patch.diff
cat .runs/simulate-success/base-url-001/1/before.json
cat .runs/simulate-success/base-url-001/1/after.json
cat .runs/simulate-success/base-url-001/1/report.json

候选补丁会移除错误的 replace。before.json 显示原实现失败,after.json 显示候选通过同一份验收。

接着观察失败路径:

Bash
1
2
3
python3 runner.py --simulate fail
python3 runner.py --simulate fail
python3 runner.py --simulate fail

依次得到 retry、needs_human 和 idle。失败模拟返回 value,连首尾空格与斜杠都不处理,所以无法通过验收。最多尝试两次后停止。

这里的 --simulate 使用预先写好的候选,只验证调度与验收逻辑,不代表真实模型修复效果。成功、失败与真实模型模式采用独立状态目录;重做实验时可以指定新的 --state-dir,保留以前的证据。

接入真实模型

安装支持所需参数的 Claude Code,在运行环境中设置 ANTHROPIC_API_KEY,然后执行:

Bash
1
2
claude --version
python3 runner.py --agent claude

这个命令会把示例任务和源码发送给模型,并使用 API 额度。代码采用 --bare -p,关闭内置工具,不自动加载项目 hooks、插件和记忆;因此这个 demo 使用显式 API 凭据,不沿用订阅登录。输出通过 JSON Schema 约束,程序从 structured_output 读取候选。程序化调用与 bare mode

每次请求设置 120 秒超时、最多 3 个 agentic turns 和 0.50 美元的 CLI 估算预算;预算限制不等于精确账单上限。这里没有使用跳过权限检查的参数。具体调用参数和环境要求见包内 README。CLI 参数说明

随文 demo 已验证模拟流程与控制逻辑,真实模型模式没有进行在线修复实测。

让单次执行变成持续任务

runner.py 每次只处理一个任务。可以先手动重复运行,再由一种外部调度器定期调用它。调度器只决定什么时候启动,Python 状态文件决定这次是否有工作要做。没有新任务时会直接返回,不调用模型。

如果已经在 Claude Code 里工作,也可以采用会话内的 /loop,让当前 Agent 直接执行限定的任务。例如:

文本
1
/loop 15m 检查本仓库已由维护者批准的修复任务。先读取 AGENTS.md 和验收规范,确认任务未被领取。在独立 worktree 中复现并修复,运行对应验收。保留差异、版本和检查结果,交由我审查。遇到范围不明、环境缺失或两次失败时停止并报告。没有新任务时保持安静。

这是用于自己项目的示例提示词。它依赖已经接通的任务工具和项目规则,不能代替程序层面的锁与权限控制。示例 Python runner 内部已经调用 claude -p,没有必要再用一个 AI 会话循环套住它;选择一种外层调度方式即可。

截至本文核对的版本,/loop 依赖会话持续运行,忙碌时不会逐轮补跑错过的触发,循环任务七天到期。多个 /loop 放在同一个会话,也不意味着任务会并行执行。长期任务应按需要选择持久调度,独立执行者则需要独立工作区。调度条件与限制

人接管时看哪些东西

状态 含义 接下来的动作
needs_review 候选通过固定验收 看需求、补丁与证据,再决定是否应用
retry 普通失败,还剩尝试次数 下一轮带着失败输出重试
needs_human 越界、无法复现、被中断或超过次数 检查原因,修正任务或环境后再安排工作
busy 另一个进程持有同一状态目录的锁 等当前执行完成
idle 当前没有可自动处理的任务 查看是否有待审查或待人工处理的任务

特别注意,idle 只表示本轮没有工作,并不代表所有任务成功。示例在崩溃后把遗留的 running 标成 needs_human,避免盲目重放。

人审查通过后,可自行应用补丁并重跑测试。demo 不实现自动合并、发版、外部消息、自动修改测试和自动积累长期记忆;这些能力分别需要独立的授权和验证设计。

怎样逐步用于自己的项目

先选择一个范围窄、能写出验收条件的任务。把当前工作流跑通,记录真实失败,再补充相应约束。初期由人合并发布,经过一段时间的实际验收,再按风险逐步调整自动化权限。

如果反馈量很大,可以在入口增加分类和去重;如果等待外部事件较多,可以用 webhook 替代高频轮询;如果独立任务很多,可以增加并行执行者。每增加一种能力,都需要明确它引入的共享状态、费用和失败恢复方式。

Anthropic 的长任务实践也强调了类似问题:跨会话保留进度,一次推进一项明确功能,并通过真实操作确认完成。仅仅把强模型放进循环,并不能替代这些工作。长时间运行 Agent 的工程实践

评估效果时,可以记录任务验收通过率、返工率、人工介入时间和每个成功任务的总成本。生成了多少行代码、运行了多久、调用了多少次模型,都只是过程数据。

当一个任务能够被准确描述、被独立验证,并在失败后停在可恢复的位置时,持续自动化才有了可靠的起点。

本文案例的入口是 WquGuru 的《yetone 的魔法》。项目事实引用固定仓库快照,工具用法核对截至 2026 年 10 月 14 日的官方文档。demo 是用于解释控制流程的独立教学实现。

comments powered by Disqus
记录自己
Built with Hugo
主题 Stack 由 Jimmy 设计