Workflows en Graphe
Construisez des flux IA complexes avec conditions, branches, exécution parallèle et export de diagrammes Mermaid.
Pourquoi des Workflows en Graphe ?
Alors que les workflows linéaires (Multi-Step) sont parfaits pour les pipelines simples, les Workflows en Graphe vous permettent de construire des flux complexes avec des conditions, des branches, des boucles et une exécution parallèle. Pensez-y comme des organigrammes pour l'IA — vous définissez des nœuds (actions) et des arêtes (connexions), et le moteur de graphe gère l'exécution.
import { createOrka, OpenAIAdapter } from 'orkajs';import { startNode, endNode, actionNode, conditionNode, llmNode, edge } from 'orkajs/graph'; const orka = createOrka({ llm: new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY! }), vectorDB: myVectorDB,}); const graph = orka.graph({ name: 'smart-support', nodes: [ startNode('start'), actionNode('classify', async (ctx) => { const result = await ctx.llm.generate( `Classify as "technical" or "general": ${ctx.input}` ); ctx.metadata.category = result.content.trim().toLowerCase(); return ctx; }), conditionNode('router', (ctx) => ctx.metadata.category as string), llmNode('respond', { temperature: 0.3 }), endNode('end'), ], edges: [ edge('start', 'classify'), edge('classify', 'router'), edge('router', 'respond', 'technical'), // If category === 'technical' edge('router', 'respond', 'general'), // If category === 'general' edge('respond', 'end'), ],}); const result = await graph.run('How do I configure SSL?'); console.log(result.output); // Final responseconsole.log(result.path); // ['start', 'classify', 'router', 'respond', 'end']console.log(result.steps); // Detailed execution trace# Types de Nœuds
Orka fournit sept types de nœuds pour construire des workflows en graphe :
startNode(id)Point d'entrée du graphe
endNode(id)Point de sortie, arrête l'exécution
actionNode(id, fn)Action personnalisée qui transforme le contexte
conditionNode(id, fn)Nœud de branchement, retourne un label
llmNode(id, opts)Appelle le LLM avec options configurables
retrieveNode(id, name)Recherche sémantique dans une base
parallelNode(id, nodeIds)Exécuter plusieurs nœuds simultanément
# Détails des Nœuds
startNode(id) / endNode(id)
Points d'entrée et de sortie du graphe. Chaque graphe doit avoir exactement un startNode et au moins un endNode.
startNode('start') // Entry pointendNode('end') // Exit point - stops execution and returns resultactionNode(id, fn)
Exécute une logique personnalisée. Reçoit le contexte du graphe et doit le retourner (possiblement modifié).
actionNode('process', async (ctx) => { // Access input, output, metadata, llm const processed = ctx.input.toUpperCase(); ctx.output = processed; ctx.metadata.processedAt = Date.now(); return ctx;})conditionNode(id, fn)
Nœud de branchement qui retourne un label string. Le graphe suit l'arête correspondant à ce label.
conditionNode('router', (ctx) => { const score = ctx.metadata.score as number; if (score > 0.8) return 'high'; if (score > 0.5) return 'medium'; return 'low';}) // Edges:edge('router', 'premium-response', 'high')edge('router', 'standard-response', 'medium')edge('router', 'fallback-response', 'low')llmNode(id, options)
Appelle le LLM avec le contexte actuel. Construit automatiquement un prompt à partir de l'entrée + docs récupérés.
llmNode('generate', { temperature: 0.7, maxTokens: 1000, systemPrompt: 'You are a helpful assistant.'})retrieveNode(id, knowledgeName, options)
Effectue une recherche sémantique et ajoute les résultats à ctx.retrievedDocs.
retrieveNode('search', 'documentation', { topK: 5, minScore: 0.7})parallelNode(id, nodeIds)
Exécute plusieurs nœuds simultanément. Les résultats sont fusionnés dans ctx.parallelResults.
// Define nodes to run in parallelactionNode('fetch-weather', async (ctx) => { ... }),actionNode('fetch-news', async (ctx) => { ... }),actionNode('fetch-stocks', async (ctx) => { ... }), // Parallel node runs all three simultaneouslyparallelNode('gather-data', ['fetch-weather', 'fetch-news', 'fetch-stocks']) // After execution:// ctx.parallelResults = {// 'fetch-weather': { ... },// 'fetch-news': { ... },// 'fetch-stocks': { ... }// }# Arêtes
Les arêtes connectent les nœuds et définissent le flux. Pour les nœuds de condition, vous pouvez spécifier un label à faire correspondre.
import { edge } from 'orkajs/graph'; // Simple edge: from -> toedge('start', 'process') // Conditional edge: from -> to (when condition returns 'label')edge('router', 'premium-path', 'premium')edge('router', 'standard-path', 'standard') // Multiple edges from one nodeedges: [ edge('start', 'classify'), edge('classify', 'router'), edge('router', 'technical-support', 'technical'), edge('router', 'general-support', 'general'), edge('router', 'sales', 'sales'), edge('technical-support', 'end'), edge('general-support', 'end'), edge('sales', 'end'),]# Export Mermaid
Visualisez votre graphe sous forme de diagramme Mermaid pour la documentation ou le débogage :
console.log(graph.toMermaid()); // Output:// graph TD// start((start))// classify[classify]// router{router}// respond[respond]// end((end))// start --> classify// classify --> router// router -->|technical| respond// router -->|general| respond// respond --> end💡 Astuce
Collez la sortie Mermaid dans mermaid.live ou n'importe quel renderer Markdown qui supporte Mermaid pour visualiser votre graphe.
Exemple Complet
import { createOrka, OpenAIAdapter, PineconeAdapter } from 'orkajs';import { startNode, endNode, actionNode, conditionNode, llmNode, retrieveNode, parallelNode, edge } from 'orkajs/graph'; const orka = createOrka({ llm: new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY! }), vectorDB: new PineconeAdapter({ apiKey: process.env.PINECONE_API_KEY! }),}); const supportGraph = orka.graph({ name: 'intelligent-support', nodes: [ startNode('start'), // Classify the query actionNode('classify', async (ctx) => { const result = await ctx.llm.generate( `Classify this query as 'technical', 'billing', or 'general': ${ctx.input}` ); ctx.metadata.category = result.content.trim().toLowerCase(); return ctx; }), // Route based on classification conditionNode('router', (ctx) => ctx.metadata.category as string), // Technical path: search docs first retrieveNode('search-docs', 'technical-docs', { topK: 5 }), llmNode('technical-response', { temperature: 0.3, systemPrompt: 'You are a technical support expert. Use the provided documentation.' }), // Billing path: direct response llmNode('billing-response', { temperature: 0.5, systemPrompt: 'You are a billing support agent. Be helpful and clear about policies.' }), // General path llmNode('general-response', { temperature: 0.7 }), endNode('end'), ], edges: [ edge('start', 'classify'), edge('classify', 'router'), edge('router', 'search-docs', 'technical'), edge('router', 'billing-response', 'billing'), edge('router', 'general-response', 'general'), edge('search-docs', 'technical-response'), edge('technical-response', 'end'), edge('billing-response', 'end'), edge('general-response', 'end'), ],}); // Run the graphconst result = await supportGraph.run('How do I configure SSL certificates?'); console.log('Category:', result.metadata.category); // 'technical'console.log('Path:', result.path); // ['start', 'classify', 'router', 'search-docs', 'technical-response', 'end']console.log('Answer:', result.output);Imports Tree-shakeable
// ✅ Import only what you needimport { startNode, endNode, actionNode, conditionNode, llmNode, retrieveNode, parallelNode, edge } from 'orkajs/graph'; // ✅ Or import from main packageimport { startNode, endNode, actionNode, edge } from 'orkajs';