”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 使用 Java + Quarkus + Langchain 构建可靠的 AI 代理 - 部分 AI 即服务

使用 Java + Quarkus + Langchain 构建可靠的 AI 代理 - 部分 AI 即服务

发布于2024-11-08
浏览:697

Autores

@herbertbeckman - LinkedIn
@rndtavares - LinkedIn

Partes do artigo

  1. Agente de IA confiável em prod com Java Quarkus Langchain4j - Parte 1 - AI as Service (este artigo)

  2. Agente de IA confiável em prod com Java Quarkus Langchain4j - Parte 2 - Memória (em breve)

  3. Agente de IA confiável em prod com Java Quarkus Langchain4j - Parte 3 - RAG (em breve)

  4. Agente de IA confiável em prod com Java Quarkus Langchain4j - Parte 4 - Guardrails (em breve)

Introdução

Sempre que temos um "boom" de uma tecnologia emergente, as empresas ficam ansiosas por aplicá-las e colher os resultados tão esperados do ponto de vista do negócio. É a corrida pela inovação e a disputa pelas vantagens do pioneirismo. No meio dessa corrida, muitas das vezes, as empresas, que antes estavam ansiosas, acabam desistindo por uma série de fatores, sendo um dos principais a confiabilidade de um sistema de forma geral. A inteligencia artificial (IA) está neste momento em uma das suas maiores provas de resistência e nosso trabalho como desenvolvedores de softwares é demostrar as empresas que sim, é possível realizar uma série de tarefas e processos com o uso consciente e correto da IA. Neste artigo iremos demonstrar, em 3 partes, quais são as funcionalidades e processos que devemos ter em um agente de IA confiável em produção para uma empresa ter os tão esperados resultados, bem como implementarmos juntos alguns conceitos utilizados no mercado. Iremos também detalhar os pontos de atenção desta solução e pedimos para que você, dev, realize o máximo de testes e nos dê o maior número de feedbacks possíveis para que, juntos, possamos melhorar ainda mais esse entendimento.

Funcionalidades Implementadas

  • Chat
  • Tools
  • Chat Memory
  • Retrieval-Augmented Generation (RAG)
  • Guardrails

Conceitos e definições

Assistente vs Copiloto vs Agente

Uma das primeiras dúvidas que se pode ter é no que um agente se diferencia dos demais casos de uso da IA. O Agente tem funcionalidades mais ligadas pra automação, enquantos os outros tem suas atividades voltadas à finalidade de assistentcia e otimização do tempo. Abaixo detalho melhor cada um dos casos de uso.

Assistentes

Os assistentes conseguem nos auxiliar e economizar um bom tempo verificando informação e sendo uma boa fonte de troca de conhecimento. Eles falam SOBRE os assuntos mais variados e podem nos ser úteis quando precisamos de uma linha de raciocínio claro para analisar as premisas de uma argumentação. Claro, eles tem bem mais poderes que isso, mas quero que você foque no que um assistente faz: ele conversa com você e somente isso. Ele pode falar sobre, sumarizar, detalhar, etc. Como exemplos temos: ChatGPT, Claude AI e Gemini.

Copilotos

Já os copilotos são um pouco mais poderosos que os assistentes. Eles conseguem realmente fazer algo, uma ação mais concreta como alterar um texto e ou sugerir modificações em tempo real, bem como dar dicas durante uma modificação e/ou evento acontecendo dentro de um contexto. Porém, como dito antes, ele depende do contexto pra fazer isso e nem sempre ele tem todas as informações necessárias para realizar um boa sugestão, ele também depende de sua autorização expressa, criando uma dependência direta com o usuário. Exemplos bons são: Github Copilot, Codium e Microsoft Copilot.

Agentes

Os agentes tem como objetivo principal realizar tarefas com objetivos claros. Tem o seu foco na automatização, ou seja, eles realmente fazem concreto e de forma autônoma. Tudo isso só se faz possível através das ferramentas que disponibilizamos a eles. O Agente não é o LLM em si, mas sim a sua aplicação que coordena esse LLM. Entenda o LLM como o cérebro do sistema, que toma as decisões, e a sua aplicação como os membros do corpo desse cérebro. Do que adianta eu pensar em pegar um copo de água se não consigo alcançá-lo com a minha mão? O seu agente proporciona ao LLM o poder de fazer algo de forma segura, auditável e, principalmente, confiável.

Partindo pra ação

