聊《别急着换赛道:大数据经验在 AI 项目里到底值多少?》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。
摘要
> 做了一个支持多租户的 RAG 企业知识库系统,Demo 阶段一切顺利,接入团队真实业务后,权限校验和可观测性成了最大的坑。这篇文章复盘整个项目踩坑全过程,给想从大数据转向大模型工程的同学一些参考。
---
目录
- 大数据与大模型的交叉点
- 数据治理:大数据工程师的隐性优势
- 向量数据库:选型与坑
- RAG 数据管道:从数据 lake 到向量检索
- 落地项目复盘:Demo 到团队的三个翻车现场
- 排查过程:权限问题的完整定位链路
- 代码解释:日志和可观测性的关键实现
- 失败原因:常见错误的分类和区分方法
- 适用边界:什么时候该照搬,什么时候不能
- 总结
---
大数据与大模型的交叉点

我做了这么多年大数据开发,转大模型的时候最惊讶的发现是:基础能力其实重叠度很高。Spark 做数据清洗的经验、Kafka 做实时处理的经验、Hive/Parquet 做数据格式的经验和大模型应用开发完全不冲突。
区别在于,大模型工程引入了一些数据工程师以前很少碰的东西:向量检索、权限隔离、prompt 追踪、token 计费。这些东西看起来是新领域,但本质上是"数据怎么管、怎么控、怎么追"的问题,而这是大数据工程师的老本行。
我参与的一个企业知识库 RAG 项目,输入是几十万份 PDF 和 Word 文档,走的是经典的"清洗-切片-embedding-入库-检索"流程。前半段和传统 ETL 没有区别,后半段加了向量检索和 LLM 调用。整个项目我负责的是数据管道和权限层,刚好踩遍了从 Demo 到生产的所有坑。
---
数据治理:大数据工程师的隐性优势

很多转型的大数据工程师会低估自己在数据治理上的积累。大模型应用的数据质量问题是真实存在的——来源不明、格式混乱、更新滞后的文档,喂给 embedding 模型后,检索效果直接断崖式下跌。
我们项目里踩过一个具体的坑:某个部门的文档长期存在 S3 上,没有版本记录,也没有权限标签。切完片后,RAG 系统会把所有文档同等对待地检索返回,导致低权限用户也能通过向量相似度拿到不该看到的内容。这不是模型问题,是数据治理问题。
解决方式是用 Hive 的权限体系做一层过滤,在查询向量之前先根据用户的 tenant_id 和 role 过滤出允许访问的文档集合,再在这个子集里做向量检索。这段逻辑写在 RAG 管道的检索阶段之前,属于数据层的工作,正是大数据工程师的强项。
---
向量数据库:选型与踩坑
我们项目用了 Milvus 作为向量数据库,原因很简单:团队熟悉,生态友好,支持元数据过滤。选型过程本身没太多争议,但实际应用中有几个坑值得说。
第一个坑是规模。 Demo 阶段几万条数据没问题,到了百万级,全表扫描式的相似度搜索就扛不住了。必须加 HNSW 索引,调调 efConstruction 和 M 参数,否则查询延迟会明显上升。
第二个坑是元数据过滤的性能。 Milvus 支持在搜索时带 filter 条件做标量过滤,这在权限场景下非常关键。但要注意,filter 字段必须是标量类型,不能是向量类型,而且过滤条件下的查询性能会受影响。如果 tenant 维度特别多,要考虑把过滤逻辑前置到数据写入阶段,按租户分 collection 或分 shard。
第三个坑是数据一致性。 源文档更新了,向量库里的旧数据不会自动删除,导致检索结果过期。我们后来加了一个基于消息队列的同步机制,源数据变更时触发增量更新和旧数据清理。
---
RAG 数据管道:从数据 lake 到向量检索
RAG 管道的核心链路是固定的,但每个环节都有细节可以挖。我们以 PDF 文档为例,整个管道分四步:
第一步:解析。 用 Unstructured 或 PyPDF2 提取文本,同时保留文档的元数据(来源、创建时间、author 字段)。这步和大数据的 schema on read 思路一样——原始数据不动,解析时按需读。
第二步:切片。 切片策略直接影响检索质量。我们用了一个滑动窗口方案,窗口大小 500 tokens,重叠 50 tokens,保证语义连贯性不被截断。切完后每个 chunk 打上一个 metadata 标签,包含 docid、chunkindex、tenantid 和 accesslevel。
第三步:Embedding。 我们用的是 OpenAI 的 text-embedding-3-small,因为速度够用、质量稳定。如果是内网部署场景,可以考虑 BGE-large-zh 这类开源模型,成本低但效果也不错。Embedding 完之后写入向量数据库。
第四步:检索与生成。 用户提问后,先根据 tenant_id 做权限过滤,再在过滤后的集合里做向量相似度搜索,取 top-k 个 chunk,拼进 prompt 里让 LLM 生成回答。
# RAG 检索阶段的核心逻辑:先权限过滤,再向量检索
def rag_retrieve(query: str, user_context: dict, top_k: int = 5):
tenant_id = user_context["tenant_id"]
roles = user_context.get("roles", [])
# 1. 构建权限过滤条件
filter_expr = f'tenant_id == "{tenant_id}" and (access_level == "public" or "admin" in access_levels)'
# 2. 生成查询向量
query_embedding = get_embedding(query)
# 3. 在 Milvus 中执行带过滤的向量搜索
results = collection.query(
anns_field="embedding",
query_vector=query_embedding,
limit=top_k,
expr=filter_expr,
output_fields=["content", "doc_id", "chunk_index", "source_file"]
)
# 4. 组装 prompt
context_text = "\n\n".join([r["content"] for r in results])
prompt = build_prompt(query, context_text)
return call_llm(prompt)
这段代码的关键在 expr 参数——它决定了权限过滤在哪里生效。我们最开始没加这个过滤,直接在所有数据上做向量搜索,结果就是不同租户的用户能搜到别人的内部文档。权限问题不是出在 LLM 层,而是出在数据层。
---

