Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ This repo contains a collection of tutorials, demos, and how-to guides on how to
| [Step-back prompting in Langchain RAG](./langchain-qdrant-step-back-prompting) | Step-back prompting for RAG, implemented in Langchain | OpenAI, Qdrant, Cohere, Langchain |
| [Collaborative Filtering and MovieLens](./sparse-vectors-movies-reco) | A notebook demonstrating how to build a collaborative filtering system using Qdrant | Sparse Vectors, Qdrant |
| [Use semantic search to navigate your codebase](./code-search/) | Implement semantic search application for code search task | Qdrant, Python, sentence-transformers, Jina |
| [AgentsKit RAG with Qdrant](./agentskit-rag-typescript) | Build a provider-neutral RAG pipeline in TypeScript with Qdrant vector memory | AgentsKit, TypeScript, Qdrant |

| [Incremental Embedding Updates](./temporal-data-drift) | Sync embeddings with changing raw text data | Qdrant Cloud Inference, Python |
1 change: 1 addition & 0 deletions agentskit-rag-typescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
43 changes: 43 additions & 0 deletions agentskit-rag-typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# AgentsKit RAG with Qdrant

This example builds a small retrieval-augmented generation pipeline in TypeScript with
[`@agentskit/rag`](https://www.npmjs.com/package/@agentskit/rag) and Qdrant-backed vector
memory from [`@agentskit/memory`](https://www.npmjs.com/package/@agentskit/memory).

The included embedder is local, deterministic, and credential-free so the complete
ingest/retrieve flow is easy to reproduce. Replace `src/embed.ts` with any production
embedding provider without changing the RAG or Qdrant integration.

## Run locally

Start Qdrant:

```bash
docker run --rm -p 6333:6333 qdrant/qdrant:v1.15.4
```

In another terminal:

```bash
npm install
npm run demo
```

To use Qdrant Cloud, provide the cluster URL and API key:

```bash
QDRANT_URL="https://your-cluster.cloud.qdrant.io" \
QDRANT_API_KEY="your-api-key" \
npm run demo
```

## What the example demonstrates

- creating a cosine collection through the Qdrant REST API;
- chunking and ingesting documents with AgentsKit;
- preserving source IDs and payload metadata in Qdrant;
- retrieving ranked context through the standard AgentsKit `Retriever` contract;
- swapping the embedder independently of the vector database.

Run `npm test` for the credential-free embedder checks and `npm run check` for strict
TypeScript validation.
20 changes: 20 additions & 0 deletions agentskit-rag-typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "agentskit-rag-qdrant-example",
"private": true,
"type": "module",
"scripts": {
"check": "tsc --noEmit",
"demo": "tsx src/demo.ts",
"test": "vitest run"
},
"dependencies": {
"@agentskit/memory": "0.11.4",
"@agentskit/rag": "^0.5.0"
},
"devDependencies": {
"@types/node": "^25.0.0",
"tsx": "^4.20.0",
"typescript": "^6.0.0",
"vitest": "^4.0.0"
}
}
50 changes: 50 additions & 0 deletions agentskit-rag-typescript/src/demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { qdrant } from '@agentskit/memory'
import { createRAG } from '@agentskit/rag'
import { embed } from './embed.js'

const qdrantUrl = process.env.QDRANT_URL ?? 'http://localhost:6333'
const collection = process.env.QDRANT_COLLECTION ?? 'agentskit_docs'

async function ensureCollection(): Promise<void> {
const response = await fetch(`${qdrantUrl}/collections/${collection}`, {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
vectors: { size: 64, distance: 'Cosine' },
}),
})

if (!response.ok) {
throw new Error(`Could not create Qdrant collection: ${await response.text()}`)
}
}

await ensureCollection()

const rag = createRAG({
embed,
store: qdrant({
url: qdrantUrl,
apiKey: process.env.QDRANT_API_KEY,
collection,
}),
chunkSize: 400,
chunkOverlap: 40,
topK: 3,
})

await rag.ingest([
{
id: 'agentskit-overview',
content: 'AgentsKit is a modular TypeScript toolkit for agents, memory, tools, RAG, evaluation, and observability.',
metadata: { source: 'overview' },
},
{
id: 'qdrant-overview',
content: 'Qdrant stores and searches high-dimensional vectors with payload metadata and cosine similarity.',
metadata: { source: 'qdrant' },
},
])

const results = await rag.search('Which toolkit provides TypeScript RAG and agent memory?')
console.log(results)
18 changes: 18 additions & 0 deletions agentskit-rag-typescript/src/embed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const dimensions = 64

export async function embed(text: string): Promise<number[]> {
const vector = Array.from({ length: dimensions }, () => 0)
const tokens = text.toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? []

for (const token of tokens) {
let hash = 2166136261
for (const character of token) {
hash ^= character.codePointAt(0) ?? 0
hash = Math.imul(hash, 16777619)
}
vector[(hash >>> 0) % dimensions]! += 1
}

const magnitude = Math.hypot(...vector)
return magnitude === 0 ? vector : vector.map(value => value / magnitude)
}
27 changes: 27 additions & 0 deletions agentskit-rag-typescript/tests/embed.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { describe, expect, it } from 'vitest'
import { embed } from '../src/embed.js'

describe('local example embedder', () => {
it('is deterministic and normalized', async () => {
const [first, second] = await Promise.all([
embed('AgentsKit Qdrant RAG'),
embed('AgentsKit Qdrant RAG'),
])

expect(first).toEqual(second)
expect(first).toHaveLength(64)
expect(Math.hypot(...first!)).toBeCloseTo(1)
})

it('puts related text closer than unrelated text', async () => {
const [query, related, unrelated] = await Promise.all([
embed('typescript rag memory'),
embed('agentskit typescript rag memory'),
embed('cooking pasta recipe'),
])
const similarity = (left: number[], right: number[]) =>
left.reduce((sum, value, index) => sum + value * right[index]!, 0)

expect(similarity(query, related)).toBeGreaterThan(similarity(query, unrelated))
})
})
13 changes: 13 additions & 0 deletions agentskit-rag-typescript/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"lib": ["ES2022", "DOM"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noEmit": true,
"skipLibCheck": true,
"strict": true,
"target": "ES2022",
"types": ["node"]
},
"include": ["src", "tests"]
}