Skip to main content

What You’re Building

An AI that answers questions using YOUR documents instead of guessing. Before Alchemyst:
const answer = await llm.generate("What's our refund policy?");
// "I don't have information about your specific refund policy..." ❌
After Alchemyst:
const answer = await llm.generate("What's our refund policy?");
// "We offer a 30-day money back guarantee. Contact support@example.com..." ✅

Prerequisites

  • Alchemyst account (sign up)
  • Your ALCHEMYST_AI_API_KEY
  • Node.js 18+ or Python 3.9+
Time to complete: 10 minutes

Step 1: Install

npm install @alchemystai/sdk

Step 2: Initialize

import AlchemystAI from '@alchemystai/sdk';

const client = new AlchemystAI({
  apiKey: process.env.ALCHEMYST_AI_API_KEY,
});

Step 3: Store a Document

const result = await client.v1.context.add({
  documents: [{
    content: "Our refund policy: We offer a 30-day money back guarantee. Contact support@example.com to request a refund."
  }],
  context_type: 'resource',
  source: 'documentation',
  scope: 'internal'
});

console.log(`✅ Stored ${result.documents?.length || 0} documents`);
// Output: ✅ Stored 1 documents
What just happened:
  • Your document was chunked into searchable pieces (~500 word chunks)
  • Embeddings were generated automatically (vector representations)
  • Everything is indexed and ready to search
Verify it worked:
  1. Visit platform.getalchemystai.com/context
  2. You should see your document listed
  3. Click to view chunks and metadata

Step 4: Search for Context

const userQuestion = "What's your refund policy?";

const { contexts } = await client.v1.context.search({
  query: userQuestion,
  similarity_threshold: 0.7,
  scope: 'internal'
});

console.log(`Found ${contexts?.length || 0} relevant chunks`);
// Output: Found 1 relevant chunks

console.log(contexts[0].content);
// Output: "Our refund policy: We offer a 30-day money back guarantee..."

Understanding similarity_threshold

ValueMeaningWhen to Use
0.5Somewhat relevantExploratory searches, broad topics
0.7RelevantStart here - good balance
0.9Very relevantPrecise matches, technical queries
Tip: Start at 0.7, lower to 0.5 if you get no results, raise to 0.9 if results are too broad.

Step 5: Feed Context to Your LLM

import OpenAI from 'openai';

const openai = new OpenAI();

const prompt = contexts?.length 
  ? `Context:\n${contexts.map(c => c.content).join('\n\n')}\n\nQuestion: ${userQuestion}`
  : userQuestion;

const response = await openai.chat.completions.create({
  model: "gpt-4",
  messages: [{ role: "user", content: prompt }]
});

console.log(response.choices[0].message.content);
// Output: "We offer a 30-day money back guarantee. To request a refund, contact support@example.com."
Result: Your AI now answers from YOUR data, not generic training data.

Complete Working Example

import AlchemystAI from '@alchemystai/sdk';
import OpenAI from 'openai';

const alchemyst = new AlchemystAI({
  apiKey: process.env.ALCHEMYST_AI_API_KEY,
});

const openai = new OpenAI();

async function main() {
  // 1. Store document
  await alchemyst.v1.context.add({
    documents: [{
      content: "Our refund policy: We offer a 30-day money back guarantee. Contact support@example.com to request a refund."
    }],
    context_type: 'resource',
    source: 'docs',
    scope: 'internal'
  });
  console.log("✅ Document stored");

  // 2. Search for context
  const userQuestion = "What's your refund policy?";
  const { contexts } = await alchemyst.v1.context.search({
    query: userQuestion,
    similarity_threshold: 0.7,
    scope: 'internal'
  });
  console.log(`Found ${contexts?.length} relevant chunks`);

  // 3. Generate answer with context
  const prompt = contexts?.length
    ? `Context:\n${contexts.map(c => c.content).join('\n\n')}\n\nQuestion: ${userQuestion}`
    : userQuestion;

  const response = await openai.chat.completions.create({
    model: "gpt-4",
    messages: [{ role: "user", content: prompt }]
  });

  console.log("AI Response:", response.choices[0].message.content);
}

