如何设计一个 JVM 语言下的 LLM 应用开发框架?以 Chocolate Factory 为例

本文将介绍 Chocolate Factory 框架背后的一系列想法和思路。在我们探索和设计框架的过程中,受到了:LangChain4j、LangChain、LlamaIndex、Spring AI、Semantic Kernel、PromptFlow 的大量启发。

欢迎一起来探索:https://github.com/unit-mesh/chocolate-factory 。

顺带一提,在我们的参考架构里,框架/SDK 只是整体参考架构的一小部分。

f43804860ac723f814502e7f8bc76e0a.png

在 SDK 之后,还需要关注于 LLM 应用平台的设计。

为什么开发 JVM 语言的 LLM 开发框架

在过去的几个月里,我们一直在结合已有的 JVM 体系基础设施构建 LLM 应用。诸如于:

  • 在 ArchGuard Co-mate 里,我们探索了架构 Copilot + Agent Tool (架构分析工具)的正确打开方式。

  • 在 AI 辅助编程工具 AutoDev 中,我们给出了市面上最好的开源 IDE 插件的上下文构建模式 —— 构建复杂上下文(语言、框架、规范等)的 Prompt 策略,以及支持不同的大语言模型(除了市面上的大模型,还有内部的 MaaS 平台)。

  • ……

这些尝试是围绕于我们过去的另外一个假设:当大模型成本降低,可靠性上升之后,AIGC 会与业务应用紧密结合。在这时,我们需要致力于:

  • 开发结合 LLM 内部基础设施的 SDK

  • 探索快速构建 RAG (检索增强)相关 PoC (概念证明)的方式

  • 更友好的 prompt 开发与设计工具

简单来说,我们需要一些封装内部基础设施的框架、调试应用,以快速支撑 AI 应用的开发。

诸如于,我们在构建本地化代码搜索时,需要如下的工具:

dependencies {
// 核心库implementation("cc.unitmesh:cocoa-core:0.3.4")
// 代码拆分implementation("cc.unitmesh:code-splitter:0.3.4")
// Elastisearch 向量化存储,普通搜索implementation("cc.unitmesh:store-elasticsearch:0.3.4")
// 本地化的 embedding,用于每次更新代码,重新 embedding => CPUimplementation("cc.unitmesh:sentence-transformers:0.3.4")
}

每个模块并不复杂,只是一些基本的 API 与基本的逻辑封装。但是,可以直接与现有的 AI 应用结合,再配套内部的 MaaS(模型即服务平台),就能快速在现有的业务中接入 LLM 能力。

起步:总结 LLM 应用所需要的能力

如先前的文档所说,我们将 LLM 优先的软件架构分为三类:。

edd356acee630262cb7b9b7deb5b844e.png

  • 通过简单的 prompt 来与大模型进行交互。

  • 结合向量化(Embedding)的方式,来与大模型交互。

  • 自动化规划的方式,基于 workflow 的方式。

在当前,我们优先关注于结合向量化,也就是大部分 RAG 场景下的交互。即基于特定的业务场景,分析用户的意图,自动提供相应的上下文,交由大模型来进行自动化分析。对于这三种类型来说,或者不同场景时,各自的关注度是不一样的。

  • 与大模型的 API 交互。主要会由两部分组成,第一部分是:依赖于 MaaS 平台的标准的 LLM 接口模型封装;第二部分是:支持流式、自定义二次处理结果的交互 API。(第二部分其实是最难的)

  • 文档向量化(Embedding)。主要会由三部分组成,第一部分是:支持不同格式文档的处理,及其拆分模式与策略的代码化设计;第二部分是:不同精度(出于成本考虑)需求的向量化模型接入;第三部分是:支持查询扩展的中间层与多种类型数据库检索,即结合向量数据库和传统数据库。

  • 自动化规划(Flow + Agent)。尽管我们尝试去做更多相关的尝试,但是由于精力有限,并不能给出一个非常精确的结果。所以,在这里就暂时不展开这部分相关的内容。

