基于LangChain4j从0到1搭建自己的的AI智能体并部署上线-1

文章介绍

本文章将介绍:

  • 什么是Langchain4j
  • 快速入门Langchain4j实现基本对话功能
  • 实现其进阶用法(聊天记忆,隔离对话,FunctionCalling)
  • 从0到1落地一个医疗智能体项目,实现特定角色的医疗咨询服务以及挂号,取消挂号服务

什么是langchain4j?

LangChain4j 是一个 Java 版本的 LangChain 框架

简单来说,你可以把它理解为:

  • "为 Java 开发者量身打造的 AI 应用开发工具箱"。

快速入门

下面以以接入阿里云的通义千问为例:

引依赖(Maven)

XML 复制代码
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-spring-boot-starter</artifactId>
            <version>1.0.0-beta3</version>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
        </dependency>

配参数(API)

XML 复制代码
langchain4j.community.dashscope.chat-model.api-key=${DASH_SCOPE_API_KEY}
langchain4j.community.dashscope.chat-model.model-name=qwen-flash

DASH_SCOPE_API_KEY为API-key,需要配置环境变量

测试

java 复制代码
/**
*通义千问大模型
*/
@Autowired
private QwenChatModel qwenChatModel;
@Test
public void testDashScopeQwen(){
    //向模型提问
    Stringanswer=qwenChatModel.chat("你好");
    //输出结果
    System.out.println(answer);
}

进阶

使用AIService

AIService使用面向接口和动态代理的方式完成程序的编写,更灵活的实现高级功能。

java 复制代码
@AiService
public interface Assistant {
    String chat(String userMessage);

}
java 复制代码
@SpringBootTest
public classAIServiceTest{
    @Autowired
    privateQwenChatModelqwenChatModel;
    @Test
    public void testChat(){
        //创建AIService
        Assistant assistant=AiServices.create(Assistant.class,qwenChatModel);
        //调用service的接口
        String answer=assistant.chat("Hello");
        System.out.println(answer);
    }
}

实现聊天记忆

测试对话是否记忆(默认没有记忆)

java 复制代码
@SpringBootTest
public class ChatMemoryTest {

    @Autowired
    private Assistant assistant;

    @Test
    public void testChatMemory() {
        String answer1 = assistant.chat("我是张三");
        System.out.println(answer1);

        String answer2 = assistant.chat("我是谁");
        System.out.println(answer2);
    }
}

使用AIService实现聊天记忆

java 复制代码
@AiService(
        wiringMode = EXPLICIT,
        chatModel = "qwenChatModel",
        chatMemory = "chatMemory"
)
public interface MemoryChatAssistant {

    String chat(String message);
}
java 复制代码
@Configuration
public class MemoryChatAssistantConfig {

    @Bean
    public ChatMemory chatMemory() {
        //设置聊天记录的message数量
        return MessageWindowChatMemory.withMaxMessages(10);
    }
}

测试

java 复制代码
@Autowired
private MemoryChatAssistant memoryChatAssistant;

@Test
public void testChatMemory4() {
    String answer1 = memoryChatAssistant.chat("我是张三");
    System.out.println(answer1);
    String answer2 = memoryChatAssistant.chat("我是谁");
    System.out.println(answer2);
}

实现聊天记忆隔离

创建记忆隔离对话智能体

java 复制代码
@AiService(
        wiringMode = EXPLICIT,
        chatMemory = "chatMemory",
        chatMemoryProvider = "chatMemoryProvider"
)
public interface SeparateChatAssistant {

    /**
     * 分离聊天记录
     * @param memoryId 聊天id
     * @param userMessage 用户消息
     * @return
     */
    String chat(@MemoryId int memoryId, @UserMessage String userMessage);
}

配置ChatMemoryProvider

java 复制代码
@Configuration
public class SeparateChatAssistantConfig {

    @Bean
    public ChatMemoryProvider chatMemoryProvider() {
        return memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .maxMessages(10)
                .build();
    }
}

测试

java 复制代码
@Autowired
private SeparateChatAssistant separateChatAssistant;

@Test
public void testChatMemory5() {
    String answer1 = separateChatAssistant.chat(1, "我是张三");
    System.out.println(answer1);
    String answer2 = separateChatAssistant.chat(1, "我是谁");
    System.out.println(answer2);
    String answer3 = separateChatAssistant.chat(2, "我是谁");
    System.out.println(answer3);
}

实现聊天记忆持久化(mongoDB)

