Skip to content

Commit 732f134

Browse files
Update docs examples (#271)
* update vector storage docs - Add Vector DB guide examples (`search`) - Update JS examples to include required "key" field for `upsert` - Update Vector DB guide to reflect both SDKs return `similarity` field - Remove unnecessary distance-to-similarity conversions - Add note about JS backwards compatibility with `distance` parameter * Update subheadings in Vector DB guide * Update based on suggestions * Fix grammar in SDK requirement note
1 parent 93acbc0 commit 732f134

File tree

3 files changed

+332
-15
lines changed

3 files changed

+332
-15
lines changed

content/Guides/vector-db.mdx

Lines changed: 316 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,16 @@ Vector storage is created automatically when your agent first calls `context.vec
4444

4545
## Vector Storage API
4646

47+
For complete API documentation, see:
48+
- [JavaScript SDK Vector API Reference](/SDKs/javascript/api-reference#vector-storage)
49+
- [Python SDK Vector API Reference](/SDKs/python/api-reference#vector-storage)
50+
4751
### Upserting Documents
4852

4953
The `upsert` operation inserts new documents or updates existing ones. You can provide either text (which gets automatically converted to embeddings) or pre-computed embeddings.
5054

51-
**SDK Differences:**
52-
- **JavaScript SDK**: No key field required, search returns `distance` (0 = perfect match)
53-
- **Python SDK**: Requires `key` field for each document, search returns `similarity` (1.0 = perfect match)
55+
**SDK Requirements:**
56+
- **Both SDKs**: Require a `key` field for each document
5457

5558
**Idempotent Behavior:**
5659
The upsert operation is idempotent - upserting with an existing key updates the existing vector rather than creating a duplicate. The same internal vector ID is reused, ensuring your vector storage remains clean and efficient.
@@ -62,10 +65,12 @@ The upsert operation is idempotent - upserting with an existing key updates the
6265
const ids = await context.vector.upsert(
6366
'knowledge-base',
6467
{
68+
key: 'doc-1',
6569
document: 'Agentuity is an agent-native cloud platform',
6670
metadata: { category: 'platform', source: 'docs' }
6771
},
6872
{
73+
key: 'doc-2',
6974
document: 'Vector storage enables semantic search capabilities',
7075
metadata: { category: 'features', source: 'docs' }
7176
}
@@ -75,6 +80,7 @@ const ids = await context.vector.upsert(
7580
const embeddingIds = await context.vector.upsert(
7681
'custom-embeddings',
7782
{
83+
key: 'embedding-1',
7884
embeddings: [0.1, 0.2, 0.3, 0.4],
7985
metadata: { id: 'doc-1', type: 'custom' }
8086
}
@@ -130,7 +136,7 @@ const results = await context.vector.search('knowledge-base', {
130136
// Process results
131137
results.forEach(result => {
132138
console.log(`Found: ${result.metadata.source}`);
133-
console.log(`Similarity: ${1 - result.distance}`);
139+
console.log(`Similarity: ${result.similarity}`);
134140
});
135141
```
136142

@@ -158,20 +164,25 @@ for result in results:
158164
- `similarity` (optional): Minimum similarity threshold (0.0-1.0)
159165
- `metadata` (optional): Filter results by metadata key-value pairs
160166

167+
**Search Results:**
168+
- **Both SDKs**: Return results with `similarity` field (1.0 = perfect match, 0.0 = no match)
169+
- **Note**: The JavaScript SDK also returns a `distance` field for backward compatibility; prefer `similarity`
170+
161171
### Deleting Vectors
162172

163-
Remove specific vectors from storage using their IDs (JavaScript) or keys (Python).
173+
Remove specific vectors from storage using their keys.
164174

165175
<CodeExample>
166176
```javascript
167177
// JavaScript/TypeScript
168178
// Delete single vector
169-
const deletedCount = await context.vector.delete('knowledge-base', 'vector-id-1');
179+
const deletedCount = await context.vector.delete('knowledge-base', 'doc-1');
180+
170181

171182
// Delete multiple vectors
172183
const bulkDeleteCount = await context.vector.delete(
173184
'knowledge-base',
174-
'id-1', 'id-2', 'id-3'
185+
'doc-1', 'doc-2', 'doc-3'
175186
);
176187
```
177188

@@ -185,6 +196,304 @@ count = await context.vector.delete("knowledge-base", "doc_1")
185196
```
186197
</CodeExample>
187198

199+
## Practical Examples
200+
201+
For more code examples, see:
202+
- [JavaScript SDK Examples](/SDKs/javascript/examples#vector-storage-usage)
203+
- [Python SDK Examples](/SDKs/python/examples#vector-storage)
204+
205+
### Building a Simple RAG System
206+
207+
This example demonstrates a complete Retrieval-Augmented Generation (RAG) pattern - searching for relevant context and using it to generate informed responses.
208+
209+
<CodeExample js={`// JavaScript/TypeScript - Simple RAG implementation
210+
import { AgentHandler } from '@agentuity/sdk';
211+
212+
const handler: AgentHandler = async (request, response, context) => {
213+
const { question } = await request.data.json();
214+
215+
try {
216+
// 1. Search for relevant context (top 5 results)
217+
const searchResults = await context.vector.search('knowledge-base', {
218+
query: question,
219+
limit: 5,
220+
similarity: 0.7
221+
});
222+
223+
// 2. Handle no results gracefully
224+
if (searchResults.length === 0) {
225+
return response.json({
226+
answer: "I couldn't find relevant information to answer your question.",
227+
sources: []
228+
});
229+
}
230+
231+
// 3. Assemble context from search results (defensive handling)
232+
const contextTexts = searchResults.map(result =>
233+
result.metadata?.content ?? result.metadata?.text ?? ''
234+
);
235+
const assembledContext = contextTexts.join('\\n\\n');
236+
237+
// 4. Generate response using context (example with AI Gateway)
238+
const prompt = \`Answer the question based on the following context:
239+
240+
Context: \${assembledContext}
241+
242+
Question: \${question}
243+
244+
Answer:\`;
245+
246+
// Use your preferred LLM here (OpenAI, Anthropic, etc.)
247+
const llmResponse = await generateAnswer(prompt);
248+
249+
// 5. Return answer with sources
250+
return response.json({
251+
answer: llmResponse,
252+
sources: searchResults.map(r => ({
253+
id: r.id,
254+
key: r.key,
255+
title: r.metadata?.title,
256+
similarity: r.similarity
257+
}))
258+
});
259+
260+
} catch (error) {
261+
context.logger.error('RAG query failed:', error);
262+
return response.json({
263+
error: 'Failed to process your question',
264+
details: error.message
265+
});
266+
}
267+
};
268+
269+
export default handler;`} py={`# Python - Simple RAG implementation
270+
from agentuity import AgentRequest, AgentResponse, AgentContext
271+
272+
async def run(request: AgentRequest, response: AgentResponse, context: AgentContext):
273+
data = await request.data.json()
274+
question = data.get("question")
275+
276+
try:
277+
# 1. Search for relevant context (top 5 results)
278+
search_results = await context.vector.search(
279+
"knowledge-base",
280+
query=question,
281+
limit=5,
282+
similarity=0.7
283+
)
284+
285+
# 2. Handle no results gracefully
286+
if not search_results:
287+
return response.json({
288+
"answer": "I couldn't find relevant information to answer your question.",
289+
"sources": []
290+
})
291+
292+
# 3. Assemble context from search results
293+
context_texts = [
294+
result.metadata.get("content", result.metadata.get("text", ""))
295+
for result in search_results
296+
]
297+
assembled_context = "\\n\\n".join(context_texts)
298+
299+
# 4. Generate response using context (example with AI Gateway)
300+
prompt = f"""Answer the question based on the following context:
301+
302+
Context: {assembled_context}
303+
304+
Question: {question}
305+
306+
Answer:"""
307+
308+
# Use your preferred LLM here (OpenAI, Anthropic, etc.)
309+
llm_response = await generate_answer(prompt)
310+
311+
# 5. Return answer with sources
312+
return response.json({
313+
"answer": llm_response,
314+
"sources": [
315+
{
316+
"id": result.id,
317+
"key": result.key,
318+
"title": result.metadata.get("title"),
319+
"similarity": result.similarity
320+
}
321+
for result in search_results
322+
]
323+
})
324+
325+
except Exception as e:
326+
context.logger.error(f"RAG query failed: {e}")
327+
return response.json({
328+
"error": "Failed to process your question",
329+
"details": str(e)
330+
})`} />
331+
332+
**Key Points:**
333+
- **Semantic search** finds relevant documents based on meaning, not keywords
334+
- **Similarity threshold** of 0.7 balances relevance with recall
335+
- **Context assembly** combines multiple sources for comprehensive answers
336+
- **Error handling** ensures graceful failures with helpful messages
337+
- **Source attribution** provides transparency about where information came from
338+
339+
### Semantic Search with Metadata Filtering
340+
341+
This example shows how to combine semantic similarity with metadata filters for precise results - like finding products that match both meaning and business criteria.
342+
343+
<CodeExample js={`// JavaScript/TypeScript - Product search with filters
344+
import { AgentHandler } from '@agentuity/sdk';
345+
346+
const handler: AgentHandler = async (request, response, context) => {
347+
const { query, maxPrice, category, inStock } = await request.data.json();
348+
349+
try {
350+
// Build metadata filters based on criteria
351+
const metadataFilters = {};
352+
if (category) metadataFilters.category = category;
353+
if (inStock !== undefined) metadataFilters.inStock = inStock;
354+
355+
// Search with semantic similarity + metadata filters
356+
const searchResults = await context.vector.search('products', {
357+
query,
358+
limit: 10,
359+
similarity: 0.65, // Lower threshold for broader results
360+
metadata: metadataFilters
361+
});
362+
363+
// Post-process: Apply price filter and sort by relevance
364+
const filteredResults = searchResults
365+
.filter(result => !maxPrice || result.metadata.price <= maxPrice)
366+
.map(result => ({
367+
...result.metadata,
368+
similarity: result.similarity
369+
}))
370+
.sort((a, b) => b.similarity - a.similarity);
371+
372+
return response.json({
373+
query,
374+
filters: { maxPrice, category, inStock },
375+
resultCount: filteredResults.length,
376+
products: filteredResults.slice(0, 5) // Top 5 results
377+
});
378+
379+
} catch (error) {
380+
context.logger.error('Product search failed:', error);
381+
return response.json({
382+
error: 'Search failed',
383+
products: []
384+
});
385+
}
386+
};
387+
388+
export default handler;`} py={`# Python - Product search with filters
389+
from agentuity import AgentRequest, AgentResponse, AgentContext
390+
391+
async def run(request: AgentRequest, response: AgentResponse, context: AgentContext):
392+
data = await request.data.json()
393+
query = data.get("query")
394+
max_price = data.get("maxPrice")
395+
category = data.get("category")
396+
in_stock = data.get("inStock")
397+
398+
try:
399+
# Build metadata filters based on criteria
400+
metadata_filters = {}
401+
if category:
402+
metadata_filters["category"] = category
403+
if in_stock is not None:
404+
metadata_filters["inStock"] = in_stock
405+
406+
# Search with semantic similarity + metadata filters
407+
search_results = await context.vector.search(
408+
"products",
409+
query=query,
410+
limit=10,
411+
similarity=0.65, # Lower threshold for broader results
412+
metadata=metadata_filters
413+
)
414+
415+
# Post-process: Apply price filter and sort by relevance
416+
filtered_results = []
417+
for result in search_results:
418+
# Apply price filter
419+
if max_price and result.metadata.get("price", 0) > max_price:
420+
continue
421+
422+
product = dict(result.metadata)
423+
product["similarity"] = result.similarity
424+
filtered_results.append(product)
425+
426+
# Sort by similarity score
427+
filtered_results.sort(key=lambda x: x["similarity"], reverse=True)
428+
429+
return response.json({
430+
"query": query,
431+
"filters": {"maxPrice": max_price, "category": category, "inStock": in_stock},
432+
"resultCount": len(filtered_results),
433+
"products": filtered_results[:5] # Top 5 results
434+
})
435+
436+
except Exception as e:
437+
context.logger.error(f"Product search failed: {e}")
438+
return response.json({
439+
"error": "Search failed",
440+
"products": []
441+
})`} />
442+
443+
**Key Techniques:**
444+
- **Metadata filters** are applied at the vector search level for efficiency
445+
- **Post-processing** handles filters that can't be done at search time (like price ranges)
446+
- **Lower similarity threshold** (0.65) catches more potential matches when using strict filters
447+
448+
## Common Pitfalls & Solutions
449+
450+
### Empty Search Results
451+
**Problem**: Your search returns empty results even though relevant data exists.
452+
453+
**Solutions**:
454+
- **Lower the similarity threshold**: Start at 0.5 and increase gradually
455+
- **Check your metadata filters**: They use exact matching, not fuzzy matching
456+
- **Verify document format**: Ensure documents were upserted with text content
457+
458+
```javascript
459+
// Adaptive threshold example
460+
let results = await context.vector.search('kb', {
461+
query,
462+
similarity: 0.8
463+
});
464+
465+
if (results.length === 0) {
466+
// Try again with lower threshold
467+
results = await context.vector.search('kb', {
468+
query,
469+
similarity: 0.5
470+
});
471+
}
472+
```
473+
474+
### Duplicate Documents
475+
**Problem**: Same content appears multiple times in search results.
476+
477+
**Solution**: Vector upsert is idempotent when using the same key:
478+
- Always use consistent `key` values for your documents
479+
- Upserting with an existing key updates the vector rather than creating a duplicate
480+
- The same internal vector ID is reused, keeping your storage clean
481+
482+
### Performance Issues
483+
**Problem**: Vector operations take too long.
484+
485+
**Solutions**:
486+
- **Batch operations**: Upsert 100-500 documents at once, not one by one
487+
- **Limit search results**: Use `limit: 10` instead of retrieving all matches
488+
- **Optimize metadata**: Keep metadata objects small and focused
489+
490+
### Irrelevant Search Results
491+
**Problem**: Search returns irrelevant or unexpected documents.
492+
493+
**Solutions**:
494+
- **Check similarity scores**: Results with similarity < 0.7 may be poor matches
495+
- **Review metadata filters**: Remember they're AND conditions, not OR
496+
- **Verify embeddings**: Ensure consistent text preprocessing before upserting
188497

189498
## Best Practices
190499

0 commit comments

Comments
 (0)