feat(config,runtime): 上下文预算按模型窗口推导 + usage/缓存可观测 + sidecar 存全文(T1)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 窗口声明:models.json 组级 contextWindow + env LLM_CONTEXT_WINDOW(per-group materialize 播种,0 不写盘、负数报错)。

- 预算三态:CONTEXT_MAX_TOKENS 未设置 ⇒ 按窗口推导;>0 显式覆盖;0|负 关闭压缩(不变)。推导 budget = min(窗口×比例, 窗口−输出保留),比例默认 2/3(CONTEXT_BUDGET_RATIO,1M ⇒ 699050 tokens ≈ 683Ki);输出保留 = max(32768, 该组 maxTokens) 且封顶窗口/2(防配置矛盾时退化成 1 token);窗口未声明 ⇒ 回落 131072 + 恰好一条 WARN。接线在组合根 wire_context.go(唯一决策点)。

- usage/缓存可观测(此前**完全不可观测**):Response 增 CacheHit/CacheMiss/Reasoning tokens + CacheHitRate;解析 OpenAI 兼容(prompt_cache_hit_tokens/prompt_cache_miss_tokens/cached_tokens/reasoning_tokens)与 anthropic(cache_read_input_tokens/cache_creation_input_tokens)两种形状;记录点在适配器 record(换入口不丢成本日志),日志无凭据;单次输入 ≥预算 90% 记 WARN(不阻断)。

- 根治清单「续读」假承诺:抽取期不再按注入预算截断(sidecar 存全文,只受 MaxSidecarBytes=8 MiB 安全上限,ExtractorVersion v1→v2 旧 sidecar 失效重抽);注入预算改在装配期生效 ⇒ ref.Chars/Truncated 是注入侧事实、游标真是继续读的起点;document-read 的 limit 上限改为独立具名常量。

- 顺手修越界:config.go 505 行(超 500)拆出 llm.go。

- 主会话独立验收:隔离 worktree(HEAD + 本切片 39 文件,排除在途 gateway)build/vet 绿、全树 59 包 0 FAIL、linux+darwin 交叉编译绿;变异独立复现(比例改 0.25 ⇒ 推导用例红 262144 vs 699050)。
This commit is contained in:
AskaEth committed 2026-10-04 17:57:08 +08:00
1 parent 5b2b385524
commit 72b95df22b
39 files changed
+2496 -377

No files matched your search

+28 -7
View File
@@ -97,6 +97,14 @@ LLM_REASONING_KIND=
# ⚠️ 非法值(非 bool,如 maybe)= 启动即报错,不静默回退。
# LLM_MULTIMODAL=false
# 声明该端点的【模型上下文窗口】(token)。窗口是上下文压缩预算的推导依据:
# 未设置 CONTEXT_MAX_TOKENS 时 budget = min(窗口 × CONTEXT_BUDGET_RATIO, 窗口 − 输出保留)。
# ⚠️ 本仓没有任何 provider 侧的窗口发现代码(GET /models 的自动发现另立设计),窗口只能声明:
# 不声明就回落 131072(128 Ki)并记一条 WARN,默认模型的 1M 窗口因此吃不到。
# 它必须与 models.json 组的 contextWindow 一致(那一处是面板一等来源,本键只给 .env 合成的组播种)。
# 负数 = 启动即报错。
# LLM_CONTEXT_WINDOW=1048576
# Provider = anthropic:Anthropic Messages /v1/messages(tool_use.input 为结构化对象,工具循环更稳)
# ANTHROPIC_BASE_URL=https://api.anthropic.com
# ANTHROPIC_API_KEY=sk-ant-xxxxx
@@ -199,13 +207,26 @@ CAPACITY_L3_CACHE_TTL=5m
# 可配置);模型派发时可用 subagent_spawn 的 timeout_seconds 参数按调用覆盖。
SUBAGENT_TIMEOUT_SEC=600
# ---- 护栏与上下文预算(design/04 §1.3 / §5) ----
# 上下文溢出压缩预算(token,离线估算值)。默认启用:会话估算上下文超过它时,
# 更早的轮次被摘要成一条前导摘要消息,近期尾部(含尾部工具结果)原样保留。
# 默认 32768 = loop.DefaultMaxContextTokens:默认模型(gpt-4o)窗口 128K,
# 它只占四分之一,给系统提示词/工具 schema/本轮输出留足余量,且远高于日常对话
# 体量(正常聊天几乎不触发压缩)。设 0(或负数)= 关闭压缩,行为与接线前一致。
CONTEXT_MAX_TOKENS=32768
# ---- 护栏与上下文预算(design/04 §1.3 / §5、附录 A.3) ----
# 上下文溢出压缩预算(token,离线估算值)。会话估算上下文超过它时,更早的轮次被摘要成
# 一条前导摘要消息,近期尾部(含尾部工具结果)原样保留。三种取值:
# · 未设置(默认,即把本行注释掉)= 按模型窗口推导:
# budget = min(窗口 × CONTEXT_BUDGET_RATIO, 窗口 − 输出保留)
# 输出保留 = max(32768, 本组 maxTokens);窗口未声明 ⇒ 回落 131072 并记一条 WARN。
# · > 0 = 显式覆盖(原样使用)
# · 0 / 负数 = 关闭压缩(行为与本条接线前逐字节一致)
# ⚠️ 旧模板里的 CONTEXT_MAX_TOKENS=32768 取自「gpt-4o 128K 窗口的四分之一」,那个前提已失效
# (默认模型窗口 1M)。若你的 .env 里还留着它,它就是【显式覆盖 32768】,会盖掉按窗口推导
# (1M 窗口 ⇒ 699050 token)——要吃到推导,把那一行注释掉。固定低阈值会既丢上下文、又因反复
# 压缩作废 KV 缓存前缀而反复烧钱(附录 A.2)。
# CONTEXT_MAX_TOKENS=
# 推导比例:预算占窗口的比例,主人 2026-10-04 裁决取 1/2–3/4 之间,默认 2/3。
# 写法:小数(0.5)或分数(1/2)都接受;非法值 = 记一条 WARN 并回落 2/3。
# CONTEXT_BUDGET_RATIO=2/3
# 单份附件注入模型可见上下文的字符上限(sidecar 仍存全文,只受 8 MiB 安全上限约束)。
# 留空 = 按推导出的预算算:推导预算的 1/8(按一个回合最多 5 个附件留余量),下限 64K 字符。
# 它决定「每回合往上下文里放多少」,也因此决定清单行给的续读游标从哪里开始。
# DOCUMENT_MAX_INJECT_CHARS=
# 内容策略关键词表(逗号分隔,大小写不敏感的子串匹配)。留空 = 不挂关键词护栏
# (项目不内置任何词表,只有你显式声明过的词才会被拦)。敏感信息(API key /
# bearer token / 密码 / 私钥)为阻断档、请求与响应各有长度上限,这两项是代码