Spring AI 与 Docker Model Runner

发布 | Mark Pollack | 2025年4月10日 | ...

本文作者为 Eddú Meléndez

Docker 最近在 Apple silicon 上 发布了 Docker Desktop for Mac 4.40.0 版本中的 Model Runner。Docker Model Runner 提供了一个本地推理 API,设计上兼容 OpenAI API,使其能够轻松地与 Spring AI 集成,作为 Spring AI 1.0.0-M7 版本的一部分。模型作为标准的 OCI artifact 在 Docker Hub 的 ai 命名空间下分发。

先决条件

  • 下载 Docker Desktop for Mac 4.40.0。

  • 选择以下选项之一来启用 Model Runner

    选项 1

  • 启用 Model Runner `docker desktop enable model-runner --tcp 12434`。

  • 将 base-url 设置为 `https://:12434/engines\`

    选项 2

  • 启用 Model Runner `docker desktop enable model-runner`。

  • 使用 Testcontainers 并按如下方式设置 base-url

@Container
private static final SocatContainer socat = new SocatContainer().withTarget(80, "model-runner.docker.internal");

@Bean
public OpenAiApi chatCompletionApi() {
	var baseUrl = "http://%s:%d/engines".formatted(socat.getHost(), socat.getMappedPort(80));
	return OpenAiApi.builder().baseUrl(baseUrl).apiKey("test").build();
}

接下来,拉取模型 `docker model pull ai/gemma3` 并确认其在本地可用 `docker model list`

依赖项

访问 start.spring.io,选择 Spring Web、OpenAI 和 Testcontainers,然后生成项目。

必须列出以下依赖项

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.ai</groupId>
	<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.ai</groupId>
	<artifactId>spring-ai-spring-boot-testcontainers</artifactId>
	<scope>test</scope>
</dependency>

另外,请确保 Spring AI BOM 存在

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-bom</artifactId>
			<version>${spring-ai.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

配置 Spring AI

要使用 Docker Model Runner,我们需要配置 OpenAI 客户端指向正确的端点并使用之前拉取的模型

对于选项 1:让我们配置 src/main/resources/application.properties

spring.ai.openai.api-key=ignored
spring.ai.openai.base-url=https://:12434/engines
spring.ai.openai.chat.options.model=ai/gemma3

对于选项 2(使用 Testcontainers):转到 `TestcontainersConfiguration`,定义 SocatContainer bean 并使用 `DynamicPropertyRegistrar` bean 注册属性。

@TestConfiguration(proxyBeanMethods = false)
class TestcontainersConfiguration {

    @Bean
    SocatContainer socat() {
        return new SocatContainer(DockerImageName.parse("alpine/socat:1.8.0.1"))
                .withTarget(80, "model-runner.docker.internal");
    }
    
    @Bean
    DynamicPropertyRegistrar properties(SocatContainer socat) {
        return (registrar) -> {
            registrar.add("spring.ai.openai.base-url", () -> "http://%s:%d/engines".formatted(socat.getHost(), socat.getMappedPort(80)));
            registrar.add("spring.ai.openai.api-key", () -> "test-api-key");
            registrar.add("spring.ai.openai.chat.options.model", () -> "ai/gemma3");
        };
    }
}

聊天示例

现在,让我们创建一个简单的控制器

@RestController
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return this.chatClient.prompt()
                .user(message)
                .call()
                .content();
    }

   @GetMapping("/chat-stream")
    public Flux<String> chatStream(@RequestParam String message) {
        return this.chatClient.prompt()
                .user(message)
                .stream()
                .content();
    }

}

使用 `./mvnw spring-boot:test-run` 运行应用程序

使用 httpie,让我们调用 `/chat` 端点

http :8080/chat message=="tell me a joke"

我们也可以调用 `/chat-stream` 端点

http :8080/chat-stream message=="tell me a haiku about docker containers"

工具示例

如果与支持工具调用的模型一起使用,Docker Model Runner 当然也支持工具调用。

创建一个 `FunctionCallConfig` 类并添加一个简单的函数

@Configuration(proxyBeanMethods = false)
class FunctionCallConfig {

    @Bean
    @Description("Get the stock price")
    public Function<MockStockService.StockRequest, MockStockService.StockResponse> stockFunction() {
        return new MockStockService();
    }

    static class MockStockService implements Function<MockStockService.StockRequest, MockStockService.StockResponse> {

        public record StockRequest(String symbol) {}
        public record StockResponse(double price) {}

        @Override
        public StockResponse apply(StockRequest request) {
            double price = request.symbol().contains("AAPL") ? 198 : 114;
            return new StockResponse(price);
        }
    }
    
}

现在,让我们注册 `stockFunction` 函数

@GetMapping("/stocks")
public String stocks(@RequestParam String message) {
    return this.chatClient.prompt()
            .user(message)
            .tools("stockFunction")
            .call()
            .content();
}

运行应用程序 `./mvnw spring-boot:test-run` 并调用 `/stocks` 端点

http :8080/stocks message=="What's AAPL and NVDA stock price?"

响应应类似于 `AAPL stock price is 198.0 and NVDA stock price is 114.0.`,基于我们设置的硬编码值。

参考资料

结论

Docker Model Runner 使您能够更快地迭代、保持本地化并访问兼容 OpenAI 的 API。它通过与 Spring AI 的 OpenAI 模块无缝集成来简化开发体验,使开发人员能够使用他们熟悉的内部开发工具。这使团队能够按照自己的节奏构建和测试 AI 应用程序——本地、安全且高效。未来,与 Testcontainers 的集成将使按需拉取和运行模型更加容易,进一步简化设置和测试工作流。Docker Model Runner 使您能够更快地迭代、保持本地化并访问兼容 OpenAI 的 API。它通过与 Spring AI 的 OpenAI 模块无缝集成来简化开发体验,使开发人员能够使用他们熟悉的内部开发工具。这使团队能够按照自己的节奏构建和测试 AI 应用程序——本地、安全且高效。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有