前言

提示:承上启下,系列文章,通过前言会议一下上篇章内容,引入本文内容:

根据前面6章的介绍,基本已经讲完AI GC的主流程。用户输入—大模型处理——输出处理,以及多轮对话的实现。从本章节开始对介绍基础流程外的扩展大模型能力的内容:

本文介绍 通过知识库扩展大模型的知识面 ,提升其文档能力。

知识库的使用包含几个核心步骤:数据导入、数据分块、数据向量化 及知识库的使用


一、数据导入

使用文档加载器即可。支持的文本类型:word、txt、pdf等文件,这里以txt举例(其他类型的文档加载器,可参考这篇文章了解(转载):【LangChain】langchain_community.document_loaders 模块:加载器):

#导入document_loaders中的 TextLoader 。用于处理txt文件。还有其他的文件加载器
from langchain_community.document_loaders import TextLoader
file_path = "你的文件地址.txt"
#初始化加载器。并用load方法获取文件里面的内容
loader = TextLoader(file_path, encoding="utf-8")
documents = loader.load()

二、数据分块

Q :为什么需要进行数据分开?
A : 因为大模型单次处理的token数有上限。若不分块,当文件内容较多时,提问时会将整个文本内容拼接进提示词,导致超过大模型能处理的上限长度。同时,分块后,进行提示词增强时,仅使用关联度最高的几部分内容,能够有效降低提问时的token消耗,降低成本

数据分块的实现,这里用langchain中核心的常用的分割器RecursiveCharacterTextSplitter实现。

1. 认识RecursiveCharacterTextSplitter

