聊《我用大数据经验做了次 AI 项目,最先失效的是旧方法》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。
摘要
去年我们组接到一个需求:把内部文档库接进大模型,做问答系统。前端同学两周搞了个LangChain Demo,检索准确率90%,演示效果不错。结果真要让业务部门用,第一个被质疑的不是效果,而是"凭什么你能搜到这份文件"——权限漏洞差点酿成事故。
这次项目让我意识到,大数据出身的人做AI应用,最容易踩的坑不是模型调用,而是权限、日志、可观测这三座大山。
---
目录
- 大数据与大模型的交叉点
- 数据治理:从小团队的角度说取舍
- 向量数据库:选型只看三个指标
- RAG数据管道:一个真实案例
- 权限控制:我最先推翻的旧思路
- 代码解释:权限过滤的实现原理
- 日志和可观测:小团队怎么不卷死自己
- 失败原因:三种错误的区分方法
- 适用边界:什么情况下别照搬
- 总结
大数据与大模型的交叉点
数据工程师做AI项目,天然优势在数据管道。大模型落地离不开数据清洗、特征工程、向量化、RAG检索,这些全是数据工程的活。
但交叉点之外,差异也很明显:
- 数据质量定义不同:传统数仓讲究完整性、一致性;RAG更关注语义密度、上下文窗口利用率
- 反馈闭环速度不同:传统ETL是批量跑批,LLM应用是实时交互,出错立刻暴露
- 运维关注点不同:过去看任务成功率、延迟;现在要看token成本、响应时间、权限边界
我的判断是:数据工程师转型AI,别去卷Prompt Engineering和模型微调,那是算法团队的地盘。你的核心价值在数据管道和工程化兜底。
---
数据治理:从小团队的角度说取舍
我们当时做了两个决策,到现在还在争论值不值:
第一个决策:放弃GraphRAG,先做向量检索。
理由很简单——团队只有三个人,图谱构建和维护的成本远高于收益。Demo里GraphRAG看着很美,但真实业务场景下,知识更新频率高,图谱同步跟不上就是灾难。我们先用简单的向量检索+元数据过滤跑起来,权限控制先做简单粗暴的角色过滤。
第二个决策:嵌入模型选本地部署的BGE-M3,而不是API。
成本账算清楚了:日均2000次查询,API调用一个月几万块。BGE-M3本地部署,显存占用20GB左右,单机可扛。关键是数据不出内网,权限问题从源头解决。
这两个决策的共同逻辑是:先让系统跑起来,再谈优化。小团队经不起过度设计的拖沓。
---
向量数据库:选型只看三个指标
我们试过Milvus、Weaviate、Chroma,最后选了Milvus Lite(单机版)。
不是因为它最强,而是因为:
1. 部署成本低:Docker起一个容器就行,不需要K8s集群
2. 与生态兼容好:支持LangChain、LlamaIndex,接入快
3. 元数据过滤原生支持:这是权限控制的底层支撑
选型时我看到很多文章推荐企业版方案,但对我们这种团队来说,能用是最重要的标准。等真的跑到百万级向量、需要分布式扩展时,再迁移也不迟。
---
RAG数据管道:一个真实案例
我们当时的流程是:
输入:公司内部Wiki文档,约5万篇,格式混杂(Markdown、HTML、PDF)
处理步骤:
1. 文本抽取和清洗(去掉导航栏、广告、页眉页脚)
2. 按语义段落切分,chunk size 512,overlap 64
3. 用BGE-M3生成向量,存入Milvus
4. 元数据打标:文档类型、密级、可见角色
可观察结果:
- 检索延迟控制在200ms以内(P99)
- 权限过滤正确率100%(经过测试用例验证)
- 首查命中率(Top-3包含正确答案)约85%
这个结果不是最理想的,但对于内部工具来说够用。关键是建立了基线,后续可以持续优化。
---
权限控制:我最先推翻的旧思路
Demo阶段我们没做权限,直接查全库。上线前才发现这个问题。
排查过程:
现象:某部门员工A,职级普通,却能在问答中获取到"高管会议纪要"的内容。
验证动作:
1. 检查检索逻辑,发现向量检索本身没有过滤机制
2. 查元数据,发现会议纪要的密级字段确实存在,但查询时未透传用户角色
3. 定位到LangChain的Retriever没有在query时注入权限条件
排除结果:不是模型问题,不是向量库问题,是管道设计遗漏。
解决方案代码:
from langchain.vectorstores import Milvus from langchain.retrievers import VectorStoreRetriever def format_role_filter(user_roles: list[str]) -> dict: """将用户角色映射为文档密级过滤条件""" role_hierarchy = { "public": ["intern", "employee", "manager", "director", "executive"], "internal": ["employee", "manager", "director", "executive"], "confidential": ["manager", "director", "executive"], "secret": ["director", "executive"], } # 取用户角色能访问的最低密级 available_levels = set() for level, roles in role_hierarchy.items(): if any(r in roles for r in user_roles): available_levels.add(level) return {"document_class": {"$in": sorted(available_levels)}} def build_retriever(user_roles: list[str], collection: Milvus) -> VectorStoreRetriever: role_filter = format_role_filter(user_roles) retriever = VectorStoreRetriever( vectorstore=collection, search_type="similarity", search_kwargs={ "k": 5, "filter": role_filter } ) return retriever---
代码解释:权限过滤的实现原理
上面这段关键代码解决的是"检索时如何让权限条件生效"的问题。拆开来看:
**format_role_filter函数**
- 输入:用户的角色列表,比如
["employee"]或["manager", "director"] - 核心逻辑:用一个预定义的
role_hierarchy字典,把角色映射到可访问的文档密级。遍历每个密级,检查用户角色是否在该密级的允许列表中,收集所有可访问的密级 - 输出:一个MongoDB风格的过滤字典,例如
{"document_class": {"$in": ["confidential", "internal", "public"]}} - 异常处理:如果传入的空角色列表,
available_levels为空,最终返回{"document_class": {"$in": []}},这会导致检索结果为零——这是一个有意的设计,空权限意味着无权访问,而不是返回全部数据
**build_retriever函数**
- 输入:用户角色列表和一个已初始化的Milvus集合对象
- 核心逻辑:先调用
format_role_filter生成过滤条件,然后通过search_kwargs的filter参数将其注入到向量检索中。这里的关键是filter在向量检索阶段就生效,而不是检索完成后再过滤结果 - 输出:一个配置好的
VectorStoreRetriever实例,后续可以直接用于LangChain的chain - 异常处理:如果
collection未正确初始化或user_roles类型不对,会在函数入口处抛出异常。生产环境中建议在调用方做类型校验,避免运行时才暴露问题
为什么必须在检索阶段过滤?
这是整个实现原理的核心。如果先把向量检索结果拿回来再做后处理过滤,会出现两个问题:一是召回率下降——不该看到的文档占用了top-K的名额,真正相关的内容可能被挤出去;二是信息泄露风险——即使后处理能拦住,检索结果已经经过网络传输,审计上也说不通。所以在filter参数里注入权限条件,是从根源上阻断越权访问。
---
日志和可观测:小团队怎么不卷死自己
这是另一个踩坑点。Demo阶段随便打印print()就能调试。生产环境不行。
我们做了三件事:
1. 结构化日志
所有关键操作打日志,格式统一:
{ "timestamp": "2024-03-15T10:30:00Z", "user_id": "u_12345", "action": "rag_query", "query": "2024年Q4预算...", "result_count": 3, "latency_ms": 187, "permissions_applied": true, "model": "bge-m3", "tokens_used": 1024 }2. 追踪关键链路
每个请求生成一个trace_id,贯穿query→检索→生成→返回全流程。出了问题能迅速定位是哪个环节。
3. 成本看板
每天统计token消耗、查询次数、热门问题。预算可控是持续运营的基础。
这些工作加起来不超过3天,但如果没有它们,问题排查成本会指数级上升。
---
失败原因:三种错误的区分方法
项目期间踩了好几个坑,失败原因总结下来有三种类型:
| 类型 | 特征 | 例子 | 排查方向 |
|------|------|------|----------|
| 业务错误 | 逻辑符合预期但结果不对 | 检索返回了无关文档 | 检查chunk策略、embedding质量 |
| 配置错误 | 参数/权限/路径不对 | 元数据字段名写错 | 对比配置与代码实际读取的路径 |
| 环境错误 | 部署/依赖/资源问题 | GPU显存不足导致模型加载失败 | 检查环境配置、资源限制 |
区分方法是:先看报错信息,再看日志,最后复现。大多数时候,错误信息已经给出了线索,不需要大规模debug。业务错误和配置错误的区别在于——改配置能解决的是配置错误,改策略或模型才是业务错误。
---
适用边界:什么情况下别照搬
这套方案的适用边界比较清晰:
适合:
- 小团队(3-10人),快速迭代
- 内部工具,用户规模有限
- 文档类知识,结构相对清晰
不适合:
- 公开SaaS产品,需要复杂权限体系(比如行级权限、动态权限)
- 实时性要求极高的场景(检索延迟敏感,Milvus Lite单机版有瓶颈)
- 多模态内容(图像、音频、视频),当前方案只处理文本
取舍的核心原则:先跑通,再优化。不要因为追求完美架构而推迟上线。等规模上来了,再考虑迁移到分布式向量库或引入更精细的权限模型。
---
总结
从大数据转大模型,最容易误判的是自己的核心竞争力。数据工程师的价值不在调Prompt,而在构建可靠的数据管道和工程化兜底。
权限、日志、可观测——这三个话题在Demo阶段看起来不重要,但正式上线时会决定项目的生死。小团队尤其要重视,因为技术债的复利效应在小团队身上更明显。
我的建议是:别急着学新框架,先把你熟悉的数据管道能力迁移过来。LangChain也好,LlamaIndex也罢,工具在变,但数据治理的逻辑不变。把基础打牢,转型自然水到渠成。
资料展示
下面是我整理的AI大模型学习资料和工具包预览,适合收藏后按主题逐步学习。
如果你想看完整资料目录,可以在评论区留言「资料」;也欢迎告诉我你更关注AI大模型里的哪类内容。