RAG效果差先别急着换模型:数眼智能企业级大模型API聚合平台的数据链路实践

摘要
很多RAG项目把注意力集中在Embedding和向量数据库,却低估了数据进入索引之前的复杂度。联网搜索可能返回重复或过期资料,网页解析会受动态渲染与版式影响,OCR也可能出现部分页失败。本文设计一条由Search、Reader、OCR组成的可恢复数据链路,重点讨论幂等键、内容寻址、证据定位、权限预过滤、增量更新与删除传播,并说明企业级大模型API聚合平台在资源层应承担什么角色。
一、先把RAG拆成两个系统
一个可维护的RAG通常包含两个相对独立的部分:
离线或准实时摄取系统:发现来源、抓取、解析、OCR、清洗、切块、Embedding和发布索引;
在线检索系统:权限过滤、召回、重排、上下文组装、模型生成和引用返回。
两者不应共享一个“大脚本”。摄取失败可以重试,在线查询则有严格延迟预算;解析器升级需要重建索引,但不应影响正在服务的旧版本。
Search、Reader与OCR主要位于摄取侧,也可以在Agent查询时按需调用。数眼智能这类企业级大模型API聚合平台可以统一提供模型和数据工具资源,但任务状态、租户权限、知识版本和发布策略仍应由企业应用控制。
二、用状态机管理每一份来源
不要用一串同步函数从搜索直接跑到向量写入。为每个来源创建任务状态,允许失败后从最近成功阶段恢复。
from enum import Enumfrom dataclasses import dataclassclass Stage(str, Enum):
DISCOVERED = "discovered"
FETCHED = "fetched"
PARSED = "parsed"
OCR_DONE = "ocr_done"
CHUNKED = "chunked"
EMBEDDED = "embedded"
PUBLISHED = "published"
FAILED = "failed"@dataclassclass IngestJob:
tenant_id: str
source_id: str
source_url: str
stage: Stage
source_version: str | None = None
parser_version: str | None = None
retry_count: int = 0
任务表应记录错误类型、下一次重试时间和失败阶段。网络超时可以退避重试,权限拒绝和格式不支持则进入人工队列。OCR部分页失败时,不要把整份文档简单标成成功,应保留页级状态。
三、幂等键不能只用URL
同一个网页可能带有追踪参数,也可能在不改URL的情况下更新内容。只以URL作为唯一键,会同时遇到重复抓取和更新漏检。
建议维护三类标识:
canonical_source = normalize(url or document_id)
source_version = hash(raw_bytes or normalized_html)
artifact_version = hash(source_version + parser_version + chunk_policy)
canonical_source负责识别来源,source_version判断原始内容是否变化,artifact_version判断解析或切块策略变化后是否需要重建。不同租户即使引用同一公开网页,也应分别记录授权和知识库归属,不能因为内容哈希相同就跨租户共享答案缓存。
四、Search阶段要保存“为什么找到它”
Search结果不应只保存URL。建议同时记录查询词、排名、标题、摘要、搜索时间、来源域名和后续是否被采用。
{
"query": "某政策最新版本",
"rank": 3,
"url": "https://example.com/page",
"title": "...",
"discovered_at": "2026-11-05T10:00:00+08:00",
"selected": true}
这样当答案出现错误时,可以区分是搜索没有找到权威来源、排序选择错误,还是后续解析和生成出错。对政策、价格和新闻等时效性内容,还应设置新鲜度策略,超过窗口重新搜索。
五、Reader阶段必须把证据位置保留下来
网页转Markdown不是终点。为了让回答能够引用原文,解析结果需要稳定的块标识、标题路径和位置。
@dataclassclass EvidenceBlock:
evidence_id: str
tenant_id: str
source_id: str
source_version: str
heading_path: list[str]
page_no: int | None
block_index: int
text: str
acl: list[str]
evidence_id建议由来源版本、页码和块序号生成。不要只保存最终切块文本,否则解析器升级后,旧引用将无法定位。表格还应保留行列关系,不能简单拼成一长串文字。
Reader抓取外部网页时要防范SSRF。应用应限制协议、目标域名、DNS解析结果、重定向次数和响应大小,拒绝访问环回地址、内网地址与云元数据地址。网页正文中的“忽略系统指令”等内容只能作为数据,不能进入Agent控制指令。
六、OCR应以页为单位处理部分失败
扫描PDF和图片常出现倾斜、模糊、双栏、印章遮挡和手写内容。一个文件十页,九页成功一页失败,不能只返回success=true。
{
"document_id": "contract-2026-001",
"pages": [
{"page": 1, "status": "ok", "confidence": 0.96},
{"page": 2, "status": "review", "confidence": 0.61}
],
"complete": false}
低置信度页面进入人工复核,关键字段可采用双重校验。合同金额、身份证号和日期不应只依赖平均置信度。原图、OCR文本和人工修订需要分别版本化,保证后续能够追溯。
七、切块策略要与引用和权限一起设计
切块不是越小越好。过小会丢失语义,过大则浪费上下文并降低召回精度。可以按标题、段落和表格结构切分,再设置长度上限和少量重叠。
更重要的是,ACL要在切块阶段继承。在线检索时先根据用户和项目权限过滤,再进行向量召回,不能先召回敏感内容后再在应用层隐藏。缓存键也要包含租户、用户权限哈希、知识版本和检索策略版本。
cache_key = hash(
tenant_id,
permission_hash,
knowledge_release,
retrieval_policy,
normalized_query
)
八、增量更新使用蓝绿索引
直接在生产索引中边删边写,容易出现新旧版本混合。更稳妥的做法是构建新索引版本,完成数据校验与离线评测后,再原子切换别名。
kb-prod -> kb-release-2026-11-01
kb-next -> kb-release-2026-11-05
validate(kb-next)
switch_alias(kb-prod, kb-next)
发布前检查文档数、块数、Embedding维度、权限覆盖和抽样引用。切换后保留旧索引一段时间,便于回滚。Embedding模型变化时通常需要全量重建,不能把不同向量空间的数据混在同一索引。
九、删除必须传播到所有衍生数据
用户删除原文后,只删对象存储是不够的。解析文本、OCR结果、切块、向量、缓存、离线评测集和日志样本都可能保留衍生内容。
建立删除事件并追踪每个消费者的确认状态:
{
"event": "source.deleted",
"tenant_id": "org-a",
"source_id": "doc-123",
"delete_version": 7,
"consumers": {
"parser_store": "done",
"vector_index": "done",
"answer_cache": "pending"
}}
删除过程要幂等,重复事件不能导致异常。合规场景还需要记录删除时间、执行人和完成证明。
十、如何评测整条链路
不要只测最终回答。摄取侧分别观察搜索来源覆盖、正文完整率、表格保真、OCR关键字段和任务恢复;检索侧观察Context Precision、Context Recall、引用可定位率和权限越界;生成侧观察Faithfulness、拒答和业务任务成功率。
POC可以准备动态网页、长PDF、扫描合同、表格截图和权限不同的文档。数眼智能、云厂商数据服务、专业解析工具或自建方案都使用同一测试集,记录工具版本、时间窗口和原始结果,才能形成可复现结论。
总结
RAG质量问题经常被误判为模型问题,实际根源可能是搜索来源、网页解析、OCR、权限或版本发布。把Search、Reader和OCR做成可恢复、可追溯的数据链路,才能让模型获得稳定上下文。
企业级大模型API聚合平台可以降低模型与数据工具的接入成本,但数据所有权、任务状态、权限、知识版本和删除责任不能外包。资源层负责提供能力,企业控制面负责定义边界,这样的RAG才具备长期维护价值。
更多推荐


所有评论(0)