掘金 人工智能 05月07日 14:23
深入浅出:langchain RAG 实战
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入浅出地介绍了 RAG(检索增强生成)的核心原理,阐述了它如何弥补大型语言模型(LLM)在知识方面的不足,通过检索外部知识库,让 LLM 能够回答关于特定、最新或私有数据的问题,减少幻觉,并增加答案的可追溯性。同时,借助 LangChain 这一框架,详细讲解了构建 RAG 应用的六个主要步骤:加载、分割、嵌入、存储、检索和生成,并提供了一个实际案例,即构建一个技术文档问答机器人,帮助读者快速理解并实践 RAG 技术。

📖RAG 的核心思想是检索增强生成,通过在文本生成前增加“检索”步骤,让 LLM 在回答问题前先“查阅资料”,弥补其知识盲区,保持知识常新,减少幻觉,并增加答案的可追溯性。

🛠️LangChain 框架将 RAG 流程模块化,为每个步骤提供抽象和实现,包括 DocumentLoaders(加载)、TextSplitters(分割)、Embeddings(嵌入)、VectorStores(存储)、Retrievers(检索)和 LLMs & Chains(生成),方便开发者轻松构建 RAG 应用。

🤖通过 LangChain RAG 实战案例,展示了如何创建一个能够回答关于流行 Python 库 requests 官方文档中问题的问答机器人,详细步骤包括获取并加载文档、处理和分割文本、创建并存储向量、构建检索器以及组装 RAG Chain。

💡优化 RAG 的关键在于精细化文本分割策略,选择更合适的嵌入模型,以及优化检索策略。精细化文本分割旨在创建既包含足够上下文、又不过长的小块,并且尽量不破坏语义完整性。

引言:大型语言模型的“知”与“不知”——为何需要 RAG

大型语言模型(LLM)如 GPT,无疑是近年来最令人瞩目的技术突破。它们能写诗、写代码、翻译,展现出惊人的语言能力,仿佛拥有了某种“知”。

然而,这种“知”并非全知全能。它们的知识来源于训练数据,有明确的截止日期。对于训练之后的新事件、特定行业的最新报告,或是企业内部的私密文档,它们是“不知”的。更麻烦的是,面对未知,它们有时会自信满满地“胡说八道”,产生所谓的“幻觉”。

现实应用中,我们恰恰需要 LLM 回答那些关于特定、最新或私有数据的问题。如何弥合 LLM 的通用能力与特定知识之间的鸿沟?

答案便是 RAG(Retrieval Augmented Generation),检索增强生成。它赋予 LLM “查阅资料”的能力,让生成基于事实。

本文,我们就来深入浅出地理解 RAG 的核心原理,并借助 LangChain 这一强大框架,通过一个具体的实战案例,带你亲手构建一个 RAG 应用。

一、RAG 的核心原理:让 LLM 学会“查资料”

既然我们知道大型语言模型并非无所不知,尤其对新知识和特定领域知识存在盲区,那么一个自然的想法就是:我们能不能给它一本“参考书”,让它在回答问题前先去查阅呢?

这就是 RAG 的核心思想:检索增强生成(Retrieval Augmented Generation) 。顾名思义,它是在传统的文本生成(Generation)过程之前,增加了一个“检索”(Retrieval)步骤。

它的基本逻辑非常直观:

    用户提出一个问题。系统根据问题,去一个外部的知识库里查找最相关的资料片段。将找到的资料片段,连同用户的问题一起,作为上下文提供给大型语言模型。大型语言模型根据这些上下文资料,生成最终的答案。

RAG 的优势,正是为了弥补 LLM 的不足而生:

那么,RAG 具体是如何实现“查资料”这个过程的呢?我们可以将其拆解为六个主要步骤,就像一套精密的流水线:

    加载 (Loading):  这是第一步,你需要把你想要 LLM 学习的知识(比如 PDF 文档、网页、数据库记录等)加载到系统中。分割 (Splitting):  原始文档可能很长,但 LLM 的上下文窗口有限,也为了更精细地检索,我们需要把长文档切分成更小的、有意义的文本块(chunks)。嵌入 (Embedding):  计算机不理解文字,但理解数字。这一步是将分割好的文本块转换成高维度的数字向量。这些向量捕捉了文本的语义信息,意思相近的文本,它们的向量在向量空间中距离也更近。存储 (Storing):  将这些文本向量存储到一个专门的数据库中,通常是向量数据库(Vector Store) 。向量数据库能够高效地存储和检索海量向量。检索 (Retrieval):  当用户提出问题时,同样将用户的问题转换成一个向量。然后,在向量数据库中查找与用户问题向量最相似(距离最近)的文本块向量。这些相似的文本块就是系统找到的“相关资料”。生成 (Generation):  最后一步,将用户原始问题和检索到的相关文本块一起打包,发送给大型语言模型。LLM 阅读这些资料,并根据资料生成一个流畅、准确的答案。

理解了这六个步骤,你就把握了 RAG 的核心脉络。接下来,我们将看看 LangChain 这个工具如何帮助我们轻松地实现这套流程。

二、LangChain:构建 RAG 应用的“瑞士军刀”

理解了 RAG 的原理和六个步骤后,你可能会想:要把这些步骤一步步自己实现,是不是很复杂?加载不同格式的文档、选择合适的文本分割策略、对接各种嵌入模型和向量数据库、最后还要把检索结果巧妙地喂给 LLM……听起来工作量不小。

幸运的是,我们有像 LangChain 这样的框架。

为什么说 LangChain 是构建 RAG 应用的利器?

RAG 的流程涉及多个独立的环节:数据加载、处理、存储、检索、再到最后的生成。LangChain 的设计哲学就是将这些环节模块化。它为 RAG 的每一个步骤都提供了抽象和实现,并且最重要的是,它让这些模块之间可以轻松地连接和协作,形成一个流畅的工作流,也就是所谓的“链”(Chain)。

你可以把 LangChain 的模块看作是乐高积木。每块积木都有特定的功能(加载文档、分割文本、存储向量等),而 LangChain 提供了各种连接件,让你能把这些积木按照 RAG 的流程组装起来。

让我们看看 LangChain 的核心模块如何对应 RAG 的六个步骤:

总而言之,LangChain 提供了一个结构化的方式来思考和实现 RAG 应用。它把复杂的底层细节封装起来,让你能更专注于业务逻辑和流程编排。

理论讲得再多,不如动手实践。接下来,我们就用 LangChain,一步步构建一个真实的 RAG 应用,让你亲身体验它的强大之处。

三、LangChain RAG 实战:构建一个技术文档问答机器人

理论终归要落地。现在,我们来卷起袖子,用 LangChain 构建一个实际的 RAG 应用。我们的目标是创建一个问答机器人,能够回答关于流行 Python 库 requests 的官方文档中的问题。

案例设定

requests 库是 Python 中用于发送 HTTP 请求的利器,其官方文档非常详尽。但当你急需查找某个函数的具体用法、某个参数的含义,或者某个特定场景(如上传文件、设置代理)的处理方法时,在浩瀚的文档中搜索有时并不高效。一个能够直接理解你的问题并从文档中提取答案的机器人,将大大提升效率。

选择技术文档作为案例,是因为它具有代表性:信息密集、专业术语多、结构化程度不一(代码示例、文字说明、表格等),是 RAG 技术非常适合处理的场景。

环境准备

首先,确保你已经创建了一个名为 techdocs_bot 的项目目录,并在其中初始化了 uv 环境(如果需要帮助,请参考 深入浅出:langchain 快速上手(DeepSeek 版) 这篇文章关于环境配置的部分)。

我们需要安装以下依赖:

在 techdocs_bot 项目目录下,打开终端,运行以下命令安装依赖:

uv venvsource .venv/bin/activateuv add langchain langchain-community langchain_huggingface  bs4 python-dotenvuv add text2vec transformers[torch] sentence-transformers chromadb

实战步骤 1:获取并加载文档

我们的知识来源是 requests 的官方在线文档。LangChain 提供了 WebBaseLoader 来加载网页内容。我们可以指定几个关键页面的 URL。

