ai-functions 2.1.3 → 2.4.0
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +90 -1
- package/README.md +38 -0
- package/dist/ai-promise.d.ts +3 -3
- package/dist/ai-promise.d.ts.map +1 -1
- package/dist/ai-promise.js +135 -64
- package/dist/ai-promise.js.map +1 -1
- package/dist/ai-schemas.d.ts +56 -0
- package/dist/ai-schemas.d.ts.map +1 -0
- package/dist/ai-schemas.js +53 -0
- package/dist/ai-schemas.js.map +1 -0
- package/dist/ai.d.ts +16 -242
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +51 -858
- package/dist/ai.js.map +1 -1
- package/dist/batch/anthropic.d.ts +6 -4
- package/dist/batch/anthropic.d.ts.map +1 -1
- package/dist/batch/anthropic.js +83 -145
- package/dist/batch/anthropic.js.map +1 -1
- package/dist/batch/bedrock.d.ts +8 -30
- package/dist/batch/bedrock.d.ts.map +1 -1
- package/dist/batch/bedrock.js +155 -338
- package/dist/batch/bedrock.js.map +1 -1
- package/dist/batch/cloudflare.d.ts +8 -20
- package/dist/batch/cloudflare.d.ts.map +1 -1
- package/dist/batch/cloudflare.js +68 -189
- package/dist/batch/cloudflare.js.map +1 -1
- package/dist/batch/google.d.ts +6 -20
- package/dist/batch/google.d.ts.map +1 -1
- package/dist/batch/google.js +70 -238
- package/dist/batch/google.js.map +1 -1
- package/dist/batch/index.d.ts +4 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +4 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/batch/memory.d.ts +1 -1
- package/dist/batch/memory.d.ts.map +1 -1
- package/dist/batch/memory.js +14 -10
- package/dist/batch/memory.js.map +1 -1
- package/dist/batch/openai.d.ts +11 -14
- package/dist/batch/openai.d.ts.map +1 -1
- package/dist/batch/openai.js +52 -156
- package/dist/batch/openai.js.map +1 -1
- package/dist/batch/provider.d.ts +111 -0
- package/dist/batch/provider.d.ts.map +1 -0
- package/dist/batch/provider.js +233 -0
- package/dist/batch/provider.js.map +1 -0
- package/dist/batch-map.d.ts.map +1 -1
- package/dist/batch-map.js +23 -17
- package/dist/batch-map.js.map +1 -1
- package/dist/batch-queue.d.ts +65 -0
- package/dist/batch-queue.d.ts.map +1 -1
- package/dist/batch-queue.js +169 -14
- package/dist/batch-queue.js.map +1 -1
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +27 -14
- package/dist/budget.js.map +1 -1
- package/dist/cache.d.ts +23 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +36 -15
- package/dist/cache.js.map +1 -1
- package/dist/context.d.ts +26 -8
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +64 -62
- package/dist/context.js.map +1 -1
- package/dist/digital-objects-registry.d.ts +229 -0
- package/dist/digital-objects-registry.d.ts.map +1 -0
- package/dist/digital-objects-registry.js +617 -0
- package/dist/digital-objects-registry.js.map +1 -0
- package/dist/embeddings.d.ts +2 -2
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/eval/runner.d.ts +8 -0
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/runner.js +41 -35
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval-log/in-memory.d.ts +34 -0
- package/dist/eval-log/in-memory.d.ts.map +1 -0
- package/dist/eval-log/in-memory.js +84 -0
- package/dist/eval-log/in-memory.js.map +1 -0
- package/dist/eval-log/index.d.ts +29 -0
- package/dist/eval-log/index.d.ts.map +1 -0
- package/dist/eval-log/index.js +39 -0
- package/dist/eval-log/index.js.map +1 -0
- package/dist/eval-log/types.d.ts +101 -0
- package/dist/eval-log/types.d.ts.map +1 -0
- package/dist/eval-log/types.js +16 -0
- package/dist/eval-log/types.js.map +1 -0
- package/dist/function-registry.d.ts +176 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +685 -0
- package/dist/function-registry.js.map +1 -0
- package/dist/generate.d.ts +9 -3
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +18 -18
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -18
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +118 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +187 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/budget.d.ts +84 -0
- package/dist/middleware/budget.d.ts.map +1 -0
- package/dist/middleware/budget.js +110 -0
- package/dist/middleware/budget.js.map +1 -0
- package/dist/middleware/cache.d.ts +103 -0
- package/dist/middleware/cache.d.ts.map +1 -0
- package/dist/middleware/cache.js +228 -0
- package/dist/middleware/cache.js.map +1 -0
- package/dist/middleware/embed-cache.d.ts +99 -0
- package/dist/middleware/embed-cache.d.ts.map +1 -0
- package/dist/middleware/embed-cache.js +128 -0
- package/dist/middleware/embed-cache.js.map +1 -0
- package/dist/middleware/index.d.ts +11 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +11 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/trace.d.ts +103 -0
- package/dist/middleware/trace.d.ts.map +1 -0
- package/dist/middleware/trace.js +176 -0
- package/dist/middleware/trace.js.map +1 -0
- package/dist/primitives.d.ts +120 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +398 -26
- package/dist/primitives.js.map +1 -1
- package/dist/retry.d.ts +66 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +115 -8
- package/dist/retry.js.map +1 -1
- package/dist/sandbox.d.ts +36 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +44 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/schema.js +2 -2
- package/dist/schema.js.map +1 -1
- package/dist/telemetry.d.ts +128 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +285 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/template.d.ts.map +1 -1
- package/dist/template.js +6 -1
- package/dist/template.js.map +1 -1
- package/dist/tool-orchestration.d.ts +66 -4
- package/dist/tool-orchestration.d.ts.map +1 -1
- package/dist/tool-orchestration.js +123 -23
- package/dist/tool-orchestration.js.map +1 -1
- package/dist/type-guards.d.ts +28 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +29 -0
- package/dist/type-guards.js.map +1 -0
- package/dist/types.d.ts +155 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +36 -1
- package/dist/types.js.map +1 -1
- package/dist/wrap-for-v3.d.ts +80 -0
- package/dist/wrap-for-v3.d.ts.map +1 -0
- package/dist/wrap-for-v3.js +89 -0
- package/dist/wrap-for-v3.js.map +1 -0
- package/examples/00-quickstart.ts +232 -0
- package/examples/01-rag-chatbot.ts +212 -0
- package/examples/02-multi-agent-research.ts +290 -0
- package/examples/03-email-classification.ts +379 -0
- package/examples/04-content-moderation.ts +400 -0
- package/examples/05-document-extraction.ts +455 -0
- package/examples/06-streaming-chat-nextjs.ts +437 -0
- package/examples/07-cloudflare-worker.ts +483 -0
- package/examples/08-batch-processing.ts +491 -0
- package/examples/09-budget-constrained.ts +527 -0
- package/examples/10-tool-orchestration.ts +565 -0
- package/examples/11-retry-resilience.ts +403 -0
- package/examples/12-caching-strategies.ts +422 -0
- package/examples/README.md +145 -0
- package/package.json +29 -25
- package/src/ai-promise.ts +226 -140
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +71 -1176
- package/src/batch/anthropic.ts +96 -161
- package/src/batch/bedrock.ts +203 -454
- package/src/batch/cloudflare.ts +99 -282
- package/src/batch/google.ts +91 -297
- package/src/batch/index.ts +4 -1
- package/src/batch/memory.ts +15 -10
- package/src/batch/openai.ts +65 -193
- package/src/batch/provider.ts +336 -0
- package/src/batch-map.ts +29 -24
- package/src/batch-queue.ts +200 -11
- package/src/budget.ts +31 -18
- package/src/cache.ts +45 -17
- package/src/context.ts +106 -77
- package/src/digital-objects-registry.ts +750 -0
- package/src/errors.ts +37 -0
- package/src/eval/runner.ts +60 -36
- package/src/eval-log/in-memory.ts +90 -0
- package/src/eval-log/index.ts +46 -0
- package/src/eval-log/types.ts +110 -0
- package/src/function-registry.ts +874 -0
- package/src/generate.ts +33 -28
- package/src/index.ts +122 -21
- package/src/logger.ts +232 -0
- package/src/middleware/budget.ts +171 -0
- package/src/middleware/cache.ts +299 -0
- package/src/middleware/embed-cache.ts +195 -0
- package/src/middleware/index.ts +23 -0
- package/src/middleware/trace.ts +248 -0
- package/src/primitives.ts +589 -62
- package/src/retry.ts +144 -18
- package/src/sandbox.ts +52 -0
- package/src/schema.ts +8 -8
- package/src/telemetry.ts +403 -0
- package/src/template.ts +8 -4
- package/src/tool-orchestration.ts +213 -48
- package/src/type-guards.ts +31 -0
- package/src/types.ts +186 -27
- package/src/wrap-for-v3.ts +105 -0
- package/test/ai-promise.test.ts +1080 -0
- package/test/ai-proxy.test.ts +1 -1
- package/test/batch-autosubmit-errors.test.ts +49 -37
- package/test/batch-blog-posts.test.ts +87 -129
- package/test/core-functions.test.ts +183 -579
- package/test/decide.test.ts +154 -322
- package/test/define.test.ts +211 -8
- package/test/digital-objects-registry.test.ts +760 -0
- package/test/embedding-cache-middleware.test.ts +140 -0
- package/test/fill-template.test.ts +89 -0
- package/test/generate-core.test.ts +140 -229
- package/test/implicit-batch.test.ts +22 -65
- package/test/retry-policy-integration.test.ts +117 -0
- package/test/sandbox-execution.test.ts +155 -0
- package/test/schema.test.ts +55 -19
- package/test/template.test.ts +1164 -0
- package/test/tool-orchestration.test.ts +270 -0
- package/test/wrap-for-v3.test.ts +612 -0
- package/vitest.config.js +6 -0
- package/vitest.config.ts +20 -0
- package/LICENSE +0 -21
- package/dist/rpc/auth.d.ts +0 -69
- package/dist/rpc/auth.d.ts.map +0 -1
- package/dist/rpc/auth.js +0 -136
- package/dist/rpc/auth.js.map +0 -1
- package/dist/rpc/client.d.ts +0 -62
- package/dist/rpc/client.d.ts.map +0 -1
- package/dist/rpc/client.js +0 -103
- package/dist/rpc/client.js.map +0 -1
- package/dist/rpc/deferred.d.ts +0 -60
- package/dist/rpc/deferred.d.ts.map +0 -1
- package/dist/rpc/deferred.js +0 -96
- package/dist/rpc/deferred.js.map +0 -1
- package/dist/rpc/index.d.ts +0 -22
- package/dist/rpc/index.d.ts.map +0 -1
- package/dist/rpc/index.js +0 -38
- package/dist/rpc/index.js.map +0 -1
- package/dist/rpc/local.d.ts +0 -42
- package/dist/rpc/local.d.ts.map +0 -1
- package/dist/rpc/local.js +0 -50
- package/dist/rpc/local.js.map +0 -1
- package/dist/rpc/server.d.ts +0 -165
- package/dist/rpc/server.d.ts.map +0 -1
- package/dist/rpc/server.js +0 -405
- package/dist/rpc/server.js.map +0 -1
- package/dist/rpc/session.d.ts +0 -32
- package/dist/rpc/session.d.ts.map +0 -1
- package/dist/rpc/session.js +0 -43
- package/dist/rpc/session.js.map +0 -1
- package/dist/rpc/transport.d.ts +0 -306
- package/dist/rpc/transport.d.ts.map +0 -1
- package/dist/rpc/transport.js +0 -731
- package/dist/rpc/transport.js.map +0 -1
- package/src/batch/anthropic.js +0 -256
- package/src/batch/bedrock.js +0 -584
- package/src/batch/cloudflare.js +0 -287
- package/src/batch/google.js +0 -359
- package/src/batch/index.js +0 -30
- package/src/batch/memory.js +0 -187
- package/src/batch/openai.js +0 -402
- package/src/eval/index.js +0 -7
- package/src/eval/models.js +0 -119
- package/src/eval/runner.js +0 -147
- package/test/schema.test.js +0 -96
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RAG Chatbot Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates building a Retrieval-Augmented Generation (RAG) chatbot
|
|
5
|
+
* using ai-functions. It shows how to:
|
|
6
|
+
* - Generate embeddings for documents
|
|
7
|
+
* - Perform semantic search
|
|
8
|
+
* - Generate context-aware responses
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```bash
|
|
12
|
+
* ANTHROPIC_API_KEY=sk-... npx tsx examples/01-rag-chatbot.ts
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
write,
|
|
18
|
+
list,
|
|
19
|
+
configure,
|
|
20
|
+
EmbeddingCache,
|
|
21
|
+
MemoryCache,
|
|
22
|
+
GenerationCache,
|
|
23
|
+
withRetry,
|
|
24
|
+
} from '../src/index.js'
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Document Store (In-memory for this example)
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
interface Document {
|
|
31
|
+
id: string
|
|
32
|
+
content: string
|
|
33
|
+
embedding?: number[]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const documents: Document[] = [
|
|
37
|
+
{
|
|
38
|
+
id: 'doc-1',
|
|
39
|
+
content:
|
|
40
|
+
'ai-functions is a TypeScript library that simplifies AI integration. It provides template literals for natural AI calls like `const poem = await write`a haiku about TypeScript``.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'doc-2',
|
|
44
|
+
content:
|
|
45
|
+
'The library supports batch processing with 50% cost savings through provider batch APIs. Use createBatch() to process large workloads efficiently.',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'doc-3',
|
|
49
|
+
content:
|
|
50
|
+
'Built-in retry logic with exponential backoff handles rate limits automatically. Use withRetry() or RetryPolicy for custom retry behavior.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'doc-4',
|
|
54
|
+
content:
|
|
55
|
+
'Budget tracking monitors token usage and costs. BudgetTracker supports alerts at configurable thresholds and enforces spending limits.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'doc-5',
|
|
59
|
+
content:
|
|
60
|
+
'The list primitive generates arrays with automatic batching. Use list`5 ideas`.map() to process each item in parallel.',
|
|
61
|
+
},
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Simple Embedding Simulation (replace with real embeddings in production)
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
function simulateEmbedding(text: string): number[] {
|
|
69
|
+
// Simple word-based embedding simulation
|
|
70
|
+
// In production, use a real embedding model
|
|
71
|
+
const words = text.toLowerCase().split(/\s+/)
|
|
72
|
+
const embedding = new Array(128).fill(0)
|
|
73
|
+
|
|
74
|
+
for (const word of words) {
|
|
75
|
+
const hash = word.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)
|
|
76
|
+
embedding[hash % 128] += 1
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Normalize
|
|
80
|
+
const magnitude = Math.sqrt(embedding.reduce((acc, val) => acc + val * val, 0))
|
|
81
|
+
return embedding.map((val) => (magnitude > 0 ? val / magnitude : 0))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function cosineSimilarity(a: number[], b: number[]): number {
|
|
85
|
+
let dotProduct = 0
|
|
86
|
+
for (let i = 0; i < a.length; i++) {
|
|
87
|
+
dotProduct += a[i] * b[i]
|
|
88
|
+
}
|
|
89
|
+
return dotProduct
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// RAG Chatbot Implementation
|
|
94
|
+
// ============================================================================
|
|
95
|
+
|
|
96
|
+
class RAGChatbot {
|
|
97
|
+
private documents: Document[]
|
|
98
|
+
private responseCache: GenerationCache
|
|
99
|
+
|
|
100
|
+
constructor(documents: Document[]) {
|
|
101
|
+
this.documents = documents
|
|
102
|
+
this.responseCache = new GenerationCache({
|
|
103
|
+
defaultTTL: 3600000, // 1 hour cache
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// Generate embeddings for all documents
|
|
107
|
+
for (const doc of this.documents) {
|
|
108
|
+
doc.embedding = simulateEmbedding(doc.content)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Find the most relevant documents for a query
|
|
114
|
+
*/
|
|
115
|
+
private findRelevantDocs(query: string, topK: number = 3): Document[] {
|
|
116
|
+
const queryEmbedding = simulateEmbedding(query)
|
|
117
|
+
|
|
118
|
+
const scored = this.documents.map((doc) => ({
|
|
119
|
+
doc,
|
|
120
|
+
score: cosineSimilarity(queryEmbedding, doc.embedding!),
|
|
121
|
+
}))
|
|
122
|
+
|
|
123
|
+
return scored
|
|
124
|
+
.sort((a, b) => b.score - a.score)
|
|
125
|
+
.slice(0, topK)
|
|
126
|
+
.map(({ doc }) => doc)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generate a response using RAG
|
|
131
|
+
*/
|
|
132
|
+
async chat(userMessage: string): Promise<string> {
|
|
133
|
+
console.log(`\nUser: ${userMessage}`)
|
|
134
|
+
console.log('---')
|
|
135
|
+
|
|
136
|
+
// Step 1: Retrieve relevant documents
|
|
137
|
+
const relevantDocs = this.findRelevantDocs(userMessage)
|
|
138
|
+
console.log(`Found ${relevantDocs.length} relevant documents`)
|
|
139
|
+
|
|
140
|
+
// Step 2: Build context from retrieved documents
|
|
141
|
+
const context = relevantDocs.map((doc, i) => `[Source ${i + 1}]: ${doc.content}`).join('\n\n')
|
|
142
|
+
|
|
143
|
+
// Step 3: Generate response with context
|
|
144
|
+
const response =
|
|
145
|
+
await write`You are a helpful assistant that answers questions about ai-functions library.
|
|
146
|
+
|
|
147
|
+
Context from documentation:
|
|
148
|
+
${context}
|
|
149
|
+
|
|
150
|
+
User Question: ${userMessage}
|
|
151
|
+
|
|
152
|
+
Please provide a helpful, accurate response based on the context above. If the context doesn't contain enough information, say so.`
|
|
153
|
+
|
|
154
|
+
console.log(`Assistant: ${response}`)
|
|
155
|
+
return response
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Generate follow-up questions
|
|
160
|
+
*/
|
|
161
|
+
async suggestFollowUps(topic: string): Promise<string[]> {
|
|
162
|
+
const suggestions =
|
|
163
|
+
await list`3 follow-up questions someone might ask about ${topic} related to ai-functions library`
|
|
164
|
+
return suggestions
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// Main Example
|
|
170
|
+
// ============================================================================
|
|
171
|
+
|
|
172
|
+
async function main() {
|
|
173
|
+
console.log('\n=== RAG Chatbot Example ===\n')
|
|
174
|
+
|
|
175
|
+
// Configure the AI provider
|
|
176
|
+
configure({
|
|
177
|
+
model: 'sonnet',
|
|
178
|
+
provider: 'anthropic',
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// Initialize the chatbot with our documents
|
|
182
|
+
const chatbot = new RAGChatbot(documents)
|
|
183
|
+
|
|
184
|
+
// Example conversation
|
|
185
|
+
const questions = [
|
|
186
|
+
'How do I generate a list of items?',
|
|
187
|
+
'What are the cost savings for batch processing?',
|
|
188
|
+
'How does retry handling work?',
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
for (const question of questions) {
|
|
192
|
+
await chatbot.chat(question)
|
|
193
|
+
console.log('')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Generate follow-up suggestions
|
|
197
|
+
console.log('\n--- Suggested follow-up questions ---')
|
|
198
|
+
const followUps = await chatbot.suggestFollowUps('batch processing')
|
|
199
|
+
for (const suggestion of followUps) {
|
|
200
|
+
console.log(`- ${suggestion}`)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
main()
|
|
205
|
+
.then(() => {
|
|
206
|
+
console.log('\n=== Example Complete ===\n')
|
|
207
|
+
process.exit(0)
|
|
208
|
+
})
|
|
209
|
+
.catch((error) => {
|
|
210
|
+
console.error('\nError:', error.message)
|
|
211
|
+
process.exit(1)
|
|
212
|
+
})
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Agent Research Workflow Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates a multi-agent research workflow where different
|
|
5
|
+
* specialized agents collaborate to research a topic. It shows how to:
|
|
6
|
+
* - Define specialized agents with different roles
|
|
7
|
+
* - Coordinate agent interactions
|
|
8
|
+
* - Aggregate and synthesize results
|
|
9
|
+
* - Use tool orchestration for complex workflows
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```bash
|
|
13
|
+
* ANTHROPIC_API_KEY=sk-... npx tsx examples/02-multi-agent-research.ts
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
ai,
|
|
19
|
+
write,
|
|
20
|
+
list,
|
|
21
|
+
is,
|
|
22
|
+
configure,
|
|
23
|
+
AgenticLoop,
|
|
24
|
+
createTool,
|
|
25
|
+
createToolset,
|
|
26
|
+
type Tool,
|
|
27
|
+
} from '../src/index.js'
|
|
28
|
+
import { z } from 'zod'
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Agent Definitions
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
interface AgentResult {
|
|
35
|
+
agent: string
|
|
36
|
+
findings: string[]
|
|
37
|
+
confidence: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Research Planner Agent - Creates a research plan
|
|
42
|
+
*/
|
|
43
|
+
async function plannerAgent(topic: string): Promise<string[]> {
|
|
44
|
+
console.log('\n[Planner Agent] Creating research plan...')
|
|
45
|
+
|
|
46
|
+
const questions = await list`5 key research questions to thoroughly investigate: "${topic}"
|
|
47
|
+
|
|
48
|
+
Consider:
|
|
49
|
+
- Background and context
|
|
50
|
+
- Current state and trends
|
|
51
|
+
- Key players and stakeholders
|
|
52
|
+
- Challenges and opportunities
|
|
53
|
+
- Future implications`
|
|
54
|
+
|
|
55
|
+
console.log('[Planner Agent] Research questions:')
|
|
56
|
+
questions.forEach((q, i) => console.log(` ${i + 1}. ${q}`))
|
|
57
|
+
|
|
58
|
+
return questions
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Fact Finder Agent - Gathers factual information
|
|
63
|
+
*/
|
|
64
|
+
async function factFinderAgent(question: string): Promise<AgentResult> {
|
|
65
|
+
console.log(`\n[Fact Finder] Researching: ${question.substring(0, 50)}...`)
|
|
66
|
+
|
|
67
|
+
const { facts, sources, confidence } =
|
|
68
|
+
await ai`Research this question and provide factual information:
|
|
69
|
+
"${question}"
|
|
70
|
+
|
|
71
|
+
Provide your response with:
|
|
72
|
+
- facts: array of factual findings (3-5 key facts)
|
|
73
|
+
- sources: where this information typically comes from
|
|
74
|
+
- confidence: your confidence level 0-1 in these facts`
|
|
75
|
+
|
|
76
|
+
const result: AgentResult = {
|
|
77
|
+
agent: 'FactFinder',
|
|
78
|
+
findings: facts as string[],
|
|
79
|
+
confidence: (confidence as number) || 0.7,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log(
|
|
83
|
+
`[Fact Finder] Found ${result.findings.length} facts (confidence: ${result.confidence})`
|
|
84
|
+
)
|
|
85
|
+
return result
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Critical Analyst Agent - Analyzes and critiques
|
|
90
|
+
*/
|
|
91
|
+
async function analystAgent(findings: string[]): Promise<AgentResult> {
|
|
92
|
+
console.log('\n[Analyst Agent] Analyzing findings...')
|
|
93
|
+
|
|
94
|
+
const { analysis, gaps, confidence } = await ai`Critically analyze these research findings:
|
|
95
|
+
${findings.map((f, i) => `${i + 1}. ${f}`).join('\n')}
|
|
96
|
+
|
|
97
|
+
Provide:
|
|
98
|
+
- analysis: array of analytical insights (3-4 insights)
|
|
99
|
+
- gaps: any gaps or areas needing more research
|
|
100
|
+
- confidence: confidence in the analysis 0-1`
|
|
101
|
+
|
|
102
|
+
const result: AgentResult = {
|
|
103
|
+
agent: 'Analyst',
|
|
104
|
+
findings: analysis as string[],
|
|
105
|
+
confidence: (confidence as number) || 0.6,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log(`[Analyst Agent] Generated ${result.findings.length} insights`)
|
|
109
|
+
return result
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Synthesizer Agent - Creates final summary
|
|
114
|
+
*/
|
|
115
|
+
async function synthesizerAgent(allResults: AgentResult[]): Promise<string> {
|
|
116
|
+
console.log('\n[Synthesizer Agent] Creating final synthesis...')
|
|
117
|
+
|
|
118
|
+
const allFindings = allResults.flatMap((r) => r.findings)
|
|
119
|
+
|
|
120
|
+
const synthesis = await write`Create a comprehensive research summary from these findings:
|
|
121
|
+
|
|
122
|
+
${allFindings.map((f, i) => `- ${f}`).join('\n')}
|
|
123
|
+
|
|
124
|
+
Structure the summary with:
|
|
125
|
+
1. Executive Summary (2-3 sentences)
|
|
126
|
+
2. Key Findings (bullet points)
|
|
127
|
+
3. Analysis & Insights
|
|
128
|
+
4. Recommendations
|
|
129
|
+
5. Areas for Further Research`
|
|
130
|
+
|
|
131
|
+
return synthesis
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// Coordinator - Orchestrates the Multi-Agent Workflow
|
|
136
|
+
// ============================================================================
|
|
137
|
+
|
|
138
|
+
class ResearchCoordinator {
|
|
139
|
+
private topic: string
|
|
140
|
+
private results: AgentResult[] = []
|
|
141
|
+
|
|
142
|
+
constructor(topic: string) {
|
|
143
|
+
this.topic = topic
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async run(): Promise<string> {
|
|
147
|
+
console.log(`\n${'='.repeat(60)}`)
|
|
148
|
+
console.log(`Research Topic: ${this.topic}`)
|
|
149
|
+
console.log('='.repeat(60))
|
|
150
|
+
|
|
151
|
+
// Phase 1: Planning
|
|
152
|
+
const questions = await plannerAgent(this.topic)
|
|
153
|
+
|
|
154
|
+
// Phase 2: Parallel Research
|
|
155
|
+
console.log('\n[Coordinator] Starting parallel research phase...')
|
|
156
|
+
const researchPromises = questions.slice(0, 3).map((q) => factFinderAgent(q))
|
|
157
|
+
const researchResults = await Promise.all(researchPromises)
|
|
158
|
+
this.results.push(...researchResults)
|
|
159
|
+
|
|
160
|
+
// Phase 3: Analysis
|
|
161
|
+
const allFacts = researchResults.flatMap((r) => r.findings)
|
|
162
|
+
const analysisResult = await analystAgent(allFacts)
|
|
163
|
+
this.results.push(analysisResult)
|
|
164
|
+
|
|
165
|
+
// Phase 4: Synthesis
|
|
166
|
+
const finalReport = await synthesizerAgent(this.results)
|
|
167
|
+
|
|
168
|
+
// Phase 5: Quality Check
|
|
169
|
+
const isQualityOk =
|
|
170
|
+
await is`This research summary is well-structured and comprehensive: "${finalReport.substring(
|
|
171
|
+
0,
|
|
172
|
+
200
|
|
173
|
+
)}..."`
|
|
174
|
+
|
|
175
|
+
if (!isQualityOk) {
|
|
176
|
+
console.log('[Coordinator] Quality check failed, requesting revision...')
|
|
177
|
+
// In production, you might iterate on the summary
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return finalReport
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
getResults(): AgentResult[] {
|
|
184
|
+
return this.results
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ============================================================================
|
|
189
|
+
// Tool-Based Research Agent (Alternative Approach)
|
|
190
|
+
// ============================================================================
|
|
191
|
+
|
|
192
|
+
const searchTool = createTool({
|
|
193
|
+
name: 'search',
|
|
194
|
+
description: 'Search for information on a topic',
|
|
195
|
+
parameters: {
|
|
196
|
+
query: z.string().describe('Search query'),
|
|
197
|
+
},
|
|
198
|
+
execute: async ({ query }) => {
|
|
199
|
+
// Simulate search results
|
|
200
|
+
console.log(` [Tool] Searching: ${query}`)
|
|
201
|
+
return {
|
|
202
|
+
results: [
|
|
203
|
+
`Result 1 for "${query}": Found relevant information...`,
|
|
204
|
+
`Result 2 for "${query}": Additional context...`,
|
|
205
|
+
],
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
const analyzeTool = createTool({
|
|
211
|
+
name: 'analyze',
|
|
212
|
+
description: 'Analyze and synthesize information',
|
|
213
|
+
parameters: {
|
|
214
|
+
data: z.string().describe('Data to analyze'),
|
|
215
|
+
perspective: z.string().describe('Analysis perspective'),
|
|
216
|
+
},
|
|
217
|
+
execute: async ({ data, perspective }) => {
|
|
218
|
+
console.log(` [Tool] Analyzing from ${perspective} perspective...`)
|
|
219
|
+
return {
|
|
220
|
+
analysis: `Analysis of "${data.substring(
|
|
221
|
+
0,
|
|
222
|
+
30
|
|
223
|
+
)}..." from ${perspective} perspective: Key insights identified.`,
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
async function toolBasedResearch(topic: string): Promise<void> {
|
|
229
|
+
console.log('\n--- Tool-Based Research Agent ---')
|
|
230
|
+
|
|
231
|
+
const loop = new AgenticLoop({
|
|
232
|
+
tools: createToolset(searchTool, analyzeTool),
|
|
233
|
+
maxSteps: 5,
|
|
234
|
+
onStep: (step) => {
|
|
235
|
+
console.log(`[Step ${step.stepNumber}] Tool calls: ${step.toolCalls.length}`)
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
// This requires a model that supports tool calling
|
|
240
|
+
// For demonstration, we'll skip the actual execution
|
|
241
|
+
console.log('Tool-based agent configured with:', loop.getToolsForSDK())
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ============================================================================
|
|
245
|
+
// Main Example
|
|
246
|
+
// ============================================================================
|
|
247
|
+
|
|
248
|
+
async function main() {
|
|
249
|
+
console.log('\n=== Multi-Agent Research Workflow ===\n')
|
|
250
|
+
|
|
251
|
+
// Configure the AI provider
|
|
252
|
+
configure({
|
|
253
|
+
model: 'sonnet',
|
|
254
|
+
provider: 'anthropic',
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
// Run the research workflow
|
|
258
|
+
const coordinator = new ResearchCoordinator(
|
|
259
|
+
'The impact of Large Language Models on software development practices'
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
const report = await coordinator.run()
|
|
263
|
+
|
|
264
|
+
console.log('\n' + '='.repeat(60))
|
|
265
|
+
console.log('FINAL RESEARCH REPORT')
|
|
266
|
+
console.log('='.repeat(60))
|
|
267
|
+
console.log(report)
|
|
268
|
+
|
|
269
|
+
// Show agent statistics
|
|
270
|
+
console.log('\n--- Agent Statistics ---')
|
|
271
|
+
const results = coordinator.getResults()
|
|
272
|
+
for (const result of results) {
|
|
273
|
+
console.log(
|
|
274
|
+
`${result.agent}: ${result.findings.length} findings (confidence: ${result.confidence})`
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Demonstrate tool-based approach
|
|
279
|
+
await toolBasedResearch('AI in healthcare')
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
main()
|
|
283
|
+
.then(() => {
|
|
284
|
+
console.log('\n=== Example Complete ===\n')
|
|
285
|
+
process.exit(0)
|
|
286
|
+
})
|
|
287
|
+
.catch((error) => {
|
|
288
|
+
console.error('\nError:', error.message)
|
|
289
|
+
process.exit(1)
|
|
290
|
+
})
|