main();
Expected Output:
✅ Document stored
Found 1 relevant chunks
AI Response: We offer a 30-day money back guarantee. To request a refund, contact support@example.com.

Next: Add Real Documents

Now that you understand the basics, let’s add real documents:
import fs from 'fs';

const content = fs.readFileSync('./docs/policy.txt', 'utf-8');

await client.v1.context.add({
  documents: [{
    content: content,
    metadata: {
      file_name: "policy.txt",
      file_type: "text"
    }
  }],
  context_type: 'resource',
  source: 'documentation',
  scope: 'internal'
});

console.log("✅ File uploaded and indexed");

Advanced: Organize with Metadata

Use group_name to filter searches by category:
// Store with groups
await client.v1.context.add({
  documents: [{
    content: "Refund policy: We offer a 30-day money back guarantee...",
    metadata: {
      file_name: "refund-policy.md",
      group_name: ["customer-support", "policies"]  // ← For storage
    }
  }],
  context_type: 'resource',
  source: 'documentation',
  scope: 'internal'
});

// Search within specific groups
const { contexts } = await client.v1.context.search({
  query: "refund policy",
  scope: 'internal',
  metadata: {
    groupName: ['customer-support']  // ← For search (note: camelCase)
  }
});

console.log(`Found ${contexts?.length} results in customer-support`);
Naming difference: Storage uses group_name (snake_case in metadata) but TypeScript search uses groupName (camelCase). Python uses group_name consistently. Both refer to the same field - this is due to API design conventions.

Why use groups?

BenefitDescription
Faster searchesSmaller search space = lower latency
More relevantOnly search customer-support docs, not engineering docs
Better organizationHierarchical structure like folders
Access controlFilter by team, project, or user permissions
Example hierarchy:
group_name: ["company", "customer-support", "policies"]
group_name: ["company", "engineering", "api-docs"]
group_name: ["company", "hr", "employee-handbook"]
Learn more: Context Arithmetic

Troubleshooting

Error symptoms:
  • Getting documents that don’t match the query
  • Too many results to process efficiently
Common causes:
  1. similarity_threshold is too low
  2. Documents are too broad or generic
  3. Not using group filtering
Fixes:Step 1: Raise the threshold:
similarity_threshold: 0.8  // More strict (was 0.5)
Step 2: Use group filtering:
metadata: { 
  groupName: ['customer-support']  // Narrow to specific category
}
Step 3: Add more specific metadata:
await client.v1.context.add({
  documents: [{
    content: "...",
    metadata: {
      group_name: ["support", "refunds"],  // More specific
      category: "policies",
      department: "customer-service"
    }
  }]
});
Error message:
{
  "error": "Document with file_name 'policy.md' already exists",
  "code": "CONFLICT",
  "status": 409
}
Cause: Trying to add a document with duplicate file_name in metadata.Fixes:Step 1: Delete old version first:
// Delete by file name
await client.v1.context.delete({ 
  metadata: { fileName: "policy.md" } 
});

// Then add new version
await client.v1.context.add({
  documents: [{
    content: "Updated content...",
    metadata: { file_name: "policy.md" }
  }]
});
Step 2: Use versioned names:
file_name: "policy-v2.md"  // or "policy-2024-02-01.md"
Step 3: Use unique identifiers:
file_name: `policy-${Date.now()}.md`  // Timestamp
// or
file_name: `policy-${uuid()}.md`       // UUID
Error message:
{
  "error": "Invalid API key",
  "code": "UNAUTHORIZED",
  "status": 401
}
Fixes:Step 1: Verify API key is set:
console.log("API Key exists:", !!process.env.ALCHEMYST_AI_API_KEY);
// Should output: API Key exists: true
Step 2: Check key format:
// Should start with "alch_" or similar prefix
console.log("Key prefix:", process.env.ALCHEMYST_AI_API_KEY?.substring(0, 5));
Step 3: Get a new key:
Error message:
{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT",
  "status": 429,
  "retry_after": 60
}
Cause: Too many requests in a short time period.Fixes:Step 1: Add retry logic:
async function searchWithRetry(query: string, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await client.v1.context.search({ query, scope: 'internal' });
    } catch (error) {
      if (error.status === 429 && i < retries - 1) {
        const waitTime = error.retry_after || 60;
        console.log(`Rate limited. Waiting ${waitTime}s...`);
        await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
      } else {
        throw error;
      }
    }
  }
}
Step 2: Check your rate limits:
  • Free tier: 100 operations/day
  • Pro tier: 10,000 operations/day