Nesta primeira parte do artigo iremos implementar o AIService no projeto, que nada mais é do que a camada de interface com o nosso provedor de IA. Nesse projeto utilizamos o LLM da OpenAI, mas vc pode adicionar o seu provedor favorito e ajustar as dependências com base nele.

Agora que temos os conceitos bem definidos e já sabemos o que iremos fazer aqui, vamos pra codificação!

Criando o projeto

Crie um projeto quarkus, escolhendo o seu gerenciador de dependências e as extensões em Quarkus - Start coding.

Dependencias do projeto

Iremos utilizar o maven como gerenciador de dependências do projeto. A seguir as dependências iniciais que adicionamos.

Mavem


  io.quarkus
  quarkus-websockets-next



  io.quarkiverse.langchain4j
  quarkus-langchain4j-core
  0.20.3



  io.quarkiverse.langchain4j
  quarkus-langchain4j-openai
  0.20.3

Configuração do projeto

Adicione no arquivo src/main/resources/application.properties as seguintes propriedades:

quarkus.tls.trust-all=true
quarkus.langchain4j.timeout=60s
quarkus.langchain4j.openai.api-key=YOUR_OPENAI_API_KEY_HERE

Substitua YOUR_OPENAPI_KEY_HERE pela chave (apiKey) que você cadastrou na Plataforma da OpenAI.

DICA: crie uma variável de ambiente na sua IDE e depois modifique a property quarkus.langchain4j.openai.api-key para:

quarkus.langchain4j.openai.api-key=${OPEN_API_KEY:NAO_ENCONTREI_A_VAR}

Criando o nosso AIService

Primeiramente precisamos criar o nosso AIService que será a classe responsável por dar uma "personalidade" ao nosso agente. Para isso, no diretório src/main/java/, criaremos a classe de nome Agent com o seguinte código:

package ;

import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
@RegisterAiService
public interface Agent {

    @SystemMessage("""
            Você é um agente especializado em futebol brasileiro, seu nome é FutAgentBR
            Você sabe responder sobre os principais títulos dos principais times brasileiros e da seleção brasileira
            Sua resposta precisa ser educada, você pode deve responder em Português brasileiro e de forma relevante a pergunta feita

            Quando você não souber a resposta, responda que você não sabe responder nesse momento mas saberá em futuras versões.
            """)
    String chat(@UserMessage String message);
}

Como podem perceper pelo nosso SystemPrompt (@SystemMessage), criamos um agente especializado em futebol.

Criando o nosso chat

Agora que criamos o nosso agente, precisamos criar a classe que cuidará do nosso chat com ele. Para isso, no diretório src/main/java/, criaremos a classe de nome AgentWSEndpoint com o seguinte código:

package ;

import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;

@WebSocket(path = "/ws")
public class BotWSEndpoint {

    private final Agent agent;

    BotWSEndpoint(Agent agent) {
        this.agent = agent;
    }

    @OnTextMessage
    String reply(String message) {
        return agent.chat(message);
    }

}

Agora você já consegue conversar com o seu agente, que no momento ainda é um assistente, através da dev ui do quarkus. Segue alguns prints pra você se orientar:

Agente de IA confiável em prod com Java   Quarkus   Langchain- Parte  AI as Service

Agente de IA confiável em prod com Java   Quarkus   Langchain- Parte  AI as Service

Agente de IA confiável em prod com Java   Quarkus   Langchain- Parte  AI as Service

Agente de IA confiável em prod com Java   Quarkus   Langchain- Parte  AI as Service

Adicionando as nossas ferramentas (Function Calling)

Agora vamos para o detalhe que faz toda a diferença entre um agente e um assistente. Vamos dar a possibilidade do nosso agente realizar tarefas e/ou processos, adicionando as ferramentas (function calling). Antes de codificarmos isso, temos um breve gráfico demonstrando como a chamada de uma ferramenta funciona de forma macro.

Agente de IA confiável em prod com Java   Quarkus   Langchain- Parte  AI as Service
Source: superface.ai

Agora que sabemos como uma chamada de ferramenta funciona, precisamos criar a classe com nossas ferramentas, você também pode criar várias classes diferentes para cada ferramenta. Neste exemplo iremos criar uma "ToolBox", ou seja, uma caixa de ferramentas, agrupando as ferramentas que o nosso agente pode utilizar. Segue o código:

package ;

import dev.langchain4j.agent.tool.Tool;
import jakarta.enterprise.context.ApplicationScoped;

import java.time.LocalDate;
import java.time.LocalTime;

