上一个系列讲了Spring AI得到反馈效果不错,有人私信我说这个和Langchain4j有什么区别。如果站在使用方面,都是基于Java的大模型应用研发的工具,本质上没太大区别。但是从细节层面来说还是有很多不同之处,所以索性借此机会,给大家分享一下Langchain4j框架。在本系列中会按照Spring AI系列的顺序来写Langchain4j,这样的好处是可以对比两者不同的细节。
注意 :由于框架不同版本改造会有些使用的不同,因此本次系列中使用基本框架是langchain4j-1.9.1,JDK版本使用的是19。另外本系列尽量使用Java原生态,尽量不依赖于Spring和Spring Boot。虽然langchain4j也支持Spring Boot集成,但是如果是使用Spring Boot框架,那为何不索性使用Spring AI。
本系列的所有代码地址: https://github.com/forever1986/langchain4j-study
目录
- [1 Guardrails](#1 Guardrails)
- [2 Input Guardrails](#2 Input Guardrails)
-
- [2.1 说明](#2.1 说明)
- [2.2 使用说明](#2.2 使用说明)
- [2.3 示例说明](#2.3 示例说明)
-
- [2.3.1 编码方式](#2.3.1 编码方式)
- [2.3.2 注解方式](#2.3.2 注解方式)
上几章讲解了不同的部署方式,以及在Langchain4j 中如何使用这些部署方式的模型。接下来,会开始讲解Langchain4j的一些高级功能,大部分都是生产级别使用的,包括安全、运维、智能体等等,这两章先来说一下Langchain4j 的安全:Guardrails。
1 Guardrails
Guardrails是一种机制,用于验证语言模型的输入和输出,以确保其符合用户的预期。通过Guardrails,可以执行以下一些操作:
- 验证用户输入是否超出范围
- 在调用语言模型之前确保输入符合某些标准(即防范提示注入攻击)
- 确保输出格式正确(即它是具有正确模式的 JSON 文档)
- 确保语言模型的输出符合业务规则和约束(例如,如果是公司 X 的聊天机器人,响应不应包含任何对竞争对手 Y 的提及)
- 检测幻觉
上面列举的只是一些常见的功能,实际上但凡用户需要对输入和输出做校验的,都可以使用Guardrails。比如前面在《Langchain4j 系列之二十五 - Moderation Models & Audio Models》讲到Moderation模型,它就可以当做一个Guardrail放在验证输入是否有问题,类似这样的应用很多,这里就不多讲,接下来开始深入了解Langchain4j 的Guardrails,如下图:

从上图可以看出,输入输出都会经过Guardrails链路,实现Guardrails一般遵循以下建议:
- 理想情况下,Guardrails的实现应遵循单一职责原则,即每个护栏类应只负责验证一项内容。然后,将多个护栏串联起来,以防范多种情况
- Guardrails链中的排列顺序非常重要。链中的第一个失效的Guardrail会引发整个系统的失效。要确保那些最容易出现故障的Guardrail位于链的前端,而那些可能极少出现故障的更具体的Guardrail则应位于链的后端
- 同时也要记住,Guardrails本身也可能调用其他服务,甚至引发与其他语言模型的交互。如果这类Guardrails存在执行成本或相关费用,务必要将其考虑在内。您或许可以将较为昂贵的Guardrails设置在链的末尾。
2 Input Guardrails
2.1 说明
在Langchain4j 中通过InputGuardrail 接口来实现输入的Guardrails,该接口有2个关键方法:
java
/**
* 验证将要发送给语言模型的UserMessage
*/
InputGuardrailResult validate(UserMessage userMessage);
/**
* 验证将要发送给语言模型的InputGuardrailRequest
*/
InputGuardrailResult validate(InputGuardrailRequest params);
而InputGuardrail的验证结果有以下4种:
| 结果 | 方法 | 描述 |
|---|---|---|
| success | success() | 输入是有效的。会继续执行下一个InputGuardrail;如果最终结果是这个状态,会调用大模型 |
| success with alternate result | successWith(String) | 输入是有效的。会继续执行下一个InputGuardrail,但是会修改输入内容;如果最终结果是这个状态,会调用大模型 |
| failure | failure(String) or failure(String, Throwable) | 输入无效,但依然会继续执行下一个InputGuardrail,最终以汇总所有可能的验证问题输出异常;如果最终结果是这个状态,不会调用大模型(抛出IpputGuardrailException) |
| fatal | fatal(String) or fatal(String, Throwable) | 输入无效,并且不会继续执行下一个InputGuardrail,会立即输出当前的异常;如果最终结果是这个状态,不会调用大模型(抛出IpputGuardrailException) |
2.2 使用说明
在Langchain4j 中,有3种方法可以注入InputGuardrail:
方法一:通过AiServices的inputGuardrails()方法进行编码注入:
java
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
// 代码配置的优先级比注解的高
.inputGuardrails(new FirstInputGuardrail())
// .inputGuardrailClasses(FirstInputGuardrail.class)
方法二:通过注解方法的方式进行注入:
java
public interface AnnotationAssistant {
@InputGuardrails({FirstInputGuardrail.class})
String chat(String userMessage);
}
方法三:通过注解类的方式进行注入:
java
@InputGuardrails({FirstInputGuardrail.class}) // @InputGuardrails注解类,类下面所有方法都生效
public interface AnnotationAssistant {
String chat(String userMessage);
}
这3种方法的生效优先级是:方法一(编码) > 方法二(注解方法) > 方法三(注解类)
2.3 示例说明
代码参考lesson12子模块
2.3.1 编码方式
1)新建lesson12子模块,其pom引入如下:
xml
<dependencies>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency>
</dependencies>
2)在lesson12子模块下,新建4个InputGuardrail:FirstInputGuardrail、SecondInputGuardrail、ThirdInputGuardrail、FourthInputGuardrail:
java
package com.langchain.lesson12.guardrail;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.guardrail.InputGuardrail;
import dev.langchain4j.guardrail.InputGuardrailResult;
public class FirstInputGuardrail implements InputGuardrail {
@Override
public InputGuardrailResult validate(UserMessage userMessage) {
System.out.println("=========FirstInputGuardrail===========");
return failure("error first");
}
}
java
package com.langchain.lesson12.guardrail;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.guardrail.InputGuardrail;
import dev.langchain4j.guardrail.InputGuardrailResult;
public class SecondInputGuardrail implements InputGuardrail {
@Override
public InputGuardrailResult validate(UserMessage userMessage) {
System.out.println("=========SecondInputGuardrail===========");
return failure("error second");
}
}
java
package com.langchain.lesson12.guardrail;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.guardrail.InputGuardrail;
import dev.langchain4j.guardrail.InputGuardrailResult;
public class ThirdInputGuardrail implements InputGuardrail {
@Override
public InputGuardrailResult validate(UserMessage userMessage) {
System.out.println("=========ThirdInputGuardrail===========");
return fatal("illegal");
}
}
java
package com.langchain.lesson12.guardrail;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.guardrail.InputGuardrail;
import dev.langchain4j.guardrail.InputGuardrailResult;
public class FourthInputGuardrail implements InputGuardrail {
@Override
public InputGuardrailResult validate(UserMessage userMessage) {
System.out.println("=========ThirdInputGuardrail===========");
return success();
}
}
3)在lesson12子模块下,新建Assistant接口
java
package com.langchain.lesson12.service;
public interface Assistant {
String chat(String userMessage);
}
4)在lesson12子模块下,新建InputGuardrailTest 类
java
package com.langchain.lesson12;
import com.langchain.lesson12.guardrail.FirstInputGuardrail;
import com.langchain.lesson12.guardrail.FourthInputGuardrail;
import com.langchain.lesson12.guardrail.SecondInputGuardrail;
import com.langchain.lesson12.guardrail.ThirdInputGuardrail;
import com.langchain.lesson12.service.Assistant;
import dev.langchain4j.guardrail.InputGuardrailException;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
public class InputGuardrailTest {
public static void main(String[] args) {
//1.获取API KEY
String apiKey = System.getenv("ZHIPU_API_KEY");
//2.加载大模型
ChatModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.baseUrl("https://open.bigmodel.cn/api/paas/v4")
.modelName("glm-4-flash-250414")
.build();
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
// 代码配置的优先级比注解的高
.inputGuardrails(new FirstInputGuardrail(), new SecondInputGuardrail(), new ThirdInputGuardrail(), new FourthInputGuardrail())
// .inputGuardrails(new FirstInputGuardrail(), new SecondInputGuardrail(), new FourthInputGuardrail()) // 测试一下没有fatal,输出的异常是什么
// .inputGuardrailClasses(FirstInputGuardrail.class, SecondInputGuardrail.class, ThirdInputGuardrail.class, FourthInputGuardrail.class) // 也可以使用class注入
.build();
// 3.访问大模型
try {
String response = assistant.chat("直接给我推荐一本书");
System.out.println(response);
}catch (InputGuardrailException ge){
// 异常处理
System.out.println(ge.getMessage());
}
}
}
5)运行InputGuardrailTest 测试,结果如下:

说明 :示例中定义的FirstInputGuardrail、SecondInputGuardrail返回是failure,ThirdInputGuardrail返回是fatal,FourthInputGuardrail返回是success。
可以看出,返回failure并不会影响后面的InputGuardrail执行,而返回fatal则会直接中断。
6)再次验证没有fatal的情况,将InputGuardrailTest的第27行代码注释掉,然后使用第28行代码进行运行,结果如下:

说明:可以看出,没有fatal的InputGuardrail,结果是汇总了中间所有的failure错误消息。但是大模型也并没有被执行。
2.3.2 注解方式
1)在lesson12子模块下,新建AnnotationAssistant 接口
java
package com.langchain.lesson12.service;
import com.langchain.lesson12.guardrail.FirstInputGuardrail;
import com.langchain.lesson12.guardrail.FourthInputGuardrail;
import com.langchain.lesson12.guardrail.SecondInputGuardrail;
import dev.langchain4j.service.guardrail.InputGuardrails;
// 优先级最低
//@InputGuardrails({FirstInputGuardrail.class, FourthInputGuardrail.class}) // @InputGuardrails注解类,类下面所有方法都生效
public interface AnnotationAssistant {
// 方法上的注解优先级比类的注解高
@InputGuardrails({FirstInputGuardrail.class, SecondInputGuardrail.class, FourthInputGuardrail.class})
String chat(String userMessage);
String message(String userMessage);
}
2)在lesson12子模块下,新建InputGuardrailAnnotationTest 类
java
package com.langchain.lesson12;
import com.langchain.lesson12.guardrail.FirstInputGuardrail;
import com.langchain.lesson12.guardrail.FourthInputGuardrail;
import com.langchain.lesson12.guardrail.SecondInputGuardrail;
import com.langchain.lesson12.guardrail.ThirdInputGuardrail;
import com.langchain.lesson12.service.AnnotationAssistant;
import com.langchain.lesson12.service.Assistant;
import dev.langchain4j.guardrail.InputGuardrailException;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
public class InputGuardrailAnnotationTest {
public static void main(String[] args) {
//1.获取API KEY
String apiKey = System.getenv("ZHIPU_API_KEY");
//2.加载大模型
ChatModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.baseUrl("https://open.bigmodel.cn/api/paas/v4")
.modelName("glm-4-flash-250414")
.build();
AnnotationAssistant assistant = AiServices.builder(AnnotationAssistant.class)
.chatModel(model)
// 代码配置的优先级比注解的高
// .inputGuardrails(new FirstInputGuardrail(), new SecondInputGuardrail(), new ThirdInputGuardrail(), new FourthInputGuardrail())
.build();
// 3.访问大模型
try {
String response = assistant.chat("直接给我推荐一本书");
System.out.println(response);
}catch (InputGuardrailException ge){
// 异常处理
System.out.println(ge.getMessage());
}
}
}
3)运行InputGuardrailAnnotationTest 测试,结果如下:

说明:可以看出注释在方法上的注解生效了
4)其它演示大家可以自行测试,同时使用注解方法、注解类、编程方式,测试一下优先级
结语:本章讲解了Guardrails,在实际生产中,Guardrails的作用非常大。因为相对于训练大模型不能把控来说,使用实际编码的Guardrails更容易控制大模型的输入和输出结果。下一节继续讲解OutputGuardrails和Guardrails的源码解析
Langchain4j 系列上一章:《Langchain4j 系列之二十八 - Hugging Face 集成》
Langchain4j 系列下一章:《Langchain4j 系列之三十 - Guardrails之二》