feat(config,runtime): 上下文预算按模型窗口推导 + usage/缓存可观测 + sidecar 存全文(T1)
- 窗口声明: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:
1 parent
5b2b385524
commit
72b95df22b
39 files changed
+2496
-377
No files matched your search
@@ -103,6 +103,14 @@ type Group struct {
|
||||
// last-resort fallback step then falls back to LLM_FALLBACK_MAX_TOKENS, and
|
||||
// beyond that the adapter default applies.
|
||||
MaxTokens int `json:"maxTokens,omitempty"`
|
||||
// ContextWindow 声明本组的模型上下文窗口(token)。它是**声明式**的窗口来源
|
||||
// (design/04 附录 A.3 第 1 条):本仓没有任何 provider 侧窗口发现代码,而
|
||||
// 上下文溢出压缩预算要按窗口推导(config.DeriveContextBudget),因此窗口必须
|
||||
// 由人来声明。0(默认,键被省略)= 未声明,推导回落到
|
||||
// config.DefaultModelContextWindow 并记一条 WARN;负数在校验阶段报错。
|
||||
//
|
||||
// 它是 `.env` 侧 LLM_CONTEXT_WINDOW 的对偶(per-group 播种,见 materialize.go)。
|
||||
ContextWindow int `json:"contextWindow,omitempty"`
|
||||
// PersonaModels binds an individual persona to its own model name within this
|
||||
// group (裁决 03.4.1: the "role" of per-role routing is the persona a turn
|
||||
// answers as, not the LLM message role). The carrier is deliberately
|
||||
@@ -150,6 +158,9 @@ func (g Group) Validate() error {
|
||||
if g.MaxTokens < 0 {
|
||||
return fmt.Errorf("maxTokens must be >= 0, got %d", g.MaxTokens)
|
||||
}
|
||||
if g.ContextWindow < 0 {
|
||||
return fmt.Errorf("contextWindow must be >= 0 (0 = 未声明,按回落窗口推导), got %d", g.ContextWindow)
|
||||
}
|
||||
if err := validatePersonaMap("personaModels", g.PersonaModels, false); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -318,25 +329,27 @@ func loadFile(path string) ([]Group, error) {
|
||||
func GroupFromEnv(env config.LLMConfig) []Group {
|
||||
if env.Provider == ProviderAnthropic {
|
||||
return []Group{{
|
||||
ID: ProviderAnthropic,
|
||||
Provider: ProviderAnthropic,
|
||||
BaseURL: env.AnthropicBaseURL,
|
||||
APIKey: env.AnthropicAPIKey,
|
||||
Model: env.AnthropicModel,
|
||||
Fallback: env.AnthropicFallback,
|
||||
Multimodal: env.Multimodal,
|
||||
Reasoning: ReasoningOptions{Kind: strings.TrimSpace(env.ReasoningKind)},
|
||||
ID: ProviderAnthropic,
|
||||
Provider: ProviderAnthropic,
|
||||
BaseURL: env.AnthropicBaseURL,
|
||||
APIKey: env.AnthropicAPIKey,
|
||||
Model: env.AnthropicModel,
|
||||
Fallback: env.AnthropicFallback,
|
||||
Multimodal: env.Multimodal,
|
||||
ContextWindow: env.ContextWindow,
|
||||
Reasoning: ReasoningOptions{Kind: strings.TrimSpace(env.ReasoningKind)},
|
||||
}}
|
||||
}
|
||||
return []Group{{
|
||||
ID: ProviderOpenAI,
|
||||
Provider: ProviderOpenAI,
|
||||
BaseURL: env.BaseURL,
|
||||
APIKey: env.APIKey,
|
||||
Model: env.Model,
|
||||
Fallback: env.Fallback,
|
||||
Multimodal: env.Multimodal,
|
||||
Reasoning: ReasoningOptions{Kind: strings.TrimSpace(env.ReasoningKind)},
|
||||
ID: ProviderOpenAI,
|
||||
Provider: ProviderOpenAI,
|
||||
BaseURL: env.BaseURL,
|
||||
APIKey: env.APIKey,
|
||||
Model: env.Model,
|
||||
Fallback: env.Fallback,
|
||||
Multimodal: env.Multimodal,
|
||||
ContextWindow: env.ContextWindow,
|
||||
Reasoning: ReasoningOptions{Kind: strings.TrimSpace(env.ReasoningKind)},
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user