跳到主要内容

Ollama

什么是 Ollama?

Ollama 是一个先进的 AI 工具,允许用户轻松地在本地设置和运行大型语言模型(支持 CPU 和 GPU 模式)。使用 Ollama,用户可以利用强大的语言模型,如 Llama 2,甚至可以自定义和创建自己的模型。Ollama 将模型权重、配置和数据打包成一个单一的包,由 Modelfile 定义。它优化了设置和配置细节,包括 GPU 使用。

有关 Ollama 的更多详情,请查看:

演讲

观看在 Docker Con 23 的演讲:

观看 Code to the Moon 的介绍:

入门

要开始使用,请将以下依赖项添加到项目的 pom.xml 中:

<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<version>1.0.0-beta3</version>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>ollama</artifactId>
<version>1.19.1</version>
</dependency>

当 Ollama 在 testcontainers 中运行时,尝试一个简单的聊天示例代码:

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.Image;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.Container;
import org.testcontainers.ollama.OllamaContainer;
import org.testcontainers.utility.DockerImageName;

import java.io.IOException;
import java.util.List;

public class OllamaChatExample {

private static final Logger log = LoggerFactory.getLogger(OllamaChatExample.class);

static final String OLLAMA_IMAGE = "ollama/ollama:latest";
static final String TINY_DOLPHIN_MODEL = "tinydolphin";
static final String DOCKER_IMAGE_NAME = "tc-ollama/ollama:latest-tinydolphin";

public static void main(String[] args) {
// 创建并启动 Ollama 容器
DockerImageName dockerImageName = DockerImageName.parse(OLLAMA_IMAGE);
DockerClient dockerClient = DockerClientFactory.instance().client();
List<Image> images = dockerClient.listImagesCmd().withReferenceFilter(DOCKER_IMAGE_NAME).exec();
OllamaContainer ollama;
if (images.isEmpty()) {
ollama = new OllamaContainer(dockerImageName);
} else {
ollama = new OllamaContainer(DockerImageName.parse(DOCKER_IMAGE_NAME).asCompatibleSubstituteFor(OLLAMA_IMAGE));
}
ollama.start();

// 拉取模型并基于所选模型创建镜像
try {
log.info("开始拉取 '{}' 模型... 可能需要几分钟...", TINY_DOLPHIN_MODEL);
Container.ExecResult r = ollama.execInContainer("ollama", "pull", TINY_DOLPHIN_MODEL);
log.info("模型拉取完成!{}", r);
} catch (IOException | InterruptedException e) {
throw new RuntimeException("拉取模型时出错", e);
}
ollama.commitToImage(DOCKER_IMAGE_NAME);

// 构建 ChatLanguageModel
ChatLanguageModel model = OllamaChatModel.builder()
.baseUrl(ollama.getEndpoint())
.temperature(0.0)
.logRequests(true)
.logResponses(true)
.modelName(TINY_DOLPHIN_MODEL)
.build();

// 示例用法
String answer = model.chat("提供3个简短的要点解释为什么Java很棒");
System.out.println(answer);

// 停止 Ollama 容器
ollama.stop();
}
}

如果您的 Ollama 在本地运行,您也可以尝试以下聊天示例代码:

class OllamaChatLocalModelTest {
static String MODEL_NAME = "llama3.2"; // 尝试其他本地 ollama 模型名称
static String BASE_URL = "http://localhost:11434"; // 本地 ollama 基础 URL

public static void main(String[] args) {
ChatLanguageModel model = OllamaChatModel.builder()
.baseUrl(BASE_URL)
.modelName(MODEL_NAME)
.build();
String answer = model.chat("列出中国前10大城市");
System.out.println(answer);

model = OllamaChatModel.builder()
.baseUrl(BASE_URL)
.modelName(MODEL_NAME)
.responseFormat(JSON)
.build();

String json = model.chat("列出美国前10大城市");
System.out.println(json);
}
}

当 Ollama 在 testcontainers 中运行时,尝试一个简单的流式聊天示例代码:

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.Image;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.ollama.OllamaStreamingChatModel;
import dev.langchain4j.model.output.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.Container;
import org.testcontainers.ollama.OllamaContainer;
import org.testcontainers.utility.DockerImageName;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.CompletableFuture;

public class OllamaStreamingChatExample {

private static final Logger log = LoggerFactory.getLogger(OllamaStreamingChatExample.class);

static final String OLLAMA_IMAGE = "ollama/ollama:latest";
static final String TINY_DOLPHIN_MODEL = "tinydolphin";
static final String DOCKER_IMAGE_NAME = "tc-ollama/ollama:latest-tinydolphin";

public static void main(String[] args) {
DockerImageName dockerImageName = DockerImageName.parse(OLLAMA_IMAGE);
DockerClient dockerClient = DockerClientFactory.instance().client();
List<Image> images = dockerClient.listImagesCmd().withReferenceFilter(DOCKER_IMAGE_NAME).exec();
OllamaContainer ollama;
if (images.isEmpty()) {
ollama = new OllamaContainer(dockerImageName);
} else {
ollama = new OllamaContainer(DockerImageName.parse(DOCKER_IMAGE_NAME).asCompatibleSubstituteFor(OLLAMA_IMAGE));
}
ollama.start();
try {
log.info("开始拉取 '{}' 模型... 可能需要几分钟...", TINY_DOLPHIN_MODEL);
Container.ExecResult r = ollama.execInContainer("ollama", "pull", TINY_DOLPHIN_MODEL);
log.info("模型拉取完成!{}", r);
} catch (IOException | InterruptedException e) {
throw new RuntimeException("拉取模型时出错", e);
}
ollama.commitToImage(DOCKER_IMAGE_NAME);

StreamingChatLanguageModel model = OllamaStreamingChatModel.builder()
.baseUrl(ollama.getEndpoint())
.temperature(0.0)
.logRequests(true)
.logResponses(true)
.modelName(TINY_DOLPHIN_MODEL)
.build();

String userMessage = "写一首关于Java和AI的100字诗";

CompletableFuture<ChatResponse> futureResponse = new CompletableFuture<>();
model.chat(userMessage, new StreamingChatResponseHandler() {

@Override
public void onPartialResponse(String partialResponse) {
System.out.print(partialResponse);
}

@Override
public void onCompleteResponse(ChatResponse completeResponse) {
futureResponse.complete(completeResponse);
}

@Override
public void onError(Throwable error) {
futureResponse.completeExceptionally(error);
}
});

futureResponse.join();
ollama.stop();
}
}

如果您的 Ollama 在本地运行,您也可以尝试以下流式聊天示例代码:

class OllamaStreamingChatLocalModelTest {
static String MODEL_NAME = "llama3.2"; // 尝试其他本地 ollama 模型名称
static String BASE_URL = "http://localhost:11434"; // 本地 ollama 基础 URL

public static void main(String[] args) {
StreamingChatLanguageModel model = OllamaStreamingChatModel.builder()
.baseUrl(BASE_URL)
.modelName(MODEL_NAME)
.temperature(0.0)
.build();
String userMessage = "写一首关于Java和AI的100字诗";

CompletableFuture<ChatResponse> futureResponse = new CompletableFuture<>();
model.chat(userMessage, new StreamingChatResponseHandler() {

@Override
public void onPartialResponse(String partialResponse) {
System.out.print(partialResponse);
}

@Override
public void onCompleteResponse(ChatResponse completeResponse) {
futureResponse.complete(completeResponse);
}

@Override
public void onError(Throwable error) {
futureResponse.completeExceptionally(error);
}
});

futureResponse.join();
}
}

参数

OllamaChatModelOllamaStreamingChatModel 类可以使用构建器模式实例化,具有以下参数:

