1704 字
9 分钟
人生第一个开源 PR:从'这里是不是有 bug'到给阿里的框架提交修复

今天提了人生第一个开源 PR,给阿里的 AgentScope-Java 框架。

起因特别小。小到就是一行打印。


一行”目录还没创建”,我觉得不对劲#

我在学这个框架的记忆压缩,跑官方那个 MemoryCompactionExample 示例。它跑完最后会检查一下生成的记忆文件,然后打印出来。

结果我这儿永远是这一行:

── Memory files on disk ──
(memory/ directory not yet created)

目录没创建。没有任何文件内容。

按理说这个示例存在的意义,就是给你它生成的记忆文件。结果它自己打印”啥都没有”。这就很怪。

Claude 一开始给我的解释是”时序问题”——异步写入还没落盘,检查跑早了。听起来挺合理。但我心里犯嘀咕:真是没写完,那我程序都退出了,文件总该在了吧?

于是我干了件事:让它别急着下结论,去把那个临时目录翻出来看看到底有没有文件。


让它别猜,去实锤#

我让它带我看那个临时工作目录。一个 dir -Recurse 下去,真相就出来了:

memory-demo-session\
MEMORY.md ← 280 字节,明明在
memory\
2026-07-01.md ← 569 字节,也在

文件明明都在。只是不在示例去找的那个地方。

这下就不是”时序问题”了。是找错地方了。

顺着这条线让它深挖源码,根因浮出来:记忆文件是通过一个 WorkspaceManager 写的,它会自动加一层命名空间目录——默认策略下,没设 userId 时会退回用 sessionId 当目录名。所以文件真实落在 <workspace>/<sessionId>/memory/

而示例读的时候是这么写的:

Path memoryDir = workspace.resolve("memory"); // 少了 <sessionId> 这一层!
Path memoryMd = workspace.resolve("MEMORY.md");

写的时候带了一层 sessionId,读的时候没带。永远对不上。无论等多久都一样——跟异步、跟时序,半点关系没有。

我特意让它改完之后用同一个示例实跑一遍,对比给我看,别只嘴上说。修完再跑,那两个文件的内容乖乖打印出来了。这才算实锤。

一个我曾经以为”是不是我操作错了”的现象,最后是框架示例自己的 bug。


顺手又挖出第二个 bug#

修第一个的时候我还有个没解开的疑惑:这个示例跑下来,那个说好的”压缩”我一次都没见它触发过,对话消息数一路往上涨,从没变小。

这个文件顶部的注释白纸黑字写着:triggerMessages=6, keepMessages=2 所以”压缩会在几轮之后触发”。

可它就是不触发。

又是一轮源码深挖。这次的坑更隐蔽:控制”保留多少条”的那个参数 keepTokens 有个默认值,是”动态模式”。框架会按模型的上下文窗口,把它算成一个很大的 token 数(大概 8000)。而我这段 demo 对话总共才一千多 token——远远填不满 8000。于是切分逻辑一看”整段都没超”,就判定”这些我全都要留着,一条都不用压”,压缩直接跳过。

也就是说,那个注释在撒谎。照它现在的配置,压缩永远不会触发。

我让它临时加一行 .keepTokens(0)(强制按消息条数算)再跑。这次日志里终于出现了我等了半天的东西:

Compaction triggered: total=25 msgs / 1087 tokens, cutoff=23, keeping=2 msgs
context: 24 → 4 messages ★ compaction fired!

1087 tokens——正好坐实了:一千多,远小于 8000,所以之前每次都跳过。加了这行,压缩立刻正常工作。

这俩其实是一类毛病:示例的行为,对不上它自己的说明。 一个说”给你看记忆文件”却看不到,一个说”会触发压缩”却不触发。既然在改,那就一起修了。


提 PR:紧张,但流程比想象的清晰#

代码改好、本地验证通过,剩下就是把它送出去。这块我基本没经验,是被一步步带着走的。

我这个本地仓库是直接从官方 clone 的,我没有推送权。标准流程是 fork 到自己账号 → 推到自己的 fork → 对官方开 PR

中间有个我很在意的点:我本地为了练手,自己手写了两个 Java 文件,还有一堆学习时加的中文注释。这些不能混进 PR。 处理办法是:

  • git stash -u 把所有本地改动(包括我那俩练习文件)临时收起来
  • 同步官方最新代码、建一个专门的分支
  • git stash pop 把改动放回来
  • 关键一步——git add 的时候只跟那一个要修的文件的路径,其它一律不加

提交信息也有讲究,这个仓库用 Conventional Commits 规范(fix(examples): ... 这种前缀)。沿用它,PR 看起来更专业。

推上去,网页开 PR,填好标题和一段说清”问题→根因→修复→验证”的描述。点下那个绿色的 Create pull request,#1978 就挂到官方仓库上了。

后来第二个 bug 的修复,我用 git commit --amend 把它并进原来那个提交,再 git push --force-with-lease 强推更新,PR 自动就跟着变了。学到一个新姿势。


CI 红了,虚惊一场#

PR 开完没多久,检查那儿冒出个红叉。第一个开源 PR 就挂 CI,心里咯噔一下。

点进去看,失败的是 agentscope-harness 模块的一个单元测试。

但我这个 PR,从头到尾只改了 agentscope-examples 里的一个示例文件,agentscope-harness 那个模块我一行都没碰过。 一个 git diff 确认下来,我的分支跟官方 main 的全部差异就那一个文件。

一个我没动过的模块的测试挂了,逻辑上不可能是我引起的。大概率是官方 main 上一个偶发测试,或者他们自己那边的问题。虚惊一场。


这事从头到尾,代码改动加起来也就二十来行。但我觉得最值钱的不是那二十行。

是那个”这里是不是有 bug”的嘀咕,和那句”你别急着改,先给我实锤”。

如果我当时信了”哦是时序问题”,这事就过去了,我还会以为是自己哪儿没配对。但凡多问一句、多要一次证据,藏在底下的两个 bug 就露出来了。

工具越来越强,你随口一句它就能给你一版看着挺像那么回事的答案。但”它说得对不对”这个判断,还得自己来。不轻信、要证据——这大概是我这个第一次开源贡献里,真正学到的东西。

人生第一个开源 PR:从'这里是不是有 bug'到给阿里的框架提交修复
https://liuhuanblog.top/posts/dev/my-first-open-source-pr/
作者
liuhuan
发布于
2026-07-01
许可协议
CC BY-NC-SA 4.0