mongoDB使用见我下面文章,这里不再赘述

MongoDB是什么,怎么用-CSDN博客

java 复制代码
@Component
public class MongoChatMemoryStore implements ChatMemoryStore {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        Criteria criteria=Criteria.where("memoryId").is(memoryId);
        Query query=new Query(criteria);
        ChatMessages chatMessages=mongoTemplate.findOne(query, ChatMessages.class);
        if (chatMessages==null)return new LinkedList<>();
        String contentJson = chatMessages.getContent();
        return ChatMessageDeserializer.messagesFromJson(contentJson);

    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> list) {
        Criteria criteria=Criteria.where("memoryId").is(memoryId);
        Query query=new Query(criteria);
        Update update=new Update();
        update.set("content", ChatMessageSerializer.messagesToJson( list));
        mongoTemplate.upsert(query, update, ChatMessages.class);
    }

    @Override
    public void deleteMessages(Object memoryId) {
        Criteria criteria=Criteria.where("memoryId").is(memoryId);
        Query query=new Query(criteria);
        mongoTemplate.remove(query, ChatMessages.class);
    }
}

在配置类中注入持久化对象

java 复制代码
@Configuration
public class SeparateChatAssistantConfig {

    //注入持久化对象
    @Autowired
    private MongoChatMemoryStore mongoChatMemoryStore;

    @Bean
    public ChatMemoryProvider chatMemoryProvider() {
        return memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .maxMessages(10)
                .chatMemoryStore(mongoChatMemoryStore)//配置持久化对象
                .build();
    }
}

测试

java 复制代码
    @Test
    public void testSeparateChatMemory() {
        String result = separateChatAssistant.chat(1,"告诉你我叫张三,记住了");
        System.out.println(result);
        String result2 = separateChatAssistant.chat(1,"我是谁");
        System.out.println(result2);
        String result3 = separateChatAssistant.chat(2,"告诉你我叫李四,记住了");
        System.out.println(result3);
        String result4 = separateChatAssistant.chat(2,"我是谁");
        System.out.println(result4);
    }

提示词Prompt

直接加载

java 复制代码
@SystemMessage("你是我的好朋友,请用东北话回答问题。今天是{{current_date}}")//系统消息提示词
String chat(@MemoryId int memoryId, @UserMessage String userMessage);

从txt文件中加载

java 复制代码
@SystemMessage(fromResource="system.txt")
Stringchat(@MemoryIdintmemoryId,@UserMessageStringuserMessage);

Function calling函数调用

例如,大语言模型本身并不擅长数学运算。如果应用场景中偶尔会涉及到数学计算,我们可以为他提供一个"数学工具"。当我们提出问题时,大语言模型会判断是否使用某个工具。

RAG知识库

待更

java 复制代码
@Component
public class CalculatorTools {

    @Tool
    public double sum(double a, double b) {
        System.out.println("调用加法运算");
        return a + b;
    }

    @Tool
    public double squareRoot(double x) {
        System.out.println("调用平方根运算");
        return Math.sqrt(x);
    }
}

添加tools

java 复制代码
@AiService(
    wiringMode = EXPLICIT,
    chatModel = "qwenChatModel",
    chatMemoryProvider = "chatMemoryProvider",
    tools = "calculatorTools" // 配置tools
)

测试

java 复制代码
@SpringBootTest
public class ToolsTest {

    @Autowired
    private SeparateChatAssistant separateChatAssistant;

    @Test
    public void testCalculatorTools() {
        String answer = separateChatAssistant.chat(1, "1+2等于几,475695037565的平方根是多少?");
        //答案: 3, 689706.4865
        System.out.println(answer);
    }
}

项目实战(小智医疗)

创建AIService和配置类

java 复制代码
@AiService(
        wiringMode = EXPLICIT,
        chatModel = "qwenChatModel",
        chatMemoryProvider = "chatMemoryProviderXiaozhi"
)
public interface XiaozhiAgent {
    @SystemMessage(fromResource = "xiaozhi-prompt-template.txt")
    String chat(@MemoryId Long memoryId, @UserMessage String userMessage );
}
bash 复制代码
你的名字是"硅谷小智",你是一家名为"北京协和医院"的智能客服。
你是一个训练有素的医疗顾问和医疗伴诊助手。
你态度友好、礼貌且言辞简洁。

1、请仅在用户发起第一次会话时,和用户打个招呼,并介绍你是谁。

