@intentsolutionsio/jeremy-genkit-pro 2.1.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/.claude-plugin/plugin.json +22 -0
- package/LICENSE +21 -0
- package/README.md +204 -0
- package/agents/genkit-flow-architect.md +206 -0
- package/commands/init-genkit-project.md +356 -0
- package/package.json +44 -0
- package/skills/genkit-production-expert/SKILL.md +81 -0
- package/skills/genkit-production-expert/references/ARD.md +71 -0
- package/skills/genkit-production-expert/references/PRD.md +70 -0
- package/skills/genkit-production-expert/references/errors.md +57 -0
- package/skills/genkit-production-expert/references/examples.md +493 -0
- package/skills/genkit-production-expert/references/how-it-works.md +55 -0
- package/skills/genkit-production-expert/references/production-best-practices-applied.md +40 -0
- package/skills/genkit-production-expert/references/workflow-examples.md +105 -0
- package/skills/genkit-production-expert/scripts/init-genkit.sh +77 -0
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
# Examples — Genkit Production Expert
|
|
2
|
+
|
|
3
|
+
## Example 1: Question-Answering Flow with Zod Schemas
|
|
4
|
+
|
|
5
|
+
A simple Genkit flow with typed input/output, temperature tuning, and token monitoring.
|
|
6
|
+
|
|
7
|
+
### Setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install genkit @genkit-ai/googleai zod
|
|
11
|
+
npm install -D typescript tsx
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Flow Implementation
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
// src/qa-flow.ts
|
|
18
|
+
import { genkit, z } from "genkit";
|
|
19
|
+
import { googleAI, gemini25Flash } from "@genkit-ai/googleai";
|
|
20
|
+
|
|
21
|
+
const ai = genkit({
|
|
22
|
+
plugins: [googleAI()],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Strict input/output schemas
|
|
26
|
+
const QuestionInput = z.object({
|
|
27
|
+
question: z.string().min(3).describe("The user question"),
|
|
28
|
+
context: z.string().optional().describe("Optional context to ground the answer"),
|
|
29
|
+
maxWords: z.number().min(10).max(500).default(150),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const AnswerOutput = z.object({
|
|
33
|
+
answer: z.string(),
|
|
34
|
+
confidence: z.enum(["high", "medium", "low"]),
|
|
35
|
+
tokenUsage: z.object({
|
|
36
|
+
input: z.number(),
|
|
37
|
+
output: z.number(),
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export const qaFlow = ai.defineFlow(
|
|
42
|
+
{
|
|
43
|
+
name: "question-answer",
|
|
44
|
+
inputSchema: QuestionInput,
|
|
45
|
+
outputSchema: AnswerOutput,
|
|
46
|
+
},
|
|
47
|
+
async (input) => {
|
|
48
|
+
const contextBlock = input.context
|
|
49
|
+
? `\n\nContext:\n${input.context}`
|
|
50
|
+
: "";
|
|
51
|
+
|
|
52
|
+
const { text, usage } = await ai.generate({
|
|
53
|
+
model: gemini25Flash,
|
|
54
|
+
prompt: `Answer the following question in at most ${input.maxWords} words.
|
|
55
|
+
If you are unsure, say so and rate your confidence as "low".${contextBlock}
|
|
56
|
+
|
|
57
|
+
Question: ${input.question}
|
|
58
|
+
|
|
59
|
+
Respond in JSON format:
|
|
60
|
+
{"answer": "...", "confidence": "high|medium|low"}`,
|
|
61
|
+
config: {
|
|
62
|
+
temperature: 0.3,
|
|
63
|
+
maxOutputTokens: 1024,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const parsed = JSON.parse(text);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
answer: parsed.answer,
|
|
71
|
+
confidence: parsed.confidence,
|
|
72
|
+
tokenUsage: {
|
|
73
|
+
input: usage?.inputTokens ?? 0,
|
|
74
|
+
output: usage?.outputTokens ?? 0,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Start local server for testing with Genkit Developer UI
|
|
81
|
+
ai.startFlowServer({ flows: [qaFlow] });
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Local Testing
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Start the Genkit dev server
|
|
88
|
+
npx genkit start -- tsx src/qa-flow.ts
|
|
89
|
+
|
|
90
|
+
# Test with curl
|
|
91
|
+
curl -X POST http://localhost:3400/question-answer \
|
|
92
|
+
-H "Content-Type: application/json" \
|
|
93
|
+
-d '{"question": "What is Firebase Genkit?", "maxWords": 100}'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Expected Output
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"answer": "Firebase Genkit is an open-source framework for building AI-powered applications. It provides a unified API for defining flows (multi-step AI pipelines), tools (external capabilities), and retrievers (document search). Genkit supports multiple model providers including Google Gemini, has built-in observability via OpenTelemetry, and deploys to Firebase Functions or Cloud Run. It is available for Node.js, Python, and Go.",
|
|
101
|
+
"confidence": "high",
|
|
102
|
+
"tokenUsage": {
|
|
103
|
+
"input": 82,
|
|
104
|
+
"output": 94
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Deploy to Firebase Functions
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// src/index.ts — Firebase Functions entry point
|
|
113
|
+
import { onFlow } from "@genkit-ai/firebase/functions";
|
|
114
|
+
import { qaFlow } from "./qa-flow";
|
|
115
|
+
|
|
116
|
+
export const questionAnswer = onFlow(
|
|
117
|
+
{
|
|
118
|
+
name: "question-answer",
|
|
119
|
+
httpsOptions: {
|
|
120
|
+
cors: true,
|
|
121
|
+
memory: "512MiB",
|
|
122
|
+
minInstances: 1,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
qaFlow
|
|
126
|
+
);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
firebase deploy --only functions
|
|
131
|
+
# Function URL: https://us-central1-my-project.cloudfunctions.net/questionAnswer
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Example 2: RAG Flow with Firestore Vector Search
|
|
137
|
+
|
|
138
|
+
Retrieve documents, inject as context, and generate grounded answers with source citations.
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// src/rag-flow.ts
|
|
142
|
+
import { genkit, z } from "genkit";
|
|
143
|
+
import { googleAI, gemini25Flash, textEmbedding004 } from "@genkit-ai/googleai";
|
|
144
|
+
import { Firestore, FieldValue } from "firebase-admin/firestore";
|
|
145
|
+
import { initializeApp } from "firebase-admin/app";
|
|
146
|
+
|
|
147
|
+
initializeApp();
|
|
148
|
+
const db = new Firestore();
|
|
149
|
+
|
|
150
|
+
const ai = genkit({
|
|
151
|
+
plugins: [googleAI()],
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Retriever: query Firestore vector index
|
|
155
|
+
const docRetriever = ai.defineRetriever(
|
|
156
|
+
{ name: "firestore-knowledge-base" },
|
|
157
|
+
async (query: string, options?: { k?: number }) => {
|
|
158
|
+
const k = options?.k ?? 5;
|
|
159
|
+
|
|
160
|
+
// Generate query embedding
|
|
161
|
+
const { embedding } = await ai.embed({
|
|
162
|
+
embedder: textEmbedding004,
|
|
163
|
+
content: query,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Vector similarity search in Firestore
|
|
167
|
+
const snapshot = await db
|
|
168
|
+
.collection("knowledge_base")
|
|
169
|
+
.findNearest("embedding", embedding, {
|
|
170
|
+
limit: k,
|
|
171
|
+
distanceMeasure: "COSINE",
|
|
172
|
+
})
|
|
173
|
+
.get();
|
|
174
|
+
|
|
175
|
+
return snapshot.docs.map((doc) => ({
|
|
176
|
+
content: doc.data().text as string,
|
|
177
|
+
metadata: {
|
|
178
|
+
id: doc.id,
|
|
179
|
+
title: doc.data().title as string,
|
|
180
|
+
category: doc.data().category as string,
|
|
181
|
+
lastUpdated: doc.data().updatedAt?.toDate()?.toISOString() ?? "",
|
|
182
|
+
},
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Indexer: add documents to the vector store
|
|
188
|
+
const docIndexer = ai.defineIndexer(
|
|
189
|
+
{ name: "firestore-indexer" },
|
|
190
|
+
async (docs) => {
|
|
191
|
+
const batch = db.batch();
|
|
192
|
+
for (const doc of docs) {
|
|
193
|
+
const { embedding } = await ai.embed({
|
|
194
|
+
embedder: textEmbedding004,
|
|
195
|
+
content: doc.content,
|
|
196
|
+
});
|
|
197
|
+
const ref = db.collection("knowledge_base").doc();
|
|
198
|
+
batch.set(ref, {
|
|
199
|
+
text: doc.content,
|
|
200
|
+
title: doc.metadata?.title ?? "Untitled",
|
|
201
|
+
category: doc.metadata?.category ?? "general",
|
|
202
|
+
embedding,
|
|
203
|
+
updatedAt: FieldValue.serverTimestamp(),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
await batch.commit();
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// RAG flow
|
|
211
|
+
const RagInput = z.object({
|
|
212
|
+
question: z.string().min(1),
|
|
213
|
+
topK: z.number().min(1).max(20).default(5),
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const RagOutput = z.object({
|
|
217
|
+
answer: z.string(),
|
|
218
|
+
sources: z.array(z.object({
|
|
219
|
+
title: z.string(),
|
|
220
|
+
category: z.string(),
|
|
221
|
+
snippet: z.string(),
|
|
222
|
+
})),
|
|
223
|
+
cached: z.boolean(),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Simple in-memory cache for repeated queries
|
|
227
|
+
const queryCache = new Map<string, { result: z.infer<typeof RagOutput>; expiry: number }>();
|
|
228
|
+
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
229
|
+
|
|
230
|
+
export const ragFlow = ai.defineFlow(
|
|
231
|
+
{
|
|
232
|
+
name: "rag-search",
|
|
233
|
+
inputSchema: RagInput,
|
|
234
|
+
outputSchema: RagOutput,
|
|
235
|
+
},
|
|
236
|
+
async (input) => {
|
|
237
|
+
// Check cache
|
|
238
|
+
const cacheKey = `${input.question}:${input.topK}`;
|
|
239
|
+
const cached = queryCache.get(cacheKey);
|
|
240
|
+
if (cached && cached.expiry > Date.now()) {
|
|
241
|
+
return { ...cached.result, cached: true };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Retrieve relevant documents
|
|
245
|
+
const docs = await ai.retrieve({
|
|
246
|
+
retriever: docRetriever,
|
|
247
|
+
query: input.question,
|
|
248
|
+
options: { k: input.topK },
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
if (docs.length === 0) {
|
|
252
|
+
return {
|
|
253
|
+
answer: "I could not find any relevant documents to answer your question.",
|
|
254
|
+
sources: [],
|
|
255
|
+
cached: false,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Build numbered context
|
|
260
|
+
const context = docs
|
|
261
|
+
.map((d, i) => `[${i + 1}] (${d.metadata?.category}) ${d.metadata?.title}\n${d.content}`)
|
|
262
|
+
.join("\n\n---\n\n");
|
|
263
|
+
|
|
264
|
+
// Generate grounded answer
|
|
265
|
+
const { text } = await ai.generate({
|
|
266
|
+
model: gemini25Flash,
|
|
267
|
+
prompt: `You are a knowledge base assistant. Answer ONLY using the provided sources.
|
|
268
|
+
Cite sources using [1], [2], etc. If the sources don't cover the question, say so.
|
|
269
|
+
|
|
270
|
+
Sources:
|
|
271
|
+
${context}
|
|
272
|
+
|
|
273
|
+
Question: ${input.question}`,
|
|
274
|
+
config: { temperature: 0.2, maxOutputTokens: 1024 },
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const result: z.infer<typeof RagOutput> = {
|
|
278
|
+
answer: text,
|
|
279
|
+
sources: docs.map((d) => ({
|
|
280
|
+
title: d.metadata?.title ?? "Unknown",
|
|
281
|
+
category: d.metadata?.category ?? "general",
|
|
282
|
+
snippet: d.content.substring(0, 150) + "...",
|
|
283
|
+
})),
|
|
284
|
+
cached: false,
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Cache the result
|
|
288
|
+
queryCache.set(cacheKey, { result, expiry: Date.now() + CACHE_TTL_MS });
|
|
289
|
+
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
ai.startFlowServer({ flows: [ragFlow] });
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Expected Output
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"answer": "To set up VPC Service Controls for Vertex AI, you need to: 1) Create an access policy and service perimeter in Access Context Manager [1], 2) Add aiplatform.googleapis.com to the restricted services list [1], 3) Configure access levels for your CI/CD service accounts [2], and 4) Test access from both inside and outside the perimeter [3]. Changes propagate within 30 minutes.",
|
|
302
|
+
"sources": [
|
|
303
|
+
{
|
|
304
|
+
"title": "VPC-SC Configuration Guide",
|
|
305
|
+
"category": "security",
|
|
306
|
+
"snippet": "VPC Service Controls create a security perimeter around GCP resources. To configure for Vertex AI..."
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"title": "CI/CD Pipeline Security",
|
|
310
|
+
"category": "devops",
|
|
311
|
+
"snippet": "Service accounts used in CI/CD pipelines need explicit access levels in the VPC-SC perimeter..."
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"title": "Agent Engine Deployment Checklist",
|
|
315
|
+
"category": "deployment",
|
|
316
|
+
"snippet": "Before deploying to production, verify that VPC-SC perimeters allow the agent service account..."
|
|
317
|
+
}
|
|
318
|
+
],
|
|
319
|
+
"cached": false
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Example 3: Multi-Tool Agent Flow
|
|
326
|
+
|
|
327
|
+
Define tools with typed schemas, route user queries, and trace tool execution.
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
// src/agent-flow.ts
|
|
331
|
+
import { genkit, z } from "genkit";
|
|
332
|
+
import { googleAI, gemini25Flash } from "@genkit-ai/googleai";
|
|
333
|
+
|
|
334
|
+
const ai = genkit({
|
|
335
|
+
plugins: [googleAI()],
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Tool 1: Weather lookup
|
|
339
|
+
const getWeather = ai.defineTool(
|
|
340
|
+
{
|
|
341
|
+
name: "getWeather",
|
|
342
|
+
description: "Get current weather for a city",
|
|
343
|
+
inputSchema: z.object({
|
|
344
|
+
city: z.string().describe("City name, e.g. 'San Francisco'"),
|
|
345
|
+
}),
|
|
346
|
+
outputSchema: z.object({
|
|
347
|
+
city: z.string(),
|
|
348
|
+
tempF: z.number(),
|
|
349
|
+
condition: z.string(),
|
|
350
|
+
humidity: z.number(),
|
|
351
|
+
}),
|
|
352
|
+
},
|
|
353
|
+
async ({ city }) => {
|
|
354
|
+
// In production, call a real weather API
|
|
355
|
+
const mockWeather: Record<string, { tempF: number; condition: string; humidity: number }> = {
|
|
356
|
+
"san francisco": { tempF: 62, condition: "Foggy", humidity: 78 },
|
|
357
|
+
"new york": { tempF: 45, condition: "Cloudy", humidity: 55 },
|
|
358
|
+
"austin": { tempF: 85, condition: "Sunny", humidity: 40 },
|
|
359
|
+
};
|
|
360
|
+
const data = mockWeather[city.toLowerCase()] ?? { tempF: 70, condition: "Unknown", humidity: 50 };
|
|
361
|
+
return { city, ...data };
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
// Tool 2: Calendar event creation
|
|
366
|
+
const createEvent = ai.defineTool(
|
|
367
|
+
{
|
|
368
|
+
name: "createEvent",
|
|
369
|
+
description: "Create a calendar event",
|
|
370
|
+
inputSchema: z.object({
|
|
371
|
+
title: z.string(),
|
|
372
|
+
date: z.string().describe("ISO date string, e.g. 2025-03-20"),
|
|
373
|
+
startTime: z.string().describe("Start time, e.g. 14:00"),
|
|
374
|
+
durationMinutes: z.number().min(15).max(480),
|
|
375
|
+
}),
|
|
376
|
+
outputSchema: z.object({
|
|
377
|
+
eventId: z.string(),
|
|
378
|
+
title: z.string(),
|
|
379
|
+
scheduledAt: z.string(),
|
|
380
|
+
confirmed: z.boolean(),
|
|
381
|
+
}),
|
|
382
|
+
},
|
|
383
|
+
async ({ title, date, startTime, durationMinutes }) => {
|
|
384
|
+
const eventId = `evt_${Date.now().toString(36)}`;
|
|
385
|
+
return {
|
|
386
|
+
eventId,
|
|
387
|
+
title,
|
|
388
|
+
scheduledAt: `${date}T${startTime}:00`,
|
|
389
|
+
confirmed: true,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
// Agent flow with multi-turn tool usage
|
|
395
|
+
const AgentInput = z.object({
|
|
396
|
+
message: z.string(),
|
|
397
|
+
sessionId: z.string().optional(),
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const AgentOutput = z.object({
|
|
401
|
+
reply: z.string(),
|
|
402
|
+
toolsUsed: z.array(z.string()),
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
export const agentFlow = ai.defineFlow(
|
|
406
|
+
{
|
|
407
|
+
name: "assistant-agent",
|
|
408
|
+
inputSchema: AgentInput,
|
|
409
|
+
outputSchema: AgentOutput,
|
|
410
|
+
},
|
|
411
|
+
async (input) => {
|
|
412
|
+
const { text, toolRequests } = await ai.generate({
|
|
413
|
+
model: gemini25Flash,
|
|
414
|
+
tools: [getWeather, createEvent],
|
|
415
|
+
prompt: input.message,
|
|
416
|
+
system: `You are a helpful assistant with access to weather and calendar tools.
|
|
417
|
+
Use tools when the user asks about weather or wants to schedule something.
|
|
418
|
+
Be concise in your responses.`,
|
|
419
|
+
config: { temperature: 0.5 },
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
const toolsUsed = (toolRequests ?? []).map((t) => t.toolName);
|
|
423
|
+
|
|
424
|
+
return {
|
|
425
|
+
reply: text,
|
|
426
|
+
toolsUsed,
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
ai.startFlowServer({ flows: [agentFlow] });
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Testing
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
# Weather query
|
|
438
|
+
curl -X POST http://localhost:3400/assistant-agent \
|
|
439
|
+
-H "Content-Type: application/json" \
|
|
440
|
+
-d '{"message": "What is the weather in San Francisco?"}'
|
|
441
|
+
|
|
442
|
+
# Calendar + weather combined
|
|
443
|
+
curl -X POST http://localhost:3400/assistant-agent \
|
|
444
|
+
-H "Content-Type: application/json" \
|
|
445
|
+
-d '{"message": "Schedule a picnic tomorrow at 2pm for 2 hours. Also check the weather in Austin."}'
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Expected Output
|
|
449
|
+
|
|
450
|
+
Weather query:
|
|
451
|
+
```json
|
|
452
|
+
{
|
|
453
|
+
"reply": "The weather in San Francisco is currently 62F and foggy with 78% humidity. You might want to bring a jacket!",
|
|
454
|
+
"toolsUsed": ["getWeather"]
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
Combined query:
|
|
459
|
+
```json
|
|
460
|
+
{
|
|
461
|
+
"reply": "I've scheduled your picnic for tomorrow at 2:00 PM (2 hours). The weather in Austin looks great - 85F, sunny, and 40% humidity. Perfect picnic weather!",
|
|
462
|
+
"toolsUsed": ["createEvent", "getWeather"]
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Cloud Run Deployment
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
# Dockerfile
|
|
470
|
+
cat > Dockerfile << 'DOCKERFILE'
|
|
471
|
+
FROM node:20-slim
|
|
472
|
+
WORKDIR /app
|
|
473
|
+
COPY package*.json ./
|
|
474
|
+
RUN npm ci --production
|
|
475
|
+
COPY dist/ ./dist/
|
|
476
|
+
ENV PORT=8080
|
|
477
|
+
EXPOSE 8080
|
|
478
|
+
CMD ["node", "dist/agent-flow.js"]
|
|
479
|
+
DOCKERFILE
|
|
480
|
+
|
|
481
|
+
# Deploy
|
|
482
|
+
gcloud run deploy genkit-agent \
|
|
483
|
+
--source . \
|
|
484
|
+
--region us-central1 \
|
|
485
|
+
--memory 512Mi \
|
|
486
|
+
--min-instances 2 \
|
|
487
|
+
--max-instances 10 \
|
|
488
|
+
--set-secrets "GOOGLE_GENAI_API_KEY=genai-api-key:latest" \
|
|
489
|
+
--allow-unauthenticated
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# How It Works
|
|
2
|
+
|
|
3
|
+
## How It Works
|
|
4
|
+
|
|
5
|
+
### Phase 1: Requirements Analysis
|
|
6
|
+
```
|
|
7
|
+
User Request → Analyze needs → Determine:
|
|
8
|
+
- Target language (Node.js/Python/Go)
|
|
9
|
+
- Flow complexity (simple/multi-step/RAG)
|
|
10
|
+
- Model requirements (Gemini version, custom models)
|
|
11
|
+
- Deployment target (Firebase/Cloud Run/local)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Phase 2: Project Setup
|
|
15
|
+
```
|
|
16
|
+
Check existing project → If new:
|
|
17
|
+
- Initialize project structure
|
|
18
|
+
- Install dependencies
|
|
19
|
+
- Configure environment variables
|
|
20
|
+
- Set up TypeScript/Python/Go config
|
|
21
|
+
|
|
22
|
+
If existing:
|
|
23
|
+
- Analyze current structure
|
|
24
|
+
- Identify integration points
|
|
25
|
+
- Preserve existing code
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Phase 3: Implementation
|
|
29
|
+
```
|
|
30
|
+
Design flow architecture → Implement:
|
|
31
|
+
- Input/output schemas (Zod/Pydantic/Go structs)
|
|
32
|
+
- Model configuration
|
|
33
|
+
- Tool definitions (if needed)
|
|
34
|
+
- Retriever setup (for RAG)
|
|
35
|
+
- Error handling
|
|
36
|
+
- Tracing configuration
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Phase 4: Testing & Validation
|
|
40
|
+
```
|
|
41
|
+
Create test cases → Run locally:
|
|
42
|
+
- Genkit Developer UI
|
|
43
|
+
- Unit tests
|
|
44
|
+
- Integration tests
|
|
45
|
+
- Token usage analysis
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Phase 5: Production Deployment
|
|
49
|
+
```
|
|
50
|
+
Configure deployment → Deploy:
|
|
51
|
+
- Firebase Functions (with AI monitoring)
|
|
52
|
+
- Cloud Run (with auto-scaling)
|
|
53
|
+
- Set up monitoring dashboards
|
|
54
|
+
- Configure alerting
|
|
55
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Production Best Practices Applied
|
|
2
|
+
|
|
3
|
+
## Production Best Practices Applied
|
|
4
|
+
|
|
5
|
+
### 1. Schema Validation
|
|
6
|
+
- All inputs/outputs use Zod (TS), Pydantic (Python), or structs (Go)
|
|
7
|
+
- Prevents runtime errors from malformed data
|
|
8
|
+
|
|
9
|
+
### 2. Error Handling
|
|
10
|
+
```typescript
|
|
11
|
+
try {
|
|
12
|
+
const result = await ai.generate({...});
|
|
13
|
+
return result;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if (error.code === 'SAFETY_BLOCK') {
|
|
16
|
+
// Handle safety filters
|
|
17
|
+
} else if (error.code === 'QUOTA_EXCEEDED') {
|
|
18
|
+
// Handle rate limits
|
|
19
|
+
}
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 3. Cost Optimization
|
|
25
|
+
- Context caching for repeated prompts
|
|
26
|
+
- Token usage monitoring
|
|
27
|
+
- Temperature tuning for use case
|
|
28
|
+
- Model selection (Flash vs Pro)
|
|
29
|
+
|
|
30
|
+
### 4. Monitoring
|
|
31
|
+
- OpenTelemetry tracing enabled
|
|
32
|
+
- Custom span attributes
|
|
33
|
+
- Firebase Console integration
|
|
34
|
+
- Alert configuration
|
|
35
|
+
|
|
36
|
+
### 5. Security
|
|
37
|
+
- Environment variable management
|
|
38
|
+
- API key rotation support
|
|
39
|
+
- Input sanitization
|
|
40
|
+
- Output filtering
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Workflow Examples
|
|
2
|
+
|
|
3
|
+
## Workflow Examples
|
|
4
|
+
|
|
5
|
+
### Example 1: Simple Question-Answering Flow
|
|
6
|
+
|
|
7
|
+
**User Request**: "Create a Genkit flow that answers user questions using Gemini 2.5 Flash"
|
|
8
|
+
|
|
9
|
+
**Skill Response**:
|
|
10
|
+
1. Creates TypeScript project (default)
|
|
11
|
+
2. Implements flow with input validation:
|
|
12
|
+
```typescript
|
|
13
|
+
const qaFlow = ai.defineFlow(
|
|
14
|
+
{
|
|
15
|
+
name: 'qaFlow',
|
|
16
|
+
inputSchema: z.object({ question: z.string() }),
|
|
17
|
+
outputSchema: z.object({ answer: z.string() }),
|
|
18
|
+
},
|
|
19
|
+
async (input) => {
|
|
20
|
+
const { text } = await ai.generate({
|
|
21
|
+
model: gemini25Flash,
|
|
22
|
+
prompt: `Answer this question: ${input.question}`,
|
|
23
|
+
config: { temperature: 0.3 }, // Lower for factual answers
|
|
24
|
+
});
|
|
25
|
+
return { answer: text };
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
```
|
|
29
|
+
3. Sets up local testing
|
|
30
|
+
4. Provides deployment instructions
|
|
31
|
+
|
|
32
|
+
### Example 2: RAG System with Vector Search
|
|
33
|
+
|
|
34
|
+
**User Request**: "Implement RAG with Genkit for our documentation search"
|
|
35
|
+
|
|
36
|
+
**Skill Response**:
|
|
37
|
+
1. Analyzes document storage needs
|
|
38
|
+
2. Implements retriever with embeddings:
|
|
39
|
+
```typescript
|
|
40
|
+
const docRetriever = ai.defineRetriever(
|
|
41
|
+
{
|
|
42
|
+
name: 'docRetriever',
|
|
43
|
+
configSchema: z.object({ k: z.number().default(5) }),
|
|
44
|
+
},
|
|
45
|
+
async (query, config) => {
|
|
46
|
+
// Generate embedding
|
|
47
|
+
const embedding = await ai.embed({
|
|
48
|
+
embedder: textEmbedding004,
|
|
49
|
+
content: query,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Search vector database
|
|
53
|
+
const results = await vectorDB.search(embedding, config.k);
|
|
54
|
+
return results.map(doc => ({
|
|
55
|
+
content: doc.text,
|
|
56
|
+
metadata: { source: doc.source },
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
```
|
|
61
|
+
3. Creates RAG flow combining retrieval + generation
|
|
62
|
+
4. Sets up vector database connection
|
|
63
|
+
5. Implements caching for efficiency
|
|
64
|
+
|
|
65
|
+
### Example 3: Multi-Agent Tool Calling
|
|
66
|
+
|
|
67
|
+
**User Request**: "Create a Genkit agent with weather and calendar tools"
|
|
68
|
+
|
|
69
|
+
**Skill Response**:
|
|
70
|
+
1. Defines tools with proper schemas:
|
|
71
|
+
```typescript
|
|
72
|
+
const weatherTool = ai.defineTool({
|
|
73
|
+
name: 'getWeather',
|
|
74
|
+
description: 'Get current weather for a location',
|
|
75
|
+
inputSchema: z.object({ location: z.string() }),
|
|
76
|
+
outputSchema: z.object({
|
|
77
|
+
temp: z.number(),
|
|
78
|
+
conditions: z.string(),
|
|
79
|
+
}),
|
|
80
|
+
}, async ({ location }) => {
|
|
81
|
+
// Call weather API
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const calendarTool = ai.defineTool({
|
|
85
|
+
name: 'checkCalendar',
|
|
86
|
+
description: 'Check calendar availability',
|
|
87
|
+
inputSchema: z.object({ date: z.string() }),
|
|
88
|
+
outputSchema: z.object({ available: z.boolean() }),
|
|
89
|
+
}, async ({ date }) => {
|
|
90
|
+
// Check calendar API
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
2. Creates agent flow with tool access:
|
|
94
|
+
```typescript
|
|
95
|
+
const agentFlow = ai.defineFlow(async (userQuery) => {
|
|
96
|
+
const { text } = await ai.generate({
|
|
97
|
+
model: gemini25Flash,
|
|
98
|
+
prompt: userQuery,
|
|
99
|
+
tools: [weatherTool, calendarTool],
|
|
100
|
+
});
|
|
101
|
+
return text;
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
3. Implements proper error handling
|
|
105
|
+
4. Sets up tool execution tracing
|