实战指南:基于 Apache Doris 构建企业级 RAG(检索增强生成)应用

📅 发布时间:2026/7/5 1:17:49 👁️ 浏览次数:
实战指南:基于 Apache Doris 构建企业级 RAG(检索增强生成)应用
1. 引言在 LLM大语言模型应用爆发的今天RAGRetrieval-Augmented Generation检索增强生成已成为解决大模型“幻觉”和知识滞后问题的标准方案。而作为 RAG 核心组件的向量数据库其性能直接决定了检索的效率。GitHub 上的开源项目 [apache-doris-rag]提供了一个非常优秀的范例展示了如何利用Apache Doris强大的向量检索能力来构建 RAG 系统。为什么选择 Apache Doris 做 RAG极速性能基于 C 实现的 HNSW 索引查询速度极快。混合检索支持向量检索 标量筛选如WHERE categoryfinance AND vector_distance(...) 0.5这是传统专用向量数据库的弱项却是 Doris 的强项。架构统一无需维护单独的 Vector DB数仓和向量库合二为一降低运维成本。本文将深入解析该项目的核心实现带您从零构建一个基于 Apache Doris 的 RAG 系统。2. 架构设计在基于 Apache Doris 的 RAG 系统中数据流转主要分为两个阶段知识入库Indexing和问答检索Retrieval Generation。3. 环境准备在开始代码之前我们需要准备基础环境。3.1 软件依赖Apache Doris: 建议 2.0 及以上版本2.1 对向量索引支持更完善。Python: 3.8Python 库:pymysql(连接 Doris),langchain,openai(或sentence-transformers),pandas.3.2 启动 Apache Doris (Docker 快速启动)如果您还没有 Doris 环境可以使用 Docker 快速拉取一个单机版用于测试# 下载 Docker 镜像dockerpull apache/doris:2.1.0-bin-x64# 启动 (需根据官方文档配置 FE/BE 端口)# 这里假设您已经有可用的 Doris 实例IP 为 127.0.0.1FE Query Port 为 90304. 数据库设计 (Doris 端)这是最关键的一步。我们需要在 Doris 中创建一个支持向量索引的表。4.1 创建向量表登录 MySQL 客户端或 DBeaver 连接 Doris执行以下 SQLCREATEDATABASEIFNOTEXISTSrag_demo;USErag_demo;CREATETABLEIFNOTEXISTSdocuments(idVARCHAR(64)NOTNULLCOMMENT文档ID,contentTEXTCOMMENT原始文本内容,metadataTEXTCOMMENT元数据(JSON格式),vector_col ARRAYFLOATNOTNULLCOMMENT向量数据,INDEXidx_vector(vector_col)USINGHNSW PROPERTIES(dim1536,-- 对应 OpenAI embedding 维度metric_typecosine_similarity,-- 余弦相似度m16,-- HNSW 参数ef_construction200-- HNSW 参数))ENGINEOLAPDUPLICATEKEY(id)DISTRIBUTEDBYHASH(id)BUCKETS1PROPERTIES(replication_num1,enable_light_schema_changetrue);注意dim参数必须与你选用的 Embedding 模型维度一致。OpenAItext-embedding-3-small通常是 1536 维。5. 代码实现 (Python 端)参考apache-doris-rag的逻辑我们将代码分为数据导入和问答检索两部分。5.1 导入数据 (Ingestion)这段代码负责读取文件、切分文本、计算向量并存入 Doris。importosimportuuidimportjsonimportpandasaspdfromlangchain.document_loadersimportTextLoader,PyPDFLoaderfromlangchain.text_splitterimportRecursiveCharacterTextSplitterfromlangchain_openaiimportOpenAIEmbeddingsimportpymysql# 1. 配置信息DORIS_HOST127.0.0.1DORIS_PORT9030DORIS_USERrootDORIS_PASSWORDDORIS_DBrag_demo# 初始化 Embedding 模型os.environ[OPENAI_API_KEY]sk-xxxx...# 替换你的 KeyembeddingsOpenAIEmbeddings(modeltext-embedding-3-small)defget_doris_connection():returnpymysql.connect(hostDORIS_HOST,portDORIS_PORT,userDORIS_USER,passwordDORIS_PASSWORD,databaseDORIS_DB,charsetutf8mb4)defingest_docs(file_path):# 2. 加载与切分文档print(f正在加载文件:{file_path}...)loaderTextLoader(file_path,encodingutf-8)# 或者是 PyPDFLoaderdocumentsloader.load()text_splitterRecursiveCharacterTextSplitter(chunk_size500,chunk_overlap50)chunkstext_splitter.split_documents(documents)print(f文档已切分为{len(chunks)}个片段正在生成向量...)# 3. 批量生成向量并写入 Dorisconnget_doris_connection()cursorconn.cursor()batch_data[]forchunkinchunks:# 生成向量vectorembeddings.embed_query(chunk.page_content)# 准备 SQL 插入数据# Doris 接收数组格式字符串: [0.1, 0.2, ...]vector_strstr(vector)doc_idstr(uuid.uuid4())metadatajson.dumps(chunk.metadata)# 简单处理单引号转义contentchunk.page_content.replace(,\\)sqlf INSERT INTO documents (id, content, metadata, vector_col) VALUES ({doc_id}, {content}, {metadata},{vector_str}) try:cursor.execute(sql)exceptExceptionase:print(fInsert error:{e})conn.commit()conn.close()print(数据入库完成)# 运行导入if__name____main__:# 创建一个测试文件withopen(test_knowledge.txt,w,encodingutf-8)asf:f.write(Apache Doris 是一个现代化的 MPP 分析型数据库产品。它支持极速的实时数据分析。)ingest_docs(test_knowledge.txt)5.2 检索与问答 (RAG)这里我们利用 SQL 进行向量检索然后调用 LLM 回答。fromlangchain_openaiimportChatOpenAI# 初始化 LLMllmChatOpenAI(modelgpt-3.5-turbo,temperature0)defsearch_doris(query_text,top_k3):在 Doris 中进行向量搜索# 1. 将用户问题转为向量query_vectorembeddings.embed_query(query_text)vector_strstr(query_vector)connget_doris_connection()cursorconn.cursor()# 2. 执行向量检索 SQL# 使用 cosine_distance 函数结果越接近 0 越相似 (Doris 2.1 语法可能略有不同以官方文档为准)# 若 metric_type 定义为 cosine_similarity这里通常查出来的是距离排序需注意sqlf SELECT content, cosine_distance(vector_col,{vector_str}) as dist FROM documents ORDER BY dist ASC LIMIT{top_k}cursor.execute(sql)resultscursor.fetchall()conn.close()# 返回纯文本列表return[row[0]forrowinresults]defrag_chat(query):# 1. 检索上下文print(f用户提问:{query})print(正在检索 Doris...)retrieved_docssearch_doris(query)context\n\n.join(retrieved_docs)print(f检索到{len(retrieved_docs)}条相关上下文。)# 2. 构建 Promptpromptf 基于以下已知信息简洁和专业地回答用户的问题。如果无法从信息中得到答案请说我不知道。 已知信息:{context}问题:{query}# 3. LLM 生成print(正在思考...)responsellm.invoke(prompt)returnresponse.content# 运行测试if__name____main__:answerrag_chat(Apache Doris 的特点是什么)print(-*30)print(fAI 回答: \n{answer})6. 实现细节深度解析在apache-doris-rag及类似的实现中有几个核心点值得注意6.1 向量索引的选择Doris 提供了HNSW索引。在CREATE TABLE语句中我们设置了ef_construction和m。m: 节点在图中的最大连接数。值越大索引质量越高但内存占用和构建时间增加。ef_construction: 构建索引时的搜索范围。值越大索引质量越高构建越慢。PROPERTIES: 对于生产环境务必根据数据量调整这些参数。6.2 混合查询 (Hybrid Search)Doris 的一大杀手锏是可以在 SQL 中直接混合标量过滤。例如我们只想在“2023年之后”的文档中搜索# 代码片段演示sqlf SELECT content, cosine_distance(vector_col,{vector_str}) as dist FROM documents WHERE metadata LIKE %year: 2024% -- 混合过滤 ORDER BY dist ASC LIMIT 3 这种能力使得 Doris 在处理带有复杂业务逻辑的 RAG 场景如多租户隔离、权限控制时比纯向量数据库如 Milvus, Chroma更具优势。6.3 性能优化Stream Load: 在 Python 代码中我们使用了INSERT这适合小批量数据。如果是海量数据导入建议使用 Doris 的Stream LoadHTTP 接口吞吐量更高。分区 (Partitioning): 如果数据量达到亿级建议按时间或业务ID对表进行PARTITION BY进一步提升查询效率。7. 总结通过参考apache-doris-rag的实现思路我们发现基于 Apache Doris 构建 RAG 系统非常直观。它不需要引入复杂的新的向量数据库组件仅仅利用现有的数仓设施就能实现SQL Vector的强大检索能力。核心优势总结架构极简ETL 处理完的数据直接生成向量存入 Doris数据不搬家。查询灵活标准 SQL 接口运维人员零门槛上手。高并发Doris 原生的 MPP 架构能够支撑高并发的 RAG 请求。参考资料:Apache Doris 官方文档: Vector SearchGitHub 项目: GeiTe168/apache-doris-rag