2、作为一个训练有素的医疗顾问,
请基于当前临床实践和研究,针对患者提出的特定健康问题,提供详细、准确且实用的医疗建议。请同时考虑可能的病因、诊断流程、治疗方案以及预防措施,并给出在不同情境下的应对策略。对于药物治疗,请特别指明适用的药品名称、剂量和疗程。如果需要进一步的检查或就医,也请明确指示。

3、作为医疗伴诊助手,你可以回答用户就医流程中的相关问题,主要包含以下功能:
AI分导诊:根据患者的病情和就医需求,智能推荐最合适的科室。
AI挂号助手:实现智能查询是否有挂号号源服务;实现智能预约挂号服务;实现智能取消挂号服务。

4、你必须遵守的规则如下:
在获取挂号预约详情或取消挂号预约之前,你必须确保自己知晓用户的姓名(必选)、身份证号(必选)、预约科室(必选)、预约日期(必选,格式举例:2025-04-14)、预约时间(必选,格式:上午 或 下午)、预约医生(可选)。
当被问到其他领域的咨询时,要表示歉意并说明你无法在这方面提供帮助。

5、请在回答的结果中适当包含一些轻松可爱的图标和表情。

6、今天是{{current_date}}。
java 复制代码
@Configuration
public class AIConfig {
    @Autowired
    private MongoChatMemoryStore mongoChatMemoryStore;
    @Bean
    public ChatMemory chatMemory() {
        return MessageWindowChatMemory.withMaxMessages(10);
    }
    @Bean
    public ChatMemoryProvider chatMemoryProviderXiaozhi() {
        return memoryId -> MessageWindowChatMemory
                .builder()
                .id(memoryId)
                .maxMessages(50)
                .chatMemoryStore(mongoChatMemoryStore)
                .build();
    }
}

配置controller

java 复制代码
@Tag(name = "小智医疗")
@RestController
@RequestMapping("/xiaozhi")
public class XiaozhiController {
    @Autowired
    private XiaozhiAgent xiaozhiAgent;

    @Operation(summary = "对话")
    @PostMapping("/chat")
    public String chat(@RequestBody ChatForm chatForm){
        return xiaozhiAgent.chat(chatForm.getMemoryId(),chatForm.getMessage());
    }
}

建表语句(实体类)

sql 复制代码
CREATE DATABASE `guiguxiaozhi`;
USE `guiguxiaozhi`;
CREATE TABLE `appointment` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(50) NOT NULL,
  `id_card` VARCHAR(18) NOT NULL,
  `department` VARCHAR(50) NOT NULL,
  `date` VARCHAR(10) NOT NULL,
  `time` VARCHAR(10) NOT NULL,
  `doctor_name` VARCHAR(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
);
java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Appointment {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String username;
    private String idCard;
    private String department;
    private String date;
    private String time;
    private String doctorName;
}
java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class Department {
    private String name;
    private String doctorName;
    private LocalDate startTime;
    private LocalDate endTime;
}

Mapper与Service

XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.eden.mapper.AppointmentMapper">



</mapper>
java 复制代码
package com.eden.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.eden.dao.po.Appointment;
import org.apache.ibatis.annotations.Mapper;

/**
 * @author 25094
 * @create 2026/2/2 下午5:45
 * @description
 */
@Mapper
public interface AppointmentMapper extends BaseMapper<Appointment> {

}
java 复制代码
package com.eden.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.eden.dao.po.Appointment;

/**
 * @author 25094
 * @create 2026/2/3 下午2:05
 * @description
 */
public interface AppointmentService extends IService<Appointment> {
    Appointment getOne(Appointment appointment);
}
java 复制代码
@Service
public class AppointmentServiceImpl extends ServiceImpl<AppointmentMapper, Appointment> implements AppointmentService {

    /**
     * 查询订单是否存在
     * @param appointment
     * @return
     */
    @Override
    public Appointment getOne(Appointment appointment) {
        LambdaQueryWrapper<Appointment> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Appointment::getUsername, appointment.getUsername());
        queryWrapper.eq(Appointment::getIdCard, appointment.getIdCard());
        queryWrapper.eq(Appointment::getDepartment, appointment.getDepartment());
        queryWrapper.eq(Appointment::getDate, appointment.getDate());
        queryWrapper.eq(Appointment::getTime, appointment.getTime());

        return baseMapper.selectOne(queryWrapper);
    }
}

创建Tools(简单实现)