RecursiveCharacterTextSplitter(
# 每个块的最大字符数(也可按token计算,需要额外配置,感兴趣的可自行了解)
chunk_size,
# 块之间的重叠字符数,避免语义断裂
chunk_overlap,
# 分隔符优先级列表(可自定义符号),默认是[“\n\n”, “\n”, " “, “”]
separators=[”\n\n", “\n”, “,”, “。”, " ", “”], # 适配中文的分隔符
)

切割原理:
1、按照separators里的符号顺序,对文本进行分割
2、分割出chunk(块)后,判断是否超过chunk_size的长度,若超过,则根据separators中后面的分隔符对分割的结果进行二次分割,以此类推,直至满足chunk_size的长度要求,最终得到1个或多个chunk
3、第一块切割好后,切割下一部分内容,第二块的开头位子是第一块最后的chunk_overlap字数的位置
举例:
文本内容:我爱你老妈。chunk_size=4,chunk_overlap=3
则切割的结果为:我爱你老、爱你老妈。

如此切割的好处是,尽量保存了自然语义,避免因为切割文本,导致含义改变

2. 使用RecursiveCharacterTextSplitter

# 加载文本文件
loader = TextLoader(file_path, encoding="utf-8")
raw_docs = loader.load()

# 中文优化的文本分割器
text_splitter = RecursiveCharacterTextSplitter(
       chunk_size=600,
       chunk_overlap=80,
       separators=["\n\n", "\n", "。", "!", "?", ",", ";", ":", "、", ""]
)
split_docs = text_splitter.split_documents(raw_docs)

# 为文档添加自定义元数据(LangChain 0.2 推荐做法)。text_splitter.split_documents方法分割出的chunk,metadata只有一个参数source,一般为了增减检索,会自己重写metadata数据
for idx, doc in enumerate(split_docs):
    doc.metadata.update({
        "chunk_id": idx,
        "total_chunks": len(split_docs),
        "file_name": os.path.basename(file_path),
        "source_type": "text_file"
})

分割结果的数据格式(为列表格式的数据):

[Document(metadata={
        "chunk_id": idx,
        "total_chunks": len(split_docs),
        "file_name": os.path.basename(file_path),
        "source_type": "text_file"
} , page_content=''),
Document(metadata={
        "chunk_id": idx,
        "total_chunks": len(split_docs),
        "file_name": os.path.basename(file_path),
        "source_type": "text_file"
} , page_content='')]

其中每个Document代表了一个切割后的chunk数据,metadata记录的源数据地址(即从哪个文档切割出来的),page_content内存放的切割后的文本内容


三、数据向量化

通过前面的内容,可知文本数据是无法通过语义进行检索的,必须将其变为高纬向量

embeddings(嵌入):指将离散数据映射到连续向量空间的技术(通俗理解就是数据向量化)

具体实现:

#千问模型的嵌入模型的标准写法。其中model支持3个版本,这里用V2.模型名字是固定写法,不要去改变。dashscope_api_key是大模型的api_key
embeddings = DashScopeEmbeddings(
    model="text-embedding-v2",  # 推荐使用v2版本,语义效果更好
    dashscope_api_key=DASHSCOPE_API_KEY,
)

#创建持久化的向量数据库
chroma_db = Chroma.from_documents(
    #向量化的文本内容
    documents=documents,
    #embedding嵌入模型:数据向量化的具体实现
    embedding=embeddings,
    #存储得向量数据库
    persist_directory="./chroma_hybrid_db_v02",  # 持久化路径
    collection_name="hybrid_retrieval_collection"  # 自定义集合名
)

四、向量数据库的使用

向量数据库的使用就是,怎么样根据用户的提问,高效从中检索出与提问相关的内容,用户增强用户的提问

所以需要先知道向量数据库支持的检索方式,常见的有2种:
1、关键词检索:对用户提问内容进行分词(将提问内容分成一个个词语),然后检索原始文档内包含这些分词的文档内容
2、语义检索:通过向量计算与提问最相关的几个内容
3、混合筛选(主流):关键词检索 + 语义检索。将关键词检索、语义检索的结果按一个标准进行相关性评分,最后将得分最高的k个结果返回

关于分词检索的具体实现,感兴趣的可以看这个文章了解(转载):
如何实现分布式搜索(一)实现简单的分词器

代码实现(本文展示混合检索的实现):

关键词检索:

# 基于BM25和jieba的关键词检索器
class BM25Retriever(BaseRetriever):
    documents: List[Document] = Field(description="检索的文档列表")  # 显式声明Pydantic字段
    k: int = Field(default=2, description="返回的文档数量")
    bm25: Optional[BM25Okapi] = Field(default=None, description="BM25模型实例")

    def __init__(self, documents: List[Document], k: int = 2, **kwargs):
        super().__init__(documents=documents, k=k,** kwargs)  # 显式传参给父类
        # 初始化BM25(中文分词)
        corpus = [self._tokenize(doc.page_content) for doc in documents]
        self.bm25 = BM25Okapi(corpus)

    def _tokenize(self, text: str) -> List[str]:
        """中文分词(jieba)+ 清洗"""
        # 移除非中文字符
        text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9\s]", "", text)
        # jieba分词并过滤短词/停用词。jieba.lcut(text)即分词
        stop_words = {"的", "是", "在", "和", "有", "我", "你", "他", "它"}
        tokens = [t.lower() for t in jieba.lcut(text) if len(t) > 1 and t not in stop_words]
        return tokens
        
    #下面的方法即通过对用户提问query进行分词后,调用关键词检索,获得包含这些关键词的chunk,及其相识度得分bm25.get_scores(query_tokens)
    def _get_relevant_documents(
            self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> List[Document]:
        """核心检索方法(实现BaseRetriever接口)"""
        if not self.bm25:
            raise LangChainException("BM25检索器未初始化")
        query_tokens = self._tokenize(query)
        if not query_tokens:
            return []
        # 计算BM25得分并排序
        scores = self.bm25.get_scores(query_tokens)
        top_indices = scores.argsort()[-self.k:][::-1]
        return [self.documents[i] for i in top_indices]

# 初始化BM25检索器(现在可正常实例化)
bm25_retriever = BM25Retriever(documents=documents, k=2)

语义检索:

# 封装为LangChain 0.2 检索器
semantic_retriever = chroma_db.as_retriever(
    search_kwargs={"k": 2},  # 返回top2相似文档
    search_type="similarity"  # 支持similarity/similarity_score_threshold/mmr
)

#调用检索器的invoke方法,执行语义匹配。其中X为用户提问
res = semantic_retriever.invoke(x)

五、完成的demo。文本导入、分块、向量化、向量库混合检索、增强提示词、大模型生成

需要提前安装所需的包。我用的langchain版本为0.2,若报导入错误的,自行更新lib或按自己的版本调整代码内容
pip3 install langchain0.2.0 langchain-community0.2.0 langchain-core==0.2.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install chromadb chromadb dashscope jieba python-dotenv rank_bm25 -i https://pypi.tuna.tsinghua.edu.cn/simple

import os
import re
import jieba
from dotenv import load_dotenv
from typing import List, Dict, Optional, Any

# LangChain 0.2 核心导入
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import (
    RunnablePassthrough,
    RunnableLambda,
    RunnableParallel
)
from langchain_core.retrievers import BaseRetriever
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.exceptions import LangChainException
from langchain_core.pydantic_v1 import Field  # 导入Pydantic Field用于字段声明

# 千问模型与嵌入导入
from langchain_openai import ChatOpenAI
from langchain_community.embeddings.dashscope import DashScopeEmbeddings
from langchain_community.vectorstores import Chroma

# 文档加载与分割(LangChain 0.2 保持兼容)
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

# BM25与RRF算法
from rank_bm25 import BM25Okapi

# 加载环境变量
load_dotenv()
DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
if not DASHSCOPE_API_KEY:
    raise ValueError("请在.env文件中配置DASHSCOPE_API_KEY")

# ===================== 1. 初始化基础组件(LangChain 0.2 适配) =====================
# 千问嵌入模型(语义检索)
embeddings = DashScopeEmbeddings(
    model="text-embedding-v2",  # 推荐使用v2版本,语义效果更好
    dashscope_api_key=DASHSCOPE_API_KEY,
)

# 千问对话模型(生成回答)
chat_model = ChatOpenAI(
    api_key=DASHSCOPE_API_KEY,
    model="qwen-plus",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    temperature=0.1,  # 低随机性保证回答准确性
    max_tokens=1024  # LangChain 0.2 显式指定最大生成长度
)

# ===================== 2. 文档加载与预处理 =====================
def load_and_split_documents(file_path: str = "./入门教程.txt") -> List[Document]:
    """加载并分割文档(LangChain 0.2 兼容)"""
    try:
        # 加载文本文件
        loader = TextLoader(file_path, encoding="utf-8")
        raw_docs = loader.load()

        # 中文优化的文本分割器
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=600,
            chunk_overlap=80,
            separators=["\n\n", "\n", "。", "!", "?", ",", ";", ":", "、", ""]
        )
        split_docs = text_splitter.split_documents(raw_docs)

        # 为文档添加自定义元数据(LangChain 0.2 推荐做法)
        for idx, doc in enumerate(split_docs):
            doc.metadata.update({
                "chunk_id": idx,
                "total_chunks": len(split_docs),
                "file_name": os.path.basename(file_path),
                "source_type": "text_file"
            })
        print(f"✅ 成功加载并分割{len(split_docs)}个文档块")
        return split_docs
    except FileNotFoundError:
        raise LangChainException(f"文件未找到:{file_path}")
    except Exception as e:
        raise LangChainException(f"文档处理失败:{str(e)}")

