Doris & SelectDB for AI 实战:从基础 RAG 到知识图谱增强的完整实现

随着大语言模型(LLM)的普及,检索增强生成(RAG)技术成为解决大模型知识时效性、准确性问题的核心方案,通过引入外部知识库,为模型提供实时、可控的上下文,从而提升回答的准确性与可解释性。

然而,在实际落地过程中,RAG 的效果不仅取决于模型能力,更强地依赖底层数据系统的支撑能力。传统数据库或单一向量数据库,往往难以同时满足向量检索、关键词过滤、结构化分析以及高并发查询等多维需求,导致系统复杂度高、性能瓶颈明显。

Apache Doris 作为一款高性能 MPP 架构的实时分析型数据库,具备统一的混合检索与分析能力(HSAP),融合向量检索与全文搜索能力,有效降低系统复杂度并提升整体性能,可作为构建 RAG 系统的重要数据基础设施。

本文将结合实战案例,详细讲解如何基于 Apache Doris 搭建完整的 RAG 系统,涵盖环境准备、数据处理、向量入库、检索问答全流程,并讨论传统 RAG 系统的局限和一些应对方法。

相关实践:Doris & SelectDB for AI 实战:从零搭建非结构化数据智能分析洞察系统

本文示例基于 Apache Doris 展开,便于读者理解底层能力与具体实现机制。在实际生产环境中,如果更关注云上托管、弹性扩缩容、企业级治理与运维效率,也可以选择 SelectDB 作为托管方案(selectdb.com。SelectDB 是基于 Apache Doris 构建,提供 Cloud 与 Enterprise 等产品形态,能够帮助企业更快完成 AI 检索、实时分析与数据服务场景的落地。

1. 环境准备

构建本次 RAG 系统的核心组件如下:

  • LLM 引擎:Deepseek API(负责对话交互与答案生成)

  • 嵌入模型:Ollama + bge-m3:latest(生成文本向量嵌入)

  • 向量数据库:Apache Doris(存储文本片段与向量,支持 ANN 检索)

  • 文本处理:LangChain(文本分片、嵌入生成)

  • 数据处理:Pandas(数据格式转换)

2. 部署与建表

首先需完成 Apache Doris 的安装部署,具体步骤可参考官方文档。部署完成后,创建用于 RAG 系统的数据库和向量表:

SQL 复制代码
-- 创建数据库
CREATE DATABASE doris_rag_test_db;
USE doris_rag_test_db;

-- 创建向量表(支持 HNSW 索引的 ANN 检索)
CREATE TABLE `doris_rag_demo` (
  `id` int NULL,
  `content` text NULL,
  `embedding` array<float> NOT NULL,
  -- 构建 HNSW 向量索引,适配 1024 维向量的内积计算
  INDEX idx_embedding (`embedding`) USING ANN PROPERTIES(
    "dim" = "1024", 
    "ef_construction" = "40", 
    "index_type" = "hnsw", 
    "max_degree" = "32", 
    "metric_type" = "inner_product"
  )
) ENGINE=OLAP
DUPLICATE KEY(`id`)
DISTRIBUTED BY HASH(`id`) BUCKETS 1
PROPERTIES (
  "replication_allocation" = "tag.location.default: 1"
); 

3. 构建可检索知识库(离线数据处理)

3.1. 文本分片(Chunking)

长文本直接嵌入会导致向量表征失真,需先进行分片处理。本文采用 LangChain 的 RecursiveCharacterTextSplitter 实现带重叠的文本分割,保证上下文连续性:

Plaintext 复制代码
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 定义分片规则:chunk_size=400 字符,重叠 10 字符
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=400, chunk_overlap=10, length_function=len
)

# 待处理的 Doris 文档文本(完整文本见附件)
text = """
Apache Doris 简介
Apache Doris 是一款基于 MPP 架构的高性能、实时分析型数据库...(完整文本省略)
"""