@ApplicationScoped
public class AgentTools {

    @Tool
    LocalDate currentDate() {
        System.out.println("Called currentDate()");
        return LocalDate.now();
    }

    @Tool
    LocalTime currentTime() {
        System.out.println("Called currentTime()");
        return LocalTime.now();
    }

    @Tool("Calcula a soma de dois números")
    int add(int a, int b) {
        System.out.println("Called add with a="   a   ", b="   b);
        return a   b;
    }

    @Tool("Calcula a raiz quadrada de um número")
    double sqrt(int x) {
        System.out.println("Called sqrt with x="   x);
        return Math.sqrt(x);
    }
}

Logo em seguida, adicionamos no nosso agente o anotação informando pra ele quais ferramentas ele tem disponível para utilizar, através da anotação @ToolBox(AgentTools.class). Ficando da seguinte maneira:

package ;

import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.ToolBox;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
@RegisterAiService
public interface Agent {

    @ToolBox(AgentTools.class)
    @SystemMessage("""
            Você é um agente especializado em futebol brasileiro, seu nome é FutAgentBR
            Você sabe responder sobre os principais títulos dos principais times brasileiros e da seleção brasileira
            Sua resposta precisa ser educada, você pode deve responder em Português brasileiro e de forma relevante a pergunta feita

            Quando você não souber a resposta, responda que você não sabe responder nesse momento mas saberá em futuras versões.
            """)
    String chat(@UserMessage String message);
}

Agora você pode perguntar ao seu agente que horas são, qual é a data de hoje, pedir pra ele somar dois números e calcular a raiz quadrada. Essas são as ferramentas que utilizamos aqui para ilustrar, mas você pode substituir isso por uma chamada HTTP, por uma função de hashing, por uma query SQL, etc. As possibilidades aqui são muitas.

Testando via Quarkus DEV UI

Segue o print de um dos testes realizados após adicionar as ferramentas:

Agente de IA confiável em prod com Java   Quarkus   Langchain- Parte  AI as Service

Agente de IA confiável em prod com Java   Quarkus   Langchain- Parte  AI as Service

Como pode ver, pra cada chamada de ferramenta teremos um log, evidenciando que o LLM realmente chamou o código que autorizamos ele a executar.

Próximos passos

Isso encerra o início da criação no nosso Agente. Em breve adicionaremos memória ao nosso Agente na parte 2, o RAG (Retrieval-Augmented Generation) na parte 3 e os Guardrails na parte 4 deste artigo. Espero que tenham gostado e até breve.

Mas você pode já acompanhar e ver TODO o código do artigo neste repositório do GitHub.

版本声明 本文转载于:https://dev.to/herbertbeckman/agente-de-ia-confiavel-em-prod-com-java-quarkus-langchain4j-parte-1-ai-as-service-4i14?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何使用Python有效地以相反顺序读取大型文件?
    如何使用Python有效地以相反顺序读取大型文件?
    在python 反向行读取器生成器 == ord('\ n'): 缓冲区=缓冲区[:-1] 剩余_size- = buf_size lines = buffer.split('\ n'....
    编程 发布于2025-07-22
  • 如何解决AppEngine中“无法猜测文件类型,使用application/octet-stream...”错误?
    如何解决AppEngine中“无法猜测文件类型,使用application/octet-stream...”错误?
    appEngine静态文件mime type override ,静态文件处理程序有时可以覆盖正确的mime类型,在错误消息中导致错误消息:“无法猜测mimeType for for file for file for [File]。 application/application/octet...
    编程 发布于2025-07-22
  • C++成员函数指针正确传递方法
    C++成员函数指针正确传递方法
    如何将成员函数置于c [&& && && && && && && && && && &&&&&&&&&&&&&&&&&&&&&&&华仪的函数时,在接受成员函数指针的函数时,要在函数上既要提供指针又可以提供指针和指针到函数的函数。需要具有一定签名的功能指针。要通过成员函数,您需要同时提供对象指针(此...
    编程 发布于2025-07-22
  • 在UTF8 MySQL表中正确将Latin1字符转换为UTF8的方法
    在UTF8 MySQL表中正确将Latin1字符转换为UTF8的方法
    在UTF8表中将latin1字符转换为utf8 ,您遇到了一个问题,其中含义的字符(例如,“jáuòiñe”)在utf8 table tabled tablesset中被extect(例如,“致电。为了解决此问题,您正在尝试使用“ mb_convert_encoding”和“ iconv”转换受...
    编程 发布于2025-07-22
  • 我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    将我的加密库从mcrypt升级到openssl 问题:是否可以将我的加密库从McRypt升级到OpenSSL?如果是这样,如何?答案:是的,可以将您的Encryption库从McRypt升级到OpenSSL。可以使用openssl。附加说明: [openssl_decrypt()函数要求iv参...
    编程 发布于2025-07-22
  • 用户本地时间格式及时区偏移显示指南
    用户本地时间格式及时区偏移显示指南
    在用户的语言环境格式中显示日期/时间,并使用时间偏移在向最终用户展示日期和时间时,以其localzone and格式显示它们至关重要。这确保了不同地理位置的清晰度和无缝用户体验。以下是使用JavaScript实现此目的的方法。方法:推荐方法是处理客户端的Javascript中的日期/时间格式化和时...
    编程 发布于2025-07-22
  • 如何在鼠标单击时编程选择DIV中的所有文本?
    如何在鼠标单击时编程选择DIV中的所有文本?
    在鼠标上选择div文本单击带有文本内容,用户如何使用单个鼠标单击单击div中的整个文本?这允许用户轻松拖放所选的文本或直接复制它。 在单个鼠标上单击的div元素中选择文本,您可以使用以下Javascript函数: function selecttext(canduterid){ if(do...
    编程 发布于2025-07-22
  • 如何高效地在一个事务中插入数据到多个MySQL表?
    如何高效地在一个事务中插入数据到多个MySQL表?
    mySQL插入到多个表中,该数据可能会产生意外的结果。虽然似乎有多个查询可以解决问题,但将从用户表的自动信息ID与配置文件表的手动用户ID相关联提出了挑战。使用Transactions和last_insert_id() 插入用户(用户名,密码)值('test','test...
    编程 发布于2025-07-22
  • 对象拟合:IE和Edge中的封面失败,如何修复?
    对象拟合:IE和Edge中的封面失败,如何修复?
    To resolve this issue, we employ a clever CSS solution that solves the problem:position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%)...
    编程 发布于2025-07-22
  • 为什么HTML无法打印页码及解决方案
    为什么HTML无法打印页码及解决方案
    无法在html页面上打印页码? @page规则在@Media内部和外部都无济于事。 HTML:Customization:@page { margin: 10%; @top-center { font-family: sans-serif; font-weight: bo...
    编程 发布于2025-07-22
  • Java中假唤醒真的会发生吗?
    Java中假唤醒真的会发生吗?
    在Java中的浪费唤醒:真实性或神话?在Java同步中伪装唤醒的概念已经是讨论的主题。尽管存在这种行为的潜力,但问题仍然存在:它们实际上是在实践中发生的吗? Linux的唤醒机制根据Wikipedia关于伪造唤醒的文章,linux实现了pthread_cond_wait()功能的Linux实现,利用...
    编程 发布于2025-07-22
  • 为什么使用Firefox后退按钮时JavaScript执行停止?
    为什么使用Firefox后退按钮时JavaScript执行停止?
    导航历史记录问题:JavaScript使用Firefox Back Back 此行为是由浏览器缓存JavaScript资源引起的。要解决此问题并确保在后续页面访问中执行脚本,Firefox用户应设置一个空功能。 警报'); }; alert('inline Alert')...
    编程 发布于2025-07-22
  • 如何实时捕获和流媒体以进行聊天机器人命令执行?
    如何实时捕获和流媒体以进行聊天机器人命令执行?
    在开发能够执行命令的chatbots的领域中,实时从命令执行实时捕获Stdout,一个常见的需求是能够检索和显示标准输出(stdout)在cath cath cant cant cant cant cant cant cant cant interfaces in Chate cant inter...
    编程 发布于2025-07-22
  • `console.log`显示修改后对象值异常的原因
    `console.log`显示修改后对象值异常的原因
    foo = [{id:1},{id:2},{id:3},{id:4},{id:id:5},],]; console.log('foo1',foo,foo.length); foo.splice(2,1); console.log('foo2', foo, foo....
    编程 发布于2025-07-22
  • PHP SimpleXML解析带命名空间冒号的XML方法
    PHP SimpleXML解析带命名空间冒号的XML方法
    在php 很少,请使用该限制很大,很少有很高。例如:这种技术可确保可以通过遍历XML树和使用儿童()方法()方法的XML树和切换名称空间来访问名称空间内的元素。
    编程 发布于2025-07-22

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3