很多 Java 项目第一次接入 LangChain4j 时,代码会写得很顺:定义一个接口,加上 @AiService,业务层像调用普通 Java Service 一样调用它。这个设计确实舒服,尤其适合 Spring Boot 项目。

但项目稍微往前走一步,问题就来了:客服助手想用一个在线大模型,敏感内容审核想走本地模型,报表摘要又想用另一个成本更低的模型。此时如果还依赖自动装配,很容易出现启动失败、模型选错、配置难以排查的问题。

这篇文章只讲一个具体问题:LangChain4j 在 Spring Boot 多模型场景下,AI Service 应该显式绑定模型,而不是依赖自动推断。

AI Service 像 Service,但它不是普通 Service

LangChain4j 的 AI Service 思路很像 Spring Data Repository:开发者定义接口,框架在背后生成代理对象,把方法调用转换成大模型请求。

例如:

public interface ReportAssistant {

    String summarize(String content);
}

配合 @AiService 后,业务代码里可以直接注入:

@Service
public class ReportService {

    private final ReportAssistant reportAssistant;

    public ReportService(ReportAssistant reportAssistant) {
        this.reportAssistant = reportAssistant;
    }

    public String buildSummary(String rawReport) {
        return reportAssistant.summarize(rawReport);
    }
}

这就是 LangChain4j 对 Java 开发者友好的地方:它没有要求你在每个业务方法里手动拼 Prompt、调用 HTTP、解析响应,而是把“AI 调用”包装成一个接口契约。

问题也出在这里。接口看起来像普通 Service,但它背后依赖的是 ChatModelStreamingChatModel、Chat Memory、Tools、Retrieval Augmentor 等组件。一旦 Spring 容器里有多个候选 Bean,框架就必须知道当前 AI Service 到底绑定哪一个。

单模型 Demo 里自动装配没问题;真实项目里,多模型、多工具、多检索器并存才是常态。

自动装配适合 Demo,显式布线适合项目

假设一个 Spring Boot 项目里同时接入在线模型和本地模型:

<properties>
    <langchain4j.version>1.16.2</langchain4j.version>
</properties>

<dependencies>
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
        <version>${langchain4j.version}</version>
    </dependency>

    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
        <version>${langchain4j.version}</version>
    </dependency>
</dependencies>

配置可以这样拆开:

langchain4j:
  open-ai:
    chat-model:
      api-key: ${OPENAI_API_KEY}
      model-name: ${OPENAI_MODEL}
  ollama:
    chat-model:
      base-url: http://localhost:11434
      model-name: ${LOCAL_MODEL}

如果只有一个 ChatModel@AiService 可以比较轻松地自动找到它。但当容器里存在多个模型 Bean 时,自动判断就不再可靠。更工程化的做法是使用显式布线:

import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;

import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;

@AiService(
    wiringMode = EXPLICIT,
    chatModel = "openAiChatModel"
)
public interface ReportAssistant {

    @UserMessage("""
        请把下面的业务报表压缩成 300 字以内的摘要。
        要保留关键数字、风险点和下一步建议。

        {{content}}
        """)
    String summarize(String content);
}

再定义一个本地模型场景:

import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;

import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;

@AiService(
    wiringMode = EXPLICIT,
    chatModel = "ollamaChatModel"
)
public interface AuditAssistant {

    @UserMessage("""
        判断下面这段文本是否包含敏感信息。
        只返回:PASS 或 REVIEW。

        {{content}}
        """)
    String check(String content);
}

这段代码的重点不是注解本身,而是把“哪个业务能力使用哪个模型”写成了清晰的工程约束。以后排查成本、延迟、效果问题时,不需要猜某个接口到底打到了哪个模型。

具体 Bean 名称可能会随 starter、版本和自定义配置变化,实际项目中应以官方文档和本地 Spring Bean 为准。

为什么这件事值得认真设计

多模型项目里,模型选择不是简单的技术偏好,而是业务策略。

场景 更适合的模型策略 关注点
报表摘要 在线强模型 质量、长文本能力
内容初筛 本地模型或低成本模型 成本、延迟、数据边界
客服回复 稳定在线模型 可用性、语气一致性
内部知识库问答 模型 + RAG 召回质量、幻觉控制

如果所有 AI Service 都共享同一个模型,早期开发会省事,但后面会遇到三个问题。

第一,成本不可控。原本只需要低成本模型处理的任务,可能被路由到高价模型。

第二,排查困难。用户反馈“摘要质量下降”,你需要先确认它到底用了哪个模型、哪套 Prompt、有没有走检索。

第三,边界模糊。涉及内部数据、权限数据、审计数据的任务,不应该随便交给任意模型处理。

显式布线的价值,就是让这些规则沉淀在代码里,而不是只存在于某个开发者的脑子里。

Service 接口也要按业务边界拆

一个常见错误是定义一个“万能助手”:

public interface AiAssistant {

    String chat(String message);

    String summarize(String content);

    String audit(String content);

    String rewrite(String content);
}

这看起来方便,实际会让模型、Prompt、工具和权限混在一起。更合理的拆法是按业务能力拆接口:

public interface ReportAssistant {
    String summarize(String content);
}

public interface AuditAssistant {
    String check(String content);
}

public interface CustomerSupportAssistant {
    String reply(String question);
}

这样做有几个好处。

一是模型绑定更明确。报表、审核、客服可以分别绑定不同模型。

二是测试更容易。你可以分别验证摘要质量、审核误判率、客服回答风格,而不是测一个巨大的通用入口。

三是后续扩展更自然。某个接口需要增加工具调用、记忆、RAG,不会影响其他 AI Service。

Java 后端做久了会习惯分层、限界上下文、接口隔离。AI Service 也应该遵守这些工程原则。不要因为背后是大模型,就把多年积累的设计边界丢掉。

第一版项目可以这样落地

如果团队刚开始接入 LangChain4j,不建议一上来做复杂的模型网关。第一版可以先做到三件事。

第一,每个 AI Service 只负责一个业务能力。不要把所有 AI 能力塞进一个接口。

第二,涉及多个模型时使用显式布线。哪怕当前只有两个模型,也要提前把绑定关系写清楚。

第三,把模型选择、Prompt 版本、调用日志纳入可观测范围。至少要能知道一次业务请求调用了哪个 AI Service、哪个模型、耗时多少、是否失败。

可以先不用追求“智能路由”。很多企业项目的第一阶段,最需要的不是让系统自动选择模型,而是让开发者和运维人员知道:这次请求到底发生了什么。

LangChain4j 的 AI Service 降低了 Java 项目接入大模型的门槛,但它不应该把工程边界也一起隐藏掉。接口越像普通 Service,越要把模型、工具、检索器这些背后依赖设计清楚。真正可维护的 AI 应用,不是把调用写得最短,而是让每一次调用都能被理解、被测试、被替换。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