
本文经授权转载自知乎张茄子同学https://www.zhihu.com/people/chase-zh 戳文末阅读原文跳转原文
这一年来大语言模型可以说是方兴未艾,发展的如火如荼。作为数据系统方面的爱好者,如果一点也不去追逐和研究一下这个热门领域,确实会显得有点落伍。这篇文章是我最近尝试使用Rust下的 flows.network[1] 编写LLM应用的一点实战总结。
说起大语言模型,就不得不提起ChatGPT和OpenAI。虽然OpenAI今天刚换了CEO,但是不可否认它在发展和推广LLM的过程中发挥的突出作用。从个人开发者和小公司的角度,自己训练和部署大语言模型基本上是一件不现实的事情,作为一个数据系统开发爱好者,我倾向于用如下的图示表示LLM的概念。
Concept of LLM
从我们应用开发者的角度,LLM可以看作是一个由大量训练数据训练并组合成的函数,由于这个函数本身是静态的,在使用时需要使用Prompt和上下文信息(Context)作为输入。而这个函数的功能就是根据Prompt和上下文,产生最可能满足用户需要的输出。这一输出和用户的输入(Chat)由进一步作为上下文输入回LLM。
利用这种思维方式,大语言模型的开发者就不需要特别关注LLM的内部细节,只需要以某种合适的方式为LLM这个“函数”准备输入即可。当然这里面还有一些Prompt调优的细节,但是不得不说现在的大语言模型基本上已经非常“聪明”。往往简单的Prompt就可以实现很好的效果了。
要提到LLM的应用,就不得不提到RAG和Vector Store。所谓RAG是Retrieval Augmented Generation的缩写。算是伴随着LLM的兴起的涌现出的一个新的名词。RAG要解决的问题在于一般的LLM不容易快速利用新的知识进行训练。比如ChatGPT长期以来只能回答基于2021年之前的知识的问题。这一问题虽然可以利用Fine-tune来解决,但是Fine-tune本身也是一个成本较高且比较缓慢的过程。
在早期,RAG当中的信息抽取,直接使用Vector Store来实现。这是因为LLM主要应用于问答系统。对这类系统来说用户的输入往往都是自然语言的问题,使用传统的基于关键字等输入的查询系统往往并不能很好的返回结果。因此需要Vector Store结合某种语言模型来计算所谓的Embedding(向量数据)。再使用Vector Store的相似性计算来抽取相关的信息,作为上下文信息提供给LLM。传统的基于Vector Store的RAG过程可以表示如下:
Usage of Vector Store
在这个框架下,Embedding Model可以将用户的查询和已有的文档都转换成同一度量空间下的向量。这一转换过程保证了用户的查询产生的向量和相关文档内容产生的向量是相似的。因此在调用LLM时,可以通过将查询出的信息作为上下文提供给LLM,从而使得LLM可以回答文档库里的各种信息,并且可以回答最新加入的文档相关的问题。
在这一模型中,Embedding Model和LLM其实可以不是一个模型,Embedding Model的作用仅仅是生成相似向量,并不具备很强的推理能力。这种框架的能力其实有时会被Embedding Model的性能所限制。
然而,最近也有一种更为强大的RAG的框架开始被大量使用。这种框架基于所谓的Assistant模式。所谓Assistant,就是在LLM和用户的交互之间提供第三个类似Agent的角色。这个角色就好像一个聊天当中的Bot,可以接受LLM输出的调用指令,执行查询并返回结果提供给LLM作为Context。这种模式可以表示如下
Assistant Framework
这种模型基于一个很简单但是有效的想法:LLM的功能足够强大,可以将用户的问题转化或者说“翻译”成实际的格式化的语句,如SQL或者JSON描述的函数调用等。那么,如果通过Prompt Engineering,让LLM先输出一些他认为可以查询到数据的命令,再由Agent来实际执行查询并将结果返回到当前会话的Context当中,LLM就可以得到它想要的最新的资料。这种模式具有很强的灵活性,可以查询传统数据库而不需要预先使用Embedding模型进行处理,而LLM还可以根据自己的理解执行多个轮次的查询再返回结果。可以说是一种功能非常强大的框架。
此外,Assistant框架还可以用于控制外部系统,而不仅仅是查询数据,可以说将LLM的应用潜力推向了极致。
作为一个普通的开发者,对于LLM或者大的框架性的发展往往无能为力,但是我们仍可以利用已有的工具构建出一些有用的东西。LLM当中目前最火的框架可能非 LangChain[2] 莫属了。这类工具本质上其实都是Workflow管理工具,通过提供一些可以调用的函数和组织工作流的方法,允许用户将自己应用的逻辑编织出来执行。
在这里,出于兴趣,我选择了 flows.network[3] 这一工具来编写一个Telegram的GPT应用。它是一个使用Rust实现的大语言模型Workflow实现,提供了多种可以调用的方法和工具帮助描述你的LLM应用流程。你编写的应用将会被编译成WebAssembly的形式运行在 Flows.Network[4] 提供的托管平台上。目前这一托管服务也是免费的。
Flows.Network 支持的Integration
上图展示了Flows.Network已经提供的集成接口。这些接口都被实现为API并提供了Rust的SDK进行调用。用户只需要专注于组织应用的逻辑并调用这些方法即可。选择它而不是 LangChain 的另一个原因在于其框架实现非常的轻量,并没有像 LangChain 被诟病的那样引入了太多太重的抽象。
我使用 Flows.Network[5] 编写了一个可以用于问答和帮助自己学习日语的 Telegram Bot,你可以在这里[6]找到全部的源代码。这个 Bot 实现了一些方便的命令,并支持以Reply-To的形式延续会话的上下文:
问答模式
日语学习模式
值得注意的是,由于Telegram的Markdown语法支持非常的奇怪,为了使ChatGPT输出的Markdown文本可以被正确处理,我甚至专门使用 Nom[7] 编写了一个简单的Parser来处理并Escape文本中的Markdown语法。具体的代码可以参考 src/markdown.rs[8] 文件。
目前,Flows.Network[9] 的代码需要以 WebAssembly 模式才能构建,也不方便本地执行和调试。这是它目前比较大的问题。好在只要正确配置,当你push到你GitHub仓库上之时就会自动激发构建和部署,编写体验还算比较方便。希望之后这方面可以进一步提高。
本文介绍了LLM应用的一般概念并介绍了使用 Flows.Network[10] 编写一个LLM应用的体验。总体来说我个人非常喜欢可以使用Rust来进行LLM应用的编写,Flows可以说提供了必要的API从而使我不需要自己和各种接口打交道。而且使用Wasm进行部署FaaS的部署也是非常方便的体验。希望此类的平台能够大量涌现并方便各种GPT应用的开发。
同时欢迎 Fork 并试用我编写的 Telegram Bot: https://github.com/shanzi/Telegram-ChatGPT
[1]flows.network: https://flows.network
[2]LangChain: https://www.langchain.com/
[3]flows.network: https://flows.network
[4]Flows.Network: https://Flows.Network
[5]Flows.Network: https://Flows.Network
[6]这里: https://github.com/shanzi/Telegram-ChatGPT
[7]Nom: https://docs.rs/nom/latest/nom/
[8]src/markdown.rs: https://github.com/shanzi/Telegram-ChatGPT/blob/main/src/markdown.rs
[9]Flows.Network: https://Flows.Network
[10]Flows.Network: https://Flows.Network
关于 WasmEdge
WasmEdge 是轻量级、安全、高性能、可扩展、兼容OCI的软件容器与运行环境。目前是 CNCF 沙箱项目。WasmEdge 被应用在 SaaS、云原生,service mesh、边缘计算、边缘云、微服务、流数据处理等领域。
GitHub:https://github.com/WasmEdge/WasmEdge
官网:https://wasmedge.org/
Discord 群:https://discord.gg/U4B5sFTkFc
文档:https://wasmedge.org/docs