Essaim d'agent multi-utilisateurs pour ollla

Il y a longtemps, l'interface utilisateur graphique a remplacé la ligne de ligne de commande. Il peut sembler qu'une interface pseudo-graphique pourrait résoudre le problème d'interaction pour les utilisateurs non préparés, mais il y a un facteur que tout le monde ne le remarque pas.

Important! Développer une interface utilisateur graphique est moins cher que pseudo-graphique. Historiquement, juste après la version suivante du cube, OBJC a été introduit avec un éditeur de formulaire graphique où les pages pouvaient être organisées avec une souris. Dans le monde moderne, Frontend fournit un débogage de formulaire graphique via les outils de développement, ce qui est essentiellement la même chose: le code nominal sans détails techniques, et lorsque des problèmes surviennent, il y a une interface graphique qui rend la recherche de bogues moins chère.

Mais il est encore moins cher de ne pas faire une interface utilisateur du tout. Vous n'avez pas besoin d'une IP statique, d'un DSS PCI, d'un domaine promu dans Yandex et Google, ou Highload si vous décidez de ne pas réinventer la roue et de créer un autre produit Web qui coûtera trois fois plus pour attirer les visiteurs que de se développer.

Le téléphone vient du mot «téléphone», phonétique - son. Au lieu d'apprendre un grand nombre de combinaisons de bouton pour Figma, Blender, Photoshop, Unreal Engine, il est plus simple de simplement exprimer la commande. Comment pivotez-vous un dessin dans Archicad?
Agent Swarm est comme des fragments dans Android ou un routeur dans React: ils vous permettent de spécifier la portée des tâches (boutons à l'écran) en fonction de l'entrée utilisateur précédente. Par exemple, lorsqu'un appel arrive sur un téléphone SIP, vous devez d'abord comprendre si la personne veut acheter ou retourner un article en principe, puis leur offrir une liste de produits disponibles.
Le bureau de la taxe demandera toujours le débit / crédit sous forme tabulaire, donc les systèmes CRM n'iront nulle part. La tâche de LLM consiste à analyser le texte naturel à partir de chat ou de reconnaissance vocale et de le transformer en une signature de fonction avec nom et arguments afin qu'il puisse être appelé et que les données peuvent être écrites dans la base de données.
Pour résoudre ce problème, il est important de connaître plusieurs nuances:
Pour chaque session de chat ouverte, l'orchestration Swarm doit être effectuée avec un arbre d'agents ayant un historique de chat partagé entre eux et séparé pour différents utilisateurs. Dans ce code, il est implémenté sous le capot de l'agent-Swarm-Kit.
import { addSwarm } from "agent-swarm-kit" ;
export const ROOT_SWARM = addSwarm ( {
swarmName : 'root_swarm' ,
agentList : [
TRIAGE_AGENT ,
SALES_AGENT ,
] ,
defaultAgent : TRIAGE_AGENT ,
} ) ;
...
app . get ( "/api/v1/session/:clientId" , upgradeWebSocket ( ( ctx ) => {
const clientId = ctx . req . param ( "clientId" ) ;
const { complete , dispose } = session ( clientId , ROOT_SWARM )
return {
onMessage : async ( event , ws ) => {
const message = event . data . toString ( ) ;
ws . send ( await complete ( message ) ) ;
} ,
onClose : async ( ) => {
await dispose ( ) ;
} ,
}
} ) ) ;Lors de la création d'un agent, nous spécifions au moins un message système décrivant ce qu'il devrait faire. Nous spécifions le connecteur au modèle de langue, qui permettra à certains agents d'être traités localement gratuitement, tout en déléguant ceux complexes au service cloud OpenAI. Si quelque chose ne fonctionne pas, nous ajoutons des invites au tableau système, par exemple, les correctifs d'appel de fonction pour Olllama.
const AGENT_PROMPT = `You are a sales agent that handles all actions related to placing the order to purchase an item.
Tell the users all details about products in the database by using necessary tool calls
Do not send any JSON to the user. Format it as plain text. Do not share any internal details like ids, format text human readable
If the previous user messages contains product request, tell him details immidiately
It is important not to call tools recursive. Execute the search once
` ;
/**
* @see https://github.com/ollama/ollama/blob/86a622cbdc69e9fd501764ff7565e977fc98f00a/server/model.go#L158
*/
const TOOL_PROTOCOL_PROMPT = `For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call>
` ;
export const SALES_AGENT = addAgent ( {
agentName : "sales_agent" ,
completion : OLLAMA_COMPLETION ,
system : [ TOOL_PROTOCOL_PROMPT ] ,
prompt : AGENT_PROMPT ,
tools : [ SEARCH_PHARMA_PRODUCT , NAVIGATE_TO_TRIAGE ] ,
} ) ;Dans cet exemple, j'utilise Olllama pour traiter les demandes des utilisateurs. Pour ceux qui ne connaissent pas la terminologie: le processus où un modèle de langue reçoit l'historique de chat avec un utilisateur en entrée et publie un nouveau message est appelé l'achèvement. Agent-Swarm-Kit utilise une interface abstraite qui fonctionne de manière similaire avec n'importe quel fournisseur de cloud ou modèle local. Utilisez cet article pour connecter Deepseek.
import { addCompletion , IModelMessage } from "agent-swarm-kit" ;
const getOllama = singleshot ( ( ) => new Ollama ( { host : CC_OLLAMA_HOST } ) ) ;
export const OLLAMA_COMPLETION = addCompletion ( {
completionName : "ollama_completion" ,
getCompletion : async ( {
agentName ,
messages ,
mode ,
tools ,
} ) => {
const response = await getOllama ( ) . chat ( {
model : "nemotron-mini:4b" , // "mistral-nemo:12b";
keep_alive : "1h" ,
messages : messages . map ( ( message ) => omit ( message , "agentName" , "mode" ) ) ,
tools ,
} ) ;
return {
... response . message ,
mode ,
agentName ,
role : response . message . role as IModelMessage [ "role" ] ,
} ;
} ,
} ) ; La modification de l'agent actif et l'obtention de données de la base de données se font via les appels d'outils: le modèle de langue renvoie un XML spécial qui est traité par le cadre des modèles locaux ou par le fournisseur de cloud pour Openai pour appeler le code externe dans Python / JS, etc. Le résultat d'exécution est enregistré dans l'historique du chat comme {"role": "tool", "content": "Product Paracetamol found in database: fever reducer for fighting flu"} . À partir du prochain message utilisateur, le modèle de langue fonctionne avec les données de l'outil.
import { addTool , changeAgent , execute } from "agent-swarm-kit" ;
const PARAMETER_SCHEMA = z . object ( { } ) . strict ( ) ;
export const NAVIGATE_TO_SALES = addTool ( {
toolName : "navigate_to_sales_tool" ,
validate : async ( clientId , agentName , params ) => {
const { success } = await PARAMETER_SCHEMA . spa ( params ) ;
return success ;
} ,
call : async ( clientId , agentName ) => {
await commitToolOutput (
"Navigation success`,
clientId ,
agentName
) ;
await changeAgent ( SALES_AGENT , clientId ) ;
await execute ( "Say hello to the user" , clientId , SALES_AGENT ) ;
} ,
type : "function" ,
function : {
name : "navigate_to_sales_tool" ,
description : "Navigate to sales agent" ,
parameters : {
type : "object" ,
properties : { } ,
required : [ ] ,
} ,
} ,
} ) ;Pour éviter les messages initiaux initiaux de codage en dur, lors des agents de changement, une simulation de demande utilisateur se produit pour dire bonjour.
import {
addTool ,
commitSystemMessage ,
commitToolOutput ,
execute ,
getLastUserMessage ,
} from "agent-swarm-kit" ;
const PARAMETER_SCHEMA = z
. object ( {
description : z
. string ( )
. min ( 1 , "Fulltext is required" )
} )
. strict ( ) ;
export const SEARCH_PHARMA_PRODUCT = addTool ( {
toolName : "search_pharma_product" ,
validate : async ( clientId , agentName , params ) => {
const { success } = await PARAMETER_SCHEMA . spa ( params ) ;
return success ;
} ,
call : async ( clientId , agentName , params ) => {
let search = "" ;
if ( params . description ) {
search = String ( params . description ) ;
} else {
search = await getLastUserMessage ( clientId ) ;
}
if ( ! search ) {
await commitToolOutput (
str . newline ( `The products does not found in the database` ) ,
clientId ,
agentName
) ;
await execute (
"Tell user to specify search criteria for the pharma product" ,
clientId ,
agentName
) ;
return ;
}
const products = await ioc . productDbPublicService . findByFulltext (
search ,
clientId
) ;
if ( products . length ) {
await commitToolOutput (
str . newline (
`The next pharma product found in database: ${ products . map (
serializeProduct
) } `
) ,
clientId ,
agentName
) ;
await commitSystemMessage (
"Do not call the search_pharma_product next time!" ,
clientId ,
agentName
) ;
await execute (
"Tell user the products found in the database." ,
clientId ,
agentName
) ;
return ;
}
await commitToolOutput (
`The products does not found in the database` ,
clientId ,
agentName
) ;
await execute (
"Tell user to specify search criteria for the pharma product" ,
clientId ,
agentName
) ;
} ,
type : "function" ,
function : {
name : "search_pharma_product" ,
description :
"Retrieve several pharma products from the database based on description" ,
parameters : {
type : "object" ,
properties : {
description : {
type : "string" ,
description :
"REQUIRED! Minimum one word. The product description. Must include several sentences with description and keywords to find a product" ,
} ,
} ,
required : [ "description" ] ,
} ,
} ,
} ) ;Les modèles de langue peuvent former un dictionnaire des paramètres nommés pour les appels d'outils. Cependant, les modèles OpenSource gèrent cela mal s'il y a une exigence technique pour un circuit fermé; Il est plus simple d'analyser la conversation elle-même.
Plusieurs sessions Chatgpt (agents) font des appels d'outils. Chaque agent peut utiliser différents modèles, par exemple, Mistral 7B pour la communication quotidienne, Nemoton pour les conversations commerciales.
L'agent Swarm dirige les messages vers la session ChatGPT active (agent) pour chaque canal WebSocket à l'aide du paramètre URL clientId. Pour chaque nouvelle conversation avec une personne, une nouvelle chaîne est créée avec son propre essaim d'agents.
La session ChatGpt active (agent) dans l'essaim peut être modifiée en exécutant un outil.
Toutes les sessions clients utilisent un historique de messages de chat partagé pour tous les agents. L'historique de chat de chaque client stocke les 25 derniers messages avec rotation. Entre les sessions Chatgpt (agents), seuls les messages de type assistant et de type utilisateur sont transmis, tandis que les messages système et outils sont limités à la portée de l'agent, de sorte que chaque agent ne connaît que les outils qui lui sont liés. En conséquence, chaque session ChatGpt (agent) a son invite de système unique.
Si la sortie d'un agent échoue à la validation (appel d'outil inexistant, appel à outil avec des arguments incorrects, sortie vide, balises XML en sortie ou JSON en sortie), l'algorithme de sauvetage essaiera de fixer le modèle. Tout d'abord, il masquera les messages précédents du modèle; Si cela n'aide pas, il renverra un espace réservé comme "Désolé, je n'ai pas compris. Pourriez-vous s'il vous plaît répéter?"
addAgent - Enregistrez un nouvel agentaddCompletion - Enregistrez un nouveau modèle de langue: cloud, local ou simuléaddSwarm - Enregistrez un groupe d'agents pour le traitement des chats des utilisateursaddTool - Enregistrez un outil pour intégrer les modèles de langage dans les systèmes externeschangeAgent - Changer un agent actif dans l'essaimcomplete - Demandez une réponse à un message transmis à l'agent Swarmsession - Créez une session de chat, fournissez des rappels pour l'achèvement de la session et l'envoi du nouveau messagegetRawHistory - Obtenez l'historique du système brut pour le débogagegetAgentHistory - Get History Visible pour l'agent ajusté pour les mécanismes d'auto-récupération et les destinataires des messagescommitToolOutput - Envoyez le résultat de l'exécution de la fonction à l'historique. Si une fonction était appelée, l'agent gèle jusqu'à recevoir une réponsecommitSystemMessage - complément d'invite du système avec de nouvelles entréescommitFlush - Conversation claire pour l'agent si des réponses incorrectes ont été reçues ou si le modèle appelle à tort un outil récursivementexecute - Demandez au réseau neuronal de prendre l'initiative et d'écrire à l'utilisateur d'abordemit - Envoyez un message préparé à l'utilisateurgetLastUserMessage - Obtenez le dernier message de l'utilisateur (sans envisager d'exécuter)commitUserMessage - Enregistrez le message de l'utilisateur dans l'historique du chat sans réponse. Si l'utilisateur spams messages sans attendre le traitement de la demandegetAgentName - Obtenez le nom de l'agent actifgetUserHistory - Obtenez l'historique des messages utilisateurgetAssistantHistory - Obtenez l'historique des messages du modèle linguistiquegetLastAssistantMessage - Obtenez le dernier message du modèle de languegetLastSystemMessage - Obtenez le dernier supplément d'invite du système