import os# 设置USER_AGENT(必须在导入WebBaseLoader之前)os.environ['USER_AGENT'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'from langchain_community.document_loaders import WebBaseLoaderfrom dotenv import load_dotenv# 加载环境变量load_dotenv()# 定义要加载的文档 URL 列表# 这里我们选择 requests 文档的几个核心页面urls = [    "https://requests.readthedocs.io/en/latest/", # 首页/快速开始    "https://requests.readthedocs.io/en/latest/user/quickstart/", # 快速开始    "https://requests.readthedocs.io/en/latest/user/advanced/", # 高级用法    "https://requests.readthedocs.io/en/latest/api/" # API 参考 (部分)]# 使用 WebBaseLoader 加载文档loader = WebBaseLoader(urls)docs = loader.load()print(f"成功加载了 {len(docs)} 个文档页面。")# 可以打印第一个文档的内容看看# print(docs[0].page_content[:500])

WebBaseLoader 会自动抓取网页内容并进行初步清理,将其转换为 LangChain 的 Document 对象列表。每个 Document 对象包含 page_content(文本内容)和 metadata(如来源 URL)。

实战步骤 2:处理和分割文本

加载进来的文档可能很长,直接喂给 LLM 会超出其上下文窗口。而且,为了更精确地检索,我们需要将长文档分割成更小的、有逻辑关联的文本块(chunks)。RecursiveCharacterTextSplitter 是一个常用的分割器,它会尝试按段落、句子等有意义的单位进行递归分割。

对于技术文档,chunk_size(每个文本块的最大长度)和 chunk_overlap(相邻文本块之间的重叠长度)的设置很重要。我们希望保留代码示例、函数签名、参数说明等关键信息的完整性。适当的重叠可以帮助保留跨越分割边界的信息。

# 导入文本分割器from langchain_text_splitters import RecursiveCharacterTextSplitter# 初始化文本分割器# chunk_size: 每个文本块的最大字符数# chunk_overlap: 相邻文本块之间的重叠字符数# 对于技术文档,chunk_size 不宜过小,以保留代码块或段落的完整性text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)# 分割文档splits = text_splitter.split_documents(docs)print(f"原始文档分割成了 {len(splits)} 个文本块。")# 可以打印第一个分割块的内容看看# print(splits[0].page_content)

通过分割,我们将原始的几个长网页变成了数百个更小的文本块,每个块都更适合进行嵌入和检索。

实战步骤 3:创建并存储向量

现在,我们将这些文本块转换成向量,并存储到向量数据库中。我们将使用 HuggingFace 的本地 embedding 模型 (BAAI/bge-small-en-v1.5) 来生成向量,并使用 Chroma 作为向量数据库。OpenAI 等一些平台也提供 embedding 服务,需要配置 API key来使用,这里我们使用的是本地模型,方便我们测试。

# 导入嵌入模型和向量数据库from langchain_huggingface.embeddings import HuggingFaceEmbeddingsfrom langchain_community.vectorstores import Chroma# 初始化嵌入模型model_name = "BAAI/bge-small-en-v1.5"model_kwargs = {'device': 'cpu'}encode_kwargs = {'normalize_embeddings': True}# 初始化向量数据库并从分割后的文本块创建# directory 参数指定向量数据存储的路径,方便后续加载persist_directory = './chroma_db'vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings, persist_directory=persist_directory)print(f"文本块已嵌入并存储到向量数据库:{persist_directory}")# 如果数据库已经存在,下次可以直接加载# vectorstore = Chroma(persist_directory=persist_directory, embedding_function=embeddings)

这一步是 RAG 的核心基础设施。我们将所有文档的“语义指纹”(向量)存储起来,构建了一个可高效搜索的索引。

实战步骤 4:构建检索器

向量数据库存储了向量,但我们需要一个“检索器”来封装查询逻辑。检索器知道如何接收用户查询,将其转换为向量,然后在向量数据库中查找最相似的文本块。

我们可以直接从向量数据库实例创建一个检索器。as_retriever() 方法非常方便。我们可以设置 k 参数,指定检索器在每次查询时返回多少个最相关的文本块。

