CatGPT Copilot:基于 LangChain 构建代码知识库

LangChain 是 LLMs 领域的瑞士军刀,封装了模型、提示、存储、索引、链、代理等核心能力,极大提高了 LLMs 应用的开发效率。CatGPT Copilot 直接内嵌了 LangChain.js 提供 LLMs 处理服务,其实更合理的方式应该从云端部署 LangChain 提供 API 调用服务,方便扩展和管理。哎,管不了那么多了,本地先怼上,还快一点…

代码知识库

一个聪明的 Code Copilot 必然要结合代码上下文语境和知识库文档进行定制,提供更精准的代码检索和生成能力,主要有以下使用场景:

  • 代码片段:日常业务许多都是机械的搬砖工作,一个项目可以拆解成若干成熟的代码片段。还在翻文件“CV”代码?何不让 Copilot 帮你管理代码片段。一键收藏代码片段,敲个关键词直接输出,还能描述场景自动补全组件属性和函数出入参;
  • 知识库文档:组件库辣么多,方法属性记不住,翻文档挺浪费时间,那就让 Copilot 先学习一遍,有问题直接问她就行;
    常用的微调训练方案有 Fine-tuning(微调)和 Embedding(嵌入):
  • Fine-tuning:提供更多小样本进行微调学习,使模型在特定知识领域更具专业性;支持私有化部署,适用于公司内部数据;
  • Embedding:将知识文本转化为向量并持久化到向量数据库,在对话阶段通过相似度匹配召回相关文本,嵌入到请求上下文提供给模型进行查询;向量转换过程在线完成,存在数据泄露风险;

Fine-tuning 需要的时间成本和机器资源较高,本次采用 Embedding 方案基于开源代码和文档微调,过程如下:

Embedding

文本加载&分割

首先需要以文本形式导入本地代码和知识库文档,LangChain 支持多种文件加载程序,可以灵活适配各种文本来源:

  • 纯文本:用户输入的文本补全、光标所在上下文代码片段
  • 文件/文件夹:代码项目文件,注意剔除 node_modules 等冗余文件
  • 网页:官方文档、语雀知识库等其他在线文档
  • GitLab:团队或域内代码仓库

受语言模型入参文本数量的限制,还需要对文本进行分割再分批传输,可以使用默认文本分割器,只需指定最大字数即可;对于代码文本,建议使用代码分割器,对指定语言根据语法和语义分割更独立的代码块,保持代码连续性。

向量转换&存储

文本装载和切片后,需要使用 OpenAIEmbedding 服务将本文片段转化为向量 Embedding,该过程按输入 token 计费,默认模型是 text-embedding-ada-002,可以传入 basePath 参数指定代理服务器科学上网。如果担心数据安全可以使用 TensorFlowEmbeddings 或 HuggingFaceInferenceEmbeddings 等私有化服务。

Embedding 后需要将向量数据持久化。对于本地代码这类实时文本,可以直接存储到内存(Memory)中,方便实时调用;像文档知识库这类比较固定的文本,可以写个脚本预处理一下,持久化到向量数据库(Milvus)或保存文件(HNSWLib),节省 Embedding 费用。可以使用 addVectors 或 addDocuments 方法合并多个存储实例,实现多套方案并存。

内容召回

知识库转向量数据后如何在实时问答中使用呢?可以用 RetrievalQAChainConversationalRetrievalQA 这些比较成熟的文档检索链,它能从向量库和对话历史中检索相关文档,并注入到请求上下文中进行查询,整个过程都是黑盒执行,于我这种要求不高的懒人正好。

如果需要定制更精细的回归算法和策略,可以基于 LangChain 提供的各类召回器编写召回函数,调整特征参数并在向量检索出入口做数据处理,将处理后的文本嵌入请求 history 入参即可。

模型选择

LangChain 默认集成了 ChatGPT 模型服务,如果要调用其他模型,只需要继承 BaseChatModel 基类并补全网络请求方法即可,主流模型都能在 GitHub 搜到现成代码。

ChatGPT

