Repository navigation
feat(minimax-bot): 群接力的跳数上限与摘要交接(默认关闭) - #68
Conversation
MyPrototypeWhat
left a comment
There was a problem hiding this comment.
感谢这个 PR。两道护栏默认关闭、只放在面板里,relay-guard.mjs 写成纯函数,测试也补齐了。我在本地拉了 711fa19 跑了一遍,下面是几点意见。
需要修改
1. 护栏关闭时,英文界面下的群常驻简报不再逐字一致
我实际生成了护栏全关时的群常驻简报,拿 711fa19 和 main(0.7.8)的输出逐字节比较:英文下多了一个空格(main 是 "...from a private chat.\n\n## Team",这个 PR 是 "...from a private chat. \n\n## Team"),中文一致。原因在 plugins/minimax-ai/minimax-bot/miniapp/node/engine.mjs:381:数组末尾总会追加 relayGuard ? t('brief.groupOneOwner') : '',然后整体 join(t('brief.sentenceJoin'))。中文的 brief.sentenceJoin 是空串(strings.mjs:245),英文是一个空格(strings.mjs:554),所以护栏关闭时英文末尾会多一个空格。这和 PR 描述里"护栏关着时与官方版逐字一致"的说法不符,已有会话升级后也会看到简报变化。群提示词(groupPrompt)本身中英文都一致。
建议只在需要时才把这一句放进数组,例如:
[
t('brief.groupNoPost'),
...(relayGuard ? [t('brief.groupOneOwner')] : []),
].join(t('brief.sentenceJoin'))另外,tests/relay-guard.test.mjs:169 那条"逐字一致"的测试比较的是同一份新代码在开/关两种状态下的输出,所以没能发现这个问题。建议补一个断言:护栏关闭时简报不以空格结尾(或者直接和一份 0.7.8 的基准字符串比较),中英文各测一次。
可选建议
- 收口回合实际是第 N+1 回合,而且没带 taskId:描述和
README.md:36说收口"替代被拦下的那一跳,不是额外多一轮"。但看实现,到上限时被拦下的那一跳并没有执行(engine.mjs:1177、engine.mjs:1217),而convergeGroup会让最后发言的 Bot 再跑一次模型回合(engine.mjs:341),所以一轮实际跑 limit+1 次模型回合;README.md:105写的 "spends one extra Agent turn" 反而更贴近实现。另外,链里其他回合都会传taskId: options.taskId(engine.mjs:1190),收口回合的调用(engine.mjs:341,参数从 engine.mjs:1230 传入)没有带taskId,由任务派发进来的群接力,收口这一回合就不会挂在原任务下。建议把描述/README 统一成一种说法,并给收口回合也带上taskId。 - 服务端没有上限:面板
panels.js里的输入框写了max: 20,但relay-guard.mjs:58的hopLimit只处理了下限,通过 HTTP 接口直接 PATCH 一个很大的数也会被接受。可以在hopLimit里也夹一下上限,和面板保持一致。 - replyMode 为 all 时的行为:engine.mjs:1177 的检查在队列里还有人时就会收口。"所有人回复"模式下初始队列本身就有多个成员,这时上限会截掉后面的成员,并让最后一个发言的成员做收口。如果这是有意为之,在面板提示或 README 里提一句会比较清楚;如果不是,可以只统计由 @ 交接产生的跳数。
- 交接内容可能重复:上一位的回复本身已经出现在下一位提示词的"新消息"里(groupPrompt 会列出来),交接块又把它的尾部带了一遍。这不影响正确性,只是在描述里说明一下交接块的定位(强调结论),能帮使用者判断要不要打开它。
- 版本号:这个 PR 把版本升到 0.7.9,#56 目前也是 0.7.9。两者谁后合入,谁就需要再升一个版本(plugin.json、package.json、mcp.mjs 里的 serverInfo 以及 README 的版本小节),这里先提个醒。
我这边核对过的
- 改动只在插件目录内,没有读写 dataDir 以外的东西,没有新增文件读写或网络调用。
- 模型这一侧改不了这两个设置:MCP 的
create_group/update_group都没有新增参数(mcp.mjs 里的 schema 和参数转发都没变),list_team返回的字段也没变,硬塞max_hops/handover不会生效,测试里也覆盖到了。面板走的 HTTPPATCH /api/groups/:id(server.mjs:615)会把请求体原样交给updateGroup,和其他群字段一样,这是面板自己的写入路径。 - 护栏关闭时
groupPrompt不加任何内容,接力循环的行为和之前一致(limit 为 0 时两处新判断都不会触发)。 - 新测试放在插件自己的
tests/里,仓库根目录test/没有改动。 - 本地结果(Node 22):先在插件的
tests/里执行npm ci,再跑node --test tests/*.test.mjs,231/231 通过;不装依赖时会有 7 个 UI/jsdom 相关的测试文件失败,main 上也是同样这 7 个。仓库根目录npm run check通过 validate,69 条测试全部通过。
主要就是第 1 点,改完我再看一眼。
…guardrails; clamp the hop limit; file the wrap-up under its task
|
按你说的第一条改了,另外几条也一并处理了,麻烦再看一眼。 英文多一个空格那条:one-owner 那句现在只在护栏开着时才进数组(就是你写的那种展开写法),不再拿一个空串占位,所以 join 的分隔符不会多出来。我把上游 0.7.8 和这个分支各自生成的群常驻简报拉出来逐字节比了一遍,英文和中文现在都一样(英文 6021 字符、中文 3095 字符,两边完全一致)。测试也补了:护栏关着时简报里不能出现那句 one owner、结尾不能是空白,而且群那一段必须正好是基线那几句用本语言的分隔符接起来——中英文各测一次。我自己的门禁里也补了一个专门比常驻简报的场景,之前只比了群提示词那一段,所以这个洞才漏过去。 收口回合那条:描述是我写错了。被拦下的那一跳确实不执行,但收口自己就是一次模型回合,所以到上限的一轮实际跑 limit+1 次回合,README 里那句 one extra Agent turn 反而是准的。我把 PR 描述、README 的 0.7.9 小节和代码注释都统一成 limit+1 这个说法。taskId 也补上了:收口那一回合现在带着 taskId 进去,所以从任务派发进来的群接力,收口也挂在原任务下。 上限没夹那条:hopLimit 现在把上限夹到 20,跟面板输入框的范围一致,不管这个值是面板填的还是直接 PATCH 进来的,补了一条断言。 replyMode=all 那条:行为我留着没改——上限算的是这一轮一共几个 Bot 说话,初始就在队列里的成员也算进去,跟「最多几个 Bot 说话」是一个意思。我在面板提示和 README 的 0.7.9 小节里都写明了这句。如果你们的口径是只数 @ 交接产生的跳数,我改起来很快,这是仅个人判断,需要确认。 交接内容可能重复那条:行为也没改,定位在文档里说清楚了——上一位的原话本来就在「新消息」里,交接块是把它的结论再强调一遍,模型自己判断要不要用。面板提示和 README 都加了这句。 版本号那条:按你说的保持 0.7.9。#56 现在在 0.7.8,今天没有真冲突;哪边先合,后合的那个再往后升一位。 边界照旧,正文里那几条没变。 |
MyPrototypeWhat
left a comment
There was a problem hiding this comment.
Reviewed at 9f37a28.
Thanks for the fixes. The three items from the last pass are in good shape:
- Standing brief with the guardrails off.
buildStandingBriefonly spreadsbrief.groupOneOwnerwhen the guard is on (engine.mjs:385-388), so the Englishbrief.sentenceJoinspace is no longer left hanging. With both knobs off, the group part is the same four baseline sentences joined by the language separator, then## Team/## 团队with no trailing space. The new test covers en and zh against that baseline (tests/relay-guard.test.mjs:177-201). - Hop limit clamped to 20.
HOP_LIMIT_MAX = 20andhopLimitusesMath.min(…, HOP_LIMIT_MAX)for both number and digit-string inputs (relay-guard.mjs:50-72). Create/update go through the same helper (team.mjs:337,:370), so a large PATCH value is stored as 20. Covered bytests/relay-guard.test.mjs:95-101. - Wrap-up carries
taskId.convergeGrouppassestaskId: options.taskIdintorunBotTurn(engine.mjs:349-351), and the group close path forwards it (engine.mjs:1237-1239). The new test files the wrap-up undertask_abc(tests/relay-guard.test.mjs:204-219). README / code comments now say a round that hits the limit runslimit + 1Agent turns.
Defaults stay off, the MCP catalogue still omits max_hops / handover, and changes stay inside the plugin tree. Locally on Node 20.19.2: plugin suite 236 pass / 0 fail (node --test plugins/minimax-ai/minimax-bot/tests/*.test.mjs); scripts/validate.mjs reports plugins/minimax-ai/minimax-bot: OK and repository: OK; root test/**/*.test.mjs is 69/69 pass. GitHub check runs for this head were empty / pending when I looked (mergeable_state unstable).
Optional
-
PR description still has the old wrap-up wording. The GitHub PR body still says the wrap-up “替代被拦下的那一跳,不是额外多一轮” (not an extra turn). README and the code comments on this head correctly say
limit + 1. Worth aligning the PR body with README so reviewers don’t get two stories. -
Version vs #56. This PR correctly ships as
0.7.9. #56 is still on0.7.8on its current head, so there is no live clash today; if both land, the later merge should bump past whatever is already on main (plugin.json,package.json,mcp.mjsserverInfo, and the README version section). -
convergeGroupJSDoc. The options typedef still listssourceMessageId/sourceMessageIds/signaland nottaskId(engine.mjs:333), while the implementation now uses it. A one-line typedef update would match the code.
|
三条可选的我都处理了。你抓的正文那句我确实漏了——代码注释和 README 那一轮改了,PR 正文还写着旧说法,现在正文也统一成 limit+1 了(测试数也改成 18)。版本号按你说的保持 0.7.9,#56 现在还在 0.7.8,就没冲突,哪边先合我再往后升一位(plugin.json、package.json、mcp.mjs 的 serverInfo、README 的版本小节四处一起)。convergeGroup 的 typedef 补上 taskId 了(engine.mjs:325),刚推的 8cebd83 就是这一行,没强推。 本地这边同步的数:插件套件 236 过 / 0 挂,validate 报 minimax-bot: OK 和 repository: OK,根测试 69/69——和你跑的一致,我这轮在 Node 22.23.3 上跑的,你那边 20.19.2,两个版本都对得上。CI 那个 check 在 03:40 也跑起来了,success,mergeable_state 现在是 clean。 麻烦你再看一眼。 |
给群接力加两道护栏,都是群自己的字段,默认都是关的:maxHops 填 0 表示不限,和现在完全一样;handover 默认 false。
跳数上限说的是一条消息引发的接力链最多几个 Bot 说话(上限 20,面板和接口夹的是同一个范围)。到上限时不再 @ 别人,群里给一条系统提示(已达接力上限(N 跳),本轮讨论在此收口),并让刚说话的那个 Bot 收个口。被拦下的那一跳不执行,但收口自己就是一次模型回合,所以到上限的一轮实际跑 limit+1 次模型回合;这一回合带着 taskId 进去,所以从任务派发进来的群接力,收口也挂在原任务下。
摘要交接是把上一位的结论放进下一位的提示词里,做法是把上一位回复的尾部带过去。下一位因此不用再翻一遍工具重新挖同一件事。它只是提示词里的一段文本,不额外起模型回合、不额外调工具、不写记忆。上一位的原话本来就在"新消息"里,交接块是把结论再强调一遍,模型自己判断要不要用。
这两个设置是面板专属的:我在群设置里填,MCP 的 update_group 里没有这两个参数,模型即使硬塞也不生效。理由是上限属于成本与安全的设定,不该由正在接力的那一方自己决定要不要继续接。相应地,模型能看到的工具目录与 list_team 的返回,跟改之前逐字节相同。
上限算的是这一轮一共几个 Bot 说话,初始就在队列里的成员也算进去——"所有人回复"的群里,这个上限连第一轮的几位成员一起算。
为什么做这个:官方 README 自己写着 There is no fixed reply cap。我在 mcode 里对官方版跑了一套 8 步对照,第 4 步只是群里 @ 了一个成员问标题怎么起,结果一轮接力跑了 4 跳(考据→执笔→考据→执笔)、313 秒、6 次工具调用,最后也没落到一个能用的标题上。
测过什么:上游 0.7.8 自己的 216 条测试,214 过。另外 2 条(头像库的并发上传、写入失败后重试)在我这台机器上单独跑通过、全量跑失败,上游原版全量跑也是同样这 2 条,所以不是这次改动带来的。我给这个功能补了 18 个测试用例:上限填 0 等于不限,写坏的值读作不限而不是把群静音,超过 20 会被夹住,护栏关着时群里的常驻简报与群提示词都与官方版逐字一致(中英文各一次),maxHops=2 时四跳链只跳两跳并收口,收口那一回合自己失败时链路照样停在两跳、通知仍在,模型改不了这两个设置。
边界:宿主 permissionMode 是 bypassPermissions,工具权限的执行门一次都不会触发,这套护栏是行为约束,不是安全边界。我只核了上游 0.7.8 的代码,其他版本未验证。默认跳数给多少、收口时要不要在群里明说,这是我仅个人判断、不是结论,需要确认——我在 issue #66 问过。实跑环境:zcode 里的 m3.1 flash 迭代代码,在 mcode 里对着官方 bot 和自己改的 bot-lab 做 8 步对照,机器是 linux-ubuntu-24.04。