
做企业智能问答、内部知识库 RAG 最头疼的不是大模型回答不准而是底层存储架构太臃肿。常规落地方案基本是三件套单独部署向量库存文档 Embedding、Elasticsearch 做关键词检索、OLAP 数仓存业务工单结构化数据。用户提问时需要分别向三个系统发起查询再写代码融合多份召回结果中间多一层网络开销文档更新还要维护三套同步任务运维压力翻倍。现在换 Apache Doris 就能砍掉一半组件一套数据库承接 RAG 全流程的数据存储与检索需求不用额外新增中间件。1.架构对比三件套 vs Doris 统一底座1.1 传统三件套的问题痛点表现多份存储同一份文档切片进向量库、ES、数仓磁盘与同步脚本长期维护多路召回融合向量 / BM25 / SQL 过滤各查一次应用层重排、去重更新不同步向量已更新、ES 未索引回答滞后或缺失扩容复杂峰值时分别扩容向量服务与 ES1.2 Doris 统一底座能做什么 / 不能做什么❝能收敛存储 检索层文档切片、业务标签、向量、全文索引 同表存储Stream Load / Flink CDC / INSERT 写入后 同一集群 可查检索 报表统计 同表完成无需再同步到 BI 数仓❝不能替代仍需外部组件组件说明Embedding 服务bge-m3、OpenAI Embedding 等文档解析与切片PDF/Word/HTML → chunkLLMDeepSeek、GPT 等生成回答RRF / 学习排序BM25 与向量 分数级 融合应用层2.端到端落地流程文档源 → 切片 → Embedding → 写入 Doris → BUILD INDEX → 混合检索 SQL → 拼 Prompt → LLM 回答2.1 环境依赖pip install langchain-community langchain-text-splitters sentence-transformers pymysql2.2 落地案例建表bge-m31024 维要点使用 inner_product 时Embedding 必须归一化SEARCH() 跨字段 OR 时每个字段都要建倒排索引中文正文建议 parser chineseCREATE DATABASE IF NOT EXISTS enterprise_rag; USE enterprise_rag; DROP TABLE IF EXISTS doc_knowledge; CREATE TABLE doc_knowledge ( id INT NOT NULL, title VARCHAR(500), content TEXT, category VARCHAR(100) COMMENT 产品/售后/财务, create_time DATETIME, embedding ARRAYFLOAT NOT NULL COMMENT 1024维与 bge-m3 一致, INDEX idx_title ( title ) USING INVERTED PROPERTIES ( parser chinese, support_phrase true ), INDEX idx_content ( content ) USING INVERTED PROPERTIES ( parser chinese, support_phrase true ), INDEX idx_embedding ( embedding ) USING ANN PROPERTIES ( index_type hnsw, metric_type inner_product, dim 1024, max_degree 32, ef_construction 40 ) ) ENGINEOLAP DUPLICATE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 4 PROPERTIES ( replication_allocation tag.location.default: 1 );2.3 写入与索引构建小批量 / 实时INSERT 或 Stream Load 后索引 异步构建数据可先查全文/向量走索引需等待 BUILD 完成。大批量离线建议流程-- 1. 灌数据Stream Load / INSERT / rag_demo_insert.py -- 2. 显式构建索引 BUILD INDEX idx_title ON doc_knowledge; BUILD INDEX idx_content ON doc_knowledge; BUILD INDEX idx_embedding ON doc_knowledge; -- 3. 查看进度 SHOW BUILD INDEX WHERE TableName doc_knowledge;2.4 Python 写入示例完整脚本scripts/rag_demo_insert.pyimport pymysql from sentence_transformers import SentenceTransformer from langchain_text_splitters import RecursiveCharacterTextSplitter model SentenceTransformer(BAAI/bge-m3) chunks RecursiveCharacterTextSplitter(chunk_size400, chunk_overlap40).split_text(open(doc.md).read()) vecs model.encode(chunks, normalize_embeddingsTrue) conn pymysql.connect(host10.26.20.3, port9132, userroot, password, autocommitTrue) with conn.cursor() as cur: for i, (text, vec) in enumerate(zip(chunks, vecs)): vec_sql [ ,.join(f{x:.8f}for x in vec) ] cur.execute( INSERT INTO enterprise_rag.doc_knowledge (id, title, content, category, create_time, embedding) VALUES (%s, %s, %s, %s, NOW(), CAST(%s AS ARRAYFLOAT)), (i 1, 手册, text, 售后, vec_sql), )LangChain 集成无独立 apache-doris-vector 包from langchain_community.vectorstores.apache_doris import ApacheDoris, ApacheDorisSettings from langchain_community.embeddings import HuggingFaceEmbeddings settings ApacheDorisSettings( host10.26.20.3, port9132, usernameroot, password, databaseenterprise_rag, tabledoc_knowledge, ) embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-m3, encode_kwargs{normalize_embeddings: True}, ) store ApacheDoris(embeddings, configsettings) docs store.similarity_search(退款售后流程, k5)3.混合检索 SQL业务问题示例「近三个月、售后类、与退款相关的处理方案」方案 A推荐 — 结构化 MATCH 向量排序-- query_vec 由 bge-m3 对用户问题编码得到1024 维已归一化 SELECT id, title, content, inner_product_approximate(embedding, [/* query_vec */]) AS sim FROM enterprise_rag.doc_knowledge WHERE create_time 2026-04-01 AND category 售后 AND content MATCH_ANY 退款 售后 ORDER BY sim DESC LIMIT 5;实测等价 case英文 demo 数据SELECT id, title FROM rag_smoke_test.rag_hybrid_demo WHERE create_time 2026-04-01 AND category after_sales AND content MATCH_ANY refund service ORDER BY l2_distance_approximate(embedding, [26.36, 7.05, 32.36, 86.39, 58.79, 27.18, 99.38, 80.19]) ASC LIMIT 2; -- 结果id0refund guide✓方案 BSEARCH() DSL4.1SELECT id, title, content, inner_product_approximate(embedding, [/* query_vec */]) AS sim FROM enterprise_rag.doc_knowledge WHERE create_time 2026-04-01 AND category 售后 AND search(content:退款 OR title:退款) ORDER BY sim DESC LIMIT 5;方案 CRRF 融合应用层生产推荐用于高质量召回def rrf_merge(rank_lists, k60, topn5): scores {} for ranks in rank_lists: for rank, doc_id in enumerate(ranks, start1): scores[doc_id] scores.get(doc_id, 0) 1.0 / (k rank) return sorted(scores.items(), keylambda x: -x[1])[:topn] # SQL-1: ORDER BY inner_product_approximate(...) DESC LIMIT 20 # SQL-2: WHERE content MATCH_ANY ... ORDER BY score() DESC LIMIT 20 # merged rrf_merge([vector_ids, bm25_ids])4.RAG 问答服务拼接def answer(query: str, rows: list[dict], llm_client) - str: context \n---\n.join(r[content] for r in rows) prompt f请仅依据下列知识库片段回答禁止编造 {context} 用户问题{query} return llm_client.complete(prompt)5.适用场景企业内部综合知识库制度、产品手册、研发文档统一入库智能客服辅助工单 解决方案混合检索人工坐席参考检索 报表一体同表统计咨询量、高频问题分类研发文档 / API 检索向量语义 关键词精确匹配对于大数据团队来说不用新增陌生中间件复用现有 Doris 运维经验上手门槛低是企业落地 RAG 性价比很高的方案。