# 执行分片
chunks = text_splitter.split_text(text)
print(f"文档分片完成,共生成 {len(chunks)} 个文本片段")

3.2 向量嵌入生成

使用 Ollama 部署的 bge-m3 模型将文本片段转换为 1024 维向量,该模型在中文文本表征上具备优异性能:

Plaintext 复制代码
from langchain_community.embeddings import OllamaEmbeddings
import pandas as pd

# 初始化嵌入模型(需本地启动 Ollama 服务,端口 11434)
embeddings = OllamaEmbeddings(model='bge-m3:latest', base_url='http://localhost:11434')

# 为每个分片生成 ID、文本内容、向量
docs = []
cur_id = 1
for chunk in chunks:
    docs.append({
        "id": cur_id,
        "content": chunk,
    })
    cur_id += 1

# 批量生成向量
contents = [d["content"] for d in docs]
vectors = embeddings.embed_documents(contents)

# 组装成 DataFrame 便于后续导入 Doris
df = pd.DataFrame([
    {
        "id": d["id"],
        "content": d["content"],
        "embedding": vec,
    }
    for d, vec in zip(docs, vectors)
])
print("向量生成完成,数据示例:")
print(df[["id", "content"]].head(2))

3.3 向量数据导入 Doris

通过 Doris 向量客户端将包含文本和向量的数据导入已创建的表中:

Plaintext 复制代码
from doris_vector_search import DorisVectorClient, AuthOptions, IndexOptions

# 配置 Doris 连接信息
auth = AuthOptions(
    host='localhost',
    query_port=9030,
    http_port=8030,
    user='root',
    password='',  # 实际环境请配置密码
)

# 初始化客户端并导入数据
client = DorisVectorClient('doris_rag_test_db', auth_options=auth)
index_options = IndexOptions(index_type="hnsw", metric_type="inner_product")
table = client.create_table(
    'doris_rag_demo',
    df,
    index_options=index_options,
)
print("数据成功导入 Doris 向量表!")

4. 在线检索与答案生成

4.1 向量检索

接收用户查询后,先将查询文本转换为向量,再通过 Doris 的 ANN 索引检索最相关的文本片段:

Plaintext 复制代码
# 用户查询问题
query = "doris支持哪些存储模型?"

# 生成查询向量
query_vec = embeddings.embed_query(query)

# 检索 Top5 相关片段
df_search = (
    table.search(query_vec)
    .limit(5)
    .select(["id", "content"])
    .to_pandas()
)

print("检索到的相关文本片段:")
for idx, row in df_search.iterrows():
    print(f"\n【片段 {idx+1}】\n{row['content']}")

检索结果示例:

4.2 结合 LLM 生成答案

将检索到的文本片段作为上下文,拼接成提示词后调用 LLM API 生成相对准确的答案:

Plaintext 复制代码
from langchain_openai import ChatOpenAI

# 拼接上下文
ctx = "\n".join(f"{r['content']}" for _, r in df_search.iterrows())
prompt =  f"""以下是检索到的 Doris 文档片段:

{ctx}

请根据上述内容回答:{query}"""

# 初始化 Deepseek LLM
llm = ChatOpenAI(
    model='deepseek-v3-1-terminus',
    api_key='your_api_key',  # 替换为实际 API Key
    base_url='https://ark.cn-beijing.volces.com/api/v3',
    temperature=1.0,  # 控制回答随机性
)

# 生成答案
resp = llm.invoke(prompt)
print("最终回答:")
print(resp.content)

最终生成的答案示例:

Apache Doris 支持多种针对性优化的存储模型,核心包括:

  1. 明细模型(Duplicate Key Model):适用于事实表的明细数据存储;
  2. 主键模型(Unique Key Model):保证 Key 唯一性,相同 Key 数据会被覆盖,支持行级别更新;
  3. 聚合模型(Aggregate Key Model):相同 Key 的 Value 列会合并,提前聚合提升查询性能。