落地项目复盘:Demo 到团队的三个翻车现场
这个项目 Demo 阶段是一个人跑的,数据是模拟的,权限逻辑是硬编码的。接入团队真实业务后,第一个星期就暴露了三个问题。
翻车一:权限校验被跳过了。 Demo 里的权限检查是在应用层做的,接入后团队要求用 API Gateway 统一做鉴权。结果 Gateway 的 JWT 解析逻辑和我们应用层的租户 ID 提取方式不一致,导致部分请求绕过了权限检查,直接返回了跨租户数据。排查花了两天,根源是两边对 token 里 tenant 字段的定义不一样。
翻车二:日志没有 Trace ID 串联。 RAG 管道涉及多个服务调用(解析服务、Embedding 服务、向量库、LLM),每个服务各自打日志,排查一个问题时需要在多个日志系统里来回切换。团队要求全链路 Trace ID,接入 SkyWalking 后发现 LLM 调用的延迟波动很大,但没有对应的 trace 记录,根本定位不到是哪个环节慢。
翻车三:向量库的连接池管理缺失。 Demo 阶段并发量小,连接池够用。团队接入后并发上来了,Milvus 的连接数爆满,查询开始出现超时。根本原因是 Embedding 服务和检索服务各自维护了自己的连接池,没有统一管理,导致实际连接数是预期值的两倍多。
这三个问题的共同点是:都不影响 Demo 的正常运行,但都会在多租户、高并发、多服务协作的场景下暴露出来。 这也是大数据工程师转型大模型时需要补的核心能力——工程化。
---
排查过程:权限问题的完整定位链路
权限问题暴露后,我们的排查过程是这样的:
现象: 部分用户在特定场景下能访问其他租户的文档内容。问题不是100%复现,大约30%的请求会出现越权。
验证动作1: 抓取问题请求的 HTTP 头,发现 JWT token 中 tenant 字段为空的情况占了约40%。这部分请求绕过了应用层的权限校验,直接走了默认 tenant 的逻辑。
验证动作2: 检查 Gateway 的 token 解析逻辑,发现它用的是标准的 JWT 中间件,从 Authorization: Bearer <token> 里提取 payload。但我们应用层期望的 tenant 信息不在标准 JWT claim 里,而是在一个自定义 header 里——两边对"tenant 从哪来"的理解不一致。
验证动作3: 修改应用层权限校验逻辑,优先从 JWT payload 读取 tenantid,fallback 到自定义 header。同时在 Gateway 层加了一层预处理,把 tenantid 统一注入到 JWT claim 里。
验证结果:修改后重新压测,越权请求降为0。问题根因是API Gateway 和应用层对 tenant 来源的约定不一致,不是代码逻辑错误,是接口约定问题。
这个排查过程给我最大的启示是:权限问题往往不是技术实现的 bug,而是架构设计阶段的接口约定缺失。 大数据项目里这种问题相对少见,因为数据管道是单向的,权限模型比较清晰。但大模型应用涉及多服务协作,权限模型的复杂度远高于传统 ETL 管道。
---
代码解释:日志和可观测性的关键实现
可观测性这块,我们用的是 Prometheus + Grafana + Loki 的组合。Prometheus 收集指标,Grafana 做可视化,Loki 存日志。关键是要在每个 RAG 管道的节点上加 instrument。
import time
from prometheus_client import Counter, Histogram, start_http_server
# 定义指标
RAG_QUERY_COUNT = Counter(
'rag_query_total',
'RAG 查询总数',
['tenant_id', 'status'] # 按租户和状态分维度
)
RAG_QUERY_LATENCY = Histogram(
'rag_query_latency_seconds',
'RAG 查询延迟',
['tenant_id'],
buckets=[0.1, 0.5, 1.0, 2.0, 5.0]
)
def tracked_rag_retrieve(query: str, user_context: dict):
tenant_id = user_context["tenant_id"]
start = time.time()
status = "success"
try:
result = rag_retrieve(query, user_context)
except Exception as e:
status = "error"
raise
finally:
elapsed = time.time() - start
RAG_QUERY_COUNT.labels(tenant_id=tenant_id, status=status).inc()
RAG_QUERY_LATENCY.labels(tenant_id=tenant_id).observe(elapsed)
return result
逐段解释:
第一段定义了 rag_query_total 这个 Counter 指标,用 tenant_id 和 status 做维度。这样在 Grafana 里可以按租户看查询量,按状态看成功率。Counter 只增不减,适合统计查询次数和错误次数。
第二段定义了 rag_query_latency_seconds 这个 Histogram 指标,用 tenant_id 做维度,bucket 分了五档(0.1s、0.5s、1s、2s、5s)。Histogram 会自动帮你统计落在每个 bucket 里的样本数,Grafana 可以直接画出 P50/P90/P99 的分位图,不用自己算。
第三段是实际的包装函数。核心逻辑是先记录开始时间,用 try/finally 保证无论成功还是异常都会上报指标。finally 块里计算耗时并更新两个指标。这里有一个细节:异常要在 finally 之后重新抛出,否则会吞掉异常,调用方感知不到错误。
输入: query 字符串和包含 tenantid 的 usercontext 字典。
核心逻辑: 在原函数外面包一层计时和指标上报。
输出: 与原函数一致,同时副作用是更新了 Prometheus 指标。
异常处理: 用 try/finally 结构,异常不会丢失。
这段代码的价值不在于实现了什么新功能,而在于让原本不可见的 RAG 管道变成了可观测的系统。团队接入后,我们可以通过 Grafana 面板实时看到每个租户的查询延迟分布、错误率趋势,出了问题第一时间知道是 LLM 调用慢还是向量检索慢,而不是靠猜。
---
失败原因:常见错误的分类和区分方法
踩了这么多坑,我把失败原因归纳为三类,每类的特征和区分方法不同:
业务错误: 逻辑本身有问题。比如权限过滤条件写反了,或者切片策略不合理导致检索质量差。特征是逻辑验证后能发现矛盾,通常有明确的预期结果可以对照。
配置错误: 代码没问题,但参数不对。比如 Milvus 的索引类型选错了(IVF_FLAT 选成了 HNSW),或者 Prometheus 的采集间隔设得太长错过了关键指标。特征是不能通过代码审查发现,需要对照文档或经验值逐个排查配置项。
环境问题: 代码和配置都没问题,但运行时环境不满足。比如连接池大小不够、网络带宽不足、LLM API 限流。特征是问题在高负载或特定场景下才出现,单机测试无法复现。
区分这三类的方法很简单:先在隔离环境中复现,再逐步替换变量定位根因。 比如权限问题,先在测试环境用相同的 token 和请求复现,确认不是代码逻辑错误;然后检查 Gateway 的配置,确认不是 token 解析问题;最后检查环境约束,确认是接口约定问题。
---
适用边界:什么时候该照搬,什么时候不能
这个项目的方案有几个明确的适用边界:
适用场景: 多租户企业知识库、需要权限隔离的 RAG 系统、对可观测性有要求的生产级大模型应用。如果你的项目不涉及多租户、不需要严格的权限控制、只是个人 Demo 或者内部小工具,这套方案的复杂度是过剩的。
不适用场景: 单租户、数据量小(低于十万条)、对延迟不敏感的场景。这种情况下直接用 LangChain 的 SimpleDirectoryLoader 加 FAISS 就够了,没必要引入 Milvus 和完整的可观测性体系。
取舍建议: 如果团队已经有现成的数据管道(Kafka、Spark、Hive),建议复用现有的数据处理能力做 RAG 的数据准备,不要重复造轮子。但如果团队没有大数据基础设施,也不建议为了做 RAG 专门搭建一套,直接用现成的向量数据库 SDK 起步更快。
另外,权限模型一定要在架构设计阶段就确定好,不要等代码写完了再补。这是这个项目给我最痛的教训——Demo 阶段的权限逻辑和生产阶段的权限逻辑是两个不同层次的东西,提前设计好可以节省大量的返工成本。
---
总结
从大数据转大模型,技术栈的差异没有想象中那么大。核心的跨越是思维方式的转变——从批处理和管道思维,转向实时交互和系统工程思维。
权限、日志和可观测性是大模型应用从 Demo 走向生产的门槛,也是大数据工程师最容易发挥优势的领域。这些能力的本质是"数据怎么管、怎么控、怎么追",和我们做 ETL 管道时的关注点是一致的。
如果你也是大数据背景想转型,我的建议是:先把 RAG 管道从头到尾手撸一遍,然后在单租户 Demo 的基础上,主动加上多租户权限隔离和全链路可观测性。这两个能力补上之后,你的项目就不再是 Demo,而是一个可以放进简历的工程实践。
资料展示
下面是我整理的AI大模型学习资料和工具包预览,适合收藏后按主题逐步学习。





需要这份AI大模型资料清单的话,在评论区回复「清单」即可;我会根据大家的问题继续补充对应的实战内容。

1万+



被折叠的 条评论
为什么被折叠?



