当前位置: 首页 > news >正文

写接口,不写实现:LangChain4j 的 @AiService 到底有多优雅?

本文将介绍LangChain4j 最有特色的设计:@AiService

如果你是一个 Spring 老手,第一次看到@AiService很可能会脱口而出:“这不就是 Spring Data Repository 的 AI 版本嘛?”
没错,就是这个感觉。

一、跑通第一个 @AiService

1.1定义接口

package com.stduying.service; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.spring.AiService; @AiService // 标记为 AI 服务接口 public interface SimpleAssistant { @SystemMessage("你是一个友好的 AI 助手,用简洁的语言回答问题") String chat(String userMessage); }

1.2 注入并使用

package com.stduying.controller.aiservice; import com.stduying.service.SimpleAssistant; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/assistant") public class AssistantController { private final SimpleAssistant assistant; public AssistantController(SimpleAssistant assistant) { this.assistant = assistant; } @GetMapping public String ask(@RequestParam String question) { return assistant.chat(question); } }

启动项目后执行:

curl "http://localhost:8080/assistant?question=什么是Spring AOP"

模型就会根据@SystemMessage的角色设定,返回一个简洁的回答。

不需要写任何实现类,不需要手动构建ChatClient,不需要拼接SystemMessageUserMessage——@AiService全帮你干了。

二、@AiService 的工作原理

当 Spring Boot 启动时,LangChain4j 会扫描所有标注了@AiService的接口,为每个接口动态生成一个代理实现,并注册为 Spring Bean。

这个代理实现大致做了这些事情:

方法调用(如 assistant.chat(userMessage)) ↓ 读取方法上的 @SystemMessage(角色设定) ↓ 把方法参数包装成 UserMessage ↓ 构建完整的 messages 列表:[SystemMessage, UserMessage] ↓ 调用底层 ChatModel 的 generate() 方法 ↓ 将模型的输出(String/对象等)返回给调用方

理解这个流程后,后面遇到其他注解(如@UserMessage@MemoryId)时,你就知道它们是在哪个环节起作用了。

三、一个接口,多个方法(多种 AI 能力)

同一个接口可以定义多个方法,每个方法可以有自己的@SystemMessage,代表不同的 AI 能力:

@AiService public interface MultiCapabilityAssistant { @SystemMessage("你是一个 Java 技术助手,专注于代码质量和性能优化") String reviewCode(String code); @SystemMessage(""" 你是一个技术文档写作专家。 把技术内容转化为清晰易懂的文档,有条理,有示例。 """) String writeDoc(String techContent); @SystemMessage("你是一个 SQL 专家,帮助优化数据库查询") String optimizeSql(String sql); }

使用起来非常直观:

@RestController @RequestMapping("/dev") public class DevAssistantController { private final MultiCapabilityAssistant assistant; public DevAssistantController(MultiCapabilityAssistant assistant) { this.assistant = assistant; } @PostMapping("/review") public String reviewCode(@RequestBody String code) { return assistant.reviewCode(code); } @PostMapping("/doc") public String writeDoc(@RequestBody String techContent) { return assistant.writeDoc(techContent); } @PostMapping("/sql") public String optimizeSql(@RequestBody String sql) { return assistant.optimizeSql(sql); } }

测试:

curl -X POST "http://localhost:8080/dev/review" -H "Content-Type: text/plain" -d "public void foo() { System.out.println(\"hello\"); }" curl -X POST "http://localhost:8080/dev/doc" -H "Content-Type: text/plain" -d "Spring AOP 的工作原理" curl -X POST "http://localhost:8080/dev/sql" -H "Content-Type: text/plain" -d "SELECT * FROM orders WHERE user_id = ?"

四、指定使用哪个模型(多模型共存)

有时我们会在同一个项目中配置多个模型,例如一个便宜的模型处理简单问答,一个更强(也更贵)的模型处理复杂任务。

配置第二个模型 Bean(第一个模型由 Starter 自动配置):

