Repository navigation
perf(agent): 主回复只追加上下文,在上一轮实际发出的请求后面续接 - #1233
Merged
Merged
Conversation
经 sub2api 的 gpt 只在 instructions+工具末尾和上一次完整请求末尾复用缓存,逐轮重新组装的请求只能命中开头约 1.8 万 token。续接后同群连续 8 轮实测 gpt-6.1-sol 47.4% 升到 90.9%,deepseek-flash 72.2% 升到 91.2%;按配置档开关,默认关。
只追加不再做成可选项,主回复默认在上一轮实际发出的请求后面续接。尾部每轮整段照发,避免删过的说明读起来缺头少尾;顺带修掉没有群历史时头部 system 被重复追加的问题(之前被按行去重盖住了)。
去掉尾部去重后重测了同群连续 8 轮,文档里的表换成这次的数字,gpt 只有去重版本的数据,单独注明。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
主回复改成只追加:不再每轮重新组装请求,而是接在上一轮实际发给模型的请求后面继续发,只追加这期间的新群消息、这一轮完整的尾部和当前消息。默认开启,不设开关。同群连续 8 轮实测,deepseek-flash 命中率从 82.9% 升到 91.9%;gpt-6.1-sol 在早先的去重版本上从 47.4% 升到 90.9%。用 gpt-6.1-sol 回放 15 个轮次,没有出现回错消息。
Why
gpt、swe 主回复的缓存命中率只有 31%~55%,DeepSeek 是 85%。查下来不是切换账号造成的:同一账号、5 分钟内的连续请求,命中也只有约 21%。原因在上游的缓存方式。
经 sub2api 转发的 gpt(上游是 ChatGPT Codex)只在两个位置复用缓存:instructions 加工具定义的末尾,以及上一次完整请求的末尾。用一条 4.7 万 token 的生产请求实测:
现在每轮都会重新组装请求:尾部每轮都在变,上一轮的当前消息也被改写成历史行,所以新请求从来不是上一条的纯追加。生产上同一会话的连续请求,每条都恰好只命中 18432。DeepSeek 能复用到任意 128 token 的前缀,但历史窗口一往前滑,命中照样会断。
pi-agent 和 DeepSeek Harness 都是只追加的单循环。DSH 遇到系统提示变化时也是把新版追加到已缓存的历史后面,而不是重写开头。
改了什么
model/agent/continuation.go和runner.go:Request.Continuation带上一轮的前缀、本轮要追加的消息和校验键。校验键是系统提示、历史前契约段和工具定义的指纹;对得上就在前缀后接着发,Runner 自己的逐轮消息照旧插在当前消息之前,对不上就按Messages完整组装。Response.Transcript是本轮最后一次实际发出的请求(经过hooks.prepare,包含每步临时插入的工具执行状态),接上模型的回复,每个工具调用都配一条结果,作为下一轮的前缀。model/assistant/append_context.go和runtime.go:每个(配置档, 会话)在内存里存一份记录。withToolResultReminder:往回找时遇到 user 消息就停。否则续接时会越过当前消息,找到上一轮 send 的结果,误加「刚执行过工具」的提醒。docs/group-prompt-cache.md新增「只追加上下文」一节。影响
验证
缓存实测:10-27 生产同群连续 8 轮,尾部整段照发,两组各加随机前缀冷启动,不计第一轮:
gpt-6.1-sol 这次网关的账号全部处于限流状态,没能重测。早先尾部按行去重的版本测过:命中率 47.4% → 90.9%,未命中 token 16.1 万 → 4.9 万。
行为回放(gpt-6.1-sol,尾部整段照发):3 个群各取第 3~7 轮,共 15 个轮次,现布局和只追加各生成一次真实回复,逐条对照当前消息。15 轮回的都是当前消息,动作一致:要表情包的 3 轮都调了 sticker,查资料的那轮两边都去搜索,没有重复上一轮的话。
单元测试:
TestRunnerContinuationIsPureAppend:第二轮请求逐条以第一轮记录开头。TestRunnerContinuationFallsBackOnKeyMismatch:校验键对不上时退回完整组装。TestAppendContextAppendsNewHistoryAndWholeTail:只追加新历史,尾部整段保留,头部不重复。TestAppendContextRestartsWhenHeadChangesOrGrowsTooLong。TestReplyTurnsContinueAppendOnly:整条回复链路连发两轮,第二次请求以第一次请求加 send 开头,当前消息在最后。gofmt -l、git diff --check干净;./model/agent、./model/assistant、./webui全包测试通过。