You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
if gpuInfo.Library != "cpu" { for cmp := range availableDynLibs { if gpuInfo.Library == strings.Split(cmp, "_")[0] && cmp != exactMatch { altDynLibs = append(altDynLibs, cmp) } } slices.Sort(altDynLibs) for _, altDynLib := range altDynLibs { dynLibs = append(dynLibs, availableDynLibs[altDynLib]) } }
接下来,它会调用另一个实用程序 GetCPUVariant,尝试优先选择最快(可能)的 CPU 变体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
if gpuInfo.Library != "cpu" { variant := gpu.GetCPUVariant() if variant != "" { for cmp := range availableDynLibs { if cmp == "cpu_"+variant { dynLibs = append(dynLibs, availableDynLibs[cmp]) break } } } else { dynLibs = append(dynLibs, availableDynLibs["cpu"]) } }
该实用程序在 gpu/cpu_common.go 中定义。它能检测 x86 平台上的 CPU 扩展:
1 2 3 4 5 6 7 8 9 10 11 12 13
funcGetCPUVariant()string { if cpu.X86.HasAVX2 { slog.Info("CPU has AVX2") return"avx2" } if cpu.X86.HasAVX { slog.Info("CPU has AVX") return"avx" } slog.Info("CPU does not have vector extensions") return"" }
该顺序将把 avx2 作为最高优先级,然后是 avx,最后是纯 CPU 变体。最后,如果上述方法都不奏效,它将回退到 CPU 变体:
1 2 3 4 5 6 7 8 9 10 11
funcgetDynLibs(gpuInfo gpu.GpuInfo) []string {
iflen(dynLibs) == 0 { dynLibs = []string{availableDynLibs["cpu"]} } slog.Debug(fmt.Sprintf("ordered list of LLM libraries to try %v", dynLibs)) return dynLibs }
然后,dynLibs 将被返回以进行加载尝试。
现在我们可以探讨一下如何生成 “GPU 信息” gpuInfo,从而使偏好成为可能。llm/llm.go中的 New 函数以 “GPU 信息” 为第一个参数调用 newLlmServer。它完成了许多重要工作:
Ollama 架构解析 | Inoki in the world
https://ift.tt/nALxzhj
最近,我偶然探索了一个名为
ollama
的项目,因为我想让我的 AMD 显卡(拥有不俗的 VRAM - 32G!)在 Windows 上获得支持。Linux 上已经有了基于 AMD ROCm 的支持。由于 ROCm 在 Windows 上的发布,它在 Windows 上也应该是开箱即用的。但是,ollama
阻止我使用它。因此,我尝试了 ZLUDA 和修改ollama
的代码,以达到我的目的。这个功能已经在 ollama v0.1.29 中合并并发布了。为了避免遗漏细节和我学到的东西,本博客负责记录我自己的
ollama
架构。在我看来,
ollama
是llama.cpp的一个精简但足够智能的封装。**它对终端用户非常友好,提供了网络接口和 cli,以便运行多个大型语言模型 (LLM) 并与之交互。**事实上,在大多数情况下,是由llama.cpp
加载并运行模型,而ollama
只是llama.cpp
的"领航员"(是的,我用了熟悉生成式人工智能的人都熟悉的一个词)。稍后会对这部分内容进行讨论。这篇文章假定你能够阅读 golang 代码或其他类似 C 语言的代码。对于代码中的关键点,我会给出一些简短的描述或类比,以便帮助更好地理解。
在这篇文章中,我将首先介绍
ollama
的项目结构。然后,将介绍围绕llama.cpp
的核心架构和实现,以及构建系统。接下来,我将介绍ollama
如何选择运行 LLM 的设备(一般指硬件)。最后,将介绍 Web 服务、客户端和实用程序以及其他部分,作为本篇文章的结束。你可以在 GitHub 上获取 ollama 的源代码。该项目主要使用 Golang 编写,下表是每个目录的简要说明:
请注意,由于项目正在开发中,这些目录可能随时被更改。
让我们先来介绍一下在
ollama
中运行 LLM 的核心llama.cpp
。llama.cpp
作为 git 子模块包含在ollama
中。您可以在llm
目录中找到它。在同一目录下还有围绕它所需的文件,稍后我们将详细介绍它们。llama.cpp
项目本身是一个开源库,最开始是用于推断纯 C/C++ 的 Meta LLaMA 模型。它后来被扩展用于运行更多模型,比如 Mistral 和 Google Gemma(最近才支持)。它利用同一作者创建的另一个项目 ggml 的功能,可在不同平台上原生运行(与 Python 项目相比)。支持的后端
目前,
llama.cpp
通过ggml
支持的一些推理后端如下:llama.cpp
可运行 x86 上的 AVX、AVX2 和 AVX512,或 ARM 上的 NEON。ggml
可以在 CPU 或 CPU 集群上运行模型。ggml
使用了一个古老的开放标准OpenCL。hipBLAS
支持,它是AMD ROCm的一部分,与cuBLAS
的应用程序接口几乎相同。llama.cpp
中的 Vulkan 支持。这项(有些漏洞)支持最初是由 Nomic 通过其 kompute 框架启动的。最近的进展是在ggml
中直接使用 Vulkan 库的实现。这些后端允许开发人员运行可在从台式电脑到智能手机等多个平台上运行的 LLM。此外,
llama.cpp
还为 Linux(包括 Android Linux)、Windows、macOS 和其他各种操作系统(如 iOS,参见 whispher.cpp on iOS)甚至 WebAssembly(whispher.wasm)提供原生支持。因此,
ollama
在诞生之初就应支持各种平台和操作系统。接下来,让我们看看构建系统,了解
ollama
如何与llama.cpp
协作。C 或 C++ 项目通常使用
cmake
(尽管现在有了更多选择)来处理编译、链接等工作。llama.cpp
也是如此:它使用编译定义(或者说 flag)来利用不同的后端。例如LLAMA_AVX
、LLAMA_AVX2
、LLAMA_AVX512
用于支持 AVX;LLAMA_METAL
;LLAMA_CUBLAS
;LLAMA_HIPBLAS
用于 AMD ROCm 支持。不过,
ollama
本身是一个 go 项目,利用的是 go 提供的构建系统。这两个构建系统共存,以构建不同的部分:cmake
用ollama.cpp
中的一些文件构建llama.cpp
,以进行“领航”并提供接口;ollama
的应用程序和 cli。除了纯 go 代码,go 编译系统还需要
cgo
来编译一些 C 语言代码。在llm
目录(用于加载和提供接口的dyn_ext_server.c
文件)和gpu
目录(用于检测 GPU 的 C 或 Objective-C 实现gpu_info_cuda.c
、gpu_info_rocm.c
和gpu_info_darwin.m
)中有一些例子。通过利用 go generate,
ollama
中的 go 编译系统还可以运行调用cmake
的命令来构建llama.cpp
。这项工作位于llm/generate
目录中,例如在 Linux 上:llm/generate/generate_darwin.go
告诉 go generate 运行gen_linux.sh
脚本来构建llama.cpp
的部分。一些适用于不同平台的脚本
目前有
gen_common.sh
、gen_linux.sh
和gen_darwin.sh
,用于在类 Unix 操作系统(如 macOS 和 Linux)上为ollama
创建llama.cpp
。同时,在 Windows 上使用的是gen_windows.ps1
PowerShell 脚本。让我们以在 Linux 上构建支持 AVX 的
llama.cpp
为例:前三行初始化变量,为编译做准备。
init_vars
调用了gen_common.sh
中的一个子程序来准备常用变量,例如其中
CMAKE_TARGETS
将把构建目标设置为ext_server
。该目标是一个库,用于从llama.cpp
为ollama
提供接口和函数,我们将在下一节讨论它。在
CMAKE_DEFS
中,只有LLAMA_AVX
是启用的。而COMMON_CPU_DEFS
的定义如下,以构建独立于位置代码的动态库(对于 gcc,它将被转换为-fpic
标志):它在终端输出 “Building AVX CPU” 之后,由
build
子程序调用cmake
:通过
cmake
编译后,它将生成一个libext_server
动态链接库(Windows 下为.dll
,Linux/BSD 下为.so
,macOS 下为.dylib
)。该库包含llama.cpp
下examples/server
的编译代码(examples/server/libext_server.a
)、命令代码和llama.cpp
的核心代码——common/libcommoa.a
和libllama.a
。它们将作为可执行文件的"载荷"嵌入主 go 程序,以方便分发。最后,它会压缩载荷,使可执行文件更小:
动态链接库最终将位于构建文件夹中的 “cpu_avx” 目录下。如果为其他变体(如 GPU)构建,它们将位于构建文件夹中的不同目录下。
然后,让我们回到
llm
目录,看看ollama
中建立在llama.cpp
基础上的实现。对于ollama
来说,引导llama.cpp
的最重要部分是:ext_server
中,包装器实现提供了ollama
可以调用的函数,例如llama_server_init
来初始化一个llama.cpp
实例,llama_server_completion
来完成一次聊天,或者llama_server_embedding
来计算文本的嵌入。ext_server
中还包含一个额外的 makefile (CMakeLists
),用于将llama.cpp/examples/server
示例作为库来构建代码。然后,它可以被llm
下的dyn_ext_server
代码加载,与llama.cpp
实例一起提供服务。ext_server
中的函数时会携带llm
目录中定义的一些参数。一般来说,请求和响应都以 JSON 格式传递,并包含更多结构信息。它们定义在ggml.go
(描述模型)和llama.go
(描述不同的请求和响应)中。llama.cpp
实例,ollama
为原始的llama.cpp
提供了一些补丁。让我们逐一研究它们。
1. 外部服务器
我们首先来看看
ext_server
。我们已经知道,动态库是在生成过程中构建的。但如何使用它们呢?在
llm/dyn_ext_server.go
中,newDynExtServer
负责加载动态库、初始化llama.cpp
实例并启动事件循环以接收任何请求并生成响应。动态链接库的加载和服务器的启动
在
newDynExtServer
中,go 函数会调用一个以dyn_init
命名的 C 函数来加载动态库。描述和所需函数被加载到struct_dynamic_llama_server
描述中,并封装在dynExtServer
(一个 go 结构)中。然后,它们会被用于另一个 C 函数
dyn_llama_server_init
,其中包含运行llama.cpp
服务器的参数,用于服务器实例初始化。如果没有问题,
newDynExtServer
将调用初始化过程中的最后一个 C 函数dyn_llama_server_start
。服务器将开始运行,并能接收来自ollama
的请求。上述 C 函数位于
llm/dyn_ext_server.c
中,并在llm/dyn_ext_server.h
中声明。让我们快速了解一下dyn_init
:它接收库路径
libPath
作为参数,并通过 C 指针(对于不熟悉 C 的人来说就是内存地址,go 能够像 go 结构体一样处理它们,存储它们并传递给其他 C 函数)返回一个dynamic_llama_server
实例或一个错误。dynamic_llama_server
结构能够存储必要的 C 函数地址,以及加载的动态链接库的引用。其定义如下:dyn_init
的核心功能是加载由libPath
指示的动态链接库,读取符号表,找到所需的 C 函数地址,并将其存储到dynamic_llama_server
结构的实例中。libPath
可以是以libext_server
为前缀的已构建动态链接库的路径。这样,基于llama.cpp
的内置库就可以被ollama
使用。加载后,对
dyn_llama_server_start
和dyn_llama_server_start
的调用实际上是直接调用动态库中的 C 函数:调用
dyn_llama_server_start
后,从动态库创建的llama.cpp
服务器就可以进行预测了。预测
当
ollama
收到预测请求时,它会调用dynExtServer
实例上的Predict
。该函数能够格式化请求(稍后会看到),并调用 C 函数dyn_llama_server_completion
开始预测:正如你所看到的,它也是直接调用从构建在
llama.cpp
上的动态库中加载的函数。由于在
Predict
函数中使用了fn func(PredictResult)
参数,这部分的一个非常好的设计就是流式响应。这是一个回调函数,可以在收到响应后立即连续发送:它还依赖于对
dyn_llama_server_completion_next_result
的便捷调用(尽管它也是直接调用基于llama.cpp
的动态库中加载的 C 函数llama_server_completion_next_result
)。其他
其他调用也类似。您可以在
llm/dyn_ext_server.go
和llm/dyn_ext_server.c
中找到它们,例如dyn_llama_server_tokenize
,dyn_llama_server_detokenize
用于 token 化或去 token 化,以及dyn_llama_server_embedding
用于计算嵌入(embedding)。2.
llama.cpp
作为ollama
的服务器接下来让我们看一下 C 部分:
ollama
说如何使用llama.cpp
作为 LLM 服务器的。在
llm/dyn_ext_server.go
的开头,cgo 的注释中有一些构建注释:它们可以为不同的平台设置不同的编译和链接标志(
darwin
用于 macOS,当然linux
用于 Linux,而windows
用于 Windows)。这样,cgo 就能找到 C 头文件(现有类型和函数的声明),将llm/dyn_ext_server.c
与 go 部分编译和链接。然后,让我们从动态库中查看
ollama
中使用的 C 函数。作为两个例子,我们从llama_server_init
和llama_server_start
开始。它们的实现位于
llm/ext_server/ext_server.cpp
,在llm/ext_server/CMakeLists.txt
中被设置为以ext_server
命名的目标库。在构建目标时,该文件将与llama.cpp
示例服务器一起编译。编译结果就是我们提到的动态链接库之一。因此,
ext_server.cpp
中的 C 函数可以从ollama
中调用,并能利用llama.cpp
中的函数。它实际上是两个项目之间的桥梁,使llama.cpp
中的示例服务器成为ollama
的 LLM 服务器(或称 llama 服务器)。在初始化过程中,
llama_server_init
会解析参数,为服务器创建上下文,并调用llama.cpp
提供的函数:例如,它会调用
llama_backend_init
来初始化后端(可以是 AVX、CUDA 等),调用llama_numa_init
来初始化 NUMA(如果存在)。然后,它会调用服务器上下文中的load_model
函数,使用给定参数加载模型,并使用initialize
函数完成初始化。如果出现错误,错误信息将被格式化为
err
参数返回,并在 go 部分进行处理。同时,在
llama_server_start
中:它为任务处理设置一些回调,并在一个新线程中启动一个事件循环。事件循环负责预测。这样,对
llama_server_start
的调用就会立即返回。此类 C 函数的更详细实现可以在同一文件中找到,即
llm/ext_server/ext_server.cpp
。3. 将库作为载荷嵌入
然后,让我们来探究一下载荷是如何完成的。
在以
payload_*
为前缀的 go 文件中,我们可以看到ollama
的选择。例如,在llm/payload_linux.go
中,有两行嵌入了每个ext_server
库的不同变体:llama.cpp/build/linux/*/*/lib/
下的所有内置库都使用类文件系统接口作为载荷嵌入。这样,ollama
就可以像在文件系统中读写一样访问它们。在初始化
ollama
的过程中,llm/payload_common.go
中的Init
将调用nativeInit
:它的主要工作是将动态库从文件系统提取到临时位置,并检查驱动程序的访问权限(如适用):
提取完成后,
ollama
可以格式化库路径(外部服务器小节中的dyn_init
函数中使用的libPath
)。选择运行环境和匹配库的方法将在决定运行位置 小节中介绍。4. 格式化请求和响应
我们再来看看 C 语言函数中使用的函数参数。
在它们的函数签名中,我们可以看到它们使用的函数参数: 在
dyn_llama_server_init
中使用了ext_server_params_t
参数,在dyn_llama_server_completion
中使用了json_req
字节数组。ext_server_params_t
参数是一个 C 结构,包含启动 llama 服务器的配置,稍后将在llm/ext_server/server.cpp
中解释(由于篇幅有限,我们不展开这部分内容)。同时,完成调用的
json_req
在llm/ext_server/ext_server.cpp
中使用如下:事实上,它包含 json 格式的完成请求,包括提示词、温度等。我们可以看到
llama_server_completion
为其创建了一个任务,并通过正常路径中的resp
返回任务 ID。否则,它将格式化错误信息并返回。如果您对其详细格式感兴趣,请查看
llm/dyn_ext_server.go
文件。5. 补丁
为了适应在
ollama
中使用多个 llama 服务器,它还对原始版本的llama.cpp
做了一些额外的修改。例如,以下补丁导出了
ggml_free_cublas
并调用它来释放一个 llama 服务器实例:做个小总结
通过对
llama.cpp
的所有额外模块和修改,ollama
能够根据需要启动 llama 服务器,通过不同编译动态库中对不同硬件的支持动态选择硬件(参见 构建系统)。运行 llama 服务器后,ollama
提供的额外模块允许发送完成请求,并在稍后检索回复。现在,我们应该清楚地了解了后面的
ollama
架构(我们也可以称其为后端)。关于后端的更多细节,读者可以查看源代码,因为它们上会经常更改。毕竟,ollama
正在积极开发中。但是,此时还有一些谜团:
ollama
如何知道选择哪种硬件和动态库?下面的章节可能就是这些问题的答案。
让我们回到动态库和
dyn_init
中的libPath
参数,在 动态链接库的加载和服务器的启动 中提到过。我们在 Embed libraries as payloads中已经知道,ollama
会将嵌入的动态库提取到一个临时目录,并通过格式化和传递libPath
到dyn_init
来加载它们。问题是:
ollama
如何通过传递不同的libPath
参数来选择库?在
llm/dyn_ext_server.go
中实现的newDynExtServer
函数中,libPath
作为第一个参数library
被传递。在 Windows 环境下,它通过调用gpu.UpdatePath(filepath.Dir(library))
进行更新,以便在PATH
中添加父目录。这样就可以无缝加载动态链接库。不过,在 Linux 或 macOS 上不必这样做。因此,我们可以知道这里的
libPath
已经是动态链接库文件的完整路径。然后,让我们检查生成libPath
的位置。通过简单搜索,我们可以在
llm/llm.go
下的newLlmServer
函数中找到答案:它会遍历
dynLibs
以调用newDynExtServer
函数。一旦加载成功,它就会返回 llama 服务器实例。在
newLlmServer
开始的地方,dynLibs
一般在getDynLibs
函数中检索,这是一个要尝试的动态链接库的有序列表:顺序是一种偏好,它从
gpuInfo gpu.GpuInfo
中获取 GPU 信息。它并不强制是 “GPU 信息”,它也可以指示使用某个 CPU 变体。我想ollama
团队可能很快就会修改它。一般来说,返回的
dynLibs
来自llm/payload_common.go
中的键值映射availableDynLibs
。它是在提取所有动态库之后在nativeInit
中生成的:它的关键字是全路径中除库文件名之外的最后一个组成部分。例如,在我的电脑上是
cpu
、cpu_avx
、cpu_avx2
、cuda_v11.3
和rocm_v5.7
。而对应值当然是完整路径。我们可以先看看
getDynLibs
函数(在llm/payload_common.go
中实现)的一般处理过程,忽略一些特定平台的情况。第一步是从 “GPU 信息” 中找到与请求完全匹配的内容:
它会根据 “GPU 信息” 中的
Library
字段生成一个requested
字符串变量,并附加一个变体(Variant)
。如果有一个与requested
字符串完全匹配的库,dynLibs
中的第一个库路径将是所请求库的路径。第一个库路径也将是加载过程中首先尝试的路径。然后,它会尝试不完全匹配的 GPU 库(可能存在版本不匹配等情况):
接下来,它会调用另一个实用程序
GetCPUVariant
,尝试优先选择最快(可能)的 CPU 变体:该实用程序在
gpu/cpu_common.go
中定义。它能检测 x86 平台上的 CPU 扩展:该顺序将把
avx2
作为最高优先级,然后是avx
,最后是纯 CPU 变体。最后,如果上述方法都不奏效,它将回退到 CPU 变体:然后,
dynLibs
将被返回以进行加载尝试。现在我们可以探讨一下如何生成 “GPU 信息”
gpuInfo
,从而使偏好成为可能。llm/llm.go
中的New
函数以 “GPU 信息” 为第一个参数调用newLlmServer
。它完成了许多重要工作:info := gpu.GetGPUInfo()
。初始检测在 2 中进行。不过,也有可能模型被标记为与模型不兼容。在这种情况下,它将回退到具有最快变体的 CPU:
让我们重点关注 2,看看在
GetGPUInfo
函数中发生了什么。让我们从最特殊的平台开始。苹果 macOS 平台,包括 XNU 内核和用户空间,通常被称为 “Darwin”。
在前面提到的
getDynLibs
中,Darwin 平台上的检测非常简单:It uses
default
library according to the “GPU information”, or just usemetal
. Thegpu.GetGPUInfo()
is ingpu/gpu_darwin.go
, as simple as possible:它会根据 “GPU 信息” 使用
default
库,或者直接使用metal
。gpu.GetGPUInfo()
在gpu/gpu_darwin.go
中,非常简单:我们可以看到,它获取内存信息,并检测
ollama
是否运行在英特尔 x86_64/amd64 平台上。如果是,它就会使用扩展速度最快的 CPU。否则,只有 ARM Mac 才能利用 Metal API 加速。据我所知,英特尔 Mac 上的 AMD 显卡应该也支持 Metal。但
ollama
不会在英特尔 Mac 上使用它。可能只是因为驱动程序或显卡本身过时了。Nvidia CUDA 和 AMD ROCm
然后,我们看一下 Nvidia 和 AMD GPU 的通用检测,因为它们在
ollama
中是耦合在一起的。实现方法在
gpu/gpu.go
中:第一个程序块调用
initGPUHandles
来定义要搜索的 GPU 库,以便使用它们获取 GPU 信息。对于 Nvidia,它会检测 Windows 上独立显卡的nvml.dll
,Linux 上的libnvidia-ml.so
,以及某些特殊设备上的libcudart.so*
,例如 Jetson 系列(感谢 最近的 PR)。第二个程序块检测 CPU 变体,它要求 CPU 至少有
AVX
变体才能支持 GPU。然后,它会检查句柄,并使用相关库查找相应的 GPU。
对于 Nvidia 独立 GPU:
它调用在
gpu/gpu_info_nvml.c
中实现的 C 函数nvml_check_vram
,以获取 VRAM。如果发现一个可用设备,它还会通过nvml_compute_capability
检查计算能力,以确保该设备可用。这样的设计使我无法在 Windows 下使用 ZLUDA 通过
ollama
在 AMD 显卡上运行 LLM。因为当时 ZLUDA 将此功能标记为未实现。然而,我的 AMD 显卡已经支持该功能。现在我不再需要 ZLUDA 了。在本篇文章中,我选择跳过
Cudart
支持,因为它并不常见。现在让我们来看看最近令人兴奋的 AMD 支持!针对 AMD 的
GetGPUInfo
代码非常简短:你可能会注意到,这是一个
else
。因此,与if
子句一起,只有在未检测到 Nvidia 处理器的情况下,才会尝试 AMD。这将导致一个问题:当安装了 Nvidia GPU 库,但未检测到 GPU 或检测到的 GPU 不兼容时,AMD 显卡也永远不会被检测到。我为此开设了一个问题。好了,让我们回到
GetGPUInfo
。如果检测到 Nvidia 显卡,“GPU 信息” 中的Library
将设为cuda
。如果是 AMD 显卡,则会设置为rocm
。因此,如果检测成功,“GPU 信息” 将与
availableDynLibs
配合,为cuda_*
或rocm_*
变体优先选择库路径。这就揭示了 GPU 是如何被检测到的,以及从一堆动态库中创建 llama 服务器时可能使用的 GPU。
让我们来看看 “前端”!在
ollama
中确实没有所谓的前端。相反,它和其他大多数 LLM 服务一样,提供了一系列 Web API。基本的 Web API 在
server
中实现,主要在server/routes.go
模块中。完整的 API 可在 GitHub 上找到。在此,我们也仅以 chat 的 completion 端点为例,快速从 API 建立起到我们在上面解析过的部分的概览。这个端点定义如下:其中
ChatHandler
是处理请求的回调。它以var req api.ChatRequest
结构创建并解析请求。处理程序会做很多事情,比如加载模型,以确保预测是可能的。一切准备就绪后,最重要的事情就来了:
它用提示(用户输入、提示等)、图像和其他选项准备预测请求。然后,它调用 runner 的
Prediction
函数,其中 runner 需要实现llm
模块下的LLM
接口:LLM
接口的定义如下:Predict
的实现来自 预测一节中描述的dynExtServer
。然后,它将调用dyn_llama_server_completion
从动态库中请求启动 llama 服务器。Ollama 的 Go API
在项目内部,
ollama
在 Go 的api
下直接提供了一个封装。用户可以利用它更方便地调用网络 API。事实上,ollama
本身也使用 Go 封装提供实际的前端——终端用户界面。此外还有 Python 和 JavaScript/TypeScript 绑定:
OpenAI API 封装器
尽管有本地 API 端点,
ollama
还在server/routes.go
中提供了与 OpenAI API 兼容(部分兼容)的端点:它实际上是从 OpenAI 请求到
ollama
本机请求的转换器,反之亦然。 如果您感兴趣,可以查看openai/openai.go
。终端 UI 利用 Web API 端点的 Go 包装器来提供基于终端的对话。 它需要一些实用程序,例如
readline
来与终端中的用户输入进行交互,以及progress
来显示进度。此外,还有用于 API 端点认证的
auth
,用于cli命令提供者的cmd
,用于单位转换的format
,用于模型文件解析的parser
等。可以根据您的意愿详细查看源代码。这篇文章已经足够长了,并且只关注ollama
的整体架构。我也希望看到更多的有关它的其他文章 😉最后,在结束前,这里给出一个关于
ollama
架构的简单图:我仍要说:
ollama
是llama.cpp
的一个薄(也许不是那么薄)但足够智能的封装。尽管它仍然有一些缺点,但我们确实需要尽可能多的此类封装,以使最终用户的生活更轻松。
via Inoki in the world https://blog.inoki.cc
May 9, 2024 at 07:45PM
The text was updated successfully, but these errors were encountered: