Em apenas alguns anos, a Inteligência Artificial passou de uma tendência emergente para um componente essencial do software moderno. ChatGPT, Grok, Gemini, e outros assistentes AI agora tem um papel importante na vida diária de todos, tanto profissionalmente quanto pessoalmente.
É por isso que 4D 21 apresenta 4D.Vectors e 4D AI Kit: para dar aos desenvolvedores 4D ferramentas simples e efetivas para adicionar funcionalidades alimentadas por IA em suas aplicações.
Já compartilhamos muitos exemplos, tutoriais, e webinars sobre IA, mas recentemente me perguntei: o que seria necessário para trazer IA para uma aplicação 4D de 30 anos?
Poderia simplesmente pedir a essa aplicação os seus 10 principais clientes e obter instantaneamente um bom gráfico de volta?
Bem, adivinha? Acabou por ser tão simples que merece o seu próprio post no blogue.
Vamos começar com o link de fontes do projeto no Github e um vídeo de demonstração rápido
A aplicação 4D Invoice
A aplicação 4D Invoice está disponível no 4D Depot há algum tempo. Serve como um exemplo sólido de como gerenciar produtos, clientes, e faturas de uma maneira limpa e estruturada. Embora já use o modo de projeto (um requisito para tirar partido das últimas funcionalidades 4D AI) ainda não utiliza DataClasses, e os seus formulários são anteriores à mais recente classe Form. A base de código é bastante grande, usa padrões de design genéricos e inclui várias regras de negócio não triviais.
Para esta experiência, o meu objetivo era adicionar um novo formulário que permitisse a interação com a aplicação através de IA. Queria que os utilizadores pudessem conversar com um assistente de IA e fazer qualquer coisa, desde perguntas simples a consultas analíticas mais complexas. Na utilização real, isto daria instantaneamente a uma aplicação bastante “antiga” um toque moderno, alinhado com as expectativas actuais dos utilizadores orientados para a IA. De facto, muito emocionante!
Outro objetivo era evitar fazer quaisquer alterações à estrutura de dados e não alterar os mecanismos de IU existentes. Por outras palavras, a atualização da IA tinha de ser altamente flexível, minimamente intrusiva e, idealmente, reutilizável noutras aplicações antigas.
Como consequência, esta atualização não se baseia em 4D.Vectors ou embeddings. Voltaremos a isso mais tarde, mas vale a pena enfatizar agora: trazer IA para sua aplicação 4D não requer necessariamente embeddings ou busca semântica. Eles são conceitos separados.
Preparando-se
Em 4D, já compartilhamos vários exemplos e webinars baseados em IA, incluindo demonstrações recentes sobre a construção de um sistema RAG usando chamadas de ferramentas. Para este projeto, decidi extrair o formulário de chat UI da demonstraçãoPeople & Skills e torná-lo mais genérico e reutilizável.
A IU do chat é constituída pelos seguintes componentes:
- O formulário AIChat, juntamente com a sua classe de formulário formAIChat.4dm. Este formulário fornece
- uma entrada de texto simples para o pedido do utilizador
- uma área Web que apresenta a conversa com o modelo
- A classe AI_ChatWithTools, responsável por:
- instanciar um bot de IA usando o auxiliar de chat do 4D AI Kit, com base no servidor de inferência e no modelo configurado em Resources/AIProvider.json
- carregar as ferramentas definidas em Resources/AITools.json
- alojar a implementação dessas ferramentas
A classe corre inteiramente em modo streaming para que o utilizador tenha uma experiência semelhante à do ChatGPT, com as respostas a aparecerem progressivamente.
- A renderização da conversa é tratada pelo singleton ChatHTMLRenderer.
Ele pega a coleção de mensagens armazenadas no AIBot e as transforma em HTML usando os modelos localizados na pasta Resources:
-
- chat-template.html
- chat-template.css
- tool-icon.svg
Vou ser honesto: a renderização HTML foi essencialmente “codificada por vibração” com a ajuda do agente GitHub do VS Code e do Claude Sonnet 4.5. A parte mais desafiante foi garantir a renderização correta durante o streaming, especialmente para elementos como tabelas, chamadas de ferramentas e gráficos.
Chamada de ferramenta de IA
Como provavelmente sabe, um modelo de IA não sabe inerentemente nada sobre os seus dados. Apenas obtém acesso aos mesmos através de ferramentas que o utilizador fornece explicitamente. Nesta secção, explicarei quais as ferramentas que implementei e como funcionam.
tool_getProducts, tool_getClients, tool_getInvoices, tool_getInvoiceLines
Todas estas ferramentas são responsáveis pela consulta da base de dados. Cada uma delas efectua uma consulta ORDA simples com base nos campos solicitados pelo modelo.
Claro que há uma vasta área para melhorias nessas funções de ferramentas. Graças às caraterísticas nativas de 4D 21 AI, poderíamos imaginar uma busca semântica usando 4D.Vetor e embeddings para aumentar a recuperação de Clients. Como isso significaria mudanças mais profundas na estrutura, escolhi um caminho mais simples.
Vamos quebrar tool_getClients, já que o mesmo padrão se aplica a todas as outras ferramentas “getter”:
Function tool_getClients($input : Object) : Object
Esta função recebe um objeto como entrada e devolve um objeto como saída. O input descreve os parâmetros de pesquisa; o output contém os dados resultantes.
Validação da entrada
var $validation; $returnObject : Object
var $entities : cs.CLIENTSSelection:=ds.CLIENTS.all()
$validation:=JSON Validate($input; This._getToolArgumentsSchema(This._functionName(Call chain)))
If (Not($validation.success))
return {error: "Could not validate input parameters against JSON Schema, call the tool again with proper input parameters"}
End if
A primeira etapa é validar se a entrada corresponde ao esquema definido em AITools.json.
A maioria dos modelos modernos produzirá chamadas de ferramenta bem estruturadas, mas, dependendo do modelo, a validação ainda é uma boa prática de segurança.
Se a validação falhar, a ferramenta retorna um objeto simples contendo uma propriedade de erro. É importante saber que o modelo lê esse valor, portanto, sinta-se à vontade para ser explícito sobre o que deu errado.
Configuração de valores de entrada padrão
$input.ID:=($input.ID) || "any"
$input.Name:=($input.Name) || "@"
$input.Contact:=($input.Contact) || "@"
$input.Total_Sales:=$input.Total_Sales || {}
$input.Total_Sales.min:=$input.Total_Sales.min || 0
$input.Total_Sales.max:=$input.Total_Sales.max || 9999999
$input.orderBy:=($input.orderBy) || {}
$input.orderBy.field:=($input.orderBy.field) || "Name"
$input.orderBy.order:=($input.orderBy.order) || "asc"
$input.top:=($input.top) || This.defaultTop
$input.countOnly:=($input.countOnly) || False
Os padrões garantem consultas ORDA seguras, mas eles também têm um benefício colateral interessante: escrever um esquema JSON completo no AITools.json pode ser tedioso. Em vez disso, deixei o GitHub Copilot inferir o esquema a partir do código e da estrutura padrão e o resultado foi excelente. O Copilot produziu uma definição de esquema precisa com base apenas na lógica acima.
Parâmetros a serem observados
- orderBy e top reduzem o tamanho da saída (e, portanto, o uso de token).
- countOnly permite que o modelo solicite apenas contagens de registos sem recuperar dados.
Exemplos práticos:
- Se o modelo precisar do número de clientes → countOnly = true
- Se pretender os 5 principais clientes → top = 5 e orderBy: {field: “Total_Sales”, order: “desc”}
Inicialização do objeto de saída
$returnObject:={}
$returnObject.form:="Clients"
$returnObject.dataClass:="CLIENTS"
$returnObject.counts:={}
$returnObject.counts.total:=$entities.length
- form indica qual formulário da interface do usuário pode ser usado para exibir os resultados – veremos isso mais tarde.
- dataClass identifica a classe de dados envolvida, importante para a consulta em várias tabelas – veremos isso também mais tarde.
- counts.total é o número de registos antes da aplicação de qualquer filtro.
Executar a consulta
If ($input.ID#"any")
$entities:=$entities.query("ID = :1"; $input.ID)
End if
$entities:=$entities.query("Name = :1 and Contact = :2 and Total_Sales >= :3 and Total_Sales <= :4 order by "+$input.orderBy.field+" "+$input.orderBy.order; $input.Name; $input.Contact; $input.Total_Sales.min; $input.Total_Sales.max)
Esta parte é simples e poderia certamente ser mais genérica, mas não foi esse o meu foco aqui.
Finalizando o resultado
$returnObject.counts.totalFiltered:=$entities.length
If ($returnObject.counts.totalFiltered>$input.top)
$entities:=$entities.slice(0; $input.top)
End if
$returnObject.counts.totalSent:=$entities.length
$returnObject.entities:=($input.countOnly) ? [] : $entities.toCollection("ID, Name, Contact, City, State, Country, Discount_Rate, Total_Sales, Comments")
return $returnObject
Detalhamento
- counts.totalFiltered: número de registos após a filtragem
- counts.totalSent: número efetivamente devolvido (após aplicação do top)
- entities: a coleção final de dados (ou vazia se countOnly = true)
Verificará que a ferramenta controla exatamente quais os atributos que são enviados para o modelo. É importante encontrar o equilíbrio certo.
O que deve ser considerado
- Qualidade da conversa: mais dados → melhores informações
- Desempenho: mais dados → processamento mais longo
- Janela de contexto: os modelos locais podem degradar-se quando sobrecarregados
- Custos: mais tokens → maior utilização nos servidores de inferência na nuvem
- Confidencialidade: tenha sempre em conta o RGPD e a sensibilidade dos dados ao escolher os campos
O resultado final
Qualquer objeto que a ferramenta retorne, 4D AI Kit o serializa de forma transparente e o entrega ao modelo. Muito conveniente, certo?
Consulta entre tabelas e o prompt inicial do sistema
Outra descoberta interessante durante esse projeto foi que o modelo não entendia automaticamente as relações entre tabelas. Por exemplo, pedidos como:
Dê-me os 5 principais clientes, a sua fatura mais elevada e o seu produto mais encomendado
nem sempre produziam resultados fiáveis.
O problema era simples: o modelo não percebia que o ID na tabela Clientes devia ser utilizado para consultar a tabela Facturas, ou que as linhas da fatura se relacionam com os produtos, e assim por diante.
Por outras palavras, eu precisava de dar ao modelo algum conhecimento sobre a estrutura, mas sem ir demasiado longe. Os meus requisitos eram:
- Nenhum prompt de sistema codificado: Não quero atualizar manualmente o aviso sempre que a estrutura for alterada.
- Nenhum catálogo de estrutura completo: O modelo não precisa de todo o esquema; apenas das relações relevantes.
Para conseguir isso, eu reutilizei uma classe muito útil de Thomas Maul: StructureInfo.4dm, extraindo apenas as relações entre as tabelas.
A partir daí, gerei o meu prompt inicial do sistema:
var $relations : Collection:=This._relationsInfos()
$systemPrompt:="You are a helpful assistant. I need your help to answer questions data stored in my application.\n"+\
"**CONTEXT**\n"+\
"The application stores data about invoices, products and clients"+\
"In some cases, you'll need to cross query several tables (dataClasses) in order to answer."+\
"To help you, here are the application relations between tables (dataClasses):\n"+\
JSON Stringify($relations)+"\n"+\
"**INSTRUCTIONS**\n"+\
"Analyze questions and answer step by step.\n"+\
"Use the tools at your disposal to answer everytime you think they are relevant.\n"+\
"**FORMATING**\n"+\
"Use HTML everytime.\n"+\
"Use bullet lists and Tables everytime everytime necessary\n"+\
"**IMPORTANT**\n"+\
"When calling tools, always include all required arguments in valid JSON.\n"+\
"Do not call a tool with empty arguments. If a value is missing, choose a reasonable default.\n"+\
"Always double check tools results before answering. Especially when they rely on vector search. \n"+\
"Indeed they may return results not matching with your search intention.\n"+\
"When tool calling returns data not related with the initial question, or that you cannot use to answer,\n"+\
"avoid detailing such results too much and stay short.\n"
Combinando estes metadados de relação com a informação dataClass devolvida por cada ferramenta, o modelo foi finalmente capaz de compreender como encadear chamadas de ferramentas entre tabelas.
Isto melhorou drasticamente a qualidade e a precisão das respostas do modelo, especialmente para análises de várias tabelas, tais como
principais clientes por receita
ou
produtos mais frequentemente encomendados
ou
maiores facturas por cliente
Experiência do utilizador: abrir um formulário 4D diretamente a partir do IU de conversação
Nas minhas experiências anteriores com RAG, mostrei frequentemente os resultados numa caixa de listagem separada. Esta abordagem funcionava, mas tinha vários inconvenientes:
- Precisava que o modelo fornecesse, numa secção oculta separada, a lista de IDs de entidades a apresentar na caixa de listagem. Isso significava mais tokens, respostas mais lentas e truques HTML extras (como ocultar a lista de IDs dentro de um bloco comentado).
- Foi necessário trabalho adicional para tornar a caixa de listagem genérica o suficiente para lidar com diferentes tipos de entidades (Clientes, Produtos, Facturas, …).
- Limitava a interface do utilizador e parecia menos integrada na aplicação.
Para este projeto, queria algo mais simples, mais elegante e mais natural para o utilizador.
Então adotei uma nova estratégia: cada vez que a IA menciona uma entidade, gera um hyperlink personalizado que abre diretamente o formulário 4D correspondente, filtrado na entidade selecionada.
Isso acabou sendo surpreendentemente fácil. Simplesmente adicionei as seguintes instruções ao prompt do sistema:
"**CUSTOM URL HANDLING**\n"+\
"Tools responses give information about the form to open when available, the dataClass and entities ID\n"+\
"When you display any element coming from a tool response, you must use a custom url so that the user can open the corresponding form\n"+\
"Such custom url must follow the following syntax examples:\n"+\
"<a href=\"myapp://openform?form=Products&dataClass=PRODUCTS&entities=989511\">A single product</a>\n"+\
"<a href=\"myapp://openform?form=Invoices&dataClass=INVOICESS&entities=654KJY,6467HGS,79864JSD\">A list of invoices</a>\n"
Uma vez que as minhas ferramentas devolvem sempre o formulário apropriado, dataClass, e a lista de entidades, o modelo tem tudo o que precisa para gerar estas ligações corretamente. Modelos de IA são muito bons nesse tipo de saída estruturada.
Manipulando as URLs personalizadas em 4D
Tudo o que precisava no lado 4D era um pequeno mecanismo de filtragem de URL na área web para intercetar myapp://links e abrir o formulário certo com a seleção de entidade certa:
Function webAreaEventHandler($formEventCode : Integer)
var $formToOpen : Text
var $entitySelectionToShow : 4D.EntitySelection
var $queryObject : Object
Case of
: ($formEventCode=On Load)
ARRAY TEXT($filters; 0)
ARRAY BOOLEAN($allowDeny; 0)
APPEND TO ARRAY($filters; "myapp://*") // Intercept all URLs starting with myapp://
APPEND TO ARRAY($AllowDeny; False) //Allow
WA SET URL FILTERS(*; "Web Area"; $filters; $allowDeny)
: ($formEventCode=On URL Filtering)
$url:=WA Get last filtered URL(*; "Web Area")
// Parse the URL to determine what to do
Case of
: ($url="myapp://openform?@")
$queryObject:=This.queryObjectFromUrl($url)
If ($queryObject=Null)
return
End if
If ($queryObject.entitiesCollection.length>0)
$entitySelectionToShow:=ds[$queryObject.dataClass].query("ID in :1"; $queryObject.entitiesCollection)
CALL WORKER("Generic"; "W_Generic"; $queryObject.form; True; $entitySelectionToShow)
Else
CALL WORKER("Generic"; "W_Generic"; $queryObject.form; False)
End if
End case
End case
O resultado
O resultado final é uma experiência de usuário muito suave, onde a UI conversacional se mistura perfeitamente com o resto da aplicação 4D.
Os usuários podem conversar com a IA, explorar dados, e abrir instantaneamente formulários 4D nativos, tudo sem alterar a lógica ou estrutura central da aplicação.
Um modelo de interação moderno, sentado bem em cima de uma aplicação 4D clássica… com quase nenhuma intrusão.
Uma ferramenta adicional: tool_createInvoice
A base de dados de amostra fornecida no projeto GitHub incluía apenas um punhado de facturas, não o suficiente para tornar as interações com a IA interessantes. Eu precisava de mais dados.
Criar um formulário personalizado ou escrever mais uma rotina de geração de facturas parecia tedioso e irrelevante para o objetivo desta demonstração. E então apercebi-me: Eu já tinha tudo o que precisava.
- Uma interface de conversação.
- Um agente de IA consciente do meu modelo de dados.
- Conhecimento de clientes, produtos, preços, relações…
Então, porque não deixar a IA criar facturas diretamente a partir da interface de conversação?
Tudo o que tinha de fazer era acrescentar uma nova ferramenta: tool_createInvoice.
A minha abordagem foi simples:
- Implementar uma função simples de criação de facturas dentro de AI_ChatWithTools.
- Envolver todo o processo numa transação:
- Em caso de erro: reverter e enviar uma mensagem de erro clara para o modelo.
- Em caso de sucesso: confirmar a fatura e devolver os seus detalhes.
- Documentar cuidadosamente o esquema da ferramenta, especialmente os seus parâmetros de entrada.
- Depois… deixe o GitHub Copilot gerar o esquema JSON dentro de AITools.json.
O resultado?
Agora posso pedir à IA para gerar uma fatura totalmente nova por conta própria. Ela encontra um cliente relevante, seleciona produtos, calcula os totais e chama a ferramenta, tudo através da mesma IU de conversação que construí anteriormente.
Acabou por ser uma forma surpreendentemente elegante de gerar dados de teste realistas.
Uma observação interessante: alguns modelos (nomeadamente o OpenAI GPT-4.1) tendem a pedir confirmação, ou pelo menos detalhes de entrada suficientes, antes de criarem efetivamente uma fatura… a não ser que se dê instruções explícitas para que não o façam.
Renderização HTML das mensagens OpenAI
Como mencionado anteriormente, esta parte foi inteiramente codificada por vibração. Reutilize-a, adapte-a ou ignore-a, conforme as suas necessidades. A ideia é simples: pegar numa coleção de mensagens OpenAI e apresentá-las de forma agradável em HTML.
Mas, claro, os nossos utilizadores finais merecem sempre mais do que um simples texto. Por isso, acrescentei algumas caraterísticas de qualidade de vida:
- Um layout limpo e estruturado para chamadas de ferramentas, incluindo uma indicação visual enquanto uma ferramenta está a ser executada.
- Um botão “Copiar” com um clique para copiar facilmente qualquer mensagem do assistente e colá-la num documento, quer seja o Microsoft Word, o Google Docs ou um e-mail.
Isto faz com que toda a experiência de conversação pareça polida e profissional, ao mesmo tempo que é rápida e leve de implementar.
- Um botão de cópia para cada tabela, que permite ao utilizador extrair instantaneamente os dados tabulares em bruto e colá-los diretamente numa folha de cálculo do tipo Excel.
Isto é extremamente conveniente quando os utilizadores pretendem manipular ou comparar dados fora da aplicação sem exportar nada.
Capacidades de criação de gráficos diretamente na IU de conversação
Quando conversam com uma IA sobre facturas, clientes ou produtos, os utilizadores esperam naturalmente gráficos. Classificações, comparações, tendências… as informações visuais fazem agora parte de qualquer experiência moderna.
Por isso, decidi trazer a renderização de gráficos em linha para a IU do chat e acabou por ser surpreendentemente fácil, graças a um pouco de vibe-coding com o GitHub Copilot.
Eu implementei em três passos:
1) Importar Chart.js dentro de chat-template.html e estender meu mecanismo de renderização HTML para reconhecer blocos <chart>…</chart>.
Enquanto a IA está a transmitir a sua resposta, uma pequena animação CSS mostra um marcador de posição do gráfico, dando uma sensação de UX polida.
2) Estender o prompt do sistema para ensinar explicitamente a IA como solicitar gráficos. Acrescentei instruções como:
"**CHARTS**\n"+\
"Create charts for rankings, comparisons, trends, or distributions. Format: <chart>{...JSON...}</chart>\n"+\
"Available types: bar, line, pie, doughnut, radar, polarArea. Always include:\n"+\
"- \"type\": chart type\n"+\
"- \"data.labels\": array of x-axis labels\n"+\
"- \"data.datasets\": array with \"label\", \"data\" (numeric array), \"backgroundColor\" (color array)\n"+\
"- \"options.responsive\": true\n"+\
"- \"options.plugins.title\": {\"display\": true, \"text\": \"Chart Title\"}\n"+\
"- \"options.scales.y.beginAtZero\": true (for bar/line charts)\n"+\
"Use distinct vibrant colors (e.g., #4caf50, #2196f3, #ff9800, #e91e63, #9c27b0). Set \"legend.display\" to false for single datasets, true for multiple.\n"
3) Deixar a IA gerar definições de gráficos em JSON, que a área Web processa instantaneamente utilizando Chart.js.
O resultado é verdadeiramente impressionante: o modelo produz imagens com significado diretamente no fluxo da conversa, enriquecendo dramaticamente a experiência.
Dito isto, eu não confiaria inteiramente no modelo para cálculos complexos. Para a utilização em produção, seria sensato adicionar uma série de ferramentas dedicadas a:
- totais e acumulação
- agrupamentos e agregações
- resumos baseados no tempo
- comparações de períodos
- extração de KPI
4D realizaria os cálculos difíceis e precisos, e a IA simplesmente os solicitaria e desenharia os gráficos com base em dados numéricos precisos e validados.
Uma combinação perfeita de fiabilidade 4D e UI alimentada por IA.
Conclusão
Então… e agora?
Essencialmente, pegámos numa aplicação de negócios um pouco antiquada e infundimos-lhe uma experiência moderna, conversacional e alimentada por IA.
Mas ver isso simplesmente como “mais uma forma de obter dados” seria perder completamente o ponto.
Claro, não é necessária uma interface de conversação para obter “os 5 principais clientes por vendas totais em 2025”.
Um ecrã básico com um par de filtros pode fazer isso.
Mas rapidamente se percebe que isto é outra coisa.
Trata-se de uma nova forma de explorar os seus dados.
Com um único formulário, a interface de chat, os utilizadores podem perguntar qualquer coisa: facturas, produtos, clientes… quer queiram uma tabela, um gráfico ou ambos.
Isto elimina a necessidade de criar inúmeros ecrãs especializados para necessidades de relatórios ligeiramente diferentes.
E depois de ver isso, também compreende que isto vai muito além da consulta e apresentação de dados.
O verdadeiro valor não está em responder “mostre-me isto”.
O verdadeiro valor está em responder “ajuda-me a compreender isto”.
O seu pedido já não deve ser:
Desenhar um gráfico comparando 2024 e 2025
mas sim:
Forneça um relatório de vendas detalhado para 2025, incluindo o total de vendas, os 5 principais produtos por receita, os 5 principais clientes por vendas, a tendência de vendas ao longo do ano e as facturas não pagas. Apresente gráficos e tabelas relevantes e explique quaisquer tendências, oportunidades e riscos notáveis. Além disso, compare o desempenho dos produtos com o registado em 2024 e sugira ideias práticas para melhorar as vendas e o fluxo de caixa. Incluir uma análise do desempenho por cliente e por produto com os principais indicadores. Pretendo números, explicações e ideias acionáveis. O seu relatório incluirá comparações entre 2024 e 2025. Os objectivos deste relatório são: como aumentar as vendas, que clientes visar, como aumentar o desempenho do produto.
E este é o tipo de resposta que você – e os seus utilizadores – irão obter:
Uma única interface de conversação, perfeitamente integrada em sua aplicação 4D, capaz de insights, raciocínio, visualização, criação…
Isso não é um recurso.
É um passo em direção a uma nova geração de aplicações de negócios.
Como reutilizar isso em seu próprio projeto?
Se ler este post e brincar com a aplicação despertou o seu interesse, pode simplesmente:
- Copiar os seguintes ficheiros para o seu projeto:
- Classes
- AI_ChatWithTools.4dm
- ChatHTMLRenderer.4dm
- formAIChat.4dm
- StructureInfo.4dm
- Formulário
- AIChat
- Recursos activos
- AIprovider.json
- AITools.json
- chat-template.html
- chat-template.css
- tool-icon.svg
- Fazer as adaptações necessárias
- Implementação do prompt do sistema e das ferramentas em AI_ChatWithTools.4dm
- Definição das ferramentas subsequentes em AITools.json (basta usar o AI para isso!)
- Adaptar a abertura do formulário à sua aplicação em formAIChat.webAreaEventHandler()
- Definições do fornecedor de IA em AIprovider.json
- Classes
E é isso!
Vou terminar com algumas observações adicionais:
Neste exemplo, toda a lógica da ferramenta é implementada dentro da classe AI_ChatWithTools. Eu fiz isso porque meu objetivo era ser o menos intrusivo possível, e porque o projeto não implementa DataClasses. Dependendo da arquitetura da sua base de código, uma prática melhor poderia ser ter essas ferramentas direcionadas para funções de DataClasses.
Todo este projeto funciona perfeitamente com o OpenAI e o modelo gpt-4.1 ou superior. Trabalhar com um modelo local levanta desafios adicionais que podem ser discutidos no fórum 4D!


