@nextsparkjs/plugin-langchain 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +41 -0
- package/api/observability/metrics/route.ts +110 -0
- package/api/observability/traces/[traceId]/route.ts +398 -0
- package/api/observability/traces/route.ts +205 -0
- package/api/sessions/route.ts +332 -0
- package/components/observability/CollapsibleJson.tsx +71 -0
- package/components/observability/CompactTimeline.tsx +75 -0
- package/components/observability/ConversationFlow.tsx +271 -0
- package/components/observability/DisabledMessage.tsx +21 -0
- package/components/observability/FiltersPanel.tsx +82 -0
- package/components/observability/ObservabilityDashboard.tsx +230 -0
- package/components/observability/SpansList.tsx +210 -0
- package/components/observability/TraceDetail.tsx +335 -0
- package/components/observability/TraceStatusBadge.tsx +39 -0
- package/components/observability/TracesTable.tsx +97 -0
- package/components/observability/index.ts +7 -0
- package/docs/01-getting-started/01-overview.md +196 -0
- package/docs/01-getting-started/02-installation.md +368 -0
- package/docs/01-getting-started/03-configuration.md +794 -0
- package/docs/02-core-concepts/01-architecture.md +566 -0
- package/docs/02-core-concepts/02-agents.md +597 -0
- package/docs/02-core-concepts/03-tools.md +689 -0
- package/docs/03-orchestration/01-graph-orchestrator.md +809 -0
- package/docs/03-orchestration/02-legacy-react.md +650 -0
- package/docs/04-advanced/01-observability.md +645 -0
- package/docs/04-advanced/02-token-tracking.md +469 -0
- package/docs/04-advanced/03-streaming.md +476 -0
- package/docs/04-advanced/04-guardrails.md +597 -0
- package/docs/05-reference/01-api-reference.md +1403 -0
- package/docs/05-reference/02-customization.md +646 -0
- package/docs/05-reference/03-examples.md +881 -0
- package/docs/index.md +85 -0
- package/hooks/observability/useMetrics.ts +31 -0
- package/hooks/observability/useTraceDetail.ts +48 -0
- package/hooks/observability/useTraces.ts +59 -0
- package/lib/agent-factory.ts +354 -0
- package/lib/agent-helpers.ts +201 -0
- package/lib/db-memory-store.ts +417 -0
- package/lib/graph/index.ts +58 -0
- package/lib/graph/nodes/combiner.ts +399 -0
- package/lib/graph/nodes/router.ts +440 -0
- package/lib/graph/orchestrator-graph.ts +386 -0
- package/lib/graph/prompts/combiner.md +131 -0
- package/lib/graph/prompts/router.md +193 -0
- package/lib/graph/types.ts +365 -0
- package/lib/guardrails.ts +230 -0
- package/lib/index.ts +44 -0
- package/lib/logger.ts +70 -0
- package/lib/memory-store.ts +168 -0
- package/lib/message-serializer.ts +110 -0
- package/lib/prompt-renderer.ts +94 -0
- package/lib/providers.ts +226 -0
- package/lib/streaming.ts +232 -0
- package/lib/token-tracker.ts +298 -0
- package/lib/tools-builder.ts +192 -0
- package/lib/tracer-callbacks.ts +342 -0
- package/lib/tracer.ts +350 -0
- package/migrations/001_langchain_memory.sql +83 -0
- package/migrations/002_token_usage.sql +127 -0
- package/migrations/003_observability.sql +257 -0
- package/package.json +28 -0
- package/plugin.config.ts +170 -0
- package/presets/lib/langchain.config.ts.preset +142 -0
- package/presets/templates/sector7/ai-observability/[traceId]/page.tsx +91 -0
- package/presets/templates/sector7/ai-observability/page.tsx +54 -0
- package/types/langchain.types.ts +274 -0
- package/types/observability.types.ts +270 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
# Orchestration (Legacy)
|
|
2
|
+
|
|
3
|
+
> **DEPRECATED**: This document describes the legacy ReAct-based orchestration approach.
|
|
4
|
+
> For new implementations, use the **Graph-Based Orchestrator** which is 25-50x faster.
|
|
5
|
+
>
|
|
6
|
+
> **Recommended**: See [Graph Orchestrator](./01-graph-orchestrator.md) for the modern approach.
|
|
7
|
+
>
|
|
8
|
+
> **Migration**: Set `LANGCHAIN_USE_GRAPH_ORCHESTRATOR=true` to enable the new system.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Orchestration enables complex multi-agent systems where a central orchestrator routes requests to specialized agents. This guide covers orchestration patterns, implementation, and best practices.
|
|
13
|
+
|
|
14
|
+
## Why Orchestration?
|
|
15
|
+
|
|
16
|
+
As applications grow, a single agent with many tools becomes:
|
|
17
|
+
- **Hard to maintain**: Too many tools, complex prompts
|
|
18
|
+
- **Slow**: LLM must consider all tools for every request
|
|
19
|
+
- **Error-prone**: More tools = more chance of wrong tool selection
|
|
20
|
+
|
|
21
|
+
Orchestration solves this by:
|
|
22
|
+
- **Separating concerns**: Each agent handles one domain
|
|
23
|
+
- **Improving focus**: Smaller tool sets = better accuracy
|
|
24
|
+
- **Enabling specialization**: Different models/temps per domain
|
|
25
|
+
|
|
26
|
+
## Architecture
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
30
|
+
│ USER REQUEST │
|
|
31
|
+
│ "Show me customer details" │
|
|
32
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
33
|
+
│
|
|
34
|
+
▼
|
|
35
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
36
|
+
│ ORCHESTRATOR │
|
|
37
|
+
│ │
|
|
38
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
39
|
+
│ │ System Prompt │ │
|
|
40
|
+
│ │ "Analyze intent and route to appropriate agent" │ │
|
|
41
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
42
|
+
│ │ │
|
|
43
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
44
|
+
│ │ Routing Tools │ │
|
|
45
|
+
│ │ route_to_task | route_to_customer | route_to_page │ │
|
|
46
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
47
|
+
│ │
|
|
48
|
+
│ Decision: route_to_customer │
|
|
49
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
50
|
+
│
|
|
51
|
+
▼
|
|
52
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
53
|
+
│ CUSTOMER AGENT │
|
|
54
|
+
│ │
|
|
55
|
+
│ Tools: search_customers, get_customer, update_customer, ... │
|
|
56
|
+
│ │
|
|
57
|
+
│ *Executes request and returns response* │
|
|
58
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
59
|
+
│
|
|
60
|
+
▼
|
|
61
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
62
|
+
│ RESPONSE │
|
|
63
|
+
│ "Here's the customer..." │
|
|
64
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Implementation
|
|
68
|
+
|
|
69
|
+
### Step 1: Configure Agents
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// langchain.config.ts
|
|
73
|
+
export const AGENTS = {
|
|
74
|
+
// Orchestrator - low temperature for consistent routing
|
|
75
|
+
'orchestrator': {
|
|
76
|
+
provider: 'ollama',
|
|
77
|
+
temperature: 0.1,
|
|
78
|
+
description: 'Routes requests to specialized agents',
|
|
79
|
+
systemPrompt: 'orchestrator',
|
|
80
|
+
createTools: () => createOrchestratorTools(),
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Specialized agents
|
|
84
|
+
'task-assistant': {
|
|
85
|
+
provider: 'ollama',
|
|
86
|
+
temperature: 0.3,
|
|
87
|
+
systemPrompt: 'task-assistant',
|
|
88
|
+
createTools: (ctx) => createTaskTools(ctx),
|
|
89
|
+
},
|
|
90
|
+
'customer-assistant': {
|
|
91
|
+
provider: 'ollama',
|
|
92
|
+
temperature: 0.3,
|
|
93
|
+
systemPrompt: 'customer-assistant',
|
|
94
|
+
createTools: (ctx) => createCustomerTools(ctx),
|
|
95
|
+
},
|
|
96
|
+
'page-assistant': {
|
|
97
|
+
provider: 'ollama',
|
|
98
|
+
temperature: 0.3,
|
|
99
|
+
systemPrompt: 'page-assistant',
|
|
100
|
+
createTools: (ctx) => createPageTools(ctx),
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Step 2: Create Routing Tools
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// tools/orchestrator.ts
|
|
109
|
+
import { z } from 'zod'
|
|
110
|
+
|
|
111
|
+
export interface RoutingResult {
|
|
112
|
+
agent: 'task' | 'customer' | 'page'
|
|
113
|
+
message: string
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface ClarificationResult {
|
|
117
|
+
action: 'clarify'
|
|
118
|
+
question: string
|
|
119
|
+
options: Array<{ label: string; description: string }>
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function createOrchestratorTools() {
|
|
123
|
+
return [
|
|
124
|
+
{
|
|
125
|
+
name: 'route_to_task',
|
|
126
|
+
description: 'Route to task agent when user mentions tasks, todos, or work items.',
|
|
127
|
+
schema: z.object({
|
|
128
|
+
message: z.string().describe('User message to forward'),
|
|
129
|
+
}),
|
|
130
|
+
func: async ({ message }) => {
|
|
131
|
+
return JSON.stringify({ agent: 'task', message })
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'route_to_customer',
|
|
136
|
+
description: 'Route to customer agent when user mentions customers, clients, or accounts.',
|
|
137
|
+
schema: z.object({
|
|
138
|
+
message: z.string().describe('User message to forward'),
|
|
139
|
+
}),
|
|
140
|
+
func: async ({ message }) => {
|
|
141
|
+
return JSON.stringify({ agent: 'customer', message })
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'route_to_page',
|
|
146
|
+
description: 'Route to page agent when user mentions pages, content, or blocks.',
|
|
147
|
+
schema: z.object({
|
|
148
|
+
message: z.string().describe('User message to forward'),
|
|
149
|
+
}),
|
|
150
|
+
func: async ({ message }) => {
|
|
151
|
+
return JSON.stringify({ agent: 'page', message })
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: 'ask_clarification',
|
|
156
|
+
description: 'Ask user for clarification when intent is unclear.',
|
|
157
|
+
schema: z.object({
|
|
158
|
+
question: z.string(),
|
|
159
|
+
options: z.array(z.object({
|
|
160
|
+
label: z.string(),
|
|
161
|
+
description: z.string(),
|
|
162
|
+
})),
|
|
163
|
+
}),
|
|
164
|
+
func: async ({ question, options }) => {
|
|
165
|
+
return JSON.stringify({ action: 'clarify', question, options })
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Step 3: Orchestrator System Prompt
|
|
173
|
+
|
|
174
|
+
```markdown
|
|
175
|
+
<!-- agents/orchestrator.md -->
|
|
176
|
+
|
|
177
|
+
You are an AI Orchestrator that routes user requests to specialized agents.
|
|
178
|
+
|
|
179
|
+
## CRITICAL RULE
|
|
180
|
+
|
|
181
|
+
**YOU CAN ONLY DO TWO THINGS:**
|
|
182
|
+
1. Call a routing tool (route_to_task, route_to_customer, route_to_page)
|
|
183
|
+
2. Respond to simple greetings
|
|
184
|
+
|
|
185
|
+
**NEVER claim to perform actions like creating, updating, or deleting data.** You don't have those tools. Only the specialized agents do.
|
|
186
|
+
|
|
187
|
+
## Routing Rules
|
|
188
|
+
|
|
189
|
+
**route_to_customer** - Use when:
|
|
190
|
+
- User mentions customers, clients, accounts (cliente, customer, cuenta)
|
|
191
|
+
- User wants to modify something about a previously discussed customer
|
|
192
|
+
- User references a customer from earlier ("modificalo", "cambialo")
|
|
193
|
+
|
|
194
|
+
**route_to_task** - Use when:
|
|
195
|
+
- User mentions tasks, to-dos, work items (tarea, task, pendiente)
|
|
196
|
+
- User wants to create, update, or list tasks
|
|
197
|
+
- User asks for suggestions to add to a task
|
|
198
|
+
|
|
199
|
+
**route_to_page** - Use when:
|
|
200
|
+
- User mentions pages, content, website (página, page, contenido)
|
|
201
|
+
- User wants to create or modify landing pages, blocks
|
|
202
|
+
|
|
203
|
+
## Context Awareness
|
|
204
|
+
|
|
205
|
+
When the user says "modificalo", "cambialo", "actualízalo":
|
|
206
|
+
- Look at the conversation history to determine WHAT they're referring to
|
|
207
|
+
- If discussing a customer → route_to_customer
|
|
208
|
+
- If discussing a task → route_to_task
|
|
209
|
+
- If discussing a page → route_to_page
|
|
210
|
+
|
|
211
|
+
## Direct Response (ONLY for greetings)
|
|
212
|
+
|
|
213
|
+
Respond directly ONLY for:
|
|
214
|
+
- "Hola" → "¡Hola! ¿En qué puedo ayudarte?"
|
|
215
|
+
- "Hello" → "Hello! How can I help you?"
|
|
216
|
+
|
|
217
|
+
For EVERYTHING else, use a routing tool.
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Step 4: Orchestrator Handler
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// orchestrator.ts
|
|
224
|
+
import { createAgent } from '@/contents/plugins/langchain/lib/agent-factory'
|
|
225
|
+
import { loadSystemPrompt } from './agents'
|
|
226
|
+
import { getAgentConfig, getAgentModelConfig, getAgentTools } from './langchain.config'
|
|
227
|
+
import type { RoutingResult, ClarificationResult } from './tools/orchestrator'
|
|
228
|
+
|
|
229
|
+
type AgentType = 'task' | 'customer' | 'page'
|
|
230
|
+
|
|
231
|
+
const AGENT_NAME_MAP: Record<AgentType, string> = {
|
|
232
|
+
task: 'task-assistant',
|
|
233
|
+
customer: 'customer-assistant',
|
|
234
|
+
page: 'page-assistant',
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface OrchestratorContext {
|
|
238
|
+
userId: string
|
|
239
|
+
teamId: string
|
|
240
|
+
sessionId: string
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface OrchestratorResponse {
|
|
244
|
+
content: string
|
|
245
|
+
sessionId: string
|
|
246
|
+
agentUsed?: 'orchestrator' | AgentType
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Extract routing decision from the LATEST turn only
|
|
251
|
+
*/
|
|
252
|
+
function extractRoutingFromMessages(messages: any[]): RoutingResult | ClarificationResult | null {
|
|
253
|
+
// Find last human message (start of current turn)
|
|
254
|
+
let lastHumanIndex = -1
|
|
255
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
256
|
+
if (messages[i]._getType() === 'human') {
|
|
257
|
+
lastHumanIndex = i
|
|
258
|
+
break
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Only search for tool results AFTER the last human message
|
|
263
|
+
for (let i = messages.length - 1; i > lastHumanIndex; i--) {
|
|
264
|
+
const msg = messages[i]
|
|
265
|
+
if (msg._getType() === 'tool') {
|
|
266
|
+
const content = typeof msg.content === 'string'
|
|
267
|
+
? msg.content
|
|
268
|
+
: JSON.stringify(msg.content)
|
|
269
|
+
try {
|
|
270
|
+
const parsed = JSON.parse(content)
|
|
271
|
+
if (parsed.action === 'clarify' || parsed.agent) {
|
|
272
|
+
return parsed
|
|
273
|
+
}
|
|
274
|
+
} catch {
|
|
275
|
+
// Not JSON
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return null
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Invoke a specialized sub-agent
|
|
284
|
+
*/
|
|
285
|
+
async function invokeSubAgent(
|
|
286
|
+
agentType: AgentType,
|
|
287
|
+
message: string,
|
|
288
|
+
context: OrchestratorContext
|
|
289
|
+
): Promise<OrchestratorResponse> {
|
|
290
|
+
const { userId, teamId, sessionId } = context
|
|
291
|
+
const agentName = AGENT_NAME_MAP[agentType]
|
|
292
|
+
|
|
293
|
+
const config = getAgentConfig(agentName)
|
|
294
|
+
if (!config?.systemPrompt) {
|
|
295
|
+
throw new Error(`Agent not found: ${agentName}`)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const agent = await createAgent({
|
|
299
|
+
sessionId: `${sessionId}-${agentType}`,
|
|
300
|
+
systemPrompt: loadSystemPrompt(config.systemPrompt as any),
|
|
301
|
+
tools: getAgentTools(agentName, { userId, teamId }),
|
|
302
|
+
modelConfig: getAgentModelConfig(agentName),
|
|
303
|
+
context: { userId, teamId },
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
const response = await agent.chat(message)
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
content: response.content,
|
|
310
|
+
sessionId,
|
|
311
|
+
agentUsed: agentType,
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Process a message through the orchestrator
|
|
317
|
+
*/
|
|
318
|
+
export async function processWithOrchestrator(
|
|
319
|
+
message: string,
|
|
320
|
+
context: OrchestratorContext
|
|
321
|
+
): Promise<OrchestratorResponse> {
|
|
322
|
+
const { userId, teamId, sessionId } = context
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
// Step 1: Create orchestrator
|
|
326
|
+
const orchestratorAgent = await createAgent({
|
|
327
|
+
sessionId: `${sessionId}-orchestrator`,
|
|
328
|
+
systemPrompt: loadSystemPrompt('orchestrator'),
|
|
329
|
+
tools: getAgentTools('orchestrator', { userId, teamId }),
|
|
330
|
+
modelConfig: getAgentModelConfig('orchestrator'),
|
|
331
|
+
context: { userId, teamId },
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
// Step 2: Get routing decision
|
|
335
|
+
const routingResponse = await orchestratorAgent.chat(message)
|
|
336
|
+
|
|
337
|
+
// Step 3: Extract routing from tool results
|
|
338
|
+
const decision = extractRoutingFromMessages(routingResponse.messages || [])
|
|
339
|
+
|
|
340
|
+
if (!decision) {
|
|
341
|
+
// Orchestrator responded directly (greeting, meta-question)
|
|
342
|
+
return {
|
|
343
|
+
content: routingResponse.content,
|
|
344
|
+
sessionId,
|
|
345
|
+
agentUsed: 'orchestrator',
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Step 4: Handle clarification
|
|
350
|
+
if ('action' in decision && decision.action === 'clarify') {
|
|
351
|
+
let response = decision.question + '\n\n'
|
|
352
|
+
decision.options.forEach((opt, i) => {
|
|
353
|
+
response += `${i + 1}. **${opt.label}**: ${opt.description}\n`
|
|
354
|
+
})
|
|
355
|
+
return {
|
|
356
|
+
content: response,
|
|
357
|
+
sessionId,
|
|
358
|
+
agentUsed: 'orchestrator',
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Step 5: Route to specialized agent
|
|
363
|
+
if ('agent' in decision) {
|
|
364
|
+
return await invokeSubAgent(
|
|
365
|
+
decision.agent,
|
|
366
|
+
decision.message || message,
|
|
367
|
+
context
|
|
368
|
+
)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return {
|
|
372
|
+
content: routingResponse.content,
|
|
373
|
+
sessionId,
|
|
374
|
+
agentUsed: 'orchestrator',
|
|
375
|
+
}
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error('[Orchestrator] Error:', error)
|
|
378
|
+
throw new Error(
|
|
379
|
+
error instanceof Error ? error.message : 'Failed to process message'
|
|
380
|
+
)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Step 5: API Integration
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
// api/ai/chat/route.ts
|
|
389
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
390
|
+
import { authenticateRequest } from '@/core/lib/auth/server'
|
|
391
|
+
import { processWithOrchestrator } from '@/themes/default/lib/langchain/orchestrator'
|
|
392
|
+
|
|
393
|
+
export async function POST(request: NextRequest) {
|
|
394
|
+
try {
|
|
395
|
+
const { user, teamId } = await authenticateRequest(request)
|
|
396
|
+
if (!user || !teamId) {
|
|
397
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const { message, sessionId } = await request.json()
|
|
401
|
+
|
|
402
|
+
const response = await processWithOrchestrator(message, {
|
|
403
|
+
userId: user.id,
|
|
404
|
+
teamId,
|
|
405
|
+
sessionId: sessionId || `session-${Date.now()}`,
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
return NextResponse.json(response)
|
|
409
|
+
} catch (error) {
|
|
410
|
+
return NextResponse.json(
|
|
411
|
+
{ error: error instanceof Error ? error.message : 'Error' },
|
|
412
|
+
{ status: 500 }
|
|
413
|
+
)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Session Management
|
|
419
|
+
|
|
420
|
+
Orchestration creates separate sessions for each agent:
|
|
421
|
+
|
|
422
|
+
```
|
|
423
|
+
Main session: user-123-session-1
|
|
424
|
+
├── Orchestrator: user-123-session-1-orchestrator
|
|
425
|
+
├── Task Agent: user-123-session-1-task
|
|
426
|
+
├── Customer Agent: user-123-session-1-customer
|
|
427
|
+
└── Page Agent: user-123-session-1-page
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
This allows:
|
|
431
|
+
- Independent conversation history per agent
|
|
432
|
+
- Context isolation between domains
|
|
433
|
+
- Parallel conversations if needed
|
|
434
|
+
|
|
435
|
+
## Handling Context References
|
|
436
|
+
|
|
437
|
+
A key challenge is handling contextual references like "modificalo" (modify it):
|
|
438
|
+
|
|
439
|
+
### Problem
|
|
440
|
+
|
|
441
|
+
```
|
|
442
|
+
User: "Show me customer StartupXYZ"
|
|
443
|
+
Agent: *shows customer details*
|
|
444
|
+
User: "modificalo, su nuevo telefono es +1 555..."
|
|
445
|
+
^^^^^^^^^^
|
|
446
|
+
What is "it"?
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Solution: Context Awareness in Prompt
|
|
450
|
+
|
|
451
|
+
```markdown
|
|
452
|
+
## Context Awareness
|
|
453
|
+
|
|
454
|
+
When the user says "modificalo", "cambialo", "actualízalo":
|
|
455
|
+
- Look at the conversation history to determine WHAT they're referring to
|
|
456
|
+
- If you were just discussing a customer → route_to_customer
|
|
457
|
+
- If you were just discussing a task → route_to_task
|
|
458
|
+
- If you were just discussing a page → route_to_page
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Solution: Extracting Only Current Turn
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
// Only look at messages after the last human message
|
|
465
|
+
function extractRoutingFromMessages(messages) {
|
|
466
|
+
let lastHumanIndex = -1
|
|
467
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
468
|
+
if (messages[i]._getType() === 'human') {
|
|
469
|
+
lastHumanIndex = i
|
|
470
|
+
break
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Search only in current turn
|
|
475
|
+
for (let i = messages.length - 1; i > lastHumanIndex; i--) {
|
|
476
|
+
// Extract routing...
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## Orchestration Patterns
|
|
482
|
+
|
|
483
|
+
### Pattern 1: Simple Router
|
|
484
|
+
|
|
485
|
+
Route based on keywords only:
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
'orchestrator': {
|
|
489
|
+
temperature: 0.1,
|
|
490
|
+
systemPrompt: 'simple-router',
|
|
491
|
+
createTools: () => [routeToTask, routeToCustomer, routeToPage],
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Pattern 2: Intent Classifier
|
|
496
|
+
|
|
497
|
+
More sophisticated intent analysis:
|
|
498
|
+
|
|
499
|
+
```markdown
|
|
500
|
+
## Intent Classification
|
|
501
|
+
|
|
502
|
+
Before routing, classify the user's intent:
|
|
503
|
+
|
|
504
|
+
1. **CRUD Operations**: Create, Read, Update, Delete
|
|
505
|
+
2. **Search/Query**: Finding specific items
|
|
506
|
+
3. **Aggregation**: Summaries, counts, reports
|
|
507
|
+
4. **Action**: Workflows, multi-step operations
|
|
508
|
+
|
|
509
|
+
Then route to the appropriate agent.
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Pattern 3: Hierarchical Orchestration
|
|
513
|
+
|
|
514
|
+
For very complex systems:
|
|
515
|
+
|
|
516
|
+
```
|
|
517
|
+
Main Orchestrator
|
|
518
|
+
├── Domain A Orchestrator
|
|
519
|
+
│ ├── Agent A1
|
|
520
|
+
│ └── Agent A2
|
|
521
|
+
└── Domain B Orchestrator
|
|
522
|
+
├── Agent B1
|
|
523
|
+
└── Agent B2
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### Pattern 4: Fallback Chain
|
|
527
|
+
|
|
528
|
+
Try agents in order:
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
async function processWithFallback(message, context) {
|
|
532
|
+
try {
|
|
533
|
+
return await processWithOrchestrator(message, context)
|
|
534
|
+
} catch {
|
|
535
|
+
// Fallback to single agent
|
|
536
|
+
return await processSingleAgent(message, context)
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
## Clarification Flow
|
|
542
|
+
|
|
543
|
+
When intent is unclear, ask for clarification:
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
{
|
|
547
|
+
name: 'ask_clarification',
|
|
548
|
+
description: 'Ask user for clarification when intent is ambiguous.',
|
|
549
|
+
schema: z.object({
|
|
550
|
+
question: z.string(),
|
|
551
|
+
options: z.array(z.object({
|
|
552
|
+
label: z.string(),
|
|
553
|
+
description: z.string(),
|
|
554
|
+
})),
|
|
555
|
+
}),
|
|
556
|
+
func: async ({ question, options }) => {
|
|
557
|
+
return JSON.stringify({
|
|
558
|
+
action: 'clarify',
|
|
559
|
+
question,
|
|
560
|
+
options,
|
|
561
|
+
})
|
|
562
|
+
},
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Example:**
|
|
567
|
+
|
|
568
|
+
```
|
|
569
|
+
User: "Create a new entry"
|
|
570
|
+
Orchestrator: *calls ask_clarification*
|
|
571
|
+
Response:
|
|
572
|
+
"What type of entry would you like to create?"
|
|
573
|
+
|
|
574
|
+
1. **Task**: Create a new task or todo item
|
|
575
|
+
2. **Customer**: Create a new customer record
|
|
576
|
+
3. **Page**: Create a new content page
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## Debugging Orchestration
|
|
580
|
+
|
|
581
|
+
### Enable Logging
|
|
582
|
+
|
|
583
|
+
```env
|
|
584
|
+
LOG_ENABLED=true
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Check Logs
|
|
588
|
+
|
|
589
|
+
```bash
|
|
590
|
+
# View orchestrator decisions
|
|
591
|
+
cat logger/ai/session-*-orchestrator.log
|
|
592
|
+
|
|
593
|
+
# View sub-agent execution
|
|
594
|
+
cat logger/ai/session-*-task.log
|
|
595
|
+
cat logger/ai/session-*-customer.log
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### Common Issues
|
|
599
|
+
|
|
600
|
+
| Issue | Cause | Solution |
|
|
601
|
+
|-------|-------|----------|
|
|
602
|
+
| Wrong agent selected | Unclear routing rules | Improve prompt keywords |
|
|
603
|
+
| Stale routing | Using old tool results | Only check current turn |
|
|
604
|
+
| Direct response instead of routing | Too permissive direct response rules | Restrict to greetings only |
|
|
605
|
+
| Infinite loops | Agent calling itself | Check recursion limits |
|
|
606
|
+
|
|
607
|
+
## Best Practices
|
|
608
|
+
|
|
609
|
+
### 1. Keep Orchestrator Lightweight
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
'orchestrator': {
|
|
613
|
+
temperature: 0.1, // Low for consistency
|
|
614
|
+
// Only routing tools, no data tools
|
|
615
|
+
createTools: () => createOrchestratorTools(),
|
|
616
|
+
}
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### 2. Be Explicit About Routing Rules
|
|
620
|
+
|
|
621
|
+
```markdown
|
|
622
|
+
## Routing Rules
|
|
623
|
+
|
|
624
|
+
**route_to_customer** - Use when message contains:
|
|
625
|
+
- "customer", "cliente", "client"
|
|
626
|
+
- "account", "cuenta"
|
|
627
|
+
- "phone", "teléfono"
|
|
628
|
+
- OR when referring to a previously discussed customer
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### 3. Handle Edge Cases
|
|
632
|
+
|
|
633
|
+
```markdown
|
|
634
|
+
## Edge Cases
|
|
635
|
+
|
|
636
|
+
- If message could match multiple agents, ask for clarification
|
|
637
|
+
- If user says "undo", route to the same agent as previous action
|
|
638
|
+
- If user says "help", respond directly with capability list
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
### 4. Monitor and Iterate
|
|
642
|
+
|
|
643
|
+
- Track routing accuracy
|
|
644
|
+
- Log misrouted requests
|
|
645
|
+
- Continuously improve prompts
|
|
646
|
+
|
|
647
|
+
## Next Steps
|
|
648
|
+
|
|
649
|
+
- [Advanced customization](../05-reference/02-customization.md)
|
|
650
|
+
- [API reference](../05-reference/01-api-reference.md)
|