当大模型不是问题时,如何应对 LLM 的工程化落地挑战?

2023-11-01

几个月前,在 Thoughtworks 的内部 AIGC 研讨会里,我们一直达成了一系列一致观点,诸如于:如果没有 “开源模型” 降低企业应用 LLM 的成本,那么 LLM 会很快消亡。所以,我们相信开源 LLM + LoRA 微调会成为企业的一种主流方式。现今,我们可以看到 LLaMA 2、Code LLaMA 2 等模型在不断刷新这种可能性。

67de0d73cff971d2066eb56142681f02.png

而在模型不是问题之后,作为架构师、开发人员,我们应该致力于:将 LLM 以工程化的方式落地。于是,在过去的几个月里,我们开发了一系列不同领域的 LLM 应用 PoC,尝试从不同的角度思考如何构建好 LLM 应用。诸如于:

  • 语言与生态的角度,探索优化语言间的交互?

  • 技术架构应该如何设计?

  • Prompt 建模与优化?

  • 有哪些模式构建更好的模型上下文?

  • 语言 API 应该包含那些内容?

其它的一些问题,还包含如何通过小模型、传统 LLM 降低大模型花费?每个问题都是一个比较有意思的问题,也是我们在落地时要考虑的。

语言与生态的角度:LLM Service as a API vs FFI

已经有大量的企业尝试使用 Python + LangChain 去构建知识增强工具的 PoC。从工程的角度意味着,我们需要考虑:

  1. 是否使用智能服务都选用 Python,对外提供 API?

  2. 在现有的语言、基础设施中,寻找可能的代码方案?

而因为 Python 的动态特性,影响了 IDE 的智能分析,进而影响了开发效率 —— 哪怕是有了 Pydantic 这样的类型库。于是 ,我对于语言的第一个考虑是:与企业现有基础设施相结合。特别是,我现有的各种库、框架使用的都是 JVM 语言编写。

语言的 AI 基础设施

0a59ea58f77014cf57d15890a9326b84.png

于是乎,我们使用 Java/Kotlin、TypeScript、Rust 语言开发了不同类型、不同场景下的应用,以查看不同语言是否能构建起 LLM 应用。

从现有的体系来看,主流的编程语言都具备深度学习相关的代码库。我们可以以 Python 生态体系下,作为一个参考示例。诸如于:

  • Java 语言体系里的 Deep Java Library,提供了大量的深度学习相关的库,使得我们可以快速构建基于 LLM 的应用。

  • Kotlin 语言体系里的 KInference,是专门为推理(inference)进行优化的,主要是针对在服务端、本地(客户端)运行 ONNX 模型推理的。

  • Rust 语言。在我们构建 CoUnit 里,使用的是 Rust 语言作为开发语言,则需要 ndarray 这样的多维数组库。

但是在大部分场景下,我们并不需要什么 AI 基础设施。作为一个基本的 AI 应用,可能只需要的是计算 Token 长度这样的功能,以便于避免无谓的 LLM 花费。

FFI 作为接口方式

0689219a2dfec5e21b03640fcee0294a.png

在这时,而 Tiktoken/Tokenizer **便是我们的第一个需要 FFI (Foreign Function Interface,外部函数接口)功能的库,用于计算 Token 长度。FFI 允许不同编程语言之间的代码相互调用和交互,诸如于使用 Python 来调用 Rust 实现的底层库,以实现更快的计算速度。

除此,从应用侧的角度来说,不论是客户端,还是服务端,都需要引入一些小的推理模型。如下是两个比较常用的基于 FFI 库:

  • Tokenizer/tokeniser。不论是 OpenAI 的 Tiktoken、还是 HuggingFace 的 Tokenizers,选择的方式都是 Rust 作为底层语言。除此,稍有差异的是 JetBrains 的 AI Assistant 使用 Kotlin 实现了 Tokenizer,或者是出于 FFI 性能的考虑。

  • ONNX Runtime。Onnx 是一个跨平台机器学习推理加速器。通常用于在客户端、服务端引入小模型推理,诸如于引入 SentenceTransformers 在本地进行相似式搜索。从实现上,ONNX 使用的是 C++ 实现的,所以其它语言下使用的也是 FFI 的形式。

唯一对我们影响比较大的点是,在某些语言下,我们可能没有那么多的参考代码、参考架构作为示例,在开发这一类应用时,时间会比较长。

LLM 应用的技术架构

与常规的应用开发相比,LLM 应用并没有什么太大的区别。只是我们需要考虑 LLM 带来的影响,以及在不同的场景下,如何去管理这些架构。

不同业务场景下,AI 应用的三种模式

0a5abc6814bc6b31428fa994d8733955.png

通常来说,根据不同的业务模式,结合 AI 的程度也是不一样的,我们将其总结为三种不同的模式。

  • 基础 LLM 应用。通常只需要使用提示工程来与预训练模型进行交互。

  • Co-pilot 型应用。使用提示工程与智能体(Agent)进行交互,该智能体预训练模型和外部工具储相结合。通常来说,根据用户意图,结合特别的模式编写 workflow,以自动构建上下文。

  • 自主智能体。使用高级智能体(Agent)自动生成提示来控制预训练模型和外部工具。通常来说,由 LLM 来自动根据用户意图生成 workflow,并自动控制外部工具。

因此,根据我们先前构造的几个 PoC,我们将其总结为四个架构设计的基本原则。

LLM 优先的软件架构原则

ecf8b0fef2d3c3b8486bc84f71de732d.png

在这里我们思考的四个架构原则,其实是受限于 LLM 能力所产生的:

  • 用户意图导向设计。设计全新的人机交互体验,构建领域特定的 AI 角色,以更好地理解用户的意图。诸如于,我们可以基于 DSL 来逐步引导用户输出更多的上下文。

  • 上下文工程。构建适合于获取业务上下文的应用架构,以生成更精准的 prompt,并探索高响应速度的工程化方式。

  • 原子能力映射。分析 LLM 所擅长的原子能力,将其与应用所欠缺的能力进行结合,进行能力映射。

  • 语言接口。探索和寻找合适的新一代 API ,以便于 LLM 对服务能力的理解、调度与编排。

而从实践的情况而言,引导用户、完善上下文是我们工程化落地的最大难点。

Prompt 建模与优化

Prompt 的模型与 Prompt 策略本身是息息相关的,通常来说,它与我们解析复杂问题的方式是相关的。

上下文相关的 Prompt 建模

在开发的时候编写 prompt 是一件痛苦的事情,在我们构建了不同的思维链模板之后,我们需要提供更好的示例。为此,我们会对 prompt 进行建模,以更好的管理和测试 prompt。以 LangChain 源码中的 prompt 作为示例:

Human: What is 2+2?
AI: 4
Human: What is 2+3?
AI: 5
Human: What is 4+4?

对应的 Python 代码如下:

examples = [
    {"input": "2+2", "output": "4"},
    {"input": "2+3", "output": "5"},
]

为此,根据我们的 Prompt 模式,需要不同的 PromptTemplate 模式,如 LangChain 里便构建了一系列复杂的 Prompt 策略模式,如: FewShotPromptWithTemplates、 FunctionExplainerPromptTemplate 等。

Prompt 模板持续优化

而在我们的 PoC 项目中,不只一种类型的 example。因此需要考虑:如何去持续对它们进行建模?如下是我们使用 Kotlin 构建的项目的一个 QA 模板示例:

@Serializable
data class QAUpdateExample(
    override val question: String,
    override val answer: String,
    val nextAction: String = "",
    val finalOutput: String = "",
) : PromptExample {
}

也是随着我们迭代的过程中,不断优化出来。而由于 prompt 中需要结合一下上下文信息,通过变量的方式,所以我们还需要诸如于 Apache Velocity 这样的模板引擎,将 prompt 中的变量采用真实的数据来替换。

上下文构建的模式:RAG 与领域特定模式

毫无疑问,在现有的 LLM (大语言模型)应用里,与智能客服、知识问答相关的场景是最多的。在这些场景之下,开发人员一直在探索更好的 RAG(Retrieval-augmented generation,检查增强生成)模式,来使 LLM 回答得更加准确。

然而,事实上,在大部分的场景之下,我们可以构建专有的模式,它们的质量往往更好。但是,通用性不多,依赖于专家来构建,因此显得性价比不足。

领域特定的语言抽象

毫无疑问,代码生成相关的工具是第二大热门的 LLM 工具。与知识场景不同的是,你不一定需要 RAG 来帮你完成上下文的构建。在这个场景下,每个问题都很 “具体”,可以在 IDE、编辑器里拿到足够多的上下文。

不论是 GitHub Copilot、JetBrains AI Assistant,还是我们开源的 AutoDev,都只是在根据用户的行为,得到的结果,来生成对应的 prompt。如 Copilot 会根据最近的 20 个文件,计算与当前代码相差的 code chunk,生成 prompt。而在 AutoDev 里,我们觉得应该将规范编写入代码生成的 prompt,以生成规范化的代码。

也因此,我们相信在特定领域里,根据领域的上下文设计 DSL,设计 prompt 策略,再结合 RAG 才是最合理的方式。

检索增强生成与 Prompt 策略

41a3f0ed560ef8c1a53c7dcbe7e72818.png

在内部的培训材料里,我将 RAG 视为 prompt 策略的一种。基础的 RAG 模式需要结合向量数据库、构建知识索引。在基础 RAG 模式之下,构建出来的 prompt 并不会达到令人满意的结果。

用户都是小白,并不会以我们预期的方式来操作系统。他们的输入是模糊的,我们的挑战便是:如何将一个模糊的问题具象化?

  • 在 CoUnit 里,我们需要将用户的意图转为 DSL,其中包含中文、英文、HyDE 文档(假设性文档嵌入),以此进行语义化搜索,来获得可能的结果。

  • 在 CoUnit 这一类的场景之下,本身就是查询扩展(Query Expansion)的一种模型,在这类场景之下,还有 Query2Doc 等一系列不同的模式。而随着,我们的有效历史聊天的存储,相关的结果也会越来越准确。

  • 在 LLM 不包含我们知识的情况下,而内部又有大量的相似知识,我们又需要考虑结合 Lost in the Middle 来思考:如何在 prompt 中高效地分布我们的 chunk?即在头尾中分配最相关的结果,以让 LLM 能抓住重点。

与半年前的 LLM 一样,RAG 相关的内容在未来几个月里仍然将激烈的演进,我们依旧需要持续的学习。

转换不确定性的语言 API

在与 LLM 进行交互时,需要由自然语言作为 API。总体上可以分为两大类场景:

  1. LLM + Workflow。由 LLM 分析用户的意图,来选择合适的工具、API。

  2. LLM DSL 生成。由 LLM 分析用户的意图,结合特定上下文,输出 DSL,让应用解析,并作为程序的输入。

语言非常的奇妙,而结合 LLM 的本质则是将不确实性转为确定的函数调用参数、DSL 等。

函数调用:基于用户意图选择合适扩展工具

a1982894c23467e63f49015f64184ef5.png

简单来说,就是类似于如下的 prompt 方式:

你的任务是回答关于代码库的问题。你应该使用一组工具来收集信息,以帮助你回答问题。以下工具可供使用:

从实践上来说,通常可以分为这三种模型。

  • Tooling 模式。即上述的方式,并提供一堆可能可选择的工具。在开发应用时,往往需要结合上下文,生成动态的工具列表,以让 LLM 选择合适的工具。

  • Function Calling 模式。由 LLM 在聊天时,检测何时应该调用一个函数,传递输入给函数,并调用这个函数。

  • 意图识别小模型。即类似于 OpenAI 相似的方式进行微调,以在特定的场景下,实现类似的功能。

而除了上述的场景之后,还可以由 LLM 生成 DSL,如 JSON 等方式,由程序来处理这个函数实现类似的功能。

数据驱动:DSL 模式与引导用户

通常来说,我们会采用 DSL 作为用户与 LLM 的中间语言。即方便于 LLM 理解,也方便于人类理解,还适合程序解析。当然了,也有一些 DSL 并不需要解析,诸如于:

----------------------------------------------
|      Navigation(10x)                       |
----------------------------------------------
| Empty(2x) | ChatHeader(8x) | Empty(2x)     |
----------------------------------------------
| MessageList(10x)                           |
----------------------------------------------
| MessageInput(10x)                          |
----------------------------------------------
| Footer(10x)                                |
----------------------------------------------

它可以用为中间的呈现模式,以便于 LLM 进一步生成代码。

除此,如何基于 DSL 模型引导用户,就是一件非常有意思的事。

总结与下一步

在这一篇文章里,我们总结了过去几个月里,构建 LLM 应用的一些经验。而从这些经验里,我们发现了越来越多可复用的模式。

我们将探索如何更好地沉淀下这些模式 ,以用于支撑更快速的 LLM 应用开发。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当大模型不是问题时,如何应对 LLM 的工程化落地挑战? 的相关文章

  • 如何实现百万并发连接的 Nginx 集群

    要实现百万并发连接的 Nginx 集群 可以考虑以下几种方案 横向扩展 使用多台 Nginx 服务器来处理并发连接 通过将流量分发到多个节点 每个节点处理一部分连接 从而实现并发连接的处理能力扩展 可以使用负载均衡器 如硬件负载均衡器 Ng
  • Anaconda安装教程(超详细)

    Anaconda安装教程 超详细 2022 11 16成功配置写下这篇文章 1 Anaconda的下载 我是在官网下载的 并没有网上说的那么慢 大概5 7分钟左右就下好了 这里附上官网下载地址 Anaconda官网下载地址 进去是这样的 直
  • alibaba/COLA 4.0框架 使用记录

    文章目录 背景 COLA框架 开发情况 出现的问题 总结 建议 背景 简介 开发团队之前没用过DDD开发 第一次用https github com alibaba COLA框架试着做项目 记录一些遇到的问题 https github com
  • 红黑树与平衡二叉树区别?

    如果说平衡二叉树是一个类的话 那么红黑树就是该类的一个实例 算法的书我丢久了 一下子也找不到 我是凭记忆说的 红黑树的算法比较麻烦 但它的思想很好 如果理解了它的思想也就理解它的算法 我也只记得思想 具体算法记不得了 我就在这说说思想吧 红
  • html5 图片 遮罩层,6种炫酷的鼠标滑过图片显示遮罩层特效

    这是一款使用jQuery和CSS3制作的炫酷的鼠标滑过图片显示遮罩层特效 该图片制作层特效共6种不同的效果 使用一些简单的jQuery代码和CSS3过渡效果来完成 简单实用 可以为网站图片添加非常不错的效果 制作方法 HTML结构 该图片遮
  • 亿图脑图MindMaster(Pro)

    下载地址 https www edrawsoft cn download 微信扫码登录 无限结点
  • QT交叉编译arm

    QT环境以及交叉编译环境的搭建 提示 这个操作比较常规 我就说一下自己遇到的一些问题然后一些注意事项 文章目录 QT环境以及交叉编译环境的搭建 前言 一 QT使用方面 先得知道QT是怎么回事 QT是什么和我认为的优势 干货来了 qmake
  • 虚拟地址 到底如何映射到 物理地址 的?

    一 背景 1 讲故事 我发现有很多的 NET程序员 写了很多年的代码都没弄清楚什么是 虚拟地址 更不用谈什么是 物理地址 以及Windows是如何实现地址映射的了 这一篇我们就来聊一聊这两者之间的联系 二 地址映射研究 1 找虚拟地址 怎么
  • html调用内网海康威视摄像头

    html调用内网海康威视摄像头 我的需求很简单就是在html的主页上用iframe加载出摄像头 海康威视的摄像头无法直接调用 必须安装海康提供的web插件包 使用插件的demo是可以调用的 但是单独搞出来又无法使用 所以直接在原来的demo
  • opencv ffmpeg推流

    基于opencv采集推流 1 opencv采集rtsp解码 可以基于usb 摄像机 调用系统驱动 和rtsp 调用ffmpeg 接口 转yuv加解码 摄像机 2 ffmpeg缩放转换像素格式 3 ffmpeg编码H264 4 ffmpeg推
  • 使用JS实现对页面的繁体简体翻译转换

    使用JS实现对页面的繁体简体翻译转换 效果图 一 HTML代码 二 Js代码 总结 效果图 废话少说直接上代码 一 HTML代码
  • wsl无法连接到win上的docker

    https docs docker com desktop windows wsl
  • QT 写一个属于自己的消息弹窗MessageBox

    前言 在接触公司的一个桌面应用项目后 发现里面很多窗体都是自己写的而不是使用QT自带的 例如消息弹窗 今天这篇博客就记录下来如何自己写一个消息弹窗 内容可能有点多 但都是本人自己一步一步操作后 测试可行后才记录下博客这里来的 希望对看到这篇
  • kaldi中SHELL调用C++程序过程源码分析

    引入 kaldi真正的核心源码 都是C 写成的 这个结论可以从如下两点得以确认 1 在kaldi的源码kaldi src目录下 能看到很多扩展名为 cc的源程序 这是linux下C 源码 2 在源码中 比如kaldi src featbin
  • 和导师的第二次探讨

    Jason提问 导师 最近读文献的方面我碰到两个问题 一 就是感觉读的太杂了 人工智能方向太大 文章五花八门 二 内容刚接触感觉晦涩难懂 特别是英文文献 而且用翻译软件意思有时候翻译成中文就感觉也不对 我应该如何解决呢 导师回答 对于问题一
  • sourceInsight官网介绍及插入定制语言支持

    sourceInsight官网介绍及插入定制语言支持 版本说明 版本 作者 日期 备注 0 1 ZY 2019 6 4 初稿 目录 文章目录 sourceInsight官网介绍及插入定制语言支持 版本说明 目录 一 sourceinsigh
  • 已经有了ERP,为什么还要上MES?

    当前 制造企业面临着巨大的竞争和成本压力 利润越来越少 交货时间要求越来越短 人力成本越来越高 产品越来越复杂 大多数企业已经在使用ERP系统了 他们会想 我已经上了ERP了 为什么还需要MES系统 许多工厂车间只有很有限的IT系统 比如自
  • Vector简介说明

    转自 Vector简介说明 下文笔者讲述Vector简介说明 如下所示 Vector简介 Vector集合和ArrayList集合功能相似 底层都是通过数组来实现集合的 Vector和ArrayList最大的区别是Vector的很多方法都是
  • 开发Android硬件访问服务

    在http blog csdn net getnextwindow article details 47731597中 为Android系统添加了HAL模块 开发好一个硬件抽象层以后 我们通常还需要在应用程序框架中实现一个硬件访问服务 硬件
  • 数据结构实验--带环、相交链表问题

    一 问题描述 基于课程上机关于单链表的作业 要求进一步实现以下需求 1 构造链表后 将元素值为 m 和 n 从键盘输入 如有多个相同元素值 仅考虑首个出现的元素 的节点建立连接 注意判断节点出现的先后关系 将后面出现的节点 假设为 n 的链

随机推荐

  • 【数据结构】栈和队列

    栈和队列 一 栈 1 栈的简单介绍 2 栈的相关函数接口实现 1 初始化 2 销毁 3 压栈 4 弹栈 5 判空 6 取栈顶元素 7 栈的大小 二 队列 1 队列的简单介绍 2 队列的相关函数接口实现 1 初始化 2 销毁 3 插入 4 删
  • 台湾海峡隧道工程线路初定3个方案(图)

    台湾海峡隧道工程线路初定3个方案 图 http www sina com cn 2007年04月22日01 50 新京报 3方案示意图 本报讯 昨日 第一届海峡两岸通道 桥隧 工程学术研讨会新闻发布会在福州举行 与会专家称 台湾海峡隧道工程
  • PLC驱动伺服电机、步进电机共阳极接法-20230701

    由于工作需要 需要测试一台小型伺服电机是否好坏 记录一下接线方法 设备如下 信捷XDM 60T10 C PLC 安诺机器人 57AIM30一体化伺服电机 官网找了下技术手册 可以看到这是一款24V供电的小型伺服驱动器 供电可以用手头的这款P
  • 高通平台(8917/8937/8953...) secure boot 软件配置

    以下以8917平台为例 其他平台类似 找到对应配置文件即可 1 新建临时目录 mkdir tmp cd tmp 2 复制openssl文件到临时目录 cp LA UM LINUX android vendor qcom proprietar
  • Pytorch(GPU)详细安装教程

    如果你也是为了安装Pytorch的话 然后在安装CUDA时出现上述错误时 那么就有必要往下看了 我电脑小白 自己摸索安装 一点一点搜索 然后在我不断努力下终于安装成功 最近也是在学习深度学习 把自己安装软件过程中遇到的问题很大家分享一下 在
  • Apollo CyberRT编译问题‘Socket closed‘

    Apollo 编译问题 Socket closed apollo CyberRT编译错误 错误原因 解决方法 apollo CyberRT编译错误 Server terminated abruptly error code 14 error
  • Oracle函数 获得一个UUID

    通过Oracle函数返回一个UUID create or replace function F GET UUID p length in INT return varchar2 is Result varchar2 200 说明 返回一个指
  • GitHub入门教程

    一 注册GitHub账号 GitHub官网https github com 注册之后 登录注册的邮箱验证后注册完成 二 下载Git 有Mac Windows Linux版本的 下载地址 https git scm com downloads
  • js 提示crypto is not defined

    在使用python 调用js的时候 Crypto enc UTF8 parse 引用这个函数的时候提示这个模块没有 没定义 随即 npm install crypto 但是又报错 cryptois not found 换了一些源也没用 最后
  • r语言聚类分析_R语言ggtree画圆形的树状图展示聚类分析的结果

    今天的主要内容是实现下面这幅图 做完聚类分析通常可以选择 树形图来展示聚类分析的结果 之前公众号也分享过一篇文章 如果样本数不是很多 可以选择矩形的树状图 但是样本数如果比较多 比如今天一位公众号的读者留言说他有160多个样本 这样 矩形的
  • 微信小程序------联动选择器

    2019独角兽企业重金招聘Python工程师标准 gt gt gt picker 从底部弹起的滚动选择器 现支持五种选择器 通过mode来区分 分别是普通选择器 多列选择器 时间选择器 日期选择器 省市区选择器 默认是普通选择器 先来看看效
  • VM12+CentOS6.5+hadoop2.7.3 搭建hadoop完全分布式集群

    参考 http blog csdn net gamer gyt article details 51991893 一 安装VM 12 x 下载地址 链接 http pan baidu com s 1c2KA3gW密码 3r67 二 安装Ce
  • 移动端 - 搜索组件(suggest篇)

    这一篇博客是和 search input篇 衔接的 需要的可以看上文 移动端 搜索组件 search list篇 这里我们需要去封装这么一个组件 先说一下大致的方向 1 根据父组件传入的关键字数据发送请求获取后端数据 进行模板渲染 2 处理
  • Kettle中调用用户自定义的jar包

    在使用kettle过程中 有些功能是kettle不提供的 这样就需要想办法 不过kettle中的java代码可以解决大部分问题 下边就展示使用java代码组件调用自己编写的jar包的过程 1 创建java jar包 package test
  • GL分数阶微积分

    目录 预备公式 将积分和导数统一 p lt 0表示积分 p gt 0表示导数 整数阶和分数阶混合运算 分数阶和分数阶混合运算 预备公式 z
  • javascript的window.location方法

    Response Write 或 Response Write 退后操作 Response Write
  • 备案域名绑定服务器后 提示需要备案_小程序开发需要多少钱?

    现在越来越多的企业想通过微信小程序来宣传产品 为什么小程序那么火爆呢 奥晶科技为您解答 其优点不言而喻 1 小程序建设的成本比APP建设成本低 2 小程序能紧跟市场发展潮流 随时更新功能 3 小程序用户广泛 依托微信的亿万用户 接入方便 用
  • Linux下gdb调试工具的使用

    gdb是GNU开源组织发布的一个强大的Linux下的程序调试工具 gdb主要完成四个方面的功能 1 启动你的程序 可以按照你的自定义的要求随心所欲的运行程序 2 可让被调试的程序在你所指定的调试的断点处停住 断点可以是条件表达式 3 当程序
  • 在ubantu下cmake与make命令的简单使用

    ubantu下简单的使用cmake与make的使用 Step 1 新建一个可执行程序 首先确保你已经安装了cmake 和 g 如果没有安装 就 sudo apt get install cmake g 然后准备一个工作空间 并准备一些素材
  • 当大模型不是问题时,如何应对 LLM 的工程化落地挑战?

    几个月前 在 Thoughtworks 的内部 AIGC 研讨会里 我们一直达成了一系列一致观点 诸如于 如果没有 开源模型 降低企业应用 LLM 的成本 那么 LLM 会很快消亡 所以 我们相信开源 LLM LoRA 微调会成为企业的一种