Kotaemon支持自定义评分函数,优化检索排序策略
在企业级智能问答系统的落地实践中,一个常见的痛点是:即便使用了先进的大模型和向量检索技术,系统仍会返回“看似相关实则无关”的答案。比如用户问“2023年销售OKR完成情况”,系统却把两年前的模板文档排在首位;又或者查询“年假申请流程”,结果却被一篇标题含“假期旅游推荐”的文章干扰。
这类问题背后的核心症结,并非模型能力不足,而是检索阶段的相关性判断过于单一——过度依赖向量相似度,忽略了时间、来源权威性、关键词精确匹配等关键业务信号。而要解决这一问题,仅靠堆叠更复杂的LLM或更大规模的索引并不现实,真正需要的是对检索排序逻辑的精细化控制。
正是在这样的背景下,Kotaemon近期推出的自定义评分函数(Custom Scoring Function)机制,为RAG系统提供了前所未有的灵活性。它不再将排序视为检索模块的附属功能,而是将其提升为可编程、可配置、可验证的一等公民。
传统RAG框架中,一旦从FAISS或Elasticsearch召回Top-K结果,这些结果的顺序基本就固定了——除非额外引入一个独立的重排序模型(如Cross-Encoder)。但这种方式成本高、延迟大,且难以融入非语义信号(例如“这篇文档是否来自HR部门”)。Kotaemon的设计思路完全不同:它在初始检索之后,插入了一个轻量级但高度灵活的“评分引擎”,允许开发者以代码或配置的方式,重新定义每篇候选文档的得分。
这个过程不是简单的分数替换,而是一次多维度信号融合的决策。你可以想象这样一个场景:系统召回了100个包含“年假”的段落,其中有制度文件、员工讨论帖、历史审批记录,甚至还有外部转载的法律解读。仅靠语义相似度很难区分它们的优先级,但如果你能结合以下几个因素:
- 是否出自公司内部政策库?
- 发布时间是否在近一年内?
- 是否被超过10名管理员点赞过?
- 是否完整覆盖了“申请条件”“审批流程”“例外情形”等关键字段?
那么即使某段文字与查询的向量距离稍远,只要它在这些维度上表现优异,依然可以被提至前列。这正是自定义评分函数的价值所在——它让排序从“被动匹配”走向“主动推理”。
为了实现这一点,Kotaemon在Retriever模块中抽象出了BaseScorer接口。任何继承该接口的类都可以作为一个评分器注册到检索链路中。下面是一个典型的混合评分实现:
from kotaemon.retrievers import BaseScorer, RetrievalResults from typing import Dict, Any import math class CustomBM25PlusSemanticScorer(BaseScorer): """ 混合评分器:结合BM25关键词匹配与语义相似度 """ def __init__(self, alpha: float = 0.6, beta: float = 0.4, k1: float = 1.5, b: float = 0.75): self.alpha = alpha # BM25权重 self.beta = beta # 语义相似度权重 self.k1 = k1 self.b = b def score(self, query: str, document: str, metadata: Dict[str, Any]) -> float: # 提取原始相似度(来自向量检索) semantic_score = metadata.get("vector_similarity", 0.0) # 计算BM25(简化版模拟) tokens_q = set(query.lower().split()) tokens_d = document.lower().split() freq = sum(1 for t in tokens_d if t in tokens_q) doc_len = len(tokens_d) avg_doc_len = 200 # 假设平均长度 idf = math.log((1 + 1) / (freq + 0.5)) # 简化IDF bm25 = freq * (self.k1 + 1) / (freq + self.k1 * (1 - self.b + self.b * doc_len / avg_doc_len)) * idf # 综合加权得分 final_score = self.alpha * bm25 + self.beta * semantic_score return final_score # 注册到检索器 retriever = SomeRetriever() retriever.scorer = CustomBM25PlusSemanticScorer(alpha=0.7, beta=0.3) results: RetrievalResults = retriever.retrieve("如何申请年假?")这段代码看起来简单,但它代表了一种全新的构建范式:你不再受限于底层数据库的能力边界。无论后端用的是FAISS还是Pinecone,都不影响你在应用层加入关键词、时间衰减、点击率等任意特征。更重要的是,这种扩展完全无需修改框架核心逻辑,只需实现一个score()方法即可。
实际部署时,Kotaemon还支持更高级的评分管道(Scoring Pipeline),允许多个评分函数串联执行。例如,你可以先运行一个规则过滤器剔除明显无关项,再通过统计模型打分,最后根据发布时间做轻微降权。整个流程可以通过YAML声明式配置完成:
retriever: type: faiss_retriever scorer: type: pipeline scorers: - type: bm25_scorer weight: 0.5 - type: semantic_scorer weight: 0.4 - type: time_decay_scorer half_life: 86400 # 一天衰减一半 weight: 0.1这种设计不仅降低了非技术人员的使用门槛,也让策略变更具备了版本管理和灰度发布的可能性。当某个新评分规则上线时,可以先对10%流量生效,观察其对最终回答准确率的影响,再决定是否全量推广。
对比LangChain或LlamaIndex这类主流框架,Kotaemon在这方面的优势非常明显。那些框架虽然也能实现类似效果,但往往需要用户手动遍历结果列表、修改score字段、自行处理异常,缺乏统一的接口和生命周期管理。而在Kotaemon中,所有评分逻辑都被封装为确定性函数,保证相同输入下输出一致,这对A/B测试和故障排查至关重要。
在一个真实的企业知识库项目中,我们曾遇到这样一个案例:财务部发布了一份新的报销政策,但由于旧文档的语义嵌入更“通用”,反而在多数查询中排名更高。通过引入自定义评分函数,我们将“文档更新时间”作为显式特征加入,并设置半衰期为三天(即三天后影响力减半),新政策上线首周自动获得显著加分。仅此一项调整,就在两周内将相关问题的首条命中率从68%提升至92%。
当然,灵活性也带来了新的挑战。我们在实践中总结出几条重要经验:
- 避免过度拟合:评分公式不宜太复杂,尤其是冷启动阶段,应优先保证泛化能力;
- 保持可解释性:每个得分项都应有明确含义,比如“+0.15来自部门标签匹配”,便于调试;
- 定期校准权重:建议每月基于人工标注集进行一次A/B测试,动态调整各因子比例;
- 预计算高开销特征:如TF-IDF、句法分析等,可在文档入库时异步生成并存入元数据;
- 设置降级策略:当评分服务不可用时,自动回退至基础向量排序,确保系统可用性。
从架构上看,自定义评分函数位于检索与生成之间的关键节点:
+------------------+ +--------------------+ +-------------------+ | | | | | | | User Query +---->+ Retriever +---->+ Scoring Engine | | | | (Vector Search) | | (Custom Scorers) | +------------------+ +----------+---------+ +---------+---------+ | | v v +--------+---------+ +---------+---------+ | | | | | Knowledge Store | | Scoring Registry | | (Documents) | | (YAML/DB Config) | +------------------+ +-------------------+ ↓ +----------+-----------+ | | | Generator (LLM) | | Prompt with Ranked | | Context | +----------------------+这一层的存在,使得整个系统呈现出松耦合、高内聚的特点。评分策略集中管理,支持跨项目复用;每一步的得分变化均可记录,用于审计与优化;更重要的是,它让业务逻辑真正下沉到了检索环节——不再是“找到最像的”,而是“找到最有用的”。
回顾整个演进路径,我们会发现,RAG技术正在经历从“能答出来”到“答得准”再到“答得聪明”的转变。早期的关注点集中在如何接入大模型、如何切分文档、如何提升召回率;而现在,胜负手越来越体现在那些细微的工程决策上:你怎么定义相关性?你愿意为哪些信号付出计算代价?你如何衡量一次排序调整的真实收益?
Kotaemon通过自定义评分函数,把这些问题的答案交还给了开发者。它不预设“最优解”,而是提供一套工具,让你可以根据具体场景去探索、实验、迭代。这种设计理念,恰恰是生产级AI系统最稀缺的品质。
未来,随着更多行为数据(如点击、停留时长、用户反馈)的积累,评分函数甚至可以逐步引入在线学习机制,在保障稳定性的前提下实现自我进化。但无论如何演进,其核心思想不会改变:精准的检索,始于对相关性的深度理解,而非对相似度的盲目追逐。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考