Multi-User Agent Swarm für Ollama

Vor langer Zeit ersetzte die grafische Benutzeroberfläche die Befehlszeileneingabe. Es scheint, dass eine pseudo-grafische Schnittstelle das Interaktionsproblem für nicht vorbereitete Benutzer lösen könnte, aber es gibt einen Faktor, den nicht jeder bemerkt.

Wichtig! Die Entwicklung einer grafischen Benutzeroberfläche ist billiger als eine pseudo-grafische. Historisch gesehen wurde OBJC gleich nach der nächsten Cube -Veröffentlichung mit einem grafischen Formeditor eingeführt, bei dem Seiten mit einer Maus arrangiert werden konnten. In der modernen Welt bietet Frontend grafische Form Debugging durch Dev Tools, was im Wesentlichen dasselbe ist: Nominalcode ohne technische Details, und wenn Probleme auftreten, gibt es eine GUI, die den Fehler billiger findet.

Es ist jedoch sogar billiger, überhaupt keine Benutzeroberfläche zu erstellen. Sie benötigen keine statische IP, PCI -DSS, eine in Yandex und Google beworbene Domäne oder einen Hochladen, wenn Sie sich entscheiden, das Rad nicht neu zu erfinden und ein weiteres Webprodukt zu erstellen, das dreimal mehr kosten wird, um Besucher anzulocken, als sich zu entwickeln.

