feat:rerank too long; fix:rerank ui(agent),embedding returns 0#6663
feat:rerank too long; fix:rerank ui(agent),embedding returns 0#6663c121914yu merged 6 commits intolabring:v4.14.10-devfrom
Conversation
|
|
|
|
|
✅ Docs Preview Deployed! 🔗 👀 Click here to visit preview |
|
✅ Build Successful - Preview fastgpt Image for this PR: |
|
✅ Build Successful - Preview mcp_server Image for this PR: |
c121914yu
left a comment
There was a problem hiding this comment.
📊 PR #6663 代码审查报告
PR 标题: feat:rerank too long; fix:rerank ui(agent),embedding returns 0
作者: @YYH211
变更统计: +169 / -48 行,涉及 12 个文件
✅ 优点
- 功能设计合理: Rerank 超长文档按 token 预算分片、取最高分汇聚,思路清晰
- Bug 修复精准:
rerankModel: defaultModels.rerank?.model(原来错误指向llm)和encoding_format: 'float'均为最小化精准修复 - i18n 覆盖完整: zh-CN / zh-Hant / en 三端同步添加,无遗漏
- CI 全部通过: build / test 均为 pass
⚠️ 问题汇总
🔴 严重问题(2 个)
[1] splitTextByRerankBudget 字符估算系数偏大,中文场景下 chunk 仍可能超出 token 限制
文件: packages/service/core/dataset/search/utils.ts
// token 2 char
const approxChunkSize = docBudget * 2;问题: 对于中文文本,1 个汉字 ≈ 1 个 token(GPT tokenizer),因此 docBudget * 2 个字符 ≈ docBudget * 2 个 token,是预算的 2 倍。分出的 chunk 大概率仍然超出 rerank 模型限制,导致 API 报错。
英文场景下 1 token ≈ 4 字符,该值偏保守,但不会导致超限。
建议: 使用更保守的系数(如 1.5 或 1),或在分片后对每个 chunk 进行二次 token 验证并丢弃超限分片。
[2] 分片回退逻辑存在潜在问题
文件: packages/service/core/dataset/search/utils.ts
return splitChunks.length > 0 ? splitChunks : [formatText];问题: 当 splitText2Chunks 返回空数组时,回退为 [formatText],而此时 formatText 已被确认超出 docBudget(tokens > docBudget),这个超长文本仍然会被发送给 rerank API,导致请求失败或被截断。
建议: 回退时应至少截取前 approxChunkSize 个字符,而不是返回原始超长文本。
🟡 建议改进(3 个)
[3] for...of 顺序 await 性能问题
文件: packages/service/core/dataset/search/controller.ts
for (const item of data) {
const subChunks = await splitTextByRerankBudget({ text: parentText, docBudget });
// ...
}splitTextByRerankBudget 内部调用 countPromptTokens(CPU 密集),在顺序循环中会逐个等待。当 data 数组较大时(通常 80-100 条),这会显著增加延迟。
建议: 改为 Promise.all 并行处理:
const expandedDocumentsNested = await Promise.all(
data.map(async (item) => { ... })
);
const expandedDocuments = expandedDocumentsNested.flat();[4] Promise.reject(string) 应使用 new Error()
文件: packages/service/core/dataset/search/controller.ts
return Promise.reject('Rerank query too long');
// ...
return Promise.reject('Rerank error');使用字符串 reject 会丢失调用栈信息,不便于生产定位问题。建议:
return Promise.reject(new Error('Rerank query too long'));[5] 移除了多个表单字段的 isRequired 验证
文件: projects/app/src/pageComponents/account/model/AddModelBox.tsx
从 register+isRequired 改为 watch+onChange 时,maxContext、quoteMaxToken、batchSize、defaultToken 的 isRequired 属性被一并移除,请确认这些字段的表单校验是否通过其他方式(如 zod schema / RHF rules)覆盖,避免用户提交空值。
🟢 可选优化(2 个)
[6] 魔法数字 8000 建议提取为常量
文件: packages/service/core/ai/config/utils.ts
maxToken: dbRerankMaxToken ?? (model as RerankModelItemType).maxToken ?? 80008000 同时出现在 config/utils.ts 和 controller.ts 中,建议提取为命名常量(如 DEFAULT_RERANK_MAX_TOKEN = 8000)统一维护。
[7] encoding_format: 'float' 全局生效,建议按需配置
文件: packages/service/core/ai/embedding/index.ts
encoding_format: 'float' 对所有 embedding 模型全局生效,理论上大多数 API 默认已返回 float,但部分厂商可能不识别此参数并报错。可考虑在 embedding model schema 中添加可选的 encodingFormat 字段,按需启用,以降低兼容性风险。
🧪 测试建议
- 使用纯中文长文档验证 rerank 分片 token 估算是否正确
- 验证
querytoken 超过rerankMaxToken时的拒绝提示是否正常展示给用户 - 验证 agent 模式下 rerank 模型是否正确显示(SelectDatasetParams 修复)
- 验证 bge-m3 模型调用 embedding 后向量值不为 0
💬 总体评价
| 维度 | 评分 |
|---|---|
| 代码质量 | ⭐⭐⭐⭐☆ (4/5) |
| 安全性 | ⭐⭐⭐⭐⭐ (5/5) |
| 性能 | ⭐⭐⭐☆☆ (3/5) |
| 可维护性 | ⭐⭐⭐⭐☆ (4/5) |
🚀 审查结论
请求修改 — 主要关注点是中文场景下分片 token 估算系数偏大(严重问题1)和分片失败时的超长回退(严重问题2),这两个问题可能在实际生产中触发 rerank API 报错。其他问题为改进建议,不阻塞合并。
c121914yu
left a comment
There was a problem hiding this comment.
📊 PR #6663 代码审查报告
PR 标题: feat: rerank too long; fix: rerank ui(agent), embedding returns 0
作者: @YYH211
变更统计: +136 / -35 行,涉及 13 个文件
CI/CD 状态: ✅ 所有构建和测试通过(CLA 待签署)
✅ 优点
- 修复方向准确:三个 bug 的根因定位正确,修复简洁高效
- rerank 长文档切分设计合理:利用字符/token 比率估算分块大小,max score 聚合策略合理
- embedding
encoding_format: 'float'修复:一行修复解决了 bge-m3 输出全 0 的问题,准确定位了 OpenAI 兼容接口的返回格式问题 - SelectDatasetParams 修复简洁:
defaultModels.llm?.model→defaultModels.rerank?.model,一字之差修复 Agent 模式 UI 显示问题 - controller.ts 重构:Map 替代
find()循环,O(n) 替代 O(n²),同时稳定排序(相同 score 保留原始顺序) - i18n 三语言同步:zh-CN / zh-Hant / en 均已添加新 key
⚠️ 问题汇总
🔴 严重问题(1 个,必须修复)
[rerank/index.ts] chunkSize = 0 可能导致无限循环
当 query 占用 token 接近 rerankMaxToken(如 query 占 7999 token,maxToken = 8000),docBudget = 1。
若文档中存在特殊字符密集内容(字符/token 比率 < 1.11),则:
const chunkSize = Math.floor((text.length / docTokens) * docBudget * 0.9);
// 例如:text.length=10, docTokens=20, docBudget=1
// chunkSize = Math.floor(0.5 * 1 * 0.9) = Math.floor(0.45) = 0splitText2Chunks → commonSplit 内部有 for (let i = 0; i < text.length; i += chunkSize - overlapLen),当 chunkSize = 0 且 overlapRatio = 0 时,步长为 0,无限循环!
建议修复:
const chunkSize = Math.max(
1,
Math.floor((text.length / docTokens) * docBudget * 0.9)
);🟡 建议改进(3 个)
1. [rerank/index.ts] 注释被截断,意思不完整
// Use the document's own char/token ratio with a 0.9 safety factor to
const chunkSize = Math.floor((text.length / docTokens) * docBudget * 0.9);注释 "to" 后面没有内容,阅读者无法理解设计意图。
建议:
// Use the doc's char/token ratio with a 0.9 safety factor to estimate chunkSize
// that keeps each chunk's token count within docBudget2. [config/utils.ts] as any 类型断言导致类型安全缺失
const modelData: any = {
...model,
...dbModel?.metadata,
// ...
};
// 以及
const modelData = {
...dbModel.metadata,
isCustom: true
} as any;这两处 any 使得 maxToken 等字段赋值完全无类型保护,如果字段名拼写错误(如 maxtoken)也不会有编译报错。
建议:定义一个包含 maxToken?: number 的局部类型,至少对关键字段做显式类型标注。
3. [AddModelBox.tsx] maxToken 输入框最小值 min={1} 过低
<MyNumberInput register={register} name="maxToken" min={1} {...NumberInputStyles} />最大 token 限制为 1 没有实际意义,且可能触发 docBudget = 0 的边界情况(已有保护但不建议低值)。
建议:min={100} 或 min={512} 并增加 placeholder 提示默认值 8000。
🟢 可选优化(2 个)
1. [rerank/index.ts] 高并发场景下 countPromptTokens 批量调用开销
await Promise.all(
documents.map(async (doc) => {
const docTokens = await countPromptTokens(text); // 每个文档一次调用
})
)100 个文档 = 100 次并发 token 计算。若 tiktoken 是本地计算则问题不大;若有网络开销则建议批量计算。
2. [rerank/index.ts] chunk ID 命名可能存在极端冲突
id: `${doc.id}__chunk_${i}`若原始 doc.id 包含 __chunk_ 字符串(如 "abc__chunk_0"),生成的 "abc__chunk_0__chunk_0" 仍可正确映射,但语义上不够干净。低概率影响,可通过文档说明或使用更独特的分隔符(如 \x00chunk\x00)避免。
🧪 测试建议
- 边界测试:query token 占满 maxToken-1 的场景(docBudget=1),验证不会触发无限循环
- 空文档测试:
item.q和item.a都为空时,[item.q, item.a].filter(Boolean).join('\n').trim()会得到空字符串,rerank 是否正确跳过 - bge-m3 embedding:用 bge-m3 模型实际测试向量不为零
💬 总体评价
| 维度 | 评分 |
|---|---|
| 代码质量 | ⭐⭐⭐⭐☆ (4/5) |
| 安全性 | ⭐⭐⭐⭐⭐ (5/5) |
| 性能 | ⭐⭐⭐⭐☆ (4/5) |
| 可维护性 | ⭐⭐⭐⭐☆ (4/5) |
🚀 审查结论
需修改后合并:请修复 chunkSize = 0 可能导致的无限循环问题,其余为可选改进。
已在下方添加具体行级评论
| ...dbModel.metadata, | ||
| isCustom: true | ||
| }); | ||
| } as any; |
There was a problem hiding this comment.
🟡 as any 导致类型安全缺失
modelData.maxToken 通过 any 动态赋值,若字段名拼写错误(如 modelData.maxtoken)编译不会报错。
建议:至少为关键字段添加显式类型标注,或定义局部扩展类型:
type CustomModelData = typeof dbModel.metadata & { isCustom: boolean; maxToken?: number };f08788f to
2b80419
Compare
📊 PR Review: feat:rerank too long; fix:rerank ui(agent),embedding returns 0PR #6663 | 作者: @YYH211 | 分支: ✅ 优点
|
| 维度 | 评分 |
|---|---|
| 代码质量 | ⭐⭐⭐⭐☆ (4/5) |
| 安全性 | ⭐⭐⭐⭐⭐ (5/5) |
| 测试覆盖 | ⭐⭐⭐⭐⭐ (5/5) |
| 可维护性 | ⭐⭐⭐⭐☆ (4/5) |
🚀 审查结论
建议通过(可在合并前顺手修复上述 2 个改进建议)。核心逻辑正确,测试完整,修复了多个真实用户场景下的 bug。
Reviewed by Claude Code
* feat:rerank too long; fix:rerank ui(agent),embedding returns 0 * rerank * fix:rerank function * perf: rerank code * fix rerank * perf: model price ui --------- Co-authored-by: archer <545436317@qq.com>
* feat: model config with brand-new price calculate machanism (#6616) * fix: image read and json error (Agent) (#6502) * fix: 1.image read 2.JSON parsing error * dataset cite and pause * perf: plancall second parse * add test --------- Co-authored-by: archer <545436317@qq.com> * master message * remove invalid code * wip: model config * feat: model config with brand-new price calculate machanism * merge main branch * ajust calculate way * ajust priceTiers resolve procession * perf: price config code * fix: default price * fix: test * fix: comment * fix test --------- Co-authored-by: YeYuheng <57035043+YYH211@users.noreply.github.com> Co-authored-by: archer <545436317@qq.com> * wip: fix modal UI (#6634) * wip: fix modal UI * fix: maxInputToken set * chore: add price unit for non llm models * chore: replace question mark icon with beta tag (#6672) * feat:rerank too long; fix:rerank ui(agent),embedding returns 0 (#6663) * feat:rerank too long; fix:rerank ui(agent),embedding returns 0 * rerank * fix:rerank function * perf: rerank code * fix rerank * perf: model price ui --------- Co-authored-by: archer <545436317@qq.com> * remove llmtype field * revert model init * fix: filed * fix: model select filter * perf: multiple selector render * remove invalid checker * remove invalid i18n * perf: model selector tip * perf: model selector tip * fix cr * limit pnpm version * fix: i18n * fix action * set default mintoken * update i18n * perf: usage push * fix:rerank model ui (#6677) * fix: tier match error * fix: testr --------- Co-authored-by: Ryo <whoeverimf5@gmail.com> Co-authored-by: YeYuheng <57035043+YYH211@users.noreply.github.com>
Close: #6559