Skip to content

[Bug] Xinference rerank 吞掉上游异常并返回空列表,导致知识库丢失已融合的检索结果 #10000

Closed

Description

@LIghtJUNction

问题描述

知识库 RetrievalManager.retrieve() 只在重排序抛出异常时保留融合结果。但 XinferenceRerankProvider.rerank() 在上游失败或模型未初始化时返回 [],管理器把它视为正常结果,覆盖原有非空候选。

本次给融合阶段提供 1 条有效文档候选,让 Xinference 模型调用抛出异常,最终检索返回 0 条。对照让异常向管理器传播,原候选正确保留。上游临时故障因此会表现成知识库无匹配结果,预设降级路径未生效。

代码定位

如何复现?

  1. 让融合结果包含至少 1 个存在元数据的文档块。
  2. 配置 Xinference 重排序,并让模型的 rerank() 抛出上游不可用异常。
  3. 调用完整的 RetrievalManager.retrieve() 方法。
  4. 对照令 provider 直接向管理器传播异常。

预期:故障有日志且保留融合候选。实际:Xinference 当前路径返回空检索结果;传播异常的对照路径返回原候选。

下面脚本已在该提交的仓库根目录用 Python 3.12.14 独立运行。为避免加载完整 AstrBot 和外部依赖,通过 AST 读取并执行仓库原始类定义;方法体保持不变,日志、数据库或上游模型按用例使用 mock。断言用于确认当前缺陷与对照结果,无真实模型调用或线上平台测试。

可运行的源码级复现(仅 Python 标准库)

保存为临时 Python 脚本,在 AstrBot 仓库根目录运行:

import ast, asyncio, abc, copy, functools, re, time, dataclasses
from pathlib import Path
from types import SimpleNamespace as NS
from unittest.mock import AsyncMock, Mock
ROOT = Path.cwd()
def load(path, names, env=None):
    tree = ast.parse((ROOT/path).read_text())
    selected = [n for n in tree.body if isinstance(n, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)) and n.name in names]
    scope = dict(__name__='__main__', asyncio=asyncio, abc=abc, copy=copy, functools=functools, re=re, time=time, dataclass=dataclasses.dataclass, logger=Mock(), BaseChunker=object, deprecated=lambda **kw: lambda f:f)
    scope.update(env or {})
    mod=ast.Module(body=[ast.ImportFrom(module='__future__', names=[ast.alias(name='annotations')],level=0), *selected],type_ignores=[])
    exec(compile(ast.fix_missing_locations(mod),str(ROOT/path),'exec'),scope)
    return scope
async def main():
    class Base: pass
    s=load('astrbot/core/provider/sources/xinference_rerank_source.py',['XinferenceRerankProvider'],{'RerankProvider':Base,'register_provider_adapter':lambda *a,**k:lambda c:c,'ProviderType':NS(RERANK='rerank')})
    provider=object.__new__(s['XinferenceRerankProvider']); provider.model=NS(rerank=AsyncMock(side_effect=RuntimeError('upstream down')))
    s=load('astrbot/core/knowledge_base/retrieval/manager.py',['RetrievalResult','RetrievalManager'])
    fused=NS(chunk_id='chunk1',doc_id='doc1',kb_id='kb1',content='known answer',score=.8,chunk_index=0)
    db=NS(get_documents_with_metadata_batch=AsyncMock(return_value={'doc1':{'document':NS(doc_name='doc'),'knowledge_base':NS(kb_name='kb')}}))
    mgr=s['RetrievalManager'](NS(retrieve=AsyncMock(return_value=[])),NS(fuse=AsyncMock(return_value=[fused])),db)
    mgr._dense_retrieve=AsyncMock(return_value=[])
    kb=NS(top_k_dense=50,top_k_sparse=50,top_m_final=5,rerank_provider_id='rp')
    result=await mgr.retrieve('query',['kb1'],{'kb1':NS(kb=kb,vec_db=NS(rerank_provider=provider))})
    print('6 failed Xinference rerank: fused candidates=1, returned=',len(result))
    assert result==[]
    provider.rerank=AsyncMock(side_effect=RuntimeError('propagated failure'))
    result=await mgr.retrieve('query',['kb1'],{'kb1':NS(kb=kb,vec_db=NS(rerank_provider=provider))})
    assert len(result)==1
asyncio.run(main())

AstrBot 版本

4.28.0,master 提交 6494ac22e536d38ef6e07014d27b808d8c114bd2(2026-09-09 扫描)。

操作系统

Linux,Python 3.12.14。

部署方式

源码级单元复现;未启动完整服务。

使用的消息平台适配器

无;核心代码测试,不加载插件或平台适配器。

错误日志

以下为实际运行脚本的输出,非生产日志:

6 failed Xinference rerank: fused candidates=1, returned= 0

建议修复与验证

统一 provider 失败语义:网络/服务异常和未初始化状态应抛出明确异常,使管理器的降级逻辑生效。可同时对非空候选被异常空结果替换增加保护。补充上游失败、未初始化、成功重排三种测试。

重复检查

已检索 Xinference/rerank 与空结果相关 issue 和开放 PR。#9383/#9384 讨论代理支持,与错误被转换为空列表导致检索候选丢失不同。

检查清单

  • 已搜索相关 issue 和开放 PR,未找到相同根因的报告。
  • 复现未加载插件,问题位于 AstrBot 核心代码。
  • 已阅读项目贡献指南及行为准则。
  • 已明确区分源码级复现、mock 边界与未验证的线上场景。

Activity

  1. added a commit that references this issue on Sep 19, 2026
    0812616
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions