- 真 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 份各撞注入预算的大附件仍可能被拦。
47 lines
2.7 KiB
Go
47 lines
2.7 KiB
Go
package config
|
||
|
||
// 本文件承载内容护栏里**由部署方声明**的那一部分(design/04 §5)。
|
||
//
|
||
// 敏感信息阻断档是代码内定的安全默认值(见组合根的 buildGuardrailPipeline),
|
||
// 不对外开放配置:它不该被一次误配置关掉。这里放的是两项**与部署环境相关**的
|
||
// 取值 —— 内容策略词表,以及请求侧字符上限的显式覆盖。
|
||
//
|
||
// 请求侧字符上限为什么必须能配:它的默认值由**推导出的上下文预算**派生
|
||
// (预算被抬高到 1M 窗口能吃的量级后,硬编码的 256 Ki 字符反而成了瓶颈,
|
||
// 会把「分享大文件」整条主线打成 guardrail_input 结束,见
|
||
// cmd/cyrene/wire_guardrail.go 的说明)。派生值已经能自适应窗口,本键只是
|
||
// 给需要收紧或放宽的部署留一个显式落点。
|
||
|
||
// GuardrailMaxInputCharsEnv 是请求侧字符上限的配置键名。
|
||
//
|
||
// 它必须与 GuardrailConfig.MaxInputChars 的 env 标签**逐字一致**:WARN 文案要能
|
||
// 直接点出「改哪个键」,因此键名只能有一处出处(一致性由 guardrail_test.go 钉住)。
|
||
const GuardrailMaxInputCharsEnv = "GUARDRAIL_MAX_INPUT_CHARS"
|
||
|
||
// GuardrailConfig 承载内容护栏里由部署方声明的取值(design/04 §5)。
|
||
type GuardrailConfig struct {
|
||
// Keywords 是内容策略关键词表(GUARDRAIL_KEYWORDS,逗号分隔)。
|
||
//
|
||
// **为空时不挂关键词护栏**:项目绝不硬编一张词表 —— 凭空猜出来的词
|
||
// 只会误伤正常对话,且没有任何部署方能对它的命中负责。只有显式声明过
|
||
// 的词才会被拦(大小写不敏感的子串匹配)。
|
||
Keywords []string `env:"GUARDRAIL_KEYWORDS"`
|
||
|
||
// MaxInputChars 是**请求侧**字符上限的显式覆盖(GUARDRAIL_MAX_INPUT_CHARS)。
|
||
// 三态:
|
||
//
|
||
// - 未设置 / 0(默认)= 按推导出的上下文预算派生:
|
||
// 输入上限 = max(256 Ki, 推导预算 token 数)(1 字符 ≈ 1 token 的保守口径);
|
||
// - > 0 = 显式覆盖(原样使用);
|
||
// - < 0 = 非法:记一条 WARN 并回落派生值,绝不静默生效(AGENTS §10)。
|
||
//
|
||
// 派生态下「文档注入预算 ≤ 输入上限」这条关系不变量恒成立(1M 窗口 ⇒
|
||
// 注入 349525 字符 ≤ 上限 699050 字符);显式覆盖把它设得比注入预算小时,
|
||
// 组合根记一条 WARN 明说「大附件会被护栏拦下」,而不是等到用户上传大文件
|
||
// 时才以一个没有上下文的 input_length_exceeded 表现出来。
|
||
//
|
||
// 它只动**输入**侧:输出上限(65536)防的是模型输出失控,与上下文预算无关,
|
||
// 因此不随本键变化、也不开放配置。
|
||
MaxInputChars int `env:"GUARDRAIL_MAX_INPUT_CHARS"`
|
||
}
|