此外,Doris 还支持宽表模型、预聚合模型、星型 / 雪花模型等建模方式,适配不同业务场景需求。

至此,一个基础的 RAG 系统已经搭建完成,但该系统仅支持较为简单的知识查询问题 ,面对复杂的逻辑关系和多实体关联时,将面临知识碎片化、难以有效处理复杂问题、信息利用率低等问题。在此基础上,我们可以将结构化的实体关系知识与基础 RAG 结合,让 LLM 能基于完整的知识关联回答复杂问题

5. 知识图谱增强 RAG 设计思路

知识图谱一般构建流程包括:

  • 实体抽取:使用 LLM 从文本中识别和提取实体,如人物、地点、组织、概念等。

  • 关系抽取:识别实体之间的关系,构建三元组(头实体-关系-尾实体)。

  • 图谱构建:将实体和关系组织成图结构,存储在图数据库中。

  • 向量化:将图谱子结构转换为向量,支持语义检索。

基于 Doris 构建的核心思路是将非结构化的文档转换为结构化的知识图谱(实体 + 关系),并将知识图谱数据存入 Doris 做持久化存储,查询时先检索知识图谱的实体关系,再基于完整的结构化知识生成答案,核心步骤如下:

  1. 通过 LLM 从文档分片中抽取实体和实体间的关系;
  2. 用 NetworkX 将抽取的信息构造为图结构,并通过 Pyvis 实现可视化;
  3. 将知识图谱的实体、关系数据生成向量,存入 Doris 的graph_chunk表;
  4. 接收到用户查询后,先检索知识图谱的相关实体,再查询实体间的关系,构造子图;
  5. 基于知识图谱的子图信息,让 LLM 生成精准、全面的答案。

5.1 实体与关系的抽取

利用 LLM,通过定制化提示词从 Doris 文档分片中抽取实体和关系,实体类型限定为Organization/ Person/ Location/ Event/ Concept,关系需包含源实体、目标实体、关系描述、关系强度:

Plaintext 复制代码
def build_extract_prompt(text: str) -> str:
    """构建实体关系抽取的提示词"""
    return f"""
    目标:从文本中识别指定类型的实体和实体间的关系,使用中文输出,不翻译。
    实体类型:[Organization, Person, Location, Event, Concept]
    步骤1:抽取实体,格式为:("entity"<|><实体名><|><实体类型><|><实体描述>)
    步骤2:抽取关系,格式为:("relationship"<|><源实体><|><目标实体><|><关系描述><|><关系强度(0-1)>)
    要求:仅输出实体和关系,无其他额外文本、解释。
    输入文本:{text}
    """

# 选取Doris文档中关于发展历程、应用现状的分片(实体关系密集)
graph_text = chunks[0] + chunks[1] + chunks[2]
# 构建提示词并调用LLM抽取
prompt = build_extract_prompt(graph_text)
resp = llm.invoke(prompt)
extract_result = resp.content.strip()
print("实体关系抽取结果(部分):\n", extract_result[:500])

实体关系抽取结果示例(部分):

Plaintext 复制代码
("entity"<|>Apache Doris<|>Organization<|>一款基于MPP架构的高性能、实时分析型数据库...)\n
("entity"<|>百度<|>Organization<|>一家互联网公司,Apache Doris最初是其广告报表业务的Palo项目...)\n
("entity"<|>Apache基金会<|>Organization<|>支持开源软件项目的非营利组织,2018年接受百度捐赠的Doris...)\n
("relationship"<|>Apache Doris<|>百度<|>Apache Doris最初是百度广告报表业务的Palo项目,由百度捐赠给Apache基金会<|>0.9)\n
("relationship"<|>百度<|>Apache基金会<|>百度于2018年7月将Apache Doris捐赠给Apache基金会<|>0.8)

知识图谱的可视化

