Langchain4j 系列之二十九 - Guardrails之一

上一个系列讲了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之二

相关推荐
意疏2 小时前
Claude Code 安装全流程:从零到真正用起来
人工智能
AskHarries2 小时前
在 Windows 上使用 Python MCP 配置 Qoder CLI STDIO 服务教程
人工智能·adb·ai编程
lynn-fish2 小时前
AI标讯数据揭秘:电力电缆市场的竞争密码
人工智能·电网·储能·软件·光伏·电力·ai工具
weixin_462446232 小时前
使用 jsr:@langchain/pyodide-sandbox 构建 Python 安全沙箱(完整入门教程)
python·安全·langchain·sandbox
心心强2 小时前
(二)langchain 调用本地seepseek大模型
langchain
Loo国昌2 小时前
【LangChain1.0】第九阶段:文档处理工程 (LlamaIndex)
人工智能·后端·python·算法·langchain
罗伯特_十三3 小时前
Spring AI ChatModel 使用记录
java·人工智能·spring
AIbase20243 小时前
AI时代品牌流量争夺战:如何通过“品牌AI搜索监控”提升GEO可见度?
人工智能·chatgpt
老鱼说AI3 小时前
论文精读第七期:告别昂贵的人工标注!Math-Shepherd:如何用“零成本”自动化过程监督,让大模型数学能力暴涨?
人工智能·深度学习·神经网络·机器学习·语言模型·自然语言处理·boosting