# 从向量数据库创建检索器# search_kwargs={"k": 3} 表示检索最相似的 3 个文本块retriever = vectorstore.as_retriever(search_kwargs={"k": 3})print(f"已创建检索器,每次查询将返回最相似的 {retriever.search_kwargs['k']} 个文本块。")# 可以测试一下检索效果# query = "How to set a custom header in requests?"# retrieved_docs = retriever.invoke(query)# print(f"\n对查询 '{query}' 检索到 {len(retrieved_docs)} 个文档:")# for i, doc in enumerate(retrieved_docs):#     print(f"--- 文档 {i+1} (来源: {doc.metadata.get('source', '未知')}) ---")#     print(doc.page_content[:200] + "...") # 打印前200字符

检索器是连接用户查询和知识库的桥梁。

实战步骤 5:组装 RAG Chain

现在我们有了检索器(能找到相关资料)和大型语言模型(能理解资料并生成答案)。我们需要将它们连接起来,形成一个完整的问答流程。LangChain 的 RetrievalQA Chain 就是为此设计的。它接收一个检索器和一个 LLM,自动处理“检索 -> 将检索结果和问题一起送给 LLM -> LLM 生成答案”的流程。

# 导入 LLM 和 RetrievalQA Chainfrom langchain_deepseek import ChatDeepSeekfrom langchain.chains import RetrievalQAdeepseek_api_key = os.getenv("DEEPSEEK_API_KEY") # 初始化大型语言模型# temperature=0 表示希望模型回答更确定、更少创造性,适合问答任务llm = ChatDeepSeek(model="deepseek-chat", api_key=deepseek_api_key, temperature=0)# 创建 RetrievalQA Chain# retriever: 使用我们之前创建的检索器# llm: 使用我们初始化的 LLM# return_source_documents=True: 设置为 True 可以让 Chain 返回检索到的原始文档,方便验证qa_chain = RetrievalQA.from_chain_type(    llm,    chain_type="stuff", # "stuff" 链类型将所有检索到的文档填充到 LLM 的上下文    retriever=retriever,    return_source_documents=True)print("已成功组装 RetrievalQA Chain。")

chain_type="stuff" 是最简单的链类型,它将所有检索到的文档“填充”(stuff)到一个 Prompt 中,然后发送给 LLM。对于检索到的文档数量不多且总长度不超过 LLM 上下文窗口时,这种方式很有效。

实战步骤 6:进行问答与验证

万事俱备,只欠提问!现在我们可以向构建好的 qa_chain 提出关于 requests 文档的问题了。

# 提出问题query = "How to set a custom header in a requests GET request?"# query = "What is the timeout parameter used for?"# query = "How can I upload a file using requests?"print(f"\n--- 提问: {query} ---")# 运行 Chain 获取答案response = qa_chain.invoke({"query": query})# 打印答案print("\n--- 答案 ---")print(response["result"])# 打印检索到的原始文档(因为 return_source_documents=True)print("\n--- 答案来源 ---")if "source_documents" in response:    for i, doc in enumerate(response["source_documents"]):        print(f"文档 {i+1} (来源: {doc.metadata.get('source', '未知')})")        # print(doc.page_content[:300] + "...") # 可以打印部分内容查看else:    print("未返回来源文档。")

运行上面的代码,你会看到 LLM 基于从 requests 文档中检索到的相关片段,生成了一个关于如何设置自定义头部的答案。通过查看“答案来源”,你可以验证 LLM 的回答是否确实基于文档内容,这大大增强了回答的可信度。

至此,我们就成功地使用 LangChain 构建了一个简单的 RAG 应用,一个能够回答技术文档问题的机器人!

当然,这只是一个基础版本。在实际应用中,我们可能还需要考虑如何优化检索效果、处理更复杂的查询、提升用户体验等问题。这些,我们将在下一部分进行探讨。

四、优化与进阶:让你的 RAG 更聪明

通过第三部分的实战,我们已经成功地构建了一个基于 LangChain 的 RAG 应用,它能够从技术文档中检索信息并生成答案。这证明了 RAG 的基本流程是可行的。

然而,在现实世界中,文档的复杂性、用户查询的多样性,以及对回答质量更高的要求,意味着基础的 RAG 实现可能还不够。我们需要一些优化策略,让我们的 RAG 机器人变得更“聪明”。

优化 RAG,本质上是在优化其工作流中的各个环节:从数据处理到检索,再到最终的生成。

以下是一些关键的优化方向:

    精细化文本分割策略:

      问题:  文本分割是 RAG 的第一步,也是非常关键的一步。如果分割得不好,一个完整的概念、一段代码示例、或者一个关键的参数说明可能被硬生生截断,导致后续的嵌入和检索效果大打折扣。优化:  RecursiveCharacterTextSplitter 已经比简单的固定长度分割要好,但我们可以进一步调整 chunk_size 和 chunk_overlap 参数。对于技术文档,可能需要更大的 chunk_size 来包含完整的代码块或段落。此外,LangChain 还提供了其他分割器,例如基于特定分隔符(如 Markdown 标题、代码块标记)的分割器,可以更好地尊重文档的结构。核心:  分割的目标是创建既包含足够上下文、又不过长的小块,并且尽量不破坏语义完整性。

    选择更合适的嵌入模型:

      问题:  嵌入模型决定了文本块如何被转换为向量,直接影响向量数据库中相似度计算的准确性。不同的嵌入模型在处理不同类型的文本(通用文本、技术文本、代码等)时表现可能不同。优化:  除了 OpenAI 的 text-embedding-ada-002 或新的 third-gen-embedding 模型,社区还有许多优秀的开源嵌入模型(如 BGE, E5, Instructor-XL 等)。有些模型可能在技术领域或特定语言上表现更好。尝试不同的嵌入模型,并通过评估检索效果来选择最适合你知识库的模型。核心:  嵌入模型的质量是检索效果的基石。

    改进检索方法:

      问题:  简单的向量相似度搜索(找到向量距离最近的 k 个文本块)可能存在问题。例如,检索到的 k 个文本块可能内容高度相似,缺乏多样性,或者虽然向量相似但语义上并非最优解。

      优化:

        MMR (Maximal Marginal Relevance):  LangChain 的检索器支持 MMR 算法。它在选择 k 个文本块时,不仅考虑文本块与查询的相关性,还考虑文本块之间的相似性,从而返回既相关又多样化的结果。查询转换 (Query Transformation):  在进行向量搜索之前,可以使用 LLM 对用户原始查询进行改写、扩展,甚至生成一个“假设性”的答案(HyDE - Hypothetical Document Embeddings),然后用改写后的查询或假设性答案的向量进行检索。这有助于弥合用户查询和文档内容之间的词汇或语义差异。

      核心:  检索不仅仅是找到“相似”的,更是找到“最有用”的。

    引入后处理/重排序 (Re-ranking):

      问题:  向量数据库检索到的初步结果(比如 top-k)可能包含一些相关性较低或冗余的文档。直接将这 k 个文档全部送给 LLM 可能引入噪声。优化:  在向量检索之后,但在发送给主 LLM 之前,增加一个后处理步骤。可以使用一个更小、更快的模型(例如一个专门的排序模型)或基于规则的方法,对检索到的文档进行二次排序或过滤,只选择最相关的子集发送给 LLM。核心:  给 LLM 提供更“干净”、更聚焦的上下文。

    处理上下文窗口限制:

      问题:  LLM 的上下文窗口大小是有限的。如果检索到的相关文档块总长度超过了这个限制,就会出错或信息被截断。

      优化:

        调整 k 的值,减少检索到的文档数量。使用更先进的链类型,例如 map_reduce 或 refine,它们可以将文档分批处理或逐步提炼信息。对检索到的文档进行摘要,只将摘要发送给 LLM。选择具有更大上下文窗口的 LLM 模型。

      核心:  确保所有必要的上下文信息都能被 LLM 接收和处理。

这些优化策略并非相互独立,往往需要结合使用。例如,更好的文本分割会提升嵌入质量,进而改善检索效果。而改进检索方法和后处理则能确保发送给 LLM 的上下文是最优质的。

LangChain 为实现这些优化提供了相应的模块和接口,使得尝试和集成这些高级技术变得相对容易。在实际项目中,你需要根据你的具体知识库特点和应用需求,不断实验和调整这些参数和方法。

当然,RAG 也不是万能的,它依然面临一些固有的挑战。这些,我们将在下一部分进行探讨。

五、潜在问题与进一步探索:RAG 的边界在哪里?

