- 残留缺口:护栏量整段装配请求(字符),注入预算是单份 ⇒ 1M 窗口下 2×349525 就顶到旧上限 699050、5 份必被拦。根因是两套换算(上限按 1char=1token,注入按 loop 的 4char=1token)。 - 修法:上限 = max(262144, 推导预算 tokens × loop.CharsPerToken) ⇒ 1M 窗口 2,796,200 字符(5 份满额注入 1,747,625 有余量);不变量升级为「N 份」(maxChatDocumentsPerTurn=5,带出处注释);WARN 文案升级为多附件口径(max_documents/document_inject_chars/document_inject_total_chars/guardrail_max_input_chars/hint)。 - 同一换算常量复用:在 loop/compactor.go 的既有无导出常量旁加导出别名 loop.CharsPerToken = charsPerToken(2 行、零行为改动、不重命名)—— 这是本切片唯一触及「不要碰」清单的改动,已由主会话复核接受(替代方案是复制一个字面量 4,会重新造出两套换算)。 - 同步陈旧注释(config/guardrail.go、wire_runtime.go 里的旧公式)与 .env.example(新公式 + 多附件口径 + 1M 算例 2796200/1747625),并有「模板 ↔ 代码现算值」的一致性用例。 - 主会话独立验收:隔离 worktree(G2b 的 7 文件,排除 B1/ST1 在途)build/vet 绿、全树 59 包 0 FAIL、护栏用例 17 PASS、双平台交叉编译绿。 - 残留(未修,需裁决):预算关闭或落在 (0,81920) token 的部署,派生态上限 = 262144 而 5×64K = 327680 ⇒ 满载附件回合仍可能被拦(启动会记 WARN,不静默);真正的下限应取 max(262144, maxDocs×每份注入)。
49 lines
2.9 KiB
Go
49 lines
2.9 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 数 × CharsPerToken)
|
||
// (换算必须与附件注入预算同一个常量 loop.CharsPerToken,理由见
|
||
// cmd/cyrene/wire_guardrail.go 的说明);
|
||
// - > 0 = 显式覆盖(原样使用);
|
||
// - < 0 = 非法:记一条 WARN 并回落派生值,绝不静默生效(AGENTS §10)。
|
||
//
|
||
// 派生态下「单回合全部附件的注入预算合计 ≤ 输入上限」这条关系不变量恒成立
|
||
// (1M 窗口 ⇒ 5 份 × 349525 = 1747625 字符 ≤ 上限 2796200 字符);显式覆盖把它
|
||
// 压到合计之下时,组合根记一条 WARN 明说「多附件回合会被护栏拦下」,而不是等到
|
||
// 用户上传多个大文件时才以一个没有上下文的 input_length_exceeded 表现出来。
|
||
//
|
||
// 它只动**输入**侧:输出上限(65536)防的是模型输出失控,与上下文预算无关,
|
||
// 因此不随本键变化、也不开放配置。
|
||
MaxInputChars int `env:"GUARDRAIL_MAX_INPUT_CHARS"`
|
||
}
|