开发一个框架与过去的东西差别不多。但是,有意思的一点是,由于我们构建的是一个框架,所以当看到新的 RAG 论文,第一反应就是能否交由框架来支持。

抽象:回顾 LLM 的数据处理

5c056fae260b5c1a9dc0d1987b547b04.png

诸如于 LangChain、LlamaIndex 在这方面已经做了非常好的抽象,我们在设计的时候,参考(复制)了大量的相关思想。在 LlamaIndex 的文档上,已经给出了很好的抽象,在这里我们使用我们新开发的 RAGScript (可以直接运行)来表达相关的内容:

rag {indexing {val document = document("filename.text")val chunks = document.split()store.indexing(chunks)}querying {val results = store.findRelevant("Hello World").lowInMiddle()llm.completion {
"// 结合 results 处理 prompt"}}
}

上面的代码段非常言简意赅的表达 RAG 的过程。简单来说,一个 RAG 分为 Indexing 和 Querying 两个阶段:

  • 在 Indexing 阶段里,我们关注于如何将数据加工和分解(split),并注入到向量数据库中。

  • 在 querying 阶段里,我们关注于如何加工检索完的数据,再结合 LLM 来处理

而现有的 LangChain、LlamaIndex 已经能提供我们很好的思路。唯一的挑战是,如何结合不同场景去探索合适的应用示例。

原型:从应用 PoC 中迭代抽象接口

6fe7ed232a0818f66d4e775aa72ad6d0.png

在不同的 LLM 应用开发框架或者 LLM 数据处理引擎里,都有大量的基础设施支持。但是,由于我们的场景往往是多种多样的,所以有时候显得有些不足。诸如于:

  • 本地化向量化(Emdeddbing)模型 SentenceTransformers 的引入,以降低代码向量化的成本。为此,我们需要引入 Onnx Runtime, 以在客户端或者服务端进行向量化操作。

  • 更精准的代码切分。对于框架型代码逻辑解释而言,能提供 interface 等抽象类的拆分,保持类之间的继承关系,以及其它更精准的规则处理,明显会比通用的拆分规则更有实际意义。

同时,为了更好的开发框架,除了结合过往开发 LLM 应用的经验,还得思考一些新的场景作为试验田。诸如于:

  • 交互式的 UI 代码辅助设计场景。

  • 简单场景下的代码解释器。

  • 基于文档的规范查询。

  • 基于自然语言的语义化代码搜索。

  • 长数据场景下的测试用例设计

每种场景之下,对于框架的支持要求是不一样的。对于简单的场景,应该直接由 core 模块来提供所有的能力;对于复杂的场景,需要提供基本的 workflow 支持,以实现快速的应用开发。

除此,在复杂的场景之后,大部分的数据需要经过二次处理,也就是:

  • 实时返回模式。只用于向用户提供快速的反馈。诸如代码生成时,先返回代码;又或者是基于流式的 DSL 来响应数据。

  • 结果二次处理。当模型返回所有的结果时,需要再处理。诸如于,执行用户的代码,以生成的图表。

而在某些场景,我们需要的是长等待时间,即等待所有的用户返回内容。而对于这种不能由框架支持的功能,则稍微显得麻烦一些,我们得考虑通过示例向用户呈现这个过程。

插曲:解释 RAG 实现的 RAGScript

在开发了基本的 Chocolate Factory 功能之后,为了更好的让其他同事参与进来。我们尝试编写一系列的文档和示例,以向其他人解释:如何开发一个基于 LLM 的 RAG 应用?

为此,我们基于已有的 API 能力,构建了 RAGScript,以快速向其他人解释完整的过程。完事的示例代码如下所示:

rag("code") {// 使用 OpenAI 作为 LLM 引擎llm = LlmConnector(LlmType.OpenAI)// 使用 SentenceTransformers 作为 Embedding 引擎embedding = EmbeddingEngine(EngineType.SentenceTransformers)// 使用 Memory 作为 Retrieverstore = Store(StoreType.Memory)indexing {// 从文件中读取文档val document = document("filename.txt")// 将文档切割成 chunkval chunks = document.split()// 建立索引store.indexing(chunks)}querying {// 进行相关性查询val revelant = store.findRelevant("Hello World")// 结合 Lost in the Middle 的长上下文重新排序val results = revelant.lowInMiddle()println(results)}
}

当然了,在我们的 RAG 示例中,还提供了代码语义化解释搜索的示例。详细见:https://framework.unitmesh.cc/docs/rag 。

Prompt 调试:LLM 的工程化开发难点

尽管我们开发的是模式二(Co-pilot 型应用)优先的 LLM 应用框架,但是在我们开发示例的过程中,我们依旧会发现对于 Prompt 的调试与编排是最大的挑战

在 Chocolate Factory 的 DDD 思想的工作流中,我们推荐的实践是:

  • Apache Velocity Engine 作为模板引擎

  • 构建每个 Workflow 独立的 Context 环境,与变量 resolver。

然而,对于一个大型的组织来说,内部会存在不同的 LLM。对于不同的 LLM 而言,相应的 prompt 编写模式也是有差异的。所以,我们正在思考采用同 PromptFlow 相似的方式,即采用独立的模板文件,结合工作流编排,以适应不同模式的 promtp 差异。如下是 PromptFlow 的模板示例:

system:
You are a helpful assistant.
{% for item in chat_history %}
user:
{{item.inputs.question}}
assistant:
{{item.outputs.answer}}
{% endfor %}
user:
{{question}}

再结合 CLI 或者 IDE 可视化的方式进行探索。

总结

总的来说,这篇文章深入探讨了设计 JVM 语言的 LLM 应用开发框架的思考过程,强调了框架的多样性和复杂性,以及如何通过框架和工具来支持各种 LLM 应用场景。我们提供了有用的示例代码和抽象概念,有助于读者更好地理解和开发 LLM 应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/141414.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

历史高频行情数据存储最佳实践:DolphinDB Array Vector 使用指南

越来越多的机构使用 L1/L2 的快照行情数据进行量化金融的研究。作为一个高性能时序数据库,DolphinDB 非常适合存储和处理海量的历史高频行情数据。针对快照数据包含多档位信息的特点,DolphinDB 研发了一种方便、灵活且高效的数据结构——Array Vector&am…

[AI Agent学习] MetaGPT源码浅析

前言 工作上,需要使用AI Agent,所以需要深入学习一下AI Agent,光阅读各种文章,总觉无法深入细节,所以开看各类AI Agent相关的开源项目,此为第一篇,学习一下MetaGPT的源码。 基本目标 MetaGPT是一…

博客系统的自动化测试

本次自动化实战内容:本次测试根据博客管理系统这个项目,首先设计UI自动化测试用例,然后使用Selenium4自动化测试工具和JUnit5单元测试框架,实现web端的自动化测试。 本次项目总体实现思路:目录下有一个common包&#…

机器学习(19)---神经网络详解

神经网络 一、神经网络概述1.1 神经元模型1.2 激活函数 二、感知机2.1 概述2.2 实现逻辑运算2.3 多层感知机 三、神经网络3.1 工作原理3.2 前向传播3.3 Tensorflow实战演示3.3.1 导入数据集查看3.3.2 数据预处理3.3.3 建立模型3.3.4 评估模型 四、反向传播五、例题5.1 题15.2 题…

Day1-DeepWalk

论文《DeepWalk: Online Learning of Social Representations》 2014年发表在数据挖掘顶会ACM SIGKDD(KDD)上的论文 目的:学习节点表示 推动:将自然语言处理里面的无监督学习方法迁移至此 思路:将图结构序列化&#x…

ajax method to retrieve images as a blob

