LangChain 上手记录,从实战开始

type
status
date
slug
summary
tags
category
icon
password
最近研究 LangChain 但又不想费 token,注意到 FastChat 的 OpenAI 兼容 API服务器可以无缝地将 LangChain 与开放模型一起使用。LangChain 发展迅速,最近(2023.8.25)实践过程中发现对比之前的一个教程有了一些差异,对于私有部署的模型坑相对更多,于是记录如下(如果使用 OpenAI API 可忽略第一步 FastChat 兼容):

FastChat 兼容

首先启动 controller,然后根据本地模型路径 --model-path 启动 model_worker,映射到 OpenAI 官方支持的模型名字,再启动 RESTful API 服务器:
TODO:探究这里启动模型时是如何映射的
然后设置 OpenAI 环境变量,从本地部署的端口访问模型,因为 FastChat 没有 API KEY 的设计,所以直接设置为 EMPTY:

测试例:文档全部输入模型问答(Embedding模型,及文档问答)

如果是在 notebook 中设置环境变量,可以使用 os.environ
最后通过一个问答例子进行 LangChain 测试(注意用 pip install 安装 langchain、openai):

概念解释

  • Loader:从指定源加载文件,比如文件夹 DirectoryLoader、CSV文件 CSVLoader、Google网盘 GoogleDriveLoader、任意的网页 UnstructuredHTMLLoader、PDF PyPDFLoader
  • Document:Loader 读取数据后需要转成 Document 才能继续使用
  • Text Splitter:字面意思,用来分割文本,限制 Prompt 长度
  • Vectorstores:将 Document 存储到向量数据库,以便进行 Retrive 等操作
notion image
  • Agent & Chain:Chain 是一系列按照特定顺序执行的步骤,每个步骤可以是一个工具或者语言模型的调用,而 Agent 是控制 Chain 执行的实体,它可以根据输入和上一个步骤的输出来决定下一个步骤应该是什么。

实战例子

基本问答(模型 + schema)

普通模型和聊天模型基础:
  • ChatOpenAI 聊天模型下,使用 schema 中的 SystemMessage 等将字符串变成符合 OpenAI API 的规范;
  • ChatOpenAI 聊天模型也可以通过调用 predict 作普通模型使用;
  • 普通模型直接调用模型补全,如果本地部署的是 chat 类型的模型,使用 ChatOpenAI 的 predict 模拟普通模型问答效果更佳(初步猜测是 ChatOpenAI 发起的响应更符合 chat 模型训练时的 prompt 【待验证】);
  • 除此之外: schema 里的各种 Message 就是将字符串变成规范的 Chat OpenAI API 格式 ,各种 Template 则是在此基础上再封装了一层(类似 f string),可以传入参数动态改变 prompt;

结合 Google 搜索

sepapi key:
使用 google 搜索需要 serpapi key,先自行注册之后设置
notion image
Qwen-7B 作为模型支持的答案,我搜了一下,今天的日期是对的,可惜了登月是 1969 年的 7.20,所以后面的话不正确;
有资料说 serpapi 对中文不太友好,不知道是基于模型能力还是 API 搜索到的中文网页质量,抑或是两者都有,毕竟我使用本地模型的情况下,改成英文之后连时间都错了

总结超长文本

准备数据:
先读取文档,然后切分,最后转成 Document 的列表以便 LangChain 处理。初始化切分器时,可以设置 chunk_size 控制片段大小、chunk_overlap 控制片段间的重叠(影响片段上下文关联性)
chain_type:控制如何将 document 送给 llm
  • stuff:将所有 document 一次性扔给模型总结,容易超出文本长度
  • map_reduce:先将每个 document 进行总结,最后将所有 document 总结出的结果再进行一次总结
notion image
  • refine:总结当前 document 时,会带着对上一个 document 的总结,理论上会更加连贯
notion image
  • map_rerank:一般用在问答的 chain 上,依据用户的问题和每个 document 的相似度,将现有的 document 列表排个序,将概率最高的 document 和问题一起放入 prompt 给模型,让模型返回答案;
map_reduce 等方法可以自己重写,也可以自己设置 map_reduce 的 prompt 模版,比如此案例让模型用中文输出摘要;
可以看到最终总结效果还是挺通顺的,在我看来已经达到了值得一读,颇有见地的程度😂

基于本地文件问答

踩坑:中途卡在 nltk 的下载,卡了非常久,也不报错就是下载不下来,wget nltk_data 里的文件也不好使,最后把整个 nltk_data clone 下来了,将 packages 文件夹内容复制到 nltk_data 中,然后进去子文件夹中解压(tokenizers taggers 等);

问答总体流程

notion image

快速入门

直接使用高级封装 VectorstoreIndexCreator ,使用 index.query即可简单实现长文问答,(文章表示:使用 query_with_sources 还可以获取涉及的源,但我目前还没试出来什么效果)
对网络博文提问:

具体实现流程

高级封装虽然方便但高度抽象,还是看看具体实现过程,首先准备数据,将数据加载到 loader,然后切分,最后存储到 vectorstores,用 RetirevalQA 生成 chain :
代码中,召回和问题相似的 docs 过程如下
深入:除了向量存储检索,还可以在 langchain.retrievers 找到不同方法比如 SVM 等方法召回,以下是用 SVM 召回的例子
  • MultiQueryRetriever 生成输入问题的变体以改进检索。
  • Max marginal relevance 选择检索到的文档之间的相关性和多样性。
自定义提示:召回可以自定义 Prompt 模版
返回引文:使用 RetrievalQAWithSourcesChain 可以返回引用 source
更底层控制:可以选择更低的抽象级别实现更细致的控制,比如对相关文档如何使用(stuff,reduce_map 等)
notion image
 
对话 QA:指定一个 ConversationBufferMemory 来跟踪历史对话内容(记录输入输出);然后在对话召回的 Chain 中使用 Memory Buffer,即可进行对话;
 
数据持久化:Chroma 也能可实现本地持久化存储向量,只需要定义 persist_directory 路径,需要使用的时候可以从该路径加载;此外也可以使用在线向量数据库,比如 Pinecone

📎 参考文章

 
LLM 驱动的 Agent LLM 可控性生成探索