fix(guardrail): 输入上限与附件注入预算统一换算(多附件不再被拦)
- 残留缺口:护栏量整段装配请求(字符),注入预算是单份 ⇒ 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×每份注入)。
This commit is contained in:
1 parent
2a43035f5d
commit
137b3e9eaf
7 files changed
+366
-82
No files matched your search
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -14,14 +15,18 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
|
||||
)
|
||||
|
||||
// 本文件锁定切片 G2 的输入侧解耦(端到端实测抓到的真 bug:大附件被输入护栏拦死):
|
||||
// 本文件锁定切片 G2 / G2b 的输入侧解耦(端到端实测抓到的真 bug:大附件被输入护栏拦死):
|
||||
//
|
||||
// - 输入上限 = max(256 Ki, 推导预算 token 数)(1 字符 ≈ 1 token 的保守口径);
|
||||
// - 输入上限 = max(256 Ki, 推导预算 token 数 × CharsPerToken)(与附件注入预算
|
||||
// **同一套换算**;G2 按 1 字符 ≈ 1 token 兑,于是 2 份满额注入的附件就顶到上限,
|
||||
// G2b 统一到注入侧那个常量);
|
||||
// - GUARDRAIL_MAX_INPUT_CHARS 的三态(未设/0 = 派生、>0 = 覆盖、<0 = WARN 回落);
|
||||
// - 关系不变量「文档注入预算 ≤ 输入护栏上限」,人为破坏它必须**有 WARN**;
|
||||
// - 关系不变量「单回合全部附件的注入预算合计 ≤ 输入护栏上限」,人为破坏它必须**有 WARN**;
|
||||
// - 回归:注入正文比旧上限(262144 字符)还大时,回合**不得**以 guardrail_input 结束;
|
||||
// - 反证:显式把上限收紧后,同一个大附件回合**真的**被拦(护栏没被削弱)。
|
||||
//
|
||||
// 多附件的回归与「N 份」不变量的用例在 wire_guardrail_multidoc_test.go。
|
||||
//
|
||||
// 输出上限(guardrailMaxOutputChars)不属本切片:它防的是模型输出失控,与上下文
|
||||
// 预算无关,因此保持 65536 不动(回归见 TestGuardrailPipeline_LengthCapsOnlyCatchPathologicalPayloads)。
|
||||
|
||||
@@ -31,7 +36,8 @@ import (
|
||||
const largeDocChars = 400000
|
||||
|
||||
// TestGuardrailInputLimit_DerivedFromBudget 钉住派生公式(表驱动):
|
||||
// 输入上限 = max(下限, 推导预算 token 数) —— 小窗口部署落回下限,大窗口随预算抬高。
|
||||
// 输入上限 = max(下限, 推导预算 token 数 × CharsPerToken) —— 小预算落回下限,
|
||||
// 大窗口随预算抬高(换算与附件注入预算同一个常量,否则多附件回合会被拦死)。
|
||||
func TestGuardrailInputLimit_DerivedFromBudget(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -40,9 +46,9 @@ func TestGuardrailInputLimit_DerivedFromBudget(t *testing.T) {
|
||||
tokens int
|
||||
want int
|
||||
}{
|
||||
{"1M 窗口推导出的预算(699050)⇒ 随之上抬", 699050, 699050},
|
||||
{"预算正好等于下限 ⇒ 下限", guardrailMinInputChars, guardrailMinInputChars},
|
||||
{"小预算(600)⇒ 回落下限", 600, guardrailMinInputChars},
|
||||
{"1M 窗口推导出的预算(699050 token)⇒ 按 CharsPerToken 上抬", 699050, 2796200},
|
||||
{"预算正好等于下限 ÷ CharsPerToken ⇒ 下限", guardrailMinInputChars / loop.CharsPerToken, guardrailMinInputChars},
|
||||
{"小预算(600 token)⇒ 回落下限", 600, guardrailMinInputChars},
|
||||
{"关闭压缩(0)⇒ 下限", 0, guardrailMinInputChars},
|
||||
{"负预算 ⇒ 下限", -1, guardrailMinInputChars},
|
||||
}
|
||||
@@ -51,7 +57,7 @@ func TestGuardrailInputLimit_DerivedFromBudget(t *testing.T) {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
log, _ := capturingLog()
|
||||
if got := deriveGuardrailInputLimit(log, 0, c.tokens, 0); got != c.want {
|
||||
if got := deriveGuardrailInputLimit(log, 0, c.tokens, 0, maxChatDocumentsPerTurn); got != c.want {
|
||||
t.Fatalf("deriveGuardrailInputLimit(budget=%d) = %d, want %d", c.tokens, got, c.want)
|
||||
}
|
||||
})
|
||||
@@ -64,7 +70,7 @@ func TestGuardrailInputLimit_OverrideAndIllegalValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
log, buf := capturingLog()
|
||||
if got := deriveGuardrailInputLimit(log, 1000, 699050, 0); got != 1000 {
|
||||
if got := deriveGuardrailInputLimit(log, 1000, 699050, 0, maxChatDocumentsPerTurn); got != 1000 {
|
||||
t.Fatalf("显式覆盖 = %d, want 1000(覆盖优先于派生)", got)
|
||||
}
|
||||
if n := countRecords(logRecords(t, buf), "WARN", "guardrail input limit invalid"); n != 0 {
|
||||
@@ -72,8 +78,8 @@ func TestGuardrailInputLimit_OverrideAndIllegalValue(t *testing.T) {
|
||||
}
|
||||
|
||||
log, buf = capturingLog()
|
||||
if got := deriveGuardrailInputLimit(log, -1, 699050, 0); got != 699050 {
|
||||
t.Fatalf("非法覆盖后的回落 = %d, want 派生值 699050", got)
|
||||
if got := deriveGuardrailInputLimit(log, -1, 699050, 0, maxChatDocumentsPerTurn); got != 2796200 {
|
||||
t.Fatalf("非法覆盖后的回落 = %d, want 派生值 2796200", got)
|
||||
}
|
||||
records := logRecords(t, buf)
|
||||
if n := countRecords(records, "WARN", "guardrail input limit invalid"); n != 1 {
|
||||
@@ -87,7 +93,7 @@ func TestGuardrailInputLimit_OverrideAndIllegalValue(t *testing.T) {
|
||||
if rec["env"] != config.GuardrailMaxInputCharsEnv {
|
||||
t.Fatalf("WARN 没点出配置键: %v", rec)
|
||||
}
|
||||
if rec["value"] != float64(-1) || rec["fallback"] != float64(699050) {
|
||||
if rec["value"] != float64(-1) || rec["fallback"] != float64(2796200) {
|
||||
t.Fatalf("WARN 没带原始取值与回落值: %v", rec)
|
||||
}
|
||||
}
|
||||
@@ -98,8 +104,9 @@ func TestGuardrailInputLimit_OverrideAndIllegalValue(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestGuardrailInputLimit_RelationInvariantWithInjectBudget 钉住关系不变量
|
||||
// 「文档注入预算 ≤ 输入护栏上限」:对 1M 窗口的推导值成立,且人为把上限设得比
|
||||
// 注入预算小 ⇒ **记一条 WARN**(说明「大附件会被护栏拦下」),不静默。
|
||||
// 「单回合全部附件的注入预算合计 ≤ 输入护栏上限」:对 1M 窗口的推导值成立
|
||||
// (maxDocuments 份 × 单份注入),且人为把上限压到之下 ⇒ **记一条 WARN**
|
||||
// (消息与字段都要让人一眼看出是**多附件**问题),不静默。
|
||||
func TestGuardrailInputLimit_RelationInvariantWithInjectBudget(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -112,20 +119,23 @@ func TestGuardrailInputLimit_RelationInvariantWithInjectBudget(t *testing.T) {
|
||||
}
|
||||
|
||||
log, buf := capturingLog()
|
||||
limit := deriveGuardrailInputLimit(log, 0, budget.Budget.Tokens, budget.DocumentInjectChars)
|
||||
if budget.DocumentInjectChars > limit {
|
||||
t.Fatalf("关系不变量被破:注入预算 %d > 输入上限 %d", budget.DocumentInjectChars, limit)
|
||||
limit := deriveGuardrailInputLimit(log, 0, budget.Budget.Tokens, budget.DocumentInjectChars, maxChatDocumentsPerTurn)
|
||||
if total := budget.DocumentInjectChars * maxChatDocumentsPerTurn; total > limit {
|
||||
t.Fatalf("关系不变量被破:%d 份注入合计 %d > 输入上限 %d",
|
||||
maxChatDocumentsPerTurn, total, limit)
|
||||
}
|
||||
if limit != 699050 {
|
||||
t.Fatalf("1M 窗口下的输入上限 = %d, want 699050(= 推导预算 token 数)", limit)
|
||||
if limit != 2796200 {
|
||||
t.Fatalf("1M 窗口下的输入上限 = %d, want 2796200(= 推导预算 token 数 × %d)",
|
||||
limit, loop.CharsPerToken)
|
||||
}
|
||||
if n := countRecords(logRecords(t, buf), "WARN", "exceeds guardrail input limit"); n != 0 {
|
||||
t.Fatalf("不变量成立却记了 %d 条 WARN", n)
|
||||
}
|
||||
|
||||
// 人为破坏:把上限压到注入预算之下 ⇒ 必须有一条 WARN 明说后果。
|
||||
// 人为破坏:把上限压到「全部附件注入合计」之下 ⇒ 必须有一条 WARN 明说后果,
|
||||
// 且带上 N 份 / 单份 / 合计三项取值(否则没人看得出这是多附件问题)。
|
||||
log, buf = capturingLog()
|
||||
if got := deriveGuardrailInputLimit(log, 4096, budget.Budget.Tokens, budget.DocumentInjectChars); got != 4096 {
|
||||
if got := deriveGuardrailInputLimit(log, 4096, budget.Budget.Tokens, budget.DocumentInjectChars, maxChatDocumentsPerTurn); got != 4096 {
|
||||
t.Fatalf("收紧后的上限 = %d, want 4096", got)
|
||||
}
|
||||
records := logRecords(t, buf)
|
||||
@@ -136,9 +146,17 @@ func TestGuardrailInputLimit_RelationInvariantWithInjectBudget(t *testing.T) {
|
||||
for _, rec := range records {
|
||||
if msg, _ := rec["msg"].(string); strings.Contains(msg, "exceeds guardrail input limit") {
|
||||
found = true
|
||||
if !strings.Contains(msg, "multi-attachment") {
|
||||
t.Fatalf("WARN 文案看不出是多附件问题: %q", msg)
|
||||
}
|
||||
if rec["document_inject_chars"] != float64(budget.DocumentInjectChars) ||
|
||||
rec["guardrail_max_input_chars"] != float64(4096) {
|
||||
t.Fatalf("WARN 没写清两端取值: %v", rec)
|
||||
rec["guardrail_max_input_chars"] != float64(4096) ||
|
||||
rec["max_documents"] != float64(maxChatDocumentsPerTurn) ||
|
||||
rec["document_inject_total_chars"] != float64(budget.DocumentInjectChars*maxChatDocumentsPerTurn) {
|
||||
t.Fatalf("WARN 没写清 N 份与两端取值: %v", rec)
|
||||
}
|
||||
if hint, _ := rec["hint"].(string); hint == "" {
|
||||
t.Fatalf("WARN 没给出处置提示: %v", rec)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,26 +166,38 @@ func TestGuardrailInputLimit_RelationInvariantWithInjectBudget(t *testing.T) {
|
||||
}
|
||||
|
||||
// largeAttachmentTurn 是「一个回合 + 一份大附件」的观测结果。
|
||||
type largeAttachmentTurn struct {
|
||||
type attachmentTurn struct {
|
||||
// result 是该回合的循环结论(StopReason / Text / Documents 都在这里)。
|
||||
result *loop.Result
|
||||
// modelCalls 是模型被调用的次数(= 捕获到的请求数)。
|
||||
modelCalls int
|
||||
// assembledChars 是模型实际看到的文本总量(装配后、注入正文已在内)。
|
||||
assembledChars int
|
||||
// injectChars 是本回合如实记录的注入字符数(Result.Documents[0].Chars)。
|
||||
injectChars int
|
||||
// perDocInject 是本回合如实记录的**每份**附件注入字符数
|
||||
// (Result.Documents[i].Chars;夹具里各份等长)。
|
||||
perDocInject int
|
||||
// documents 是 Result.Documents 的条数(本回合真的注入了几份)。
|
||||
documents int
|
||||
// g2Limit 是 G2 时代的上限(推导预算 token 数按 1 字符 ≈ 1 token 兑成字符)。
|
||||
// 它让回归用例能自证「装配结果确实落在 G2/G2b 的缺口上」:装配总量超过了它。
|
||||
g2Limit int
|
||||
// inputLimit 是本回合实际生效的输入上限(a.guardrailInputLimit 的产物),
|
||||
// 供用例实证「装配总量落在上限之内」。
|
||||
inputLimit int
|
||||
}
|
||||
|
||||
// runLargeAttachmentTurn 跑一个回合:1M 窗口的推导预算(注入 349525 字符)+
|
||||
// 一份 largeDocChars 字符的 txt 附件 + 真实的护栏管线。inputLimitOverride 非 0 时
|
||||
// runAttachmentTurn 跑一个回合:1M 窗口的推导预算(单份注入 349525 字符)+ docs 份
|
||||
// largeDocChars 字符的 txt 附件 + 真实的护栏管线。inputLimitOverride 非 0 时
|
||||
// 模拟 GUARDRAIL_MAX_INPUT_CHARS 的覆盖态。
|
||||
//
|
||||
// 三个角色都是生产实现:预算取自 a.contextBudget、注入走真实的
|
||||
// loop.resolveDocuments + documents.Resolver、护栏是 a.buildGuardrailPipeline 的产物。
|
||||
// 唯一被单独钉住的是「窗口 = 1M」。
|
||||
func runLargeAttachmentTurn(t *testing.T, inputLimitOverride int) largeAttachmentTurn {
|
||||
func runAttachmentTurn(t *testing.T, docs, inputLimitOverride int) attachmentTurn {
|
||||
t.Helper()
|
||||
if docs < 1 {
|
||||
t.Fatalf("docs = %d, want ≥ 1(夹具至少要一份附件)", docs)
|
||||
}
|
||||
|
||||
dataDir := t.TempDir()
|
||||
uploads := filepath.Join(dataDir, "uploads", "documents")
|
||||
@@ -175,8 +205,13 @@ func runLargeAttachmentTurn(t *testing.T, inputLimitOverride int) largeAttachmen
|
||||
t.Fatalf("建上传目录: %v", err)
|
||||
}
|
||||
body := strings.Repeat("abcdefghij", largeDocChars/10)
|
||||
if err := os.WriteFile(filepath.Join(uploads, docE2EName), []byte(body), 0o600); err != nil {
|
||||
t.Fatalf("写上传件: %v", err)
|
||||
refs := make([]model.Document, 0, docs)
|
||||
for i := 0; i < docs; i++ {
|
||||
name := attachmentName(i)
|
||||
if err := os.WriteFile(filepath.Join(uploads, name), []byte(body), 0o600); err != nil {
|
||||
t.Fatalf("写上传件: %v", err)
|
||||
}
|
||||
refs = append(refs, model.Document{Name: name, MIME: "text/plain", Size: int64(len(body))})
|
||||
}
|
||||
|
||||
cfg := &config.Config{DataDir: dataDir}
|
||||
@@ -197,16 +232,19 @@ func runLargeAttachmentTurn(t *testing.T, inputLimitOverride int) largeAttachmen
|
||||
DocumentMaxInjectChars: budget.DocumentInjectChars,
|
||||
OnStream: func(model.StreamEvent) {},
|
||||
})
|
||||
msg := model.DocumentMessage(model.RoleUser, "看看这个附件",
|
||||
model.Document{Name: docE2EName, MIME: "text/plain", Size: int64(len(body))})
|
||||
res, err := l.Run(context.Background(), "sess-large-doc", []model.Message{msg})
|
||||
res, err := l.Run(context.Background(), "sess-large-doc",
|
||||
[]model.Message{documentTurnMessage("看看这些附件", refs)})
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
|
||||
turn := largeAttachmentTurn{result: res, modelCalls: len(adapter.reqs)}
|
||||
turn := attachmentTurn{
|
||||
result: res, modelCalls: len(adapter.reqs),
|
||||
documents: len(res.Documents), g2Limit: budget.Budget.Tokens,
|
||||
inputLimit: a.guardrailInputLimit(budget),
|
||||
}
|
||||
if len(res.Documents) > 0 {
|
||||
turn.injectChars = res.Documents[0].Chars
|
||||
turn.perDocInject = res.Documents[0].Chars
|
||||
}
|
||||
if len(adapter.reqs) > 0 {
|
||||
req := adapter.reqs[len(adapter.reqs)-1]
|
||||
@@ -217,6 +255,30 @@ func runLargeAttachmentTurn(t *testing.T, inputLimitOverride int) largeAttachmen
|
||||
return turn
|
||||
}
|
||||
|
||||
// attachmentName 造第 i 份附件的服务端存储名(32 位十六进制 + .txt,与
|
||||
// wire_document_budget_test.go 的 docE2EName 同一形态)。解析器按名取件,
|
||||
// 因此多份附件必须各有一个名字。
|
||||
func attachmentName(i int) string {
|
||||
return fmt.Sprintf("%032x.txt", i+1)
|
||||
}
|
||||
|
||||
// documentTurnMessage 造一条携带 N 份文档引用的用户消息:model 包只提供单份的
|
||||
// 构造器(DocumentMessage),多份按同一形状续部件 —— 这正是「一个回合最多
|
||||
// maxChatDocumentsPerTurn 份附件」的落库形状。
|
||||
func documentTurnMessage(text string, refs []model.Document) model.Message {
|
||||
msg := model.DocumentMessage(model.RoleUser, text, refs[0])
|
||||
for _, ref := range refs[1:] {
|
||||
msg.Content = append(msg.Content, model.Content{
|
||||
Type: model.ContentTypeDocument,
|
||||
DocumentName: ref.Name,
|
||||
DocumentMIME: ref.MIME,
|
||||
DocumentURL: ref.URL,
|
||||
DocumentSize: ref.Size,
|
||||
})
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// TestGuardrailWiring_LargeAttachmentPassesInputGuard 是**回归**,对准主人端到端
|
||||
// 实测抓到的那个真 bug:`POST /api/v1/chat` 带一个 744 KB 的 txt 附件 ⇒
|
||||
// `{"reply":"input_length_exceeded","stopReason":"guardrail_input"}` —— 历史 DTO 里
|
||||
@@ -228,7 +290,7 @@ func runLargeAttachmentTurn(t *testing.T, inputLimitOverride int) largeAttachmen
|
||||
func TestGuardrailWiring_LargeAttachmentPassesInputGuard(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
turn := runLargeAttachmentTurn(t, 0)
|
||||
turn := runAttachmentTurn(t, 1, 0)
|
||||
|
||||
// ① bug 的症状先说清楚:回合不得以 guardrail_input 结束,也不得被拦在模型之前。
|
||||
if turn.result.StopReason == agent.StopReasonGuardrailInput {
|
||||
@@ -239,9 +301,9 @@ func TestGuardrailWiring_LargeAttachmentPassesInputGuard(t *testing.T) {
|
||||
}
|
||||
// ② 前提自证:这一回合真的注入了 349525 字符(= 主人实测 DTO 里那个数),
|
||||
// 比旧上限 262144 大,所以上面那条断言不是碰巧成立。
|
||||
if turn.injectChars != 349525 {
|
||||
if turn.perDocInject != 349525 {
|
||||
t.Fatalf("注入字符数 = %d, want 349525(1M 窗口的注入预算;形状变了要复核本用例)",
|
||||
turn.injectChars)
|
||||
turn.perDocInject)
|
||||
}
|
||||
if turn.assembledChars <= guardrailMinInputChars {
|
||||
t.Fatalf("装配后的请求只有 %d 字符(≤ 旧上限 %d),复现不到「大附件被拦死」",
|
||||
@@ -260,7 +322,7 @@ func TestGuardrailWiring_LargeAttachmentPassesInputGuard(t *testing.T) {
|
||||
func TestGuardrailWiring_TightInputLimitStillBlocks(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
turn := runLargeAttachmentTurn(t, 1000)
|
||||
turn := runAttachmentTurn(t, 1, 1000)
|
||||
|
||||
if turn.result.StopReason != agent.StopReasonGuardrailInput {
|
||||
t.Fatalf("收紧上限后 StopReason = %q, want %q", turn.result.StopReason, agent.StopReasonGuardrailInput)
|
||||
@@ -272,22 +334,22 @@ func TestGuardrailWiring_TightInputLimitStillBlocks(t *testing.T) {
|
||||
t.Fatalf("被拦的回合仍调用了模型 %d 次 —— 输入侧不是前置门", turn.modelCalls)
|
||||
}
|
||||
// 注入本身照旧发生(记录如实),被拦是**上限**那一侧的判决。
|
||||
if turn.injectChars != 349525 {
|
||||
t.Fatalf("注入字符数 = %d, want 349525(注入事实不因上限收紧而改写)", turn.injectChars)
|
||||
if turn.perDocInject != 349525 {
|
||||
t.Fatalf("注入字符数 = %d, want 349525(注入事实不因上限收紧而改写)", turn.perDocInject)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGuardrailInputLimit_NotAFunctionOfInjectBudget 是变异反证:注入预算单独变化
|
||||
// **不影响**输入上限(上限只由上下文预算与显式覆盖决定),因此「注入预算 ≤ 上限」
|
||||
// **不影响**输入上限(上限只由上下文预算与显式覆盖决定),因此「N 份注入 ≤ 上限」
|
||||
// 不可能靠两边同源凑出来 —— 两端确实各自有出处。
|
||||
func TestGuardrailInputLimit_NotAFunctionOfInjectBudget(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
log, _ := capturingLog()
|
||||
if got := deriveGuardrailInputLimit(log, 0, 699050, 0); got != 699050 {
|
||||
t.Fatalf("注入预算为 0 时的输入上限 = %d, want 699050", got)
|
||||
if got := deriveGuardrailInputLimit(log, 0, 699050, 0, maxChatDocumentsPerTurn); got != 2796200 {
|
||||
t.Fatalf("注入预算为 0 时的输入上限 = %d, want 2796200", got)
|
||||
}
|
||||
if got := deriveGuardrailInputLimit(log, 0, 699050, 349525); got != 699050 {
|
||||
t.Fatalf("注入预算 349525 时的输入上限 = %d, want 699050(不受注入预算影响)", got)
|
||||
if got := deriveGuardrailInputLimit(log, 0, 699050, 349525, maxChatDocumentsPerTurn); got != 2796200 {
|
||||
t.Fatalf("注入预算 349525 时的输入上限 = %d, want 2796200(不受注入预算影响)", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user