go 服务端: 就是先把这个图片读出来 然后返回二进制的数据 byteFile, err : ioutil.ReadFile("." "/processed/" uuidStr"processed.png")if err ! nil {fmt.Println(err)}c.Header("Content-Disposition", "att…

MYSQL不常用但好用写法

ORDER BY FIELD() 自定义排序逻辑 MySql 中的排序 ORDER BY 除了可以用 ASC 和 DESC,还可以通过 「ORDER BY FIELD(str,str1,…)」 自定义字符串/数字来实现排序。这里用 order_diy 表举例,结构以及表数据展示: ORDER BY FIELD(str,str1,…) …

Android 滑动事件消费监控,Debug 环境下通用思路

Android Debug 环境下滑动事件消费监控通用思路 背景 Android 开发中,经常会遇到滑动事件冲突。在一些简单的场景下,我们如果能够知道是那个 View 拦截了事件,那我们能够很容易得解决。解决方法通常就是内部拦截法或者外部拦截法。ViewPage…

SWC 流程

一个arxml 存储SWC (可以存多个,也可以一个arxml存一个SWC)一个arxml 存储 composition (只能存一个)一个arxml 存储 system description (通过import dbc自动生成system) 存储SWC和composition的arxml文件分开&#…

注入之SQLMAP(工具注入)

i sqlmap是一个自动化的SQL注入工具,其主要功能是扫描,发现并利用给定的URL和SQL注入漏洞,其广泛的功能和选项包括数据库指纹,枚举,数据库提权,访问目标文件系统,并在获取操作权限时执行任…

Java学习星球,十月集训,五大赛道(文末送书)

目录 什么是知识星球?我的知识星球能为你提供什么?专属专栏《Java基础教程系列》内容概览:《Java高并发编程实战》、《MySQL 基础教程系列》内容概览:《微服务》、《Redis中间件》、《Dubbo高手之路》、《华为OD机试》内容概览&am…

北工大汇编——综合题(1)

题目要求 统计字符数。从键盘输入一行字符,统计字母、空格、数字、其他宇符的个数,并显示。要求:提示输入一行宇符串;键盘输入宇符串,Enter 键结束输入,并换行显示结果。 题目代码 DATAS SEGMENT;此处输…

JSP ssm 零配件管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java ssm 零配件管理系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用…

一文了解什么SEO

搜索引擎优化 (SEO) 是一门让页面在 Google 等搜索引擎中排名更高的艺术和科学。 一、搜索引擎优化的好处 搜索引擎优化是在线营销的关键部分,因为搜索是用户浏览网络的主要方式之一。 搜索结果以有序列表的形式呈现,网站在该列表中的排名越高&#x…

Android开发MVP架构记录

Android开发MVP架构记录 安卓的MVP(Model-View-Presenter)架构是一种常见的软件设计模式,用于帮助开发者组织和分离应用程序的不同组成部分。MVP架构的目标是将应用程序的业务逻辑(Presenter)、用户界面(V…

Selenium自动化测试框架常见异常分析及解决方法

01 pycharm中导入selenium报错 现象: pycharm中输入from selenium import webdriver, selenium标红 原因1: pycharm使用的虚拟环境中没有安装selenium, 解决方法: 在pycharm中通过设置或terminal面板重新安装selenium 原因2: 当前项目下有selenium.py,和系统包名冲突导致, …

Linux学习第19天:Linux并发与竞争实例: 没有规矩不成方圆

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 先说点题外话,上周参加行业年会,停更了一周。接下来的周五就要开启国庆中秋双节模式,所以有的时候,尤其是工作以后…

JAVA学习-全网最详细

🌈write in front🌈 🧸大家好,我是Aileen🧸.希望你看完之后,能对你有所帮助,不足请指正!共同学习交流. 🆔本文由Aileen_0v0🧸 原创 CSDN首发🐒 如…

表格内日期比较计算

需求&#xff1a;在表格中新增数据&#xff0c;计算开始日期中最早的和结束日期中最晚的&#xff0c;回显到下方。 <el-formref"formRef":model"ruleForm":rules"rules"style"margin-top: 20px;"label-position"top">…

Golang 的 GMP:并发编程的艺术

前言 在 Golang 的并发编程中&#xff0c;GMP 是一个重要的概念&#xff0c;它代表了 Goroutine、M&#xff08;线程&#xff09;和 P&#xff08;调度器&#xff09;。这个强大的三位一体的并发模型使得 Golang 在处理并发任务时非常高效和灵活。通过 GMP 的组合&#xff0c;…