Files
HomeAgent/internal/agent/core/tokenbudget_test.go
JianFeeeee 6ddef5e49f feat(llm): 声明真实上下文窗口 + 工作区间与窗口分离
问题:core.llm.model=AUTO,而 ModelContextWindow("auto") 匹配不到任何分支、
掉进 default 32768 —— 该源真实窗口是 1M(实测 990,034 token 的 prompt 通过),
内核却按小 30 倍的窗口算全部预算。

三处改动:
1. ModelContextWindow 补 deepseek-v4/v3 → 1M;推断不出时打日志(静默降级是
   这次问题的成因,不能再默默退回一个小值)。
2. sourceFieldDefs 补 context_window 声明:它早已被 readInt 读进 LLMSource
   并透传到 provider,但没进这张表 ⇒ WebUI 里看不见也改不了。
3. ComputeTokenBudget 新增 maxTargetTokens=600000:窗口 1M 不等于按 838K
   (80%)干活。标称窗口≠有效窗口,600K 是该源最优工作区间,所以把
   「窗口上限」(会不会被上游拒)与「工作区间」(预算分配)分开。

配套 core.llm.max_tokens 4096→32768(实测长输出样本达 18,272 token,
16384 仍会截断;上限是 cap 不是目标,短问答零成本)。

测试:tokenbudget_test.go 钉死封顶生效且小窗口不受影响;
context_window_test.go 钉死推断值与显式声明优先级。
2026-09-19 14:24:31 +08:00

52 lines
1.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package core
import (
"context"
"testing"
agentAPI "gitcode.com/JianFeeeee/HomeAgent/internal/agent/api"
)
// windowProvider 是只声明窗口的 Provider 桩(预算是纯函数,不需要真推理)。
type windowProvider struct{ n int }
func (windowProvider) Name() string { return "window-stub" }
func (windowProvider) Chat(context.Context, *agentAPI.CompletionRequest) (*agentAPI.CompletionResponse, error) {
return nil, nil
}
func (windowProvider) ChatStream(context.Context, *agentAPI.CompletionRequest) (<-chan agentAPI.StreamChunk, error) {
return nil, nil
}
func (p windowProvider) MaxContextTokens() int { return p.n }
// 回归2026-09-19窗口声明为 1M 后,工作面不能被 80% 带到 838K ——
// 该源最优区间是 600K超过就只剩成本与延迟。
func TestComputeTokenBudgetCapsTargetAtWorkingBand(t *testing.T) {
const sys = "系统提示词"
b := ComputeTokenBudget(windowProvider{1048576}, sys)
if b.MaxContext != 1048576 {
t.Errorf("MaxContext 应保留真实窗口:%d", b.MaxContext)
}
if b.TargetUsage != 600000 {
t.Errorf("TargetUsage 应封顶在 600K最优区间实际 %d", b.TargetUsage)
}
avail := 600000 - EstimateTokens(sys)
if b.MemoryTokens != avail/3 {
t.Errorf("MemoryTokens=%d want %d", b.MemoryTokens, avail/3)
}
if b.MemoryTokens+b.ContextTokens != avail {
t.Errorf("两部分应恰好用完可用预算:%d + %d != %d", b.MemoryTokens, b.ContextTokens, avail)
}
}
// 封顶只作用于“窗口远大于工作区间”的情形;普通窗口仍按 80% 走,
// 不能让这条约束把小窗口的预算也一起改掉。
func TestComputeTokenBudgetSmallWindowUnchanged(t *testing.T) {
b := ComputeTokenBudget(windowProvider{32768}, "x")
if b.TargetUsage != 26214 {
t.Errorf("小窗口应仍取 80%%got %d want %d", b.TargetUsage, 26214)
}
}