This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { OllamaEmbeddings } from 'ollama-embeddings';
|
||||
|
||||
/**
|
||||
* Singleton instance of OllamaEmbeddings.
|
||||
* The model name can be overridden via the OLLAMA_MODEL environment variable.
|
||||
*/
|
||||
const modelName = process.env.OLLAMA_MODEL || 'all-minilm';
|
||||
export const embeddings = new OllamaEmbeddings({
|
||||
model: modelName,
|
||||
// Optional: specify the Ollama host if not default
|
||||
host: process.env.OLLAMA_HOST || 'http://localhost:11434'
|
||||
});
|
||||
|
||||
/**
|
||||
* Utility to embed a single string.
|
||||
* @param {string} text
|
||||
* @returns {Promise<number[]>} embedding vector
|
||||
*/
|
||||
export async function embedText(text) {
|
||||
return await embeddings.embedQuery(text);
|
||||
}
|
||||
+66
-2
@@ -1,3 +1,67 @@
|
||||
const Agent = require('./agent');
|
||||
import dotenv from 'dotenv';
|
||||
import readline from 'readline';
|
||||
import { search_knowledge_base } from './tools/searchKnowledgeBase.js';
|
||||
import { add_to_knowledge_base } from './tools/addToKnowledgeBase.js';
|
||||
|
||||
module.exports = { Agent };
|
||||
dotenv.config();
|
||||
|
||||
/**
|
||||
* Simple command-line agent that supports two commands:
|
||||
* 1. /search <query> - searches the knowledge base
|
||||
* 2. /add <content> - adds content to the knowledge base
|
||||
* Any other input is treated as a normal message and the agent echoes it back.
|
||||
*/
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
prompt: 'Agent> '
|
||||
});
|
||||
|
||||
console.log('Agent with RAG memory using Ollama embeddings.');
|
||||
console.log('Commands:');
|
||||
console.log(' /search <query> - Search knowledge base');
|
||||
console.log(' /add <content> - Add content to knowledge base');
|
||||
console.log(' /exit - Exit');
|
||||
rl.prompt();
|
||||
|
||||
rl.on('line', async (line) => {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed === '/exit') {
|
||||
rl.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (trimmed.startsWith('/search ')) {
|
||||
const query = trimmed.slice(8).trim();
|
||||
if (!query) {
|
||||
console.log('Please provide a query.');
|
||||
} else {
|
||||
console.log(`Searching for "${query}"...`);
|
||||
const results = await search_knowledge_base(query);
|
||||
if (results.length === 0) {
|
||||
console.log('No relevant documents found.');
|
||||
} else {
|
||||
console.log('Top results:');
|
||||
results.forEach((res, idx) => {
|
||||
console.log(`${idx + 1}. [${res.id}] (${res.score.toFixed(4)})`);
|
||||
console.log(` ${res.content}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (trimmed.startsWith('/add ')) {
|
||||
const content = trimmed.slice(5).trim();
|
||||
if (!content) {
|
||||
console.log('Please provide content to add.');
|
||||
} else {
|
||||
const { id } = await add_to_knowledge_base(content);
|
||||
console.log(`Content added with id ${id}.`);
|
||||
}
|
||||
} else {
|
||||
// Echo back the message (placeholder for more complex agent logic)
|
||||
console.log(`You said: ${trimmed}`);
|
||||
}
|
||||
rl.prompt();
|
||||
}).on('close', () => {
|
||||
console.log('Goodbye!');
|
||||
process.exit(0);
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { embeddings } from '../embeddings.js';
|
||||
import { knowledgeBase } from './searchKnowledgeBase.js';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
/**
|
||||
* Add new content to the knowledge base.
|
||||
* @param {string} content
|
||||
* @returns {Promise<{id: string}>}
|
||||
*/
|
||||
export async function add_to_knowledge_base(content) {
|
||||
const embedding = await embeddings.embedQuery(content);
|
||||
const id = uuidv4();
|
||||
knowledgeBase.push({ id, content, embedding });
|
||||
return { id };
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { embeddings } from '../embeddings.js';
|
||||
|
||||
/**
|
||||
* In-memory knowledge base.
|
||||
* Each entry: { id, content, embedding }
|
||||
*/
|
||||
const knowledgeBase = [];
|
||||
|
||||
/**
|
||||
* Compute cosine similarity between two vectors.
|
||||
* @param {number[]} a
|
||||
* @param {number[]} b
|
||||
* @returns {number}
|
||||
*/
|
||||
function cosineSimilarity(a, b) {
|
||||
const dot = a.reduce((sum, ai, i) => sum + ai * b[i], 0);
|
||||
const normA = Math.sqrt(a.reduce((sum, ai) => sum + ai * ai, 0));
|
||||
const normB = Math.sqrt(b.reduce((sum, bi) => sum + bi * bi, 0));
|
||||
return dot / (normA * normB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the knowledge base for the most relevant documents.
|
||||
* @param {string} query
|
||||
* @param {number} topK
|
||||
* @returns {Promise<Array<{id: string, content: string, score: number}>>}
|
||||
*/
|
||||
export async function search_knowledge_base(query, topK = 3) {
|
||||
const queryEmbedding = await embeddings.embedQuery(query);
|
||||
const scored = knowledgeBase.map(entry => ({
|
||||
id: entry.id,
|
||||
content: entry.content,
|
||||
score: cosineSimilarity(queryEmbedding, entry.embedding)
|
||||
}));
|
||||
scored.sort((a, b) => b.score - a.score);
|
||||
return scored.slice(0, topK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose the knowledge base for other modules (e.g., add tool).
|
||||
*/
|
||||
export { knowledgeBase };
|
||||
Reference in New Issue
Block a user