@Configuration public class ModelConfig { // 主模型 qwen-max 已由 Starter 自动配置,Bean 名为 "openAiChatModel" @Bean("cheapModel") public ChatModel qwenTurboModel() { return OpenAiChatModel.builder() .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1") .apiKey(System.getenv("DASHSCOPE_API_KEY")) .modelName("qwen-turbo") // 成本更低 .build(); } }

然后在@AiService中显式声明要用哪个模型 Bean:

// 便宜模型 @AiService(wiringMode = AiServiceWiringMode.EXPLICIT, chatModel = "cheapModel") public interface EconomyAssistant { @SystemMessage("你是一个简单的问答助手") String chat(String message); } // 主力模型 @AiService(wiringMode = AiServiceWiringMode.EXPLICIT, chatModel = "openAiChatModel") public interface PremiumAssistant { @SystemMessage("你是一个高质量的技术顾问,回答要深入专业") String chat(String message); }

@AiService(wiringMode = AiServiceWiringMode.EXPLICIT, chatModel = "cheapModel"),核心作用就是精确地告诉框架,你的 AI 服务要用哪个大模型。

⚙️ wiringMode = AiServiceWiringMode.EXPLICIT:掌控依赖装配

wiringMode控制了 Spring 如何为你的 AI 服务@Autowired所需的组件。它主要有两种模式,你可以把它们理解成选择依赖注入的方式:

模式含义优点缺点
自动装配 (AUTOMATIC)这是默认模式。框架会自动在 Spring 容器里寻找所需类型(如ChatModel)的 Bean 进行注入。简单省事,依赖少时配置极快。容易冲突,当某个类型存在多个 Bean(例如你配置了两个大模型)时,实例会启动失败。
显式装配 (EXPLICIT)开发者需要明确指定要用哪个 Bean 的名字,精细控制装配过程。安全精确,可完全避免 Bean 冲突,尤其适合多模型等复杂场景。相对需要多写一点配置,指定 Bean 的名称。

你的代码选择了EXPLICIT模式,这是一种追求精确控制、避免潜在冲突的良好实践。

🧠 chatModel = "cheapModel":指定大语言模型

当使用EXPLICIT装配模式时,chatModel参数就变得至关重要。它的值"cheapModel"是一个字符串,指向你在 Spring 配置中定义的一个具体的ChatModel类型的 Bean 名字

cheapModel这个命名通常是为了区分不同型号或服务商的模型,例如 OpenAI 的gpt-3.5-turbo(成本低)对比gpt-4(成本高)。

五、手动构建:AiServices.builder()

除了注解方式,LangChain4j 也支持编程式构建 AI 服务,这在需要动态配置时非常有用。

@Configuration public class AssistantConfig { @Bean public SimpleAssistant simpleAssistant(ChatModel chatModel) { return AiServices.builder(SimpleAssistant.class) .chatModel(chatModel) // 后面还可以加 memory、tools 等 .build(); } }

六、对比 Spring AI 的写法

为了让大家直观感受@AiService的优雅,我们把前面那个多能力助手的例子用 Spring AI 实现一遍。

Spring AI 写法:

@Service public class TechAssistantService { private final ChatClient chatClient; public TechAssistantService(ChatClient.Builder builder) { this.chatClient = builder .defaultSystem("你是一个 Java 技术助手") .build(); } public String reviewCode(String code) { return chatClient.prompt() .system("你是一个代码审查专家") .user(code) .call() .content(); } public String writeDoc(String content) { return chatClient.prompt() .system("你是一个技术文档写作专家") .user(content) .call() .content(); } public String optimizeSql(String sql) { return chatClient.prompt() .system("你是一个 SQL 专家") .user(sql) .call() .content(); } }

LangChain4j @AiService 写法:

@AiService public interface TechAssistant { @SystemMessage("你是一个代码审查专家") String reviewCode(String code); @SystemMessage("你是一个技术文档写作专家") String writeDoc(String content); @SystemMessage("你是一个 SQL 专家") String optimizeSql(String sql); }

同样的功能,LangChain4j 的接口版本:

  • 代码量减少一大半

  • 更直观:看一眼接口就知道有哪些 AI 能力

  • 不需要实现类,框架自动生成

  • 与 Spring 生态无缝集成,直接@Autowired使用

七、小结

@AiService是 LangChain4j 最值得学习的设计。它借鉴了 Spring Data 的 Repository 模式,把AI 能力的声明与实现彻底分离

  • @AiService标记接口

  • @SystemMessage等注解描述能力

  • 框架自动生成代理,注入即用

这种声明式编程风格,让 Java 开发者几乎零学习成本就能写出优雅的 AI 服务代码。下一节我们将进入工具体系,看看如何给@AiService配上“手和脚”——让模型不仅能说,还能调用外部能力。

http://www.jsqmd.com/news/742879/

相关文章:

  • YOLO11性能暴增:主干网络升级 | 替换为PoolFormer主干,用最简单的池化操作替代自注意力,化繁为简的艺术
  • LMOps:构建大语言模型应用开发的工业化流水线
  • 如何用Boss直聘批量投递工具实现高效求职?日均50+投递的智能方案
  • 机器学习模型表格数据检索:方法与评估框架
  • 2026成都靠谱市场调查报告公司:专业的市场调查公司推荐/专业的市场调研公司推荐/专业的市场调研机构推荐/四川做市场调研的公司推荐/选择指南 - 优质品牌商家
  • AI代码生成质量守卫:eslint-plugin-ai-guard实战指南
  • 为Hermes Agent配置自定义模型提供商指向Taotoken的完整步骤
  • 为Hermes Agent配置Taotoken作为自定义模型提供商
  • GitHub下载速度提升300%的终极方案:Fast-GitHub浏览器插件详解
  • 2026年乐山美食店铺排行:乐山钵钵鸡推荐、乐山钵钵鸡有哪些、乐山鳝丝店谁有名、嘉州非遗临江鳝丝、帮我推荐几个乐山美食店选择指南 - 优质品牌商家
  • 华硕笔记本风扇异常修复:3种快速解决方案与参数调优指南
  • 超越自动化:2030年的工业智能体与具身智能展望
  • 基于密集预测引导的YOLOv10遮挡目标检测:我的完整改进实验记录
  • LangChain4j 入门教程
  • 从实验室原型到北斗三号量子加密车载终端:C语言跨平台调试的4层抽象泄漏与3次重构血泪教训
  • 基于 GitHub Actions 的自动化工作流实践:从代码检查到发布部署
  • 如何管理Taotoken平台上的API密钥并设置访问控制与审计
  • YOLO11性能暴增:Backbone换血 | 引入ShuffelNetV2极速主干,针对通道打乱机制进行YOLO适配,提速首选
  • 拯救你的Dell G15:开源温度控制软件TCC-G15全面评测与使用指南
  • SNIP框架:动态混合精度训练优化大模型计算效率
  • 用Python和Logisim仿真,5分钟搞定三人表决电路(附保姆级教程)
  • Go协程池gortex实战:高并发任务管理与内存优化指南
  • 从PLC握手到电子锁上锁:一文拆解CCS2直流充电的完整信号交互流程
  • 初次接入Taotoken后从控制台获取并管理API Key的完整步骤
  • BBDown:命令行玩家的终极B站视频下载解决方案
  • HPH内部结构拆解指南
  • 在 OpenClaw Agent 工作流中接入 Taotoken 实现多模型调度
  • 2026成都旧沙发翻新厂家怎么选:成都上门维修沙发、成都沙发翻新、成都真皮沙发维修、旧沙发翻新上门服务、沙发上门维修选择指南 - 优质品牌商家
  • 如何用400+免费RPG Maker插件快速打造专业级游戏:从新手到高手的完整指南
  • 告别‘系统找不到指定的文件’:Windows下用MinGW搞定GCC和Make的完整配置流程