5.2 知识图谱数据存入 Doris

将构建的知识图谱(实体、关系)生成唯一 ID 和向量嵌入,组装成指定格式后,存入 Doris 的graph_chunk表,实现知识图谱的持久化存储,并支持基于向量的语义检索:

Plaintext 复制代码
import uuid
import json

# 构造知识图谱数据记录
records = []
# 1. 处理实体
for node, data in G.nodes(data=True):
    desc = data.get('description', '')
    text_to_embed = f"{node}: {desc}"  # 实体嵌入的文本内容
    records.append({
        "id": str(uuid.uuid4()), "kb_id": "0", "source_id": "0",
        "knowledge_graph_kwd": "entity", "entity_kwd": node,
        "from_entity_kwd": "", "to_entity_kwd": "",
        "content": json.dumps(data), "text_to_embed": text_to_embed
    })
# 2. 处理关系
for u, v, data in G.edges(data=True):
    desc = data.get('description', '')
    text_to_embed = f"{u} -> {v}: {desc}"  # 关系嵌入的文本内容
    records.append({
        "id": str(uuid.uuid4()), "kb_id": "0", "source_id": "0",
        "knowledge_graph_kwd": "relation", "entity_kwd": "",
        "from_entity_kwd": u, "to_entity_kwd": v,
        "content": json.dumps(data), "text_to_embed": text_to_embed
    })

# 为实体/关系生成向量嵌入
texts_to_embed = [c["text_to_embed"] for c in records]
embedding_list = embeddings.embed_documents(texts_to_embed)
embed_iter = iter(embedding_list)
for c in records:
    c["embedding"] = next(embed_iter)

# 整理导入格式并写入Doris
data_to_insert = [{k: c[k] for k in ["id","kb_id","source_id","knowledge_graph_kwd","entity_kwd","from_entity_kwd","to_entity_kwd","content","embedding"]} for c in records]
graph_table = client.open_table('graph_chunk')
graph_table.add(data_to_insert)
print("知识图谱数据成功存入Doris的graph_chunk表!")

5.3 基于知识图谱的检索与问答

实现实体检索关系查询 两个核心函数,先根据用户查询检索相关实体,再查询实体间的所有关系,构造相关子图,最后基于子图的结构化知识生成答案:

Plaintext 复制代码
def search_entities(query: str, top_k: int = 100) -> list:
    """向量检索知识图谱中的相关实体"""
    query_vec = embeddings.embed_query(query)
    graph_table = client.open_table('graph_chunk')
    res_df = graph_table.search(query_vec)\
        .limit(top_k)\
        .where(f"knowledge_graph_kwd = 'entity'")\
        .select(["entity_kwd", "content"])\
        .to_pandas()
    return res_df.to_dict('records')

def get_relations(entity_names: list) -> list:
    """查询实体间的所有关联关系"""
    if not entity_names:
        return []
    placeholders = ','.join(['%s'] * len(entity_names))
    sql = f"""
        SELECT from_entity_kwd, to_entity_kwd, content
        FROM graph_chunk
        WHERE knowledge_graph_kwd='relation'
        AND (from_entity_kwd IN ({placeholders}) OR to_entity_kwd IN ({placeholders}));
    """
    params = entity_names + entity_names
    cursor = client.connection.cursor(dictionary=True)
    cursor.execute(sql, params)
    res = cursor.fetchall()
    cursor.close()
    return res

# 复杂查询示例:Doris是哪家公司捐赠给Apache基金会,又被哪些公司或组织所使用
query = 'Doris 是哪家公司捐赠给 Apache 基金会,又被哪些公司或组织所使用'

# 步骤1:检索相关实体
entities = search_entities(query)
entity_names = [e['entity_kwd'] for e in entities]

# 步骤2:查询实体间的关系
relations = get_relations(entity_names)