通过前面的实战,我们看到了 RAG 如何有效地将 LLM 的生成能力与外部知识库结合起来。然而,就像任何技术一样,RAG 也不是万能的“银弹”,它在实际应用中仍然面临一些挑战和潜在问题。认识到这些,有助于我们更好地应用和改进 RAG 系统。

    数据质量是基石:

      RAG 的效果高度依赖于你提供的知识库质量。如果原始文档本身就错误百出、信息过时、结构混乱,或者包含大量不相关的内容,那么无论你的检索和生成做得多好,最终 LLM 生成的答案也很难令人满意。这印证了计算机科学中的一句老话:“Garbage in, garbage out”(垃圾进,垃圾出)。挑战:  清理、组织和维护一个高质量的知识库本身就是一项艰巨的任务。

    检索的“盲点”与失败:

      向量检索是基于语义相似度。但有时候,用户的问题可能使用了与文档中完全不同的措辞,或者相关信息分散在文档的多个不相邻的段落中,导致简单的相似度搜索无法找到最相关的片段。挑战:  如何提高检索的鲁棒性,使其更能理解用户查询的真实意图,并能跨越文档结构找到分散的信息?

    LLM 的理解与生成质量:

      即使检索到了相关的文档片段,LLM 也可能未能充分理解这些信息,或者在整合多个片段的信息时出现逻辑错误。有时,LLM 仍然可能“偏离”检索到的事实,产生轻微的幻觉,或者未能充分利用提供的上下文。挑战:  如何设计更好的 Prompt 策略,或者使用更适合处理长上下文和复杂推理的 LLM 模型,确保 LLM 能够忠实且有效地利用检索到的信息?

    上下文窗口的限制:

      虽然 LLM 的上下文窗口在不断增大,但它仍然是有限的。如果检索到的相关文档片段数量过多,总长度超过了 LLM 的处理上限,我们就无法将所有信息一次性喂给 LLM。挑战:  如何在检索到的信息量大时,智能地选择、摘要或分批处理这些信息,确保关键内容不丢失,同时不超过 LLM 的限制?

这些挑战促使研究者和开发者不断探索更复杂的 RAG 架构和技术,将 RAG 推向更智能的阶段。一些进一步探索的方向包括:

RAG 仍然是一个快速发展的领域,新的技术和优化方法层出不穷。我们今天构建的只是一个基础但功能完整的 RAG 系统。理解其局限性,并关注这些进阶方向,将帮助我们构建更强大、更鲁棒的 LLM 应用。

总结:RAG——连接 LLM 与现实知识的桥梁

走到这里,我们已经完整地走过了 RAG 的旅程。从认识到大型语言模型在知识上的局限性,到理解 RAG 如何通过“检索”来增强“生成”,再到借助 LangChain 这一强大框架,亲手构建了一个能够回答技术文档问题的机器人。

我们看到了 RAG 的核心价值:它不再让 LLM 仅仅依赖于其静态的训练数据,而是赋予了它连接外部、实时、甚至私有知识的能力。这就像是给一个博览群书但记忆停留在过去的学者,提供了一个可以随时查阅最新文献和特定资料的图书馆。

LangChain 在这个过程中扮演了关键角色。它将 RAG 复杂的流程分解为可管理的模块,并提供了灵活的连接方式,极大地降低了构建这类应用的门槛。从文档加载、文本分割、向量嵌入与存储,到最后的检索与生成,LangChain 的各个组件就像精心设计的齿轮,协同工作,让整个 RAG 流程顺畅运转。

通过构建技术文档问答机器人的实战案例,我们不仅验证了 RAG 的可行性,也体会到了它在解决实际问题中的潜力——让庞杂的文档变得触手可及,让信息获取更加高效。

当然,我们也探讨了 RAG 当前面临的一些挑战,比如数据质量、检索精度、上下文限制等,并展望了一些正在发展中的高级技术,这些都指明了 RAG 未来优化的方向。

总而言之,RAG 是当前连接大型语言模型与现实世界知识最有效、最主流的技术方案之一。它让 LLM 的应用场景从通用领域拓展到特定行业和个性化需求。而 LangChain,则是帮助我们快速搭建这座“知识桥梁”的得力助手。

希望通过本文,你不仅理解了 RAG 的原理,更能动手实践,开启构建属于你自己的智能问答应用之旅

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

RAG LangChain 大型语言模型 检索增强生成
相关文章