Skip to content

feat:rerank too long; fix:rerank ui(agent),embedding returns 0#6663

Merged
c121914yu merged 6 commits intolabring:v4.14.10-devfrom
YYH211:rebase-4.14.10
Mar 27, 2026
Merged

feat:rerank too long; fix:rerank ui(agent),embedding returns 0#6663
c121914yu merged 6 commits intolabring:v4.14.10-devfrom
YYH211:rebase-4.14.10

Conversation

@YYH211
Copy link
Copy Markdown
Contributor

@YYH211 YYH211 commented Mar 26, 2026

Close: #6559

@cla-assistant
Copy link
Copy Markdown

cla-assistant bot commented Mar 26, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ c121914yu
❌ YYH211
You have signed the CLA already but the status is still pending? Let us recheck it.

@cla-assistant
Copy link
Copy Markdown

cla-assistant bot commented Mar 26, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ xqvvu
❌ YYH211
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

Docs Preview Deployed!

🔗 👀 Click here to visit preview

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-docs-pr:08d7de8caef0a81e30f3daca42bc9ac292dc5dfb

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

Build Successful - Preview fastgpt Image for this PR:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:fastgpt_08d7de8caef0a81e30f3daca42bc9ac292dc5dfb

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

Build Successful - Preview mcp_server Image for this PR:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:mcp_server_08d7de8caef0a81e30f3daca42bc9ac292dc5dfb

Copy link
Copy Markdown
Collaborator

@c121914yu c121914yu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 PR #6663 代码审查报告

PR 标题: feat:rerank too long; fix:rerank ui(agent),embedding returns 0
作者: @YYH211
变更统计: +169 / -48 行,涉及 12 个文件


✅ 优点

  1. 功能设计合理: Rerank 超长文档按 token 预算分片、取最高分汇聚,思路清晰
  2. Bug 修复精准: rerankModel: defaultModels.rerank?.model(原来错误指向 llm)和 encoding_format: 'float' 均为最小化精准修复
  3. i18n 覆盖完整: zh-CN / zh-Hant / en 三端同步添加,无遗漏
  4. 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 时,maxContextquoteMaxTokenbatchSizedefaultTokenisRequired 属性被一并移除,请确认这些字段的表单校验是否通过其他方式(如 zod schema / RHF rules)覆盖,避免用户提交空值。


🟢 可选优化(2 个)

[6] 魔法数字 8000 建议提取为常量

文件: packages/service/core/ai/config/utils.ts

maxToken: dbRerankMaxToken ?? (model as RerankModelItemType).maxToken ?? 8000

8000 同时出现在 config/utils.tscontroller.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 字段,按需启用,以降低兼容性风险。


🧪 测试建议

  1. 使用纯中文长文档验证 rerank 分片 token 估算是否正确
  2. 验证 query token 超过 rerankMaxToken 时的拒绝提示是否正常展示给用户
  3. 验证 agent 模式下 rerank 模型是否正确显示(SelectDatasetParams 修复)
  4. 验证 bge-m3 模型调用 embedding 后向量值不为 0

💬 总体评价

维度 评分
代码质量 ⭐⭐⭐⭐☆ (4/5)
安全性 ⭐⭐⭐⭐⭐ (5/5)
性能 ⭐⭐⭐☆☆ (3/5)
可维护性 ⭐⭐⭐⭐☆ (4/5)

🚀 审查结论

请求修改 — 主要关注点是中文场景下分片 token 估算系数偏大(严重问题1)和分片失败时的超长回退(严重问题2),这两个问题可能在实际生产中触发 rerank API 报错。其他问题为改进建议,不阻塞合并。

Comment thread packages/service/core/dataset/search/controller.ts Outdated
Comment thread packages/service/core/dataset/search/utils.ts Outdated
Comment thread packages/service/core/dataset/search/utils.ts Outdated
Comment thread packages/service/core/dataset/search/controller.ts Outdated
Copy link
Copy Markdown
Collaborator

@c121914yu c121914yu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 PR #6663 代码审查报告

PR 标题: feat: rerank too long; fix: rerank ui(agent), embedding returns 0
作者: @YYH211
变更统计: +136 / -35 行,涉及 13 个文件
CI/CD 状态: ✅ 所有构建和测试通过(CLA 待签署)


✅ 优点

  1. 修复方向准确:三个 bug 的根因定位正确,修复简洁高效
  2. rerank 长文档切分设计合理:利用字符/token 比率估算分块大小,max score 聚合策略合理
  3. embedding encoding_format: 'float' 修复:一行修复解决了 bge-m3 输出全 0 的问题,准确定位了 OpenAI 兼容接口的返回格式问题
  4. SelectDatasetParams 修复简洁defaultModels.llm?.modeldefaultModels.rerank?.model,一字之差修复 Agent 模式 UI 显示问题
  5. controller.ts 重构:Map 替代 find() 循环,O(n) 替代 O(n²),同时稳定排序(相同 score 保留原始顺序)
  6. 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) = 0

splitText2ChunkscommonSplit 内部有 for (let i = 0; i < text.length; i += chunkSize - overlapLen),当 chunkSize = 0overlapRatio = 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 docBudget

2. [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)避免。


🧪 测试建议

  1. 边界测试:query token 占满 maxToken-1 的场景(docBudget=1),验证不会触发无限循环
  2. 空文档测试item.qitem.a 都为空时,[item.q, item.a].filter(Boolean).join('\n').trim() 会得到空字符串,rerank 是否正确跳过
  3. bge-m3 embedding:用 bge-m3 模型实际测试向量不为零