参数描述类型示例
baseUrlOllama 服务器的基础 URL。Stringhttp://localhost:11434
modelName从 Ollama 服务器使用的模型名称。String
temperature控制生成响应的随机性。较高的值(例如 1.0)会产生更多样化的输出,而较低的值(例如 0.2)会产生更确定性的响应。Double
topK指定在生成过程中每一步考虑的最高概率标记的数量。Integer
topP通过设置顶部标记累积概率的阈值来控制生成响应的多样性。Double
repeatPenalty对模型在生成输出中重复类似标记进行惩罚。Double
seed设置随机种子以使生成的响应可重现。Integer
numPredict为每个输入提示生成的预测数量。Integer
stop如果生成,将标记响应结束的字符串列表。List<String>
format生成输出的所需格式。(已弃用,请参见 responseFormatString
responseFormat生成输出的所需格式。TEXT 或 JSON,可选带有 JSON Schema 定义ResponseFormat
supportedCapabilitiesAiServices API 使用的模型能力集(仅支持 OllamaChatModelCapabilityRESPONSE_FORMAT_JSON_SCHEMA
timeoutAPI 调用完成的最大允许时间。DurationPT60S
maxRetriesAPI 调用失败时的最大重试次数。Integer

使用示例

OllamaChatModel ollamaChatModel = OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama3.1")
.temperature(0.8)
.timeout(Duration.ofSeconds(60))
.build();

使用 Spring Boot 的示例

langchain4j.ollama.chat-model.base-url=http://localhost:11434
langchain4j.ollama.chat-model.model-name=llama3.1
langchain4j.ollama.chat-model.temperature=0.8
langchain4j.ollama.chat-model.timeout=PT60S

JSON 模式

使用构建器的 JSON 模式

OllamaChatModel ollamaChatModel = OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama3.1")
.responseFormat(ResponseFormat.JSON)
.temperature(0.8)
.timeout(Duration.ofSeconds(60))
.build();

使用构建器的 JSON 模式 已弃用

OllamaChatModel ollamaChatModel = OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama3.1")
.format("json")
.temperature(0.8)
.timeout(Duration.ofSeconds(60))
.build();

结构化输出

使用构建器的 JSON schema 定义

OllamaChatModel ollamaChatModel = OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama3.1")
.responseFormat(ResponseFormat.builder()
.type(ResponseFormatType.JSON)
.jsonSchema(JsonSchema.builder().rootElement(JsonObjectSchema.builder()
.addProperty("name", JsonStringSchema.builder().build())
.addProperty("capital", JsonStringSchema.builder().build())
.addProperty(
"languages",
JsonArraySchema.builder()
.items(JsonStringSchema.builder().build())
.build())
.required("name", "capital", "languages")
.build())
.build())
.build())
.temperature(0.8)
.timeout(Duration.ofSeconds(60))
.build();

使用 ChatRequest API 的 JSON Schema

OllamaChatModel ollamaChatModel = OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama3.1")
.build();

ChatResponse chatResponse = ollamaChatModel.chat(ChatRequest.builder()
.messages(userMessage("Tell me about Canada."))
.responseFormat(ResponseFormat.builder()
.type(ResponseFormatType.JSON)
.jsonSchema(JsonSchema.builder().rootElement(JsonObjectSchema.builder()
.addProperty("name", JsonStringSchema.builder().build())
.addProperty("capital", JsonStringSchema.builder().build())
.addProperty(
"languages",
JsonArraySchema.builder()
.items(JsonStringSchema.builder().build())
.build())
.required("name", "capital", "languages")
.build())
.build())
.build())
.build());

String jsonFormattedResponse = chatResponse.aiMessage().text();

/* jsonFormattedResponse 值:

{
"capital" : "Ottawa",
"languages" : [ "English", "French" ],
"name" : "Canada"
}

*/

带有 AiServices 的 Json Schema

OllamaChatModel 创建时具有支持的能力 RESPONSE_FORMAT_JSON_SCHEMA 时,AIService 将自动从接口返回值生成 schema。更多信息请参见 结构化输出

OllamaChatModel ollamaChatModel = OllamaChatModel.builder()
.baseUrl("...")
.modelName("...")
.supportedCapabilities(RESPONSE_FORMAT_JSON_SCHEMA)
.build();

自定义消息

OllamaChatModelOllamaStreamingChatModel 除了标准聊天消息类型外,还支持自定义聊天消息。 自定义消息可用于指定具有任意属性的消息。这对于某些模型如 Granite Guardian 非常有用, 这些模型使用非标准消息来评估用于检索增强生成(RAG)的检索上下文。

让我们看看如何使用 CustomMessage 来指定具有任意属性的消息:

OllamaChatModel ollamaChatModel = OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("granite3-guardian")
.build();

String retrievedContext = "条约制定的一个重要部分是,签署条约意味着承认对方是主权国家,并且所考虑的协议在国际法下是可执行的。因此,各国在将协议称为条约时可能非常谨慎。例如,在美国,州之间的协议是契约,而州与联邦政府之间或政府机构之间的协议是谅解备忘录。";

List<ChatMessage> messages = List.of(
SystemMessage.from("context_relevance"),
UserMessage.from("条约制定的历史是什么?"),
CustomMessage.from(Map.of(
"role", "context",
"content", retrievedContext
))
);

ChatResponse chatResponse = ollamaChatModel.chat(ChatRequest.builder().messages(messages).build());

System.out.println(chatResponse.aiMessage().text()); // "Yes"(意味着 Granite Guardian 检测到风险)