fix(guardrail): 输入护栏上限按推导预算派生(修「大附件被拦死」)+ 第 5 条 flake
- 真 bug:文档注入预算(1M 窗口 ⇒ 349525 字符)> 输入护栏硬编码上限(262144)⇒ 带 744KB 附件的 POST /chat 直接 input_length_exceeded/guardrail_input,整个回合作废(主会话活服务实测)。 - 修法:guardrailMaxInputChars → guardrailMinInputChars(256Ki 下限) + 纯函数 deriveGuardrailInputLimit(log, override, budgetTokens, docInjectChars):派生值 = max(下限, 推导预算 tokens)(1M ⇒ 699050);新增 env GUARDRAIL_MAX_INPUT_CHARS(未设/0=派生、>0=覆盖、<0=WARN 回落);预算只在 wire_context 推导一次并经 buildGuardrailPipeline(ctxBudget) 传入(避免「窗口未声明」WARN 记两遍);输出上限 65536 刻意不参与派生(有显式回归钉住)。 - 关系不变量:文档注入预算 ≤ 输入上限,人为破坏 ⇒ 一条 WARN(不静默)。 - 第 5 条 flake(exec/TestTerminal_Timeout_Killed):仅改测试、terminal.go 保持 pristine;根因不是「杀进程失效」而是旧断言的 ~200ms 精度把宿主负载下的进程创建耗时算了进去(重压下 cmd.Start 会直接返回 deadline exceeded,红行却报 process not killed)⇒ 改为「控制组证明同命令不设短超时正常退出」的确定性证明 + 把墙钟断言降级为 10s 语义判别界(子进程自然存活期拉到 30s)。 - 主会话独立验收:隔离 worktree(G2 的 10 个文件,排除 M1 在途的 wire_channels.go)build/vet 绿、全树 59 包 0 FAIL、双平台交叉编译绿、Guardrail* 全 PASS、exec -count=20 绿;变异独立复现(把派生值退回硬编码 ⇒ 4 条红,含 budget=699050 → 262144)。 - 残留缺口(已排 G2b):护栏量的是整段装配请求、注入预算是单份 ⇒ ≥2 份各撞注入预算的大附件仍可能被拦。
This commit is contained in:
1 parent
d37d4a899b
commit
51c1672355
10 files changed
+577
-48
No files matched your search
@@ -152,9 +152,19 @@ func (a *app) wireRuntime() error {
|
||||
}
|
||||
a.wireVision()
|
||||
|
||||
// 上下文溢出压缩:预算按模型窗口推导(CONTEXT_MAX_TOKENS 未设置时;
|
||||
// 显式 >0 = 覆盖、0/负 = 关闭),推导与「窗口未声明」的 WARN 都在
|
||||
// wire_context.go 一处完成。Compactor 不注入,由 loop 选用内置的结构化
|
||||
// compactor(离线、不调模型),并按同一份注入预算估算文档部件的代价。
|
||||
//
|
||||
// 它必须在护栏**之前**算出来:请求侧字符上限是这份预算的函数(切片 G2:
|
||||
// 输入上限 = max(256 Ki, 推导预算 token 数)),否则 1M 窗口下 349525 字符的
|
||||
// 附件注入会先被硬编码的 262144 上限整回合拦死。
|
||||
ctxBudget := a.contextBudget(envPath)
|
||||
|
||||
// 默认护栏策略在 buildGuardrailPipeline 里集中定义(敏感信息阻断 +
|
||||
// 长度上限 + 仅当配置提供时的关键词表,见 wire_guardrail.go)。
|
||||
guards := a.buildGuardrailPipeline()
|
||||
guards := a.buildGuardrailPipeline(ctxBudget)
|
||||
instructions := a.defaultInstructions()
|
||||
// 默认角色的 agent 也要绑它自己的 persona 视图:models.json 的 personaModels /
|
||||
// personaProviders 可能给默认角色单独指定模型或提供商,用裸栈会漏掉这一层
|
||||
@@ -252,12 +262,6 @@ func (a *app) wireRuntime() error {
|
||||
approver := audit.NewApprover(a.log)
|
||||
approvalGate := a.buildApprovalGate(approver)
|
||||
|
||||
// 上下文溢出压缩:预算按模型窗口推导(CONTEXT_MAX_TOKENS 未设置时;
|
||||
// 显式 >0 = 覆盖、0/负 = 关闭),推导与「窗口未声明」的 WARN 都在
|
||||
// wire_context.go 一处完成。Compactor 不注入,由 loop 选用内置的结构化
|
||||
// compactor(离线、不调模型),并按同一份注入预算估算文档部件的代价。
|
||||
ctxBudget := a.contextBudget(envPath)
|
||||
|
||||
a.loop = loop.New(loop.Options{
|
||||
Agent: defaultAgent,
|
||||
Agents: agents,
|
||||
|
||||
Reference in new issue
Block a user