java 复制代码
@Component
public class AppointmentTools {
    @Autowired
    private AppointmentService appointmentService;

    @Tool(name="预约挂号",value = "根据参数,先执行工具方法queryDepartment查询告诉用户是否可预约," +
            "并让用户确认所有预约信息,用户确认后再进行预约")
    public String bookAppointment(Appointment  appointment){
        Appointment appointmentDB=appointmentService.getOne( appointment);
        if (appointmentDB==null){
            appointment.setId( null);//防止大模型幻觉设置了id
            if (appointmentService.save( appointment)){
                return "预约成功";
            }else{
                return "预约失败";
            }
        }
        return "该用户已预约挂号";
    }
    @Tool(name="查询是有号源",value = "根据科室名称,日期,时间和医生查询是否有号源,并返回给用户")
    public String queryDepartment(
            @P(value = "科室名称") String name,
            @P(value = "日期") String date,
            @P(value = "时间,可选值:上午、下午") String time,
            @P(value = "医生名称",required = false) String doctor
    ){
        System.out.println("查询是否有号源");
        System.out.println("科室名称:"+ name);
        System.out.println("日期:"+ date);
        System.out.println("时间:"+ time);
        System.out.println("医生名称:"+ doctor);
        Department department=new Department();
        department.setName("小儿科");
        department.setDoctorName("王医生");
        LocalDate startTime=LocalDate.of(2026,1,1);
        department.setStartTime(startTime);
        department.setEndTime(startTime.plusDays(180));
        if (department.getName().equals( name)){
            if (department.getDoctorName().equals( doctor)||doctor==null){
                if (department.getStartTime().isBefore( LocalDate.parse(date))&&department.getEndTime().isAfter( LocalDate.parse(date))){
                   return  "该医生有号源,请确认预约信息:科室名称:"+ name+",日期:"+ date+",时间:"+ time;
                }else {
                    return "您所预定的日期对应的医生排期不在排班时间内,该医生的排班时间为"+
                            department.getStartTime()+"至"+department.getEndTime();
                }
            }else {
                return "没有该医生";
            }
        }else{
            return "没有该科室";
        }

    }
    @Tool(name="取消预约",value = "根据参数,取消预约挂号")
    public String cancelAppointment(Appointment  appointment){
        Appointment appointmentDB=appointmentService.getOne( appointment);
        if (appointmentDB!= null) {
            //删除预约记录
            if (appointmentService.removeById(appointmentDB.getId())){
                return "取消成功";
            }else {
                return "取消失败";
            }
        }
        return "您没有预约记录,请核对预约科室和时间";
    }
}

把tool配置在智能体中

java 复制代码
@AiService(
        wiringMode = EXPLICIT,
        chatModel = "qwenChatModel",
        chatMemoryProvider = "chatMemoryProviderXiaozhi",
        tools = "appointmentTools"
)
public interface XiaozhiAgent {
    @SystemMessage(fromResource = "xiaozhi-prompt-template.txt")
    String chat(@MemoryId Long memoryId, @UserMessage String userMessage );
}

测试

RAG等其他内容待更新

相关推荐
苏渡苇1 小时前
用 Spring Boot 项目给工厂装“遥控器”:一行 API 控制现场设备!
java·人工智能·spring boot·后端·网络协议·边缘计算
我待_JAVA_如初恋1 小时前
重装系统后,idea被拦截,突然无法运行
java·ide·intellij-idea
东东5161 小时前
校园短期闲置资源置换平台 ssm+vue
java·前端·javascript·vue.js·毕业设计·毕设
像少年啦飞驰点、1 小时前
零基础入门 Spring Boot:从“Hello World”到独立可运行 Web 应用的完整学习闭环
java·spring boot·web开发·编程入门·后端开发
IT 行者1 小时前
Spring MVC 慎用@InitBinder,谨防内存泄漏
java·spring·mvc
刘一说2 小时前
Java中基于属性的访问控制(ABAC):实现动态、上下文感知的权限管理
java·网络·python
java1234_小锋2 小时前
高频面试题:Java中如何安全地停止线程?
java·开发语言
虫小宝2 小时前
淘宝返利软件的日志审计系统:Java Logback+ELK Stack实现操作日志的可追溯与可视化分析
java·elk·logback
铁蛋AI编程实战2 小时前
Falcon-H1-Tiny 微型 LLM 部署指南:100M 参数也能做复杂推理,树莓派 / 手机都能跑
java·人工智能·python·智能手机