Skip to content

perf(agent): 可选只追加上下文,主回复在上一轮实际发出的请求后面续接 - #1229

Closed
SuInk wants to merge 6 commits into
claude/presentation-rules-headfrom
claude/append-only-context
Closed

SuInk wants to merge 6 commits into
claude/presentation-rules-headfrom
claude/append-only-context

Conversation

@SuInk

@SuInk SuInk commented Oct 27, 2026

Copy link
Copy Markdown
Owner

新增配置档开关「只追加上下文」(append_only_context,默认关)。打开后,主回复不再每轮重新组装请求,而是接在上一轮实际发给模型的请求后面继续发,只追加这期间的新群消息、尾部里变了的行和当前消息。同群连续 8 轮实测,gpt-6.1-sol 命中率从 47.4% 升到 90.9%,deepseek-flash 从 72.2% 升到 91.2%;用 gpt-6.1-sol 回放 15 个轮次,没有出现回错消息。

叠在 #1228(尾部逐字不变的说明挪到历史之前)上,那边合并后改基到 main。

Why

gpt、swe 主回复的缓存命中率只有 31%~55%,DeepSeek 是 85%。查下来不是切换账号造成的:同一账号、5 分钟内的连续请求,命中也只有约 21%。原因在上游的缓存方式。

经 sub2api 转发的 gpt(上游是 ChatGPT Codex)只在两个位置复用缓存:instructions 加工具定义的末尾,以及上一次完整请求的末尾。用一条 4.7 万 token 的生产请求实测:

新请求和上一次的关系 命中 token
完全相同 / 只在末尾追加两轮 46848
改动 input 第 0 条、80% 或 95% 位置的任意一处 18304

现在每轮都会重新组装请求:尾部每轮都在变,上一轮的当前消息也被改写成历史行,所以新请求从来不是上一条的纯追加。生产上同一会话的连续请求,每条都恰好只命中 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:每个(配置档, 会话)在内存里存一份记录。以缓存断点为界:
    • 历史区只追加记录里还没有的群消息,机器人自己发过的回复跳过;摘要和媒体索引不再追加,因为续接时历史不滑动。
    • 尾部按行去掉记录里已经出现过的说明(20 字以下的短行不参与去重)。
    • 当前消息原样放在最后。
    • 头部 system 变了、续接请求超过本轮完整组装的 2 倍、上一轮失败、进程重启,都会重新开一份。定时查询、事件触发这些后台入口不参与续接。
  • withToolResultReminder:往回找时遇到 user 消息就停。否则续接时会越过当前消息,找到上一轮 send 的结果,误加「刚执行过工具」的提醒。
  • 后台「上下文 → 每轮带什么」新增开关,放在「单次请求上下文上限」下面。
  • 文档:docs/group-prompt-cache.md 新增「只追加上下文」一节。

影响

  • 默认关,不打开时行为不变。
  • 打开后请求逐轮变长:按 10-27 生产 113 个相邻轮次算,每轮中位数多约 4600 字,大约十几轮重开一次,重开那一轮不命中。被缓存的 token 便宜,按生产数据拟合,每千个缓存 token 首字延迟只增加约 6 毫秒。
  • 模型的上下文里会留着以前的工具结果和回复,旧轮次的「【当前需要回复的消息】」标记也还在。回放中没有看到它因此回错消息。

验证

  • 缓存实测:10-27 生产同群连续 8 轮,现布局和只追加各加随机前缀冷启动,不计第一轮:

    现布局 只追加
    gpt-6.1-sol 命中率 47.4% 90.9%
    gpt-6.1-sol 未命中 token 16.1 万 4.9 万
    deepseek-flash 命中率 72.2% 91.2%
    deepseek-flash 未命中 token 7.9 万 4.4 万
  • 行为回放(gpt-6.1-sol):3 个群各取第 3~7 轮共 15 个轮次,现布局和只追加各生成一次真实回复,逐条对照当前消息。15 轮都回的是当前消息,动作一致(要表情包的 3 轮都调了 sticker,查资料的那轮两边都去搜索),没有重复上一轮的话。

  • 单元测试:

    • TestRunnerContinuationIsPureAppend:第二轮请求逐条以第一轮记录开头。
    • TestRunnerContinuationFallsBackOnKeyMismatch:校验键对不上时退回完整组装。
    • TestAppendContextAppendsOnlyNewHistoryAndChangedTail、TestAppendContextRestartsWhenHeadChangesOrGrowsTooLong。
    • TestReplyTurnsContinueAppendOnly:整条回复链路连发两轮,第二次请求以第一次请求加 send 开头,当前消息在最后。
  • 前端:vue-tsc --noEmit、npm test(282 项)通过;demo 模式下确认开关显示在「上下文 → 每轮带什么」,能正常打开。

  • gofmt -l、git diff --check 干净;./model/agent 测试通过。

SuInk added 6 commits October 27, 2026 01:42
API tools 只剩 agent_finalize、tool_search、tools_execute;主人专属和随发言者变化的工具目录、规则、契约和预算挪到历史之后,主人和群友交替说话时前缀逐字相同。tool_search 支持 names 和 query,tools_load 保留为别名;按需工具参数合法就直接执行,不必先加载。
媒体索引按内存短期上下文排除窗口内消息,把窗口里刚发的图也列进来,每来一张图就变;条目也没在身份隐私代理里登记,一部分真实消息 ID 发给了模型。现在按实际渲染的历史窗口过滤、逐条登记别名,并移到历史之前跟着前缀缓存走。跨群记忆常驻上限 4 改 6,并提示模型更多的用 chat_history 跨群搜。
生产上 6.6% 的轮次因正文带真实换行被打回重写,约九成纯文本回答被要求用 agent_finalize 原样重发,每次多一轮约 4 万 token 且不命中缓存。换行在发送层本就等同 [diana-line],纯文本也只在空文本或整段括号动作描述时才需要补试。
回放里「纯文本即结束、只有静默才调收尾工具」让静默翻倍;合成一个 send、只调 send 才收尾、和其他工具同步时作中途发言后,静默和工具调用都不变差。silent/silent_reason 字段去掉,say 改成 Config.Interim 回调。
回复形式规则、提及与引用说明、@ 规则、能力权限、记忆时间规则、工具执行状态说明、Skills 目录和联网调研要求在同群相邻两轮 100% 相同却每轮重算。挪到历史前后 DeepSeek 实测命中 87.0% 升到 89.4%,gpt-6.1-sol 回放行为不变。
经 sub2api 的 gpt 只在 instructions+工具末尾和上一次完整请求末尾复用缓存,逐轮重新组装的请求只能命中开头约 1.8 万 token。续接后同群连续 8 轮实测 gpt-6.1-sol 47.4% 升到 90.9%,deepseek-flash 72.2% 升到 91.2%;按配置档开关,默认关。
@SuInk
SuInk force-pushed the claude/presentation-rules-head branch 3 times, most recently from b6ee0cc to 10615ca Compare October 27, 2026 15:32
@SuInk
SuInk deleted the branch claude/presentation-rules-head October 27, 2026 15:53
@SuInk SuInk closed this Oct 27, 2026
@SuInk

SuInk commented Oct 27, 2026

Copy link
Copy Markdown
Owner Author

已由 #1233(主回复只追加上下文,在上一轮实际发出的请求后面续接)取代:本 PR 的 base 分支随 #1228 合并被删,无法重新打开。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant