当前位置:   article > 正文

【LLM01】基于LangChain+LLM的本地知识库问答:什么是LangChain及langchain的整体组成架构_langchain外挂知识库

langchain外挂知识库

声明:本篇文章转自大佬:v_JULY_v 的博客,大佬的文章写的很好,推荐学习。

前言

过去半年,随着ChatGPT的火爆,直接带火了整个LLM这个方向,然LLM毕竟更多是基于过去的经验数据预训练而来,没法获取最新的知识,以及各企业私有的知识

  • 为了获取最新的知识,ChatGPT plus版集成了bing搜索的功能,有的模型则会调用一个定位于 “链接各种AI模型、工具”的langchain的bing功能
  • 为了处理企业私有的知识,要么基于开源模型微调,要么更可以基于langchain里集成的向量数据库和LLM搭建本地知识库问答(此处的向量数据库的独特性在哪呢?举个例子,传统数据库做图片检索可能是通过关键词去搜索,向量数据库是通过语义搜索图片中相同或相近的向量并呈现结果)

所以越来越多的人开始关注langchain并把它与LLM结合起来应用,更直接推动了数据库知识图谱与LLM的结合应用(详见下一篇文章:知识图谱实战导论:从什么是KG到LLM与KG/DB的结合实战)

本文则侧重讲解

  1. 什么是LangChain及langchain的整体组成架构
  2. 通过langchain-ChatGLM构建本地知识库问答的基本流程,与每个流程背后的逻辑
  3. 解读langchain-ChatGLM项目的关键源码,不只是把它当做一个工具使用,因为对工具的原理更了解,则对工具的使用更顺畅一开始解读不易,因为涉及的项目、技术点不少,所以一开始容易绕晕,好在根据该项目的流程一步步抽丝剥茧之后,给大家呈现了清晰的代码架构过程中,我从接触该langchain-ChatGLM项目到整体源码梳理清晰并写清楚历时了近一周,而大家有了本文之后,可能不到一天便可以理清了(提升近7倍效率) ​​​,这便是本文的价值和意义之一
  4. langchain-ChatGLM项目的升级版:langchain-Chatchat
  5. 我司基于langchain-chatchat二次开发的企业多文档知识库问答系统

一、 LangChain的整体组成架构:LLM的外挂/功能库 

通俗讲,所谓langchain (官网地址GitHub地址),即把AI中常用的很多功能都封装成库,且有调用各种商用模型API、开源模型的接口,支持以下各种组件

初次接触的朋友一看这么多组件可能直接晕了(封装的东西非常多,感觉它想把LLM所需要用到的功能/工具都封装起来),为方便理解,我们可以先从大的层面把整个langchain库划分为三个大层:基础层、能力层、应用层

1.1 基础层:models、LLMs、index

1.1.1 Models:模型

各种类型的模型和模型集成,比如OpenAI的各个API/GPT-4等等,为各种不同基础模型提供统一接口
比如通过API完成一次问答

  1. import os
  2. os.environ["OPENAI_API_KEY"] = '你的api key'
  3. from langchain.llms import OpenAI
  4. llm = OpenAI(model_name="text-davinci-003",max_tokens=1024)
  5. llm("怎么评价人工智能")

得到的回答如下图所示

1.1.2 LLMS层

这一层主要强调对models层能力的封装以及服务化输出能力,主要有:

  • 各类LLM模型管理平台:强调的模型的种类丰富度以及易用性
  • 一体化服务能力产品:强调开箱即用
  • 差异化能力:比如聚焦于Prompt管理(包括提示管理、提示优化和提示序列化)、基于共享资源的模型运行模式等等

比如Google's PaLM Text APIs,再比如 llms/openai.py 文件下

  1. model_token_mapping = {
  2. "gpt-4": 8192,
  3. "gpt-4-0314": 8192,
  4. "gpt-4-0613": 8192,
  5. "gpt-4-32k": 32768,
  6. "gpt-4-32k-0314": 32768,
  7. "gpt-4-32k-0613": 32768,
  8. "gpt-3.5-turbo": 4096,
  9. "gpt-3.5-turbo-0301": 4096,
  10. "gpt-3.5-turbo-0613": 4096,
  11. "gpt-3.5-turbo-16k": 16385,
  12. "gpt-3.5-turbo-16k-0613": 16385,
  13. "text-ada-001": 2049,
  14. "ada": 2049,
  15. "text-babbage-001": 2040,
  16. "babbage": 2049,
  17. "text-curie-001": 2049,
  18. "curie": 2049,
  19. "davinci": 2049,
  20. "text-davinci-003": 4097,
  21. "text-davinci-002": 4097,
  22. "code-davinci-002": 8001,
  23. "code-davinci-001": 8001,
  24. "code-cushman-002": 2048,
  25. "code-cushman-001": 2048,
  26. }
1.1.3 Index(索引):Vector方案、KG方案

对用户私域文本、图片、PDF等各类文档进行存储和检索(相当于结构化文档,以便让外部数据和模型交互),具体实现上有两个方案:一个Vector方案、一个KG方案

1.1.3.1 Index(索引)之Vector方案

对于Vector方案:即对文件先切分为Chunks,在按Chunks分别编码存储并检索,可参考此代码文件:

langchain/libs/langchain/langchain/indexes /vectorstore.py
该代码文件依次实现

模块导入:导入了各种类型检查、数据结构、预定义类和函数
接下来,实现了一个函数_get_default_text_splitter,两个类VectorStoreIndexWrapper、VectorstoreIndexCreator

_get_default_text_splitter 函数:
这是一个私有函数,返回一个默认的文本分割器,它可以将文本递归地分割成大小为1000的块,且块与块之间有重叠

  1. # 默认的文本分割器函数
  2. def _get_default_text_splitter() -> TextSplitter:
  3. return RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=0)

为什么要进行切割?


原因很简单,embedding(text2vec,文本转化为向量)以及 LLM encoder 对输入 tokens 都有限制。embedding 会将一个 text(长字符串)的语义信息压缩成一个向量,但其对 text 包含的 tokens 是有限制的,一段话压缩成一个向量是 ok,但一本书压缩成一个向量可能就丢失了绝大多数语义

接下来是,VectorStoreIndexWrapper 类:
这是一个包装类,主要是为了方便地访问和查询向量存储(Vector Store)

  • vectorstore: 一个向量存储对象的属性
  1. vectorstore: VectorStore # 向量存储对象
  2. class Config:
  3. """Configuration for this pydantic object."""
  4. extra = Extra.forbid # 额外配置项
  5. arbitrary_types_allowed = True # 允许任意类型
  • query: 一个方法,它接受一个问题字符串并查询向量存储来获取答案
  1. # 查询向量存储的函数
  2. def query(
  3. self,
  4. question: str, # 输入的问题字符串
  5. llm: Optional[BaseLanguageModel] = None, # 可选的语言模型参数,默认为None
  6. retriever_kwargs: Optional[Dict[str, Any]] = None, # 提取器的可选参数,默认为None
  7. **kwargs: Any # 其他关键字参数
  8. ) -> str:
  9. """Query the vectorstore.""" # 函数的文档字符串,描述函数的功能
  10. # 如果没有提供语言模型参数,则使用OpenAI作为默认语言模型,并设定温度参数为0
  11. llm = llm or OpenAI(temperature=0)
  12. # 如果没有提供提取器的参数,则初始化为空字典
  13. retriever_kwargs = retriever_kwargs or {}
  14. # 创建一个基于语言模型和向量存储提取器的检索QA链
  15. chain = RetrievalQA.from_chain_type(
  16. llm, retriever=self.vectorstore.as_retriever(**retriever_kwargs), **kwargs
  17. )
  18. # 使用创建的QA链运行提供的问题,并返回结果
  19. return chain.run(question)

解释一下上面出现的提取器

提取器首先从大型语料库中检索与问题相关的文档或片段,然后生成器根据这些检索到的文档生成答案。

提取器可以基于许多不同的技术,包括:

    a.基于关键字的检索:使用关键字匹配来查找相关文档
    b.向量空间模型:将文档和查询都表示为向量,并通过计算它们之间的相似度来检索相关文档
    c.基于深度学习的方法:使用预训练的神经网络模型(如BERT、RoBERTa等)将文档和查询编码为向量,并进行相似度计算
    d.索引方法:例如倒排索引,这是搜索引擎常用的技术,可以快速找到包含特定词或短语的文档

这些方法可以独立使用,也可以结合使用,以提高检索的准确性和速度

  • query_with_sources: 类似于query,但它还返回与查询结果相关的数据源
  1. # 查询向量存储并返回数据源的函数
  2. def query_with_sources(
  3. self,
  4. question: str,
  5. llm: Optional[BaseLanguageModel] = None,
  6. retriever_kwargs: Optional[Dict[str, Any]] = None,
  7. **kwargs: Any
  8. ) -> dict:
  9. """Query the vectorstore and get back sources."""
  10. llm = llm or OpenAI(temperature=0) # 默认使用OpenAI作为语言模型
  11. retriever_kwargs = retriever_kwargs or {} # 提取器参数
  12. chain = RetrievalQAWithSourcesChain.from_chain_type(
  13. llm, retriever=self.vectorstore.as_retriever(**retriever_kwargs), **kwargs
  14. )
  15. return chain({chain.question_key: question})

最后是VectorstoreIndexCreator 类:

这是一个创建向量存储索引的类

  • vectorstore_cls: 使用的向量存储类,默认为Chroma
vectorstore_cls: Type[VectorStore] = Chroma          # 默认使用Chroma作为向量存储类

一个简化的向量存储可以看作是一个大型的表格或数据库,其中每行代表一个项目(如文档、图像、句子等),而每个项目则有一个与之关联的高维向量。向量的维度可以从几十到几千,取决于所使用的嵌入模型
例如:

Item IDVector (in a high dimensional space)
1[0.34, -0.2, 0.5, ...]
2[-0.1, 0.3, -0.4, ...]
......

至于这里的Chroma是一种常见的向量数据库,可以通过与LangChain的集成,实现基于语言模型的各种应用

  • embedding: 使用的嵌入类,默认为OpenAIEmbeddings
    embedding: Embeddings = Field(default_factory=OpenAIEmbeddings)  # 默认使用OpenAIEmbeddings作为嵌入类

顺带说一下,Huggingface 有一个 embedding 的 benchmark:https://huggingface.co/spaces/mteb/leaderboard

  • text_splitter: 用于分割文本的文本分割器
text_splitter: TextSplitter = Field(default_factory=_get_default_text_splitter)  # 默认文本分割器
  • from_loaders: 从给定的加载器列表中创建一个向量存储索引
  1. # 从加载器创建向量存储索引的函数
  2. def from_loaders(self, loaders: List[BaseLoader]) -> VectorStoreIndexWrapper:
  3. """Create a vectorstore index from loaders."""
  4. docs = []
  5. for loader in loaders: # 遍历加载器
  6. docs.extend(loader.load()) # 加载文档
  7. return self.from_documents(docs)
  • from_documents: 从给定的文档列表中创建一个向量存储索引
  1. # 从文档创建向量存储索引的函数
  2. def from_documents(self, documents: List[Document]) -> VectorStoreIndexWrapper:
  3. """Create a vectorstore index from documents."""
  4. sub_docs = self.text_splitter.split_documents(documents) # 分割文档
  5. vectorstore = self.vectorstore_cls.from_documents(
  6. sub_docs, self.embedding, **self.vectorstore_kwargs # 从文档创建向量存储
  7. )
  8. return VectorStoreIndexWrapper(vectorstore=vectorstore) # 返回向量存储的包装对象
1.1.3.2 Index(索引)之KG方案