ChatGPT 虽然是一款通用的大语言模型,但是在代码辅助领域依然强大,搭配一些精心构造的 Prompt 完全够用,但是在某些方面还有待提升:

  1. 安全性:无论是问答还是 Embedding 都需要在线查询,鬼知道 OpenAI 会不会拿我们的数据反哺模型,势必会造成企业私有数据泄露;
  2. 速度:日常使用体感上比较流畅,但是在一些即时性和无法流式输出的场景还是有些捉急;
  3. 准度:写个代码片段和正则表达式绰绰有余,但是问到一些小众知识领域容易出现“幻觉”,需要仔细甄别;
  4. 费用:代码服务使用 gpt-3.5-turbo 模型足矣,代码生成和问答等“一次性”服务花不了几个钱,但是实时推理就吃不消了;

综上所述,在企业内部推广使用需要解决两个核心问题:1) 数据私有化,不能造成信息泄露;2) 实时推理,需要提升速度、降低费用。故我们可以考虑部署代码领域私有模型,私有化、免费、够快。

CodeGeeX2-6B

CodeGeeX2-6B

CodeGeeX2 以 ChatGLM2-6B 为基座语言模型对大量代码数据进行预训练,在代码领域支持度非常好,涵盖主流编程语言。在显卡模式加持下推理速度能干到 90 字符/s,完全可以支持实时推理,NewBee!

部署方式非常简单,服务器上直接使用 transformers 调用 CodeGeeX2-6B 模型,再以 API 形式对外暴露推理服务即可,服务部署参考:codegeex-fastertransformer、请求调用参考:langchainjs_llm_nest

TabbyML

TabbyML

TabbyML 是一款自托管的 AI 编程服务,对于服务器要求很低,支持 Mac 本地环境部署,同时还开源了配套 VSCode / Vim / IntelliJ 插件。目前该模型还在内测中,以 GitHub 公网代码为数据集,尝试下来对 JavaScript 和 Python 支持较好,准确度比 CodeGeeX2 略高。

官方提供了 Docker 部署脚本,但是还没有提供Embedding 等口子,如需扩展直接直接部署模型

VSCode 插件接入 LangChain

LangChain 比较潮流,只支持 Node.js 18+,然而 VSCode 插件运行时使用的是内置的 Node.js 16 且无法升级,过程中遇到一些水土不服的问题,在此记录一下:

流式请求

LangChain 新版网络请求用的是浏览器环境的 fetch,VSCode 环境无法使用,官方文档给出了两种解决方案:

方案一,只需带着参数NODE_OPTIONS=’–experimental-fetch’运行 Node 即可,经过各种尝试 VSCode 插件运行时无法动态置入参数,未果。

方案二,使用 node-fetch 代替 fetch,具体操作如下:

  1. 安装 node-fetch 依赖
yarn add node-fetch --save

2.polyfill,新建文件 fetch-polyfill.ts 代码如下,并在 LangChain 入口文件处引入此文件

/** 
* node-fetch polyfill
* 注:langchian 仅支持 Node.js 18+,vscode 插件环境为打包好的 Node.js 16,需加载此垫片,并按此文档替换 langchain 依赖中的流式解析逻辑
* https://github.com/hwchase17/langchainjs/issues/548#issuecomment-1607846463
*/

import fetch, {Headers, Request, Response} from 'node-fetch';

declare global {
var fetch: any;
var Headers: any;
var Request: any;
var Response: any;
}

if (!globalThis.fetch) {
globalThis.fetch = fetch;
globalThis.Headers = Headers;
globalThis.Request = Request;
globalThis.Response = Response;
}
  1. 将依赖 /node_modules/langchain/dist/util/event-source-parse.cjs 文件中的 getBytes 函数改成如下代码
async function getBytes(stream, onChunk) {
stream.on('readable', () => {
let chunk;
while (null !== (chunk = stream.read())) {
onChunk(chunk);
}
});
}

重新编译 VSCode 插件,流式请求就能跑通了。

HNSWLib

HNSWLib 是一个内存向量存储器,可以将上下文保存到文件中,VSCode Node.js 16 环境也无法使用。

报错

看报错提示虽然是依赖没有安装,但实际扒编译后的源码发现是 dynamic import 在低版本环境不支持,我们只需换种 import 方式即可,CJS or ESM。

import

查看评论