Das Telefon stammt aus dem Wort "Telefon", Phonetics - Sound. Anstatt eine große Anzahl von Knopfkombinationen für Figma, Mixer, Photoshop, Unreal Engine zu lernen, ist es einfacher, den Befehl einfach zu äußern. Wie dreht man eine Zeichnung in Archicad?
Agent Swarm ist wie Fragmente in Android oder ein Router in React: Sie können den Umfang der Aufgaben (Schaltflächen auf dem Bildschirm) basierend auf früheren Benutzereingaben angeben. Wenn beispielsweise ein Anruf auf einem SIP -Telefon eingeht, müssen Sie zunächst verstehen, ob die Person einen Artikel im Prinzip kaufen oder zurückgeben möchte, und ihnen dann eine Liste verfügbarer Produkte anbieten.
Das Steuerbüro fragt immer nach Belastung/Kredit in tabellarischer Form, sodass CRM -Systeme nirgendwo hingehen werden. Die Aufgabe von LLM besteht darin, natürlichen Text aus Chat- oder Spracherkennung zu analysieren und ihn in eine Funktionssignatur mit Namen und Argumenten zu verwandeln, damit sie aufgerufen werden können und Daten in die Datenbank geschrieben werden können.
Um dieses Problem zu lösen, ist es wichtig, mehrere Nuancen zu kennen:
Für jede offene Chat -Sitzung muss Swarm Orchestration mit einem Baum von Agenten durchgeführt werden, der einen gemeinsamen Chat -Historie zwischen ihnen und für verschiedene Benutzer getrennt ist. In diesem Code wird es unter der Haube von Agent-Swarm-Kit implementiert.
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 ( ) ;
} ,
}
} ) ) ;Beim Erstellen eines Agenten geben wir mindestens eine Systemnachricht an, die beschreibt, was er tun sollte. Wir geben den Anschluss zum Sprachmodell an, mit dem einige Agenten kostenlos verarbeitet werden können und gleichzeitig komplexe an den OpenAI -Cloud -Dienst delegieren. Wenn etwas nicht funktioniert, fügen wir dem System -Array Eingabeaufforderungen hinzu, beispielsweise Funktionen für Funktionen für Ollama.
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 ] ,
} ) ;In diesem Beispiel verwende ich Ollama, um Benutzeranfragen zu verarbeiten. Für diejenigen, die mit der Terminologie nicht vertraut sind: Der Prozess, bei dem ein Sprachmodell mit einem Benutzer als Eingabe einen Chat -Verlauf empfängt und eine neue Nachricht ausgibt, wird als Fertigstellung bezeichnet. Agent-Swarm-Kit verwendet eine abstrakte Schnittstelle, die ähnlich mit jedem Cloud-Anbieter oder lokalem Modell funktioniert. Verwenden Sie diesen Artikel, um Deepseek zu verbinden.
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" ] ,
} ;
} ,
} ) ; Das Ändern des aktiven Agenten und das Erhalten von Daten aus der Datenbank erfolgt über Toolaufrufe: Das Sprachmodell gibt spezielle XML zurück, die vom Framework für lokale Modelle oder vom Cloud -Anbieter für OpenAI verarbeitet werden, um einen externen Code in Python/JS usw. zu rufen usw. Das Ausführungsergebnis wird in der Chat -Geschichte als {"role": "tool", "content": "Product Paracetamol found in database: fever reducer for fighting flu"} . Aus der nächsten Benutzernachricht arbeitet das Sprachmodell mit Daten aus dem Tool.
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 : [ ] ,
} ,
} ,
} ) ;Um die anfänglichen Agentenmeldungen zu vermeiden, wird beim Wechsel von Agenten eine Benutzeranforderungsimulation gefragt, um Hallo zu sagen.
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" ] ,
} ,
} ,
} ) ;Sprachmodelle können ein Wörterbuch mit den benannten Parametern für Toolaufrufe bilden. OpenSource -Modelle verarbeiten dies jedoch schlecht, wenn ein geschlossener Schaltkreis technisch erforderlich ist. Es ist einfacher, das Gespräch selbst zu analysieren.
Mehrere Chatgpt -Sitzungen (Agenten) tätigen Tool -Anrufe. Jeder Agent kann verschiedene Modelle verwenden, z. B. Mistral 7b für die alltägliche Kommunikation, Nemoton für Geschäftsgespräche.
Der Agent Swarm leitet Nachrichten für jeden WebSocket -Kanal an die Active ChatGPT -Sitzung (Agent) über den clientID -URL -Parameter. Für jeden neuen Chat mit einer Person wird ein neuer Kanal mit einem eigenen Schwarm von Agenten erstellt.
Die aktive Chatgpt -Sitzung (Agent) im Schwarm kann durch Ausführen eines Tools geändert werden.
Alle Client -Sitzungen verwenden einen gemeinsam genutzten Chat -Nachrichtenverlauf für alle Agenten. Der Chat -Verlauf jedes Kunden speichert die letzten 25 Nachrichten mit Rotation. Zwischen ChatGPT -Sitzungen (Agenten) werden nur Assistenten- und Benutzermeldungen übertragen, während System- und Toolnachrichten auf den Bereich des Agenten beschränkt sind, sodass jeder Agent nur die Tools kennt, die sich darauf beziehen. Infolgedessen verfügt jede ChatGPT -Sitzung (Agent) über seine eindeutige Systemaufforderung.
Wenn die Ausgabe eines Agenten die Validierung fehlschlägt (nicht existierender Tool-Anruf, Tool-Anruf mit falschen Argumenten, leere Ausgabe, XML-Tags in der Ausgabe oder JSON in der Ausgabe), versucht der Rettungsalgorithmus, das Modell zu beheben. Zunächst wird frühere Nachrichten vor dem Modell ausgeblendet. Wenn das nicht hilft, wird es einen Platzhalter wie "Entschuldigung, ich habe es nicht verstanden. Könnten Sie bitte wiederholen?"
addAgent - Registrieren Sie einen neuen AgentenaddCompletion - Registrieren Sie ein neues Sprachmodell: Cloud, Lokal oder MockaddSwarm - Registrieren Sie eine Gruppe von Agenten zur Verarbeitung von Benutzer -ChatsaddTool - Registrieren Sie ein Tool zum Integrieren von Sprachmodellen in externe SystemechangeAgent - aktivem Agenten im Schwarm veränderncomplete - Fordern Sie eine Antwort auf eine Nachricht an, die an den Agentenschwarm übergeben wurdesession - Erstellen Sie eine Chat -Sitzung und geben Sie Rückrufe für die Abschluss der Sitzung und das Senden neuer Nachricht angetRawHistory - RAW -Systemgeschichte zum Debuggen erhaltengetAgentHistory - Die Geschichte sichtbar für den Agenten sichtbar für Selbstverschreibungsmechanismus und NachrichtenempfängercommitToolOutput - Funktionsausführungsergebnis an die Geschichte senden. Wenn eine Funktion aufgerufen wurde, friert der Agent, bis er eine Antwort erhältcommitSystemMessage - Ergänzungssystem -Eingabeaufforderung mit neuen EingabencommitFlush - Löschen Sie die Konversation für Agent, wenn falsche Antworten empfangen wurden oder das Modell fälschlicherweise ein Tool rekursiv aufruftexecute - Bitten Sie das neuronale Netzwerk, zuerst die Initiative zu ergreifen und an den Benutzer zu schreibenemit - Senden Sie eine vorbereitete Nachricht an den BenutzergetLastUserMessage - Holen Sie sich die letzte Nachricht vom Benutzer (ohne die Ausführung in Betracht zu ziehen)commitUserMessage - Benutzernachricht im Chat -Verlauf ohne Antwort speichern. Wenn Benutzer Nachrichten spams spam, ohne auf die Anfrageverarbeitung zu wartengetAgentName - Active Agent Name erhaltengetUserHistory - Erhalten Sie den Verlauf der BenutzernachrichtengetAssistantHistory - Erhalten Sie die Geschichte von SprachmodellnachrichtengetLastAssistantMessage - Holen Sie sich die letzte Nachricht vom SprachmodellgetLastSystemMessage - Erhalten