对于KG方案:这部分利用LLM抽取文件中的三元组,将其存储为KG供后续检索,可参考此代码文件:langchain/libs/langchain/langchain/indexes /graph.py

  1. """Graph Index Creator.""" # 定义"图索引创建器"的描述
  2. # 导入相关的模块和类型定义
  3. from typing import Optional, Type # 导入可选类型和类型的基础类型
  4. from langchain import BasePromptTemplate # 导入基础提示模板
  5. from langchain.chains.llm import LLMChain # 导入LLM链
  6. from langchain.graphs.networkx_graph import NetworkxEntityGraph, parse_triples # 导入Networkx实体图和解析三元组的功能
  7. from langchain.indexes.prompts.knowledge_triplet_extraction import ( # 从知识三元组提取模块导入对应的提示
  8. KNOWLEDGE_TRIPLE_EXTRACTION_PROMPT,
  9. )
  10. from langchain.pydantic_v1 import BaseModel # 导入基础模型
  11. from langchain.schema.language_model import BaseLanguageModel # 导入基础语言模型的定义
  12. class GraphIndexCreator(BaseModel): # 定义图索引创建器类,继承自BaseModel
  13. """Functionality to create graph index.""" # 描述该类的功能为"创建图索引"
  14. llm: Optional[BaseLanguageModel] = None # 定义可选的语言模型属性,默认为None
  15. graph_type: Type[NetworkxEntityGraph] = NetworkxEntityGraph # 定义图的类型,默认为NetworkxEntityGraph
  16. def from_text(
  17. self, text: str, prompt: BasePromptTemplate = KNOWLEDGE_TRIPLE_EXTRACTION_PROMPT
  18. ) -> NetworkxEntityGraph: # 定义一个方法,从文本中创建图索引
  19. """Create graph index from text.""" # 描述该方法的功能
  20. if self.llm is None: # 如果语言模型为None,则抛出异常
  21. raise ValueError("llm should not be None")
  22. graph = self.graph_type() # 创建一个新的图
  23. chain = LLMChain(llm=self.llm, prompt=prompt) # 使用当前的语言模型和提示创建一个LLM链
  24. output = chain.predict(text=text) # 使用LLM链对文本进行预测
  25. knowledge = parse_triples(output) # 解析预测输出得到的三元组
  26. for triple in knowledge: # 遍历所有的三元组
  27. graph.add_triple(triple) # 将三元组添加到图中
  28. return graph # 返回创建的图
  29. async def afrom_text( # 定义一个异步版本的from_text方法
  30. self, text: str, prompt: BasePromptTemplate = KNOWLEDGE_TRIPLE_EXTRACTION_PROMPT
  31. ) -> NetworkxEntityGraph:
  32. """Create graph index from text asynchronously.""" # 描述该异步方法的功能
  33. if self.llm is None: # 如果语言模型为None,则抛出异常
  34. raise ValueError("llm should not be None")
  35. graph = self.graph_type() # 创建一个新的图
  36. chain = LLMChain(llm=self.llm, prompt=prompt) # 使用当前的语言模型和提示创建一个LLM链
  37. output = await chain.apredict(text=text) # 异步使用LLM链对文本进行预测
  38. knowledge = parse_triples(output) # 解析预测输出得到的三元组
  39. for triple in knowledge: # 遍历所有的三元组
  40. graph.add_triple(triple) # 将三元组添加到图中
  41. return graph # 返回创建的图

另外,为了索引,便不得不牵涉以下这些能力

  • Document Loaders,文档加载的标准接口
    与各种格式的文档及数据源集成,比如Arxiv、Email、Excel、Markdown、PDF(所以可以做类似ChatPDF这样的应用)、Youtube …

    相近的还有
    docstore,其中包含wikipedia.py等
    document_transformers
  • embeddings​(langchain/libs/langchain/langchain/embeddings),则涉及到各种embeddings算法,分别体现在各种代码文件中:

elasticsearch.py、google_palm.py、gpt4all.py、huggingface.py、huggingface_hub.py
llamacpp.py、minimax.py、modelscope_hub.py、mosaicml.py
openai.py
sentence_transformer.py、spacy_embeddings.py、tensorflow_hub.py、vertexai.py

1.2 能力层:Chains、Memory、Tools

如果基础层提供了最核心的能力,能力层则给这些能力安装上手、脚、脑,让其具有记忆和触发万物的能力,包括:Chains、Memory、Tool三部分

1.2.1 Chains:链接

简言之,相当于包括一系列对各种组件的调用,可能是一个 Prompt 模板,一个语言模型,一个输出解析器,一起工作处理用户的输入,生成响应,并处理输出

具体而言,则相当于按照不同的需求抽象并定制化不同的执行逻辑,Chain可以相互嵌套并串行执行,通过这一层,让LLM的能力链接到各行各业

 其中的代码文件:chains/graph_qa/base.py 便实现了一个基于知识图谱实现的问答系统,具体步骤为
首先,根据提取到的实体在知识图谱中查找相关的信息「这是通过 self.graph.get_entity_knowledge(entity) 实现的,它返回的是与实体相关的所有信息,形式为三元组」
然后,将所有的三元组组合起来,形成上下文

最后,将问题和上下文一起输入到qa_chain,得到最后的答案

  1. entities = get_entities(entity_string) # 获取实体列表。
  2. context = "" # 初始化上下文。
  3. all_triplets = [] # 初始化三元组列表。
  4. for entity in entities: # 遍历每个实体
  5. all_triplets.extend(self.graph.get_entity_knowledge(entity)) # 获取实体的所有知识并加入到三元组列表中。
  6. context = "\n".join(all_triplets) # 用换行符连接所有的三元组作为上下文。
  7. # 打印完整的上下文。
  8. _run_manager.on_text("Full Context:", end="\n", verbose=self.verbose)
  9. _run_manager.on_text(context, color="green", end="\n", verbose=self.verbose)
  10. # 使用上下文和问题获取答案。
  11. result = self.qa_chain(
  12. {"question": question, "context": context},
  13. callbacks=_run_manager.get_child(),
  14. )
  15. return {self.output_key: result[self.qa_chain.output_key]} # 返回答案
  1. # 定义基于向量数据库的问题回答类
  2. class VectorDBQAWithSourcesChain(BaseQAWithSourcesChain):
  3. """Question-answering with sources over a vector database."""
  4. # 定义向量数据库的字段
  5. vectorstore: VectorStore = Field(exclude=True)
  6. """Vector Database to connect to."""
  7. # 定义返回结果的数量
  8. k: int = 4
  9. # 是否基于token限制来减少返回结果的数量
  10. reduce_k_below_max_tokens: bool = False
  11. # 定义返回的文档基于token的最大限制
  12. max_tokens_limit: int = 3375
  13. # 定义额外的搜索参数
  14. search_kwargs: Dict[str, Any] = Field(default_factory=dict)
  15. # 定义函数来根据最大token限制来减少文档
  16. def _reduce_tokens_below_limit(self, docs: List[Document]) -> List[Document]:
  17. num_docs = len(docs)
  18. # 检查是否需要根据token减少文档数量
  19. if self.reduce_k_below_max_tokens and isinstance(
  20. self.combine_documents_chain, StuffDocumentsChain
  21. ):
  22. tokens = [
  23. self.combine_documents_chain.llm_chain.llm.get_num_tokens(
  24. doc.page_content
  25. )
  26. for doc in docs
  27. ]
  28. token_count = sum(tokens[:num_docs])
  29. # 减少文档数量直到满足token限制
  30. while token_count > self.max_tokens_limit:
  31. num_docs -= 1
  32. token_count -= tokens[num_docs]
  33. return docs[:num_docs]

_get_docs

  1. # 获取相关文档的函数
  2. def _get_docs(
  3. self, inputs: Dict[str, Any], *, run_manager: CallbackManagerForChainRun
  4. ) -> List[Document]:
  5. question = inputs[self.question_key]
  6. # 从向量存储中搜索相似的文档
  7. docs = self.vectorstore.similarity_search(
  8. question, k=self.k, **self.search_kwargs
  9. )
  10. return self._reduce_tokens_below_limit(docs)
  • 比如面向SQL数据源的:sql_database,可以重点关注这份代码文件:chains/sql_database/query.py
  • 比如面向模型对话的:chat_models,包括这些代码文件:__init__.py、anthropic.py、azure_openai.py、base.py、fake.py、google_palm.py、human.py、jinachat.py、openai.py、promptlayer_openai.py、vertexai.py

另外,还有比较让人眼前一亮的:
constitutional_ai:对最终结果进行偏见、合规问题处理的逻辑,保证最终的结果符合价值观
llm_checker:能让LLM自动检测自己的输出是否有没有问题的逻辑

1.2.2 Memory:记忆

简言之,用来保存和模型交互时的上下文状态,处理长期记忆

具体而言,这层主要有两个核心点:
\rightarrow  对Chains的执行过程中的输入、输出进行记忆并结构化存储,为下一步的交互提供上下文,这部分简单存储在Redis即可

\rightarrow  根据交互历史构建知识图谱,根据关联信息给出准确结果,对应的代码文件为:memory/kg.py

  1. # 定义知识图谱对话记忆类
  2. class ConversationKGMemory(BaseChatMemory):
  3. """知识图谱对话记忆类
  4. 在对话中与外部知识图谱集成,存储和检索对话中的知识三元组信息。
  5. """
  6. k: int = 2 # 考虑的上下文对话数量
  7. human_prefix: str = "Human" # 人类前缀
  8. ai_prefix: str = "AI" # AI前缀
  9. kg: NetworkxEntityGraph = Field(default_factory=NetworkxEntityGraph) # 知识图谱实例
  10. knowledge_extraction_prompt: BasePromptTemplate = KNOWLEDGE_TRIPLE_EXTRACTION_PROMPT # 知识提取提示
  11. entity_extraction_prompt: BasePromptTemplate = ENTITY_EXTRACTION_PROMPT # 实体提取提示
  12. llm: BaseLanguageModel # 基础语言模型
  13. summary_message_cls: Type[BaseMessage] = SystemMessage # 总结消息类
  14. memory_key: str = "history" # 历史记忆键
  15. def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
  16. """返回历史缓冲区。"""
  17. entities = self._get_current_entities(inputs) # 获取当前实体
  18. summary_strings = []
  19. for entity in entities: # 对于每个实体
  20. knowledge = self.kg.get_entity_knowledge(entity) # 获取与实体相关的知识
  21. if knowledge:
  22. summary = f"On {entity}: {'. '.join(knowledge)}." # 构建总结字符串
  23. summary_strings.append(summary)
  24. context: Union[str, List]
  25. if not summary_strings:
  26. context = [] if self.return_messages else ""
  27. elif self.return_messages:
  28. context = [
  29. self.summary_message_cls(content=text) for text in summary_strings
  30. ]
  31. else:
  32. context = "\n".join(summary_strings)
  33. return {self.memory_key: context}
  34. @property
  35. def memory_variables(self) -> List[str]:
  36. """始终返回记忆变量列表。"""
  37. return [self.memory_key]
  38. def _get_prompt_input_key(self, inputs: Dict[str, Any]) -> str:
  39. """获取提示的输入键。"""
  40. if self.input_key is None:
  41. return get_prompt_input_key(inputs, self.memory_variables)
  42. return self.input_key
  43. def _get_prompt_output_key(self, outputs: Dict[str, Any]) -> str:
  44. """获取提示的输出键。"""
  45. if self.output_key is None:
  46. if len(outputs) != 1:
  47. raise ValueError(f"One output key expected, got {outputs.keys()}")
  48. return list(outputs.keys())[0]
  49. return self.output_key
  50. def get_current_entities(self, input_string: str) -> List[str]:
  51. """从输入字符串中获取当前实体。"""
  52. chain = LLMChain(llm=self.llm, prompt=self.entity_extraction_prompt)
  53. buffer_string = get_buffer_string(
  54. self.chat_memory.messages[-self.k * 2 :],
  55. human_prefix=self.human_prefix,
  56. ai_prefix=self.ai_prefix,
  57. )
  58. output = chain.predict(
  59. history=buffer_string,
  60. input=input_string,
  61. )
  62. return get_entities(output)
  63. def _get_current_entities(self, inputs: Dict[str, Any]) -> List[str]:
  64. """获取对话中的当前实体。"""
  65. prompt_input_key = self._get_prompt_input_key(inputs)
  66. return self.get_current_entities(inputs[prompt_input_key])
  67. def get_knowledge_triplets(self, input_string: str) -> List[KnowledgeTriple]:
  68. """从输入字符串中获取知识三元组。"""
  69. chain = LLMChain(llm=self.llm, prompt=self.knowledge_extraction_prompt)
  70. buffer_string = get_buffer_string(
  71. self.chat_memory.messages[-self.k * 2 :],
  72. human_prefix=self.human_prefix,
  73. ai_prefix=self.ai_prefix,
  74. )
  75. output = chain.predict(
  76. history=buffer_string,
  77. input=input_string,
  78. verbose=True,
  79. )
  80. knowledge = parse_triples(output) # 解析三元组
  81. return knowledge
  82. def _get_and_update_kg(self, inputs: Dict[str, Any]) -> None:
  83. """从对话历史中获取并更新知识图谱。"""
  84. prompt_input_key = self._get_prompt_input_key(inputs)
  85. knowledge = self.get_knowledge_triplets(inputs[prompt_input_key])
  86. for triple in knowledge:
  87. self.kg.add_triple(triple) # 向知识图谱中添加三元组
  88. def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
  89. """将此对话的上下文保存到缓冲区。"""
  90. super().save_context(inputs, outputs)
  91. self._get_and_update_kg(inputs)
  92. def clear(self) -> None:
  93. """清除记忆内容。"""
  94. super().clear()
  95. self.kg.clear() # 清除知识图谱内容
1.2.3 Tools层,工具

其实Chains层可以根据LLM + Prompt执行一些特定的逻辑,但是如果要用Chain实现所有的逻辑不现实,可以通过Tools层也可以实现,Tools层理解为技能比较合理,典型的比如搜索、Wikipedia、天气预报、ChatGPT服务等等

1.3 应用层:Agents

1.3.1 Agents:代理

简言之,有了基础层和能力层,我们可以构建各种各样好玩的,有价值的服务,这里就是Agent

具体而言,Agent 作为代理人去向 LLM 发出请求,然后采取行动,且检查结果直到工作完成,包括LLM无法处理的任务的代理 (例如搜索或计算,类似ChatGPT plus的插件有调用bing和计算器的功能)
比如,Agent 可以使用维基百科查找 Barack Obama 的出生日期,然后使用计算器计算他在 2023 年的年龄

  1. # pip install wikipedia
  2. from langchain.agents import load_tools
  3. from langchain.agents import initialize_agent
  4. from langchain.agents import AgentType
  5. tools = load_tools(["wikipedia", "llm-math"], llm=llm)
  6. agent = initialize_agent(tools,
  7. llm,
  8. agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
  9. verbose=True)
  10. agent.run("奥巴马的生日是哪天? 到2023年他多少岁了?")

此外,关于Wikipedia可以关注下这个代码文件:langchain/docstore/wikipedia.py ...

最终langchain的整体技术架构可以如下图所示 (查看高清大图,此外,这里还有另一个架构图)

基于LangChain+LLM的本地知识库问答:从企业单文档问答到批量文档问答-CSDN博客

原文链接:https://blog.csdn.net/v_JULY_v/article/details/131552592

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号