# 步骤3:基于实体关系生成答案
kg_prompt =  f"""以下是知识图谱检索到的Doris相关实体关系:{relations} 
请根据上述结构化信息,简洁、准确地回答用户问题:{query}"""
kg_resp = llm.invoke(kg_prompt)
print("知识图谱增强RAG答案:\n", kg_resp.content)

5.4 知识图谱增强 RAG 答案示例

捐赠公司

Apache Doris 由百度于 2018 年 7 月捐赠给 Apache 基金会,其最初是百度广告报表业务的 Palo 项目,后开源并交由 Apache 基金会孵化。

主要使用的公司 / 组织

  • 互联网公司:中国市值 / 估值前 50 的互联网公司中超 80% 长期使用,包括字节跳动、阿里巴巴、腾讯、美团、小米、京东、网易、快手、微博等;
  • 云服务商:阿里云、华为云、天翼云、腾讯云、百度云、火山引擎等,均提供托管的 Apache Doris 云服务并自身使用;
  • 传统行业:金融、消费、电信、工业制造、能源、医疗、政务等行业的中大型企业均有广泛应用

6. 结束语

本文基于 Apache Doris 构建了基础 RAG 与知识图谱增强 RAG 两套完整方案,覆盖从文档分片、向量入库到实体关系抽取、图谱构建与检索的核心流程,具备工业级落地能力。相比基础 RAG,知识图谱增强方案能够缓解知识碎片化问题,提升多实体、多关系复杂问题的回答准确性与完整性;而 Apache Doris 则提供了统一的向量检索与结构化 / 非结构化数据存储能力,作为系统的高性能数据底座

后续扩展方向:

  1. 查询意图识别:基于大模型识别查询类型(简单问答 / 复杂关系),动态选择基础 RAG 或图谱增强方案;
  2. 文档增强:引入用户反馈机制,持续优化知识内容,提升检索准确率;
  3. Agentic RAG:引入智能代理,支持多步检索与推理,提升复杂问题处理能力;
  4. 增量更新:实现文档与知识图谱的增量更新,保障知识库时效性;
  5. 索引调优:针对不同规模数据优化 HNSW 参数(如 ef_construction、max_degree),提升检索性能。

读者可基于文中案例进一步探索,让 SelectDB or Apache Doris 成为业务智能化进程中统一、高效的数据基座。

附:相关资源

视频教程

  1. 基础 RAG 搭建
  2. 知识图谱增强 RAG

完整代码:github.com/freemandeal...

官方文档:Apache Doris 官方文档

相关推荐
Agent产品评测局1 小时前
生产排期与MES/ERP系统打通,实操方法详解:2026企业级智能体与超自动化集成实战指南
运维·人工智能·ai·chatgpt·自动化
GitCode官方1 小时前
一声唤醒 万物响应|AtomGit 首款开源鸿蒙 AI 硬件「小鸿」发布会圆满落幕 定义智能交互新入口
人工智能·开源·harmonyos
互联网志1 小时前
打通转化通道 赋能产业发展——高校科技成果转化的现状与破局
大数据·人工智能·物联网
z4424753261 小时前
CSS Grid布局如何实现网格项目的自动增长_设置grid-auto-flow- row
jvm·数据库·python
时序之心1 小时前
ICLR 2026两篇时间序列论文新思路:都用Patch作为建模基础单元
人工智能·iclr·时间序列
天天进步20151 小时前
实时通信的艺术:OpenWork 中 SSE 与事件流驱动的 UI 交互实现
人工智能
河野笑生1 小时前
MySQL 范式和反范式详解
数据库
AI_paid_community1 小时前
我花了一晚上把 Claude Code 彻底"薅羊毛"了——free-claude-code 项目深度实测
人工智能·claude
猫头虎1 小时前
如何搭建 24 小时 AI 直播平台:魔珐星云数字人打造无人值守 “AI 销冠” 全流程实战教程
人工智能·langchain·开源·prompt·aigc·embedding·agi