💬 总体评价

维度 评分
代码质量 ⭐⭐⭐⭐☆ (4/5)
安全性 ⭐⭐⭐⭐⭐ (5/5)
性能 ⭐⭐⭐⭐☆ (4/5)
可维护性 ⭐⭐⭐⭐☆ (4/5)

🚀 审查结论

需修改后合并:请修复 chunkSize = 0 可能导致的无限循环问题,其余为可选改进。


已在下方添加具体行级评论

Comment thread packages/service/core/ai/rerank/index.ts Outdated
Comment thread packages/service/core/ai/rerank/index.ts
...dbModel.metadata,
isCustom: true
});
} as any;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 as any 导致类型安全缺失

modelData.maxToken 通过 any 动态赋值,若字段名拼写错误(如 modelData.maxtoken)编译不会报错。

建议:至少为关键字段添加显式类型标注,或定义局部扩展类型:

type CustomModelData = typeof dbModel.metadata & { isCustom: boolean; maxToken?: number };

Comment thread projects/app/src/pageComponents/account/model/AddModelBox.tsx Outdated
@c121914yu
Copy link
Copy Markdown
Collaborator

📊 PR Review: feat:rerank too long; fix:rerank ui(agent),embedding returns 0

PR #6663 | 作者: @YYH211 | 分支: rebase-4.14.10v4.14.10-dev
变更统计: +491 / -60 行 | 15 个文件


✅ 优点

  1. 完整的问题修复链路: 从 schema 定义 → 配置加载 → 运行时处理 → UI 配置,修改贯穿全栈,设计完整。
  2. 文档切分策略合理: 使用 char/token 比例 × 0.9 安全系数估算 chunkSize,配合 text2Chunks 处理超限文档,逻辑清晰。
  3. 结果去重设计正确: existsId Set 去重 + 利用 API 返回结果已按分数降序的特性,第一次命中的 chunk 即为该文档的最高分,逻辑正确。
  4. 日志优化合理: 仅在 rerank 耗时 > 2000ms 时记录日志,减少正常场景下的日志噪音。
  5. 测试覆盖全面: 14 个测试用例覆盖正常场景、边界值、文档切分、token 计算、错误处理等,质量高。
  6. 一行 bug fix 价值高: rerankModel: defaultModels.llm?.modeldefaultModels.rerank?.model,修复了 agent 模式下 rerank 模型不显示的根本原因。
  7. encoding_format: 'float': 正确修复了 bge-m3 等模型通过 OpenAI 兼容 API 时默认返回 base64 编码导致的零向量问题。

⚠️ 问题汇总

🟡 建议改进(2 个)

1. documentsTextArray 副作用填充模式
位置: packages/service/core/ai/rerank/index.ts

// 当前代码:通过 async 并发回调中的副作用填充
let documentsTextArray: string[] = [];
await Promise.all(
  documents.map(async (doc) => {
    documentsTextArray.push(text);  // ← 并发 push,顺序不确定
    ...
  })
);

// 使用处
await countPromptTokens(documentsTextArray.join('\n') + query, '')

由于 Promise.all 中的 async 回调并发执行,documentsTextArray 的 push 顺序不能保证与 expandedDocuments 一致。虽然此变量仅用于 token 计数(顺序不影响结果),但这是容易误导阅读者的写法。

建议: 删除 documentsTextArray,直接使用:

await countPromptTokens(expandedDocuments.map(d => d.text).join('\n') + query, '')

2. Promise.reject 使用字符串而非 Error 对象
位置: packages/service/core/ai/rerank/index.ts, 约第 47 行

// 当前
return Promise.reject('No rerank model');

// 同文件下方
return Promise.reject(new Error('Rerank query too long'));  // ← 用了 Error

两处 rejection 方式不一致。字符串 rejection 在 stack trace、类型推断和错误处理链上都不如 Error 对象。同时,测试中的断言 .rejects.toBe('No rerank model') 也需要同步更新为 .rejects.toThrow('No rerank model')

建议: 统一改为 Promise.reject(new Error('No rerank model'))


🟢 可选优化(1 个)

3. rerank maxToken UI 字段缺少默认值提示
位置: projects/app/src/pageComponents/account/model/AddModelBox.tsx

maxTokenMyNumberInput 未设置 placeholder,用户不填时服务端默认 8000,但界面上无任何提示,容易产生困惑。

建议: 添加 placeholder="8000" 或在 tip 文字中注明默认值。


🧪 测试建议

  • 在真实 rerank 模型(如 bge-reranker)上验证超长文档切分后的召回效果
  • 验证 bge-m3 embedding 修复后向量值是否正常非零
  • 在 agent 模式的 SelectDatasetParams 中确认 rerank 模型默认值正确渲染

💬 总体评价

维度 评分
代码质量 ⭐⭐⭐⭐☆ (4/5)
安全性 ⭐⭐⭐⭐⭐ (5/5)
测试覆盖 ⭐⭐⭐⭐⭐ (5/5)
可维护性 ⭐⭐⭐⭐☆ (4/5)

🚀 审查结论

建议通过(可在合并前顺手修复上述 2 个改进建议)。核心逻辑正确,测试完整,修复了多个真实用户场景下的 bug。


Reviewed by Claude Code

@c121914yu c121914yu merged commit 0ceeac7 into labring:v4.14.10-dev Mar 27, 2026
5 of 6 checks passed
c121914yu added a commit that referenced this pull request Mar 28, 2026
* 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>
c121914yu added a commit that referenced this pull request Mar 30, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants