news 2026/5/3 12:32:07

LangChain4j的Prompt 注解体系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangChain4j的Prompt 注解体系

初步体验了@AiService的声明式魅力——定义接口,框架生成实现。但很多同学在实际使用中会遇到几个问题:

  • System Prompt 能不能带变量?比如不同租户有不同的公司名称、服务范围。

  • 复杂的 Prompt 能不能放在外部文件里,让非开发人员也能修改?

  • 如何在运行时动态决定 System Prompt(SaaS 多租户场景)?

这一节,我们将把@AiService相关的注解彻底讲透,包括@SystemMessage@UserMessage@V以及从文件加载 Prompt 等多个技巧。

一、@SystemMessage:定义角色的根基

@SystemMessage对应我们熟悉的 System Prompt,用于设定模型的角色、行为规范和输出格式。

1.1 基础用法

@AiService public interface Assistant { @SystemMessage("你是一个 Java 技术助手,用简洁的语言回答,代码用 Java 17 语法") String chat(String question); }

1.2 多行 System Prompt(Text Block)

对于复杂的 Prompt,使用 Java 15 引入的文本块会让代码更清爽:

@AiService public interface CustomerServiceAssistant { @SystemMessage(""" 你是"鸡翅商城"的智能客服助手小鸡。 服务范围:商品咨询、订单查询、售后服务 约束: - 只回答与鸡翅商城相关的问题 - 不确定的信息说"我帮您查一下",不要猜测 - 回复不超过 150 字 - 涉及投诉主动提出转人工 """) String chat(String userMessage); }

实际使用时,直接注入接口即可:

@RestController public class CustomerController { private final CustomerServiceAssistant assistant; // 构造器注入 @GetMapping("/cs") public String ask(@RequestParam String msg) { return assistant.chat(msg); } }

1.3 System Prompt 中的变量注入(@V)

如果你的 System Prompt 需要根据不同调用动态变化(比如多租户场景),可以在模板中使用{{variableName}}占位符,并在方法参数上用@V标记变量值。

@AiService public interface TenantAssistant { @SystemMessage("你是{{companyName}}的客服助手,服务范围:{{serviceScope}}") String chat(@V("companyName") String company, @V("serviceScope") String scope, @UserMessage String userMessage); // @UserMessage 标记用户输入 }

调用时:

curl "http://localhost:8080/tenant/chat?company=鸡翅商城&scope=商品、订单&message=我的订单在哪"

注意@UserMessage用于指明哪个参数是用户的实时输入,后面会详细解释。

二、@UserMessage:精细控制用户消息的格式

默认情况下,@AiService方法中唯一的String参数会被当作 User Message。但有时我们希望对用户消息做更复杂的包装,比如加上“请翻译成英文”的前缀,或者动态拼接内容。

2.1 在注解中定义消息模板

@AiService public interface Translator { @SystemMessage("你是一个专业翻译") @UserMessage("将以下文字翻译成{{language}}:\n{{text}}") String translate(@V("language") String lang, @V("text") String text); }

这样就不需要在每个调用点手动拼接字符串了。

2.2 标记参数即 User Message

如果不使用@UserMessage模板,也可以直接在参数上标记@UserMessage,表明该参数的内容就是用户消息:

@AiService public interface CodeAssistant { @SystemMessage("你是一个 Java 代码审查专家") String reviewCode(@UserMessage String code); }

三、@V——变量注入的多种方式

@V用于向@SystemMessage@UserMessage中的占位符提供值。除了基本类型和字符串,它还能自动展开 Java Bean(Record 或 POJO)的字段。

3.1 多个独立变量

@AiService public interface ReportGenerator { @SystemMessage("你是一个数据分析专家") @UserMessage(""" 根据以下数据生成一份{{reportType}}报告: 时间范围:{{startDate}} 至 {{endDate}} 数据:{{data}} """) String generateReport( @V("reportType") String type, @V("startDate") String start, @V("endDate") String end, @V("data") String data ); }

3.2 对象参数自动展开

如果参数是一个 Record 或普通 Java Bean,LangChain4j 会自动将其字段名作为变量名进行匹配。

package com.jichi.langchain4j.model; public record CodeReviewRequest( String language, String code, String focusAreas // "性能、空指针、事务" ) {}
package com.jichi.langchain4j.service; import com.jichi.langchain4j.model.CodeReviewRequest; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; import dev.langchain4j.service.spring.AiService; @AiService public interface CodeReviewer { @SystemMessage("你是代码审查专家") @UserMessage(""" 请 review 以下 {{language}} 代码: ```{{language}} {{code}} ``` 重点关注:{{focusAreas}} """) String review(CodeReviewRequest request); // ↑ Record 的字段名就是模板变量名 }

四、从文件加载 Prompt:分离配置与代码

将 Prompt 写在注解中,每次修改都需要重新编译、打包、部署。对于需要频繁调整或非开发人员维护的场景,最合适的做法是把 Prompt 放到外部资源文件中。

4.1 目录结构

src/main/resources/ └── prompts/ ├── customer-service.txt ├── code-review.txt └── report-generator.txt

4.2 文件内容示例(customer-service.txt)

你是"{{companyName}}"的智能客服助手。 服务范围:{{serviceScope}} 约束: - 只回答与{{companyName}}相关的问题 - 不确定的说"我帮您确认一下",不要猜测 - 回复不超过 150 字

4.3 在注解中引用文件

使用fromResource属性指定文件路径(相对于resources目录):

@AiService public interface FileBasedAssistant { @SystemMessage(fromResource = "prompts/customer-service.txt") String chat(@V("companyName") String company, @V("serviceScope") String scope, @UserMessage String message); }

这样,当产品经理需要修改话术时,只需要替换文件内容,应用无需重启(配合热加载机制)即可生效。

五、注解优先级与小贴士

  • 当同一个类或接口中多个方法都有@SystemMessage时,每个方法独立生效,互不影响。

  • 如果某个方法没有标注@SystemMessage,则不会发送 System 消息。

  • @UserMessage模板的优先级高于默认的参数即消息的规则。

  • 从文件加载模板时,文件内容仍然可以包含{{variable}}占位符,并且可以和@V配合使用。

  • 方法上的 @SystemMessage 优先级高于 类上的 @SystemMessage

六、完整示例:多场景助手

package com.jichi.langchain4j.service; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; import dev.langchain4j.service.V; import dev.langchain4j.service.spring.AiService; @AiService public interface MultiScenarioAssistant { // 场景一:简单问答 @SystemMessage("你是一个 Java 技术助手,回答简洁") String techChat(String question); // 场景二:有格式要求的翻译 @SystemMessage("你是专业翻译,保持原文风格,不添加解释") @UserMessage("将以下内容翻译成{{language}}:\n\n{{content}}") String translate(@V("language") String lang, @V("content") String content); // 场景三:从文件加载复杂 Prompt @SystemMessage(fromResource = "prompts/code-review.txt") String reviewCode(@V("language") String lang, @UserMessage String code); // 场景四:多变量 + 对象参数 @SystemMessage("你是数据分析专家") @UserMessage("分析以下{{period}}的销售数据,重点关注{{focus}}:\n{{data}}") String analyzeData(@V("period") String period, @V("focus") String focus, @V("data") String data); }
package com.jichi.langchain4j.controller; import com.jichi.langchain4j.service.MultiScenarioAssistant; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/prompt/multi") public class MultiScenarioController { private final MultiScenarioAssistant assistant; public MultiScenarioController(MultiScenarioAssistant assistant) { this.assistant = assistant; } @GetMapping("/tech") public String techChat(@RequestParam String question) { return assistant.techChat(question); } @GetMapping("/translate") public String translate(@RequestParam String language, @RequestParam String content) { return assistant.translate(language, content); } @PostMapping("/review") public String reviewCode(@RequestParam String language, @RequestBody String code) { return assistant.reviewCode(language, code); } @GetMapping("/analyze") public String analyzeData(@RequestParam String period, @RequestParam String focus, @RequestParam String data) { return assistant.analyzeData(period, focus, data); } }
# 场景一:技术问答 curl "http://localhost:8080/prompt/multi/tech?question=什么是CompletableFuture" # 场景二:翻译 curl "http://localhost:8080/prompt/multi/translate?language=英文&content=人工智能正在改变软件开发方式" # 场景三:代码 review(需要 prompts/code-review.txt 文件存在) curl -X POST "http://localhost:8080/prompt/multi/review?language=Java" \ -H "Content-Type: text/plain" \ -d "public List<User> getAll() { return userRepo.findAll(); }" # 场景四:数据分析 curl "http://localhost:8080/prompt/multi/analyze?period=2025Q1&focus=增长趋势&data=销售额1200万,同比+15%"
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 12:25:41

抖音批量下载神器:3分钟学会无水印高清视频和封面提取

抖音批量下载神器&#xff1a;3分钟学会无水印高清视频和封面提取 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback suppo…

作者头像 李华
网站建设 2026/5/3 12:24:42

高效批量卸载解决方案:Bulk Crap Uninstaller专业指南

高效批量卸载解决方案&#xff1a;Bulk Crap Uninstaller专业指南 【免费下载链接】Bulk-Crap-Uninstaller Remove large amounts of unwanted applications quickly. 项目地址: https://gitcode.com/gh_mirrors/bu/Bulk-Crap-Uninstaller Windows系统长期使用后&#x…

作者头像 李华