Step 3: Upgrade your plan: View pricing

Best Practices

Document Organization

// ✅ Good - organized with meaningful metadata
await client.v1.context.add({
  documents: [{
    content: "...",
    metadata: {
      file_name: "refund-policy-2024-02.md",
      group_name: ["customer-support", "policies"],
      department: "support",
      last_updated: "2024-02-01"
    }
  }]
});

// ❌ Bad - minimal metadata, hard to search
await client.v1.context.add({
  documents: [{
    content: "..."
  }]
});

Chunk Size Considerations

  • Small documents (under 1000 words): Add as-is
  • Medium documents (1000-5000 words): Let Alchemyst auto-chunk
  • Large documents (over 5000 words): Consider splitting by section
// For large documents, split manually for better control
const sections = [
  { title: "Introduction", content: "..." },
  { title: "Features", content: "..." },
  { title: "Pricing", content: "..." }
];

const docs = sections.map(section => ({
  content: section.content,
  metadata: {
    file_name: "product-docs.md",
    section: section.title
  }
}));

await client.v1.context.add({ documents: docs });

Search Optimization

// ✅ Good - specific query with filtering
const { contexts } = await client.v1.context.search({
  query: "How to request a refund for damaged items",
  scope: 'internal',
  similarity_threshold: 0.7,
  metadata: {
    groupName: ['customer-support', 'refunds']
  }
});

// ❌ Bad - too broad, no filtering
const { contexts } = await client.v1.context.search({
  query: "help",
  scope: 'internal'
});

Error Handling

async function safeSearch(query: string) {
  try {
    const { contexts } = await client.v1.context.search({
      query,
      scope: 'internal',
      similarity_threshold: 0.7
    });
    
    if (!contexts || contexts.length === 0) {
      console.log("No results found. Try a broader query.");
      return null;
    }
    
    return contexts;
  } catch (error) {
    console.error("Search failed:", error.message);
    // Fallback or retry logic here
    return null;
  }
}

Verify Your Setup

After implementing context search, verify everything is working:

Check Platform UI

  1. Visit platform.getalchemystai.com/context
  2. You should see your stored documents
  3. Click to view chunks and embeddings
  4. Check document count matches what you uploaded

Test with Code

// Test the complete flow
async function testSetup() {
  // 1. Store a test document
  console.log("Step 1: Storing test document...");
  await client.v1.context.add({
    documents: [{
      content: "Test document: The answer is 42",
      metadata: { file_name: "test.txt" }
    }],
    scope: 'internal'
  });
  
  // 2. Search for it
  console.log("Step 2: Searching for test document...");
  const { contexts } = await client.v1.context.search({
    query: "What is the answer?",
    scope: 'internal',
    similarity_threshold: 0.5
  });
  
  // 3. Verify results
  const found = contexts?.some(c => c.content.includes("42"));
  console.log("✅ Test passed:", found);
  
  // 4. Cleanup
  console.log("Step 3: Cleaning up...");
  await client.v1.context.delete({
    metadata: { fileName: "test.txt" }
  });
}

testSetup();
Expected Output:
Step 1: Storing test document...
Step 2: Searching for test document...
✅ Test passed: true
Step 3: Cleaning up...

What’s Next?

Add Memory

Make your AI remember users across sessions

TypeScript SDK

Complete TypeScript SDK reference

Python SDK

Complete Python SDK reference

API Reference

REST API documentation

Learn Advanced Patterns

Context Arithmetic

Master advanced filtering and organization

Usage Patterns

Best practices and anti-patterns

Real-World Use Cases

Customer support, code assistants, and more

Sample Projects

Community-built projects

Need Help?

Discord Community

Get real-time help from our community

Documentation

Browse guides and tutorials

Email Support

Contact our support team

GitHub Issues

Report bugs or request features