# 加载文档
documents = load_and_split_documents()

# ===================== 3. 语义检索器(Chroma + LangChain 0.2) =====================
# 初始化Chroma向量库(移除persist(),适配Chroma 0.4+)
chroma_db = Chroma.from_documents(
    documents=documents,
    embedding=embeddings,
    persist_directory="./chroma_hybrid_db_v02",  # 持久化路径
    collection_name="hybrid_retrieval_collection"  # 自定义集合名
)
print("✅ Chroma向量库初始化完成(自动持久化)")

# 封装为LangChain 0.2 检索器
semantic_retriever = chroma_db.as_retriever(
    search_kwargs={"k": 2},  # 返回top2相似文档
    search_type="similarity"  # 支持similarity/similarity_score_threshold/mmr
)

# ===================== 4. BM25关键词检索器(修复BaseRetriever字段声明) =====================
class BM25Retriever(BaseRetriever):
    """基于BM25的关键词检索器(实现LangChain 0.2 BaseRetriever接口)"""
    documents: List[Document] = Field(description="检索的文档列表")  # 显式声明Pydantic字段
    k: int = Field(default=2, description="返回的文档数量")
    bm25: Optional[BM25Okapi] = Field(default=None, description="BM25模型实例")

    def __init__(self, documents: List[Document], k: int = 2, **kwargs):
        super().__init__(documents=documents, k=k,** kwargs)  # 显式传参给父类
        # 初始化BM25(中文分词)
        corpus = [self._tokenize(doc.page_content) for doc in documents]
        self.bm25 = BM25Okapi(corpus)

    def _tokenize(self, text: str) -> List[str]:
        """中文分词(jieba)+ 清洗"""
        # 移除非中文字符
        text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9\s]", "", text)
        # jieba分词并过滤短词/停用词
        stop_words = {"的", "是", "在", "和", "有", "我", "你", "他", "它"}
        tokens = [t.lower() for t in jieba.lcut(text) if len(t) > 1 and t not in stop_words]
        return tokens

    def _get_relevant_documents(
            self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> List[Document]:
        """核心检索方法(实现BaseRetriever接口)"""
        if not self.bm25:
            raise LangChainException("BM25检索器未初始化")
        query_tokens = self._tokenize(query)
        if not query_tokens:
            return []
        # 计算BM25得分并排序
        scores = self.bm25.get_scores(query_tokens)
        top_indices = scores.argsort()[-self.k:][::-1]
        return [self.documents[i] for i in top_indices]

# 初始化BM25检索器(现在可正常实例化)
bm25_retriever = BM25Retriever(documents=documents, k=2)

# ===================== 5. RRF融合算法(LangChain 0.2 函数式封装) =====================
def reciprocal_rank_fusion(
        results: List[List[Document]],
        k: int = 60,
        top_n: int = 2
) -> List[Document]:
    """RRF融合算法(适配LangChain 0.2 Document对象)"""
    doc_scores: Dict[str, float] = {}
    doc_map: Dict[str, Document] = {}

    # 生成文档唯一标识(基于内容哈希+元数据)
    def get_doc_id(doc: Document) -> str:
        return f"{doc.metadata.get('file_name', 'unknown')}_{doc.metadata.get('chunk_id', '0')}_{hash(doc.page_content) % 10000}"

    # 计算RRF得分
    for result_list in results:
        for rank, doc in enumerate(result_list):
            doc_id = get_doc_id(doc)
            doc_map[doc_id] = doc
            doc_scores[doc_id] = doc_scores.get(doc_id, 0.0) + 1 / (k + rank + 1)

    # 按得分排序并返回
    sorted_docs = sorted(doc_scores.items(), key=lambda x: x[1], reverse=True)[:top_n]
    return [doc_map[doc_id] for doc_id, _ in sorted_docs]

# 封装为RunnableLambda(LangChain 0.2 推荐)
# 修复:直接从输入字典中提取semantic_docs和keyword_docs
rrf_fusion_runnable = RunnableLambda(
    lambda x: reciprocal_rank_fusion(
        results=[x["semantic_docs"], x["keyword_docs"]],
        top_n=2
    )
)

# ===================== 6. 混合检索链(修复RunnablePassthrough下标问题) =====================
# 构建混合检索流程
# 第一步:并行执行语义检索和关键词检索,同时保留问题
retrieval_step = RunnableParallel(
    semantic_docs=RunnableLambda(lambda x: semantic_retriever.invoke(x)),  # 输入直接是query字符串
    keyword_docs=RunnableLambda(lambda x: bm25_retriever.invoke(x)),
    question=RunnablePassthrough()  # 直接传递query字符串
)

# 第二步:融合检索结果并保留问题
hybrid_retrieval_chain = retrieval_step | RunnableParallel(
    context=rrf_fusion_runnable,
    question=RunnableLambda(lambda x: x["question"])  # 用Lambda提取question,替代下标
)

# 提示词模板(LangChain 0.2 兼容)
prompt = ChatPromptTemplate.from_template("""
请严格根据以下参考文档的内容回答用户问题,回答需简洁、准确,不添加无关信息:

参考文档:
{context}

用户问题:{question}
""")

# 格式化检索结果为字符串
def format_documents(docs: List[Document]) -> str:
    return "\n\n".join([f"【文档{doc.metadata.get('chunk_id', '')}{doc.page_content}" for doc in docs])

# 最终检索-生成链
# 修复:调整链式结构,确保context和question正确传递
final_chain = hybrid_retrieval_chain | RunnableParallel(
    context=RunnableLambda(lambda x: format_documents(x["context"])),
    question=RunnableLambda(lambda x: x["question"])
) | prompt | chat_model

# ===================== 7. 测试混合检索(LangChain 0.2 调用方式) =====================
if __name__ == "__main__":
    test_query = "环境污染和生态破坏责任有哪些?"
    print("===== LangChain 0.2 混合检索演示 =====")
    print(f"🔍 查询问题:{test_query}\n")

    try:
        # 1. 执行混合检索(注意:现在直接传入query字符串,而非字典)
        retrieval_result = hybrid_retrieval_chain.invoke(test_query)
        print("📄 混合检索结果(RRF融合后):")
        for idx, doc in enumerate(retrieval_result["context"], 1):
            print(f"\n{idx}. 内容:{doc.page_content}")
            print(f"   元数据:{doc.metadata}")

        # 2. 执行检索-生成链(同样直接传入query字符串)
        print("\n💡 千问模型回答:")
        answer = final_chain.invoke(test_query)
        print(answer.content)

    except LangChainException as e:
        print(f"❌ LangChain执行错误:{str(e)}")
    except Exception as e:
        print(f"❌ 系统错误:{str(e)}")

其中入门教程.txt的内容(来源:民法典)为:

第六章 医疗损害责任

  第一千二百一十八条 患者在诊疗活动中受到损害,医疗机构或者其医务人员有过错的,由医疗机构承担赔偿责任。

  第一千二百一十九条 医务人员在诊疗活动中应当向患者说明病情和医疗措施。需要实施手术、特殊检查、特殊治疗的,医务人员应当及时向患者具体说明医疗风险、替代医疗方案等情况,并取得其明确同意;不能或者不宜向患者说明的,应当向患者的近亲属说明,并取得其明确同意。

  医务人员未尽到前款义务,造成患者损害的,医疗机构应当承担赔偿责任。

  第一千二百二十条 因抢救生命垂危的患者等紧急情况,不能取得患者或者其近亲属意见的,经医疗机构负责人或者授权的负责人批准,可以立即实施相应的医疗措施。

  第一千二百二十一条 医务人员在诊疗活动中未尽到与当时的医疗水平相应的诊疗义务,造成患者损害的,医疗机构应当承担赔偿责任。

  第一千二百二十二条 患者在诊疗活动中受到损害,有下列情形之一的,推定医疗机构有过错:

  (一)违反法律、行政法规、规章以及其他有关诊疗规范的规定;

  (二)隐匿或者拒绝提供与纠纷有关的病历资料;

  (三)遗失、伪造、篡改或者违法销毁病历资料。

  第一千二百二十三条 因药品、消毒产品、医疗器械的缺陷,或者输入不合格的血液造成患者损害的,患者可以向药品上市许可持有人、生产者、血液提供机构请求赔偿,也可以向医疗机构请求赔偿。患者向医疗机构请求赔偿的,医疗机构赔偿后,有权向负有责任的药品上市许可持有人、生产者、血液提供机构追偿。

  第一千二百二十四条 患者在诊疗活动中受到损害,有下列情形之一的,医疗机构不承担赔偿责任:

  (一)患者或者其近亲属不配合医疗机构进行符合诊疗规范的诊疗;

  (二)医务人员在抢救生命垂危的患者等紧急情况下已经尽到合理诊疗义务;

  (三)限于当时的医疗水平难以诊疗。

  前款第一项情形中,医疗机构或者其医务人员也有过错的,应当承担相应的赔偿责任。

  第一千二百二十五条 医疗机构及其医务人员应当按照规定填写并妥善保管住院志、医嘱单、检验报告、手术及麻醉记录、病理资料、护理记录等病历资料。

  患者要求查阅、复制前款规定的病历资料的,医疗机构应当及时提供。

  第一千二百二十六条 医疗机构及其医务人员应当对患者的隐私和个人信息保密。泄露患者的隐私和个人信息,或者未经患者同意公开其病历资料的,应当承担侵权责任。

  第一千二百二十七条 医疗机构及其医务人员不得违反诊疗规范实施不必要的检查。

  第一千二百二十八条 医疗机构及其医务人员的合法权益受法律保护。

  干扰医疗秩序,妨碍医务人员工作、生活,侵害医务人员合法权益的,应当依法承担法律责任。

第七章 环境污染和生态破坏责任

  第一千二百二十九条 因污染环境、破坏生态造成他人损害的,侵权人应当承担侵权责任。

  第一千二百三十条 因污染环境、破坏生态发生纠纷,行为人应当就法律规定的不承担责任或者减轻责任的情形及其行为与损害之间不存在因果关系承担举证责任。

  第一千二百三十一条 两个以上侵权人污染环境、破坏生态的,承担责任的大小,根据污染物的种类、浓度、排放量,破坏生态的方式、范围、程度,以及行为对损害后果所起的作用等因素确定。

  第一千二百三十二条 侵权人违反法律规定故意污染环境、破坏生态造成严重后果的,被侵权人有权请求相应的惩罚性赔偿。

  第一千二百三十三条 因第三人的过错污染环境、破坏生态的,被侵权人可以向侵权人请求赔偿,也可以向第三人请求赔偿。侵权人赔偿后,有权向第三人追偿。

  第一千二百三十四条 违反国家规定造成生态环境损害,生态环境能够修复的,国家规定的机关或者法律规定的组织有权请求侵权人在合理期限内承担修复责任。侵权人在期限内未修复的,国家规定的机关或者法律规定的组织可以自行或者委托他人进行修复,所需费用由侵权人负担。

  第一千二百三十五条 违反国家规定造成生态环境损害的,国家规定的机关或者法律规定的组织有权请求侵权人赔偿下列损失和费用:

  (一)生态环境受到损害至修复完成期间服务功能丧失导致的损失;

  (二)生态环境功能永久性损害造成的损失;

  (三)生态环境损害调查、鉴定评估等费用;

  (四)清除污染、修复生态环境费用;

  (五)防止损害的发生和扩大所支出的合理费用。

第八章 高度危险责任

  第一千二百三十六条 从事高度危险作业造成他人损害的,应当承担侵权责任。

  第一千二百三十七条 民用核设施或者运入运出核设施的核材料发生核事故造成他人损害的,民用核设施的营运单位应当承担侵权责任;但是,能够证明损害是因战争、武装冲突、暴乱等情形或者受害人故意造成的,不承担责任。

  第一千二百三十八条 民用航空器造成他人损害的,民用航空器的经营者应当承担侵权责任;但是,能够证明损害是因受害人故意造成的,不承担责任。

  第一千二百三十九条 占有或者使用易燃、易爆、剧毒、高放射性、强腐蚀性、高致病性等高度危险物造成他人损害的,占有人或者使用人应当承担侵权责任;但是,能够证明损害是因受害人故意或者不可抗力造成的,不承担责任。被侵权人对损害的发生有重大过失的,可以减轻占有人或者使用人的责任。

  第一千二百四十条 从事高空、高压、地下挖掘活动或者使用高速轨道运输工具造成他人损害的,经营者应当承担侵权责任;但是,能够证明损害是因受害人故意或者不可抗力造成的,不承担责任。被侵权人对损害的发生有重大过失的,可以减轻经营者的责任。

  第一千二百四十一条 遗失、抛弃高度危险物造成他人损害的,由所有人承担侵权责任。所有人将高度危险物交由他人管理的,由管理人承担侵权责任;所有人有过错的,与管理人承担连带责任。

  第一千二百四十二条 非法占有高度危险物造成他人损害的,由非法占有人承担侵权责任。所有人、管理人不能证明对防止非法占有尽到高度注意义务的,与非法占有人承担连带责任。

  第一千二百四十三条 未经许可进入高度危险活动区域或者高度危险物存放区域受到损害,管理人能够证明已经采取足够安全措施并尽到充分警示义务的,可以减轻或者不承担责任。

  第一千二百四十四条 承担高度危险责任,法律规定赔偿限额的,依照其规定,但是行为人有故意或者重大过失的除外。

总结

本文基本结束,基本将AI生成的全流程讲完了,如果全掌握,基本能够自己搭建对话类型的AI应用了。后面将再用一篇文章介绍tools(从如何连接数据库角度说明)、LangServe(如何部署及生成API)

回归本文,看完后,大家需要掌握的:
1、如何加载不同类型文件的数据:图片、语音、视频等格式后续写多模态的时候再介绍

2、如何将文本内容进行分块:本文只用了一种最常用的方式,感兴趣的可自行找其他文章了解

3、如何进行向量化:向量数据库有多种,我这里用的chromadb,大家也可以试试其他的,用法差不多。这部分需要注意的就是metadata的重写,是后续实际应用中提升检索效率的重要优化方向:举例:可以将chunk的内容进行关键词提取,使用关键词检索时,直接检索metadata而不是文档内容,可以提升效率

4、如何使用向量数据库。主要知道3种检索方式。每种方式的具体实现,可以自行写代码实现,进行优化

5、就是复习一下提示词增强的内容,更好理解向量数据库的作用和RAG流程

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