Skip to content

Instantly share code, notes, and snippets.

@samkeen
Created December 1, 2025 17:35
Show Gist options
  • Select an option

  • Save samkeen/3d51fd05cdb510173bcdc775be765c0a to your computer and use it in GitHub Desktop.

Select an option

Save samkeen/3d51fd05cdb510173bcdc775be765c0a to your computer and use it in GitHub Desktop.
╭─── Claude Code v2.0.55 ──────────────────────────────────────────────────────╮
│ │ Tips for getting started │
│ Welcome back! │ Run /init to create a CLAUDE.md file with inst… │
│ │ ─────────────────────────────────────────────── │
│ ▐▛███▜▌ │ Recent activity │
│ ▝▜█████▛▘ │ No recent activity │
│ ▘▘ ▝▝ │ │
│ │ │
│ Opus 4.5 · Claude API │ │
│ ~/Projects/chroma-test │ │
╰──────────────────────────────────────────────────────────────────────────────╯
> I would like to implement a RAG using Chromadb\
We will be injesting a local directory of markdown files. These are blog
posts I have written\
I would like to have a simple abstraction so we can switch between using a
local persistent chromadb client and the cloud client.\
\
Please reseach the chromadb docs to build a plan for the code in injest the
markdown files. https://docs.trychroma.com/llms.txt
⏺ I'll research the ChromaDB documentation and explore your current codebase
to understand what's already in place. Let me do these in parallel.
⏺ Fetch(https://docs.trychroma.com/llms.txt)
⎿  Received 22.1KB (200 OK)
⏺ Search(pattern: "**/*.py")
⎿  Found 100 files (ctrl+o to expand)
⏺ Search(pattern: "**/*.md")
⎿  Found 17 files (ctrl+o to expand)
⏺ Read(pyproject.toml)
⎿  Read 10 lines
⏺ Let me get more specific documentation from ChromaDB and check the
existing project files (excluding the virtual environment).
⏺ Search(pattern: "*.py", path: "~/Projects/chroma-test")
⎿  Found 100 files (ctrl+o to expand)
⏺ context7 - resolve-library-id (MCP)(libraryName: "chromadb")
⎿  Available Libraries:
Each result includes:
… +271 lines (ctrl+o to expand)
⏺ Let me get detailed ChromaDB documentation and read the existing project
files.
⏺ context7 - get-library-docs (MCP)(context7CompatibleLibraryID:
"/chroma-core/chroma", topic: "client
configuration persistent cloud")
⎿ ### Initialize ChromaClient in TypeScript
Source: https://github.com/chroma-core/chroma/blob/main/docs/docs.trych
… +308 lines (ctrl+o to expand)
⏺ context7 - get-library-docs (MCP)(context7CompatibleLibraryID:
"/websites/cookbook_chromadb_dev", topic:
"add documents ingestion")
⎿ ### ChromaDB User-Per-Doc Strategy: Add Documents with User ID
Source: https://cookbook.chromadb.dev/strategies/multi-tenancy/naive-mu
… +268 lines (ctrl+o to expand)
⏺ Read(main.py)
⎿  Read 21 lines
⏺ Read(hello.py)
⎿  Read 7 lines
⏺ I now have a good understanding of the ChromaDB API and your current
project. I can see you already have a basic main.py with a
PersistentClient example. Let me write an initial plan and ask a few
clarifying questions.
⏺ Updated plan
⎿  /plan to preview · ~/.claude/plans/sharded-watching-flurry.md
⏺ I've drafted an initial plan. I have a few questions to finalize the
implementation details:
⏺ User answered Claude's questions:
⎿  · Where is your markdown blog posts directory located? → I don't have
that set up just yet so make the path configurable and then set it to
./injest and add a couple test files
· Which embedding model would you like to use? → ChromaDB default
· How should long blog posts be handled? → Chunk by sections
⏺ Now I have all the details I need. Let me update the plan with the
finalized approach:
⏺ Updated plan
⎿  /plan to preview · ~/.claude/plans/sharded-watching-flurry.md
⏺ I've completed the plan based on my research of the ChromaDB documentation
and your requirements. Here's the summary:
Plan Overview:
1. chroma_client.py - A factory function get_chroma_client() that:
- Returns PersistentClient for local storage (default)
- Returns CloudClient when configured via env vars
- Allows runtime switching without code changes
2. ingest.py - Markdown ingestion with:
- Recursive file discovery
- Frontmatter parsing (title, date, tags)
- Section-based chunking (splits on ##/### headers)
- Smart ID generation ({filename}_{chunk_index})
3. ./ingest/ - Test directory with 2 sample blog posts
4. Dependencies - Add python-frontmatter for parsing markdown frontmatter
Key decisions:
- Uses ChromaDB's default embeddings (all-MiniLM-L6-v2) - no extra setup
needed
- Chunks by markdown sections to preserve semantic coherence
- Environment variables for cloud config (clean separation of concerns)
⏺ User approved Claude's plan
⎿  Plan saved to: ~/.claude/plans/sharded-watching-flurry.md · /plan to
edit
RAG Implementation Plan with ChromaDB
Overview
Implement a RAG system to ingest markdown blog posts into ChromaDB with
an abstraction layer to switch between local persistent and cloud
clients. Uses ChromaDB's default embedding model (all-MiniLM-L6-v2) and
chunks documents by markdown headers.
Files to Create/Modify
1. chroma_client.py - Client Factory
Factory function to switch between persistent and cloud clients:
import os
import chromadb
from chromadb import PersistentClient, CloudClient
def get_chroma_client(
client_type: str | None = None,
persist_path: str = "./.chromadb",
tenant: str | None = None,
database: str | None = None,
api_key: str | None = None,
) -> chromadb.ClientAPI:
"""
Create a ChromaDB client based on configuration.
Args:
client_type: "persistent" or "cloud". Defaults to
CHROMA_CLIENT_TYPE env var or "persistent"
persist_path: Path for local persistent storage
tenant: Chroma Cloud tenant ID
database: Chroma Cloud database name
api_key: Chroma Cloud API key
"""
client_type = client_type or os.getenv("CHROMA_CLIENT_TYPE",
"persistent")
if client_type == "persistent":
return PersistentClient(path=persist_path)
elif client_type == "cloud":
return CloudClient(
tenant=tenant or os.getenv("CHROMA_TENANT"),
database=database or os.getenv("CHROMA_DATABASE"),
api_key=api_key or os.getenv("CHROMA_API_KEY"),
)
else:
raise ValueError(f"Unknown client type: {client_type}")
2. ingest.py - Markdown Ingestion
Main ingestion module with section-based chunking:
Key functions:
- find_markdown_files(directory: str) -> list[Path] - Recursively find
all .md files
- parse_markdown(content: str) -> tuple[dict, str] - Extract
frontmatter and body using python-frontmatter
- chunk_by_sections(content: str, min_chunk_size: int = 100) ->
list[str] - Split on ## or ### headers
- ingest_directory(directory: str, collection_name: str = "blog_posts")
- Main entry point
Chunking strategy:
- Split markdown content on header patterns (^#{2,3}\s)
- Each chunk includes the header it starts with
- Small sections are merged with the previous chunk if below
min_chunk_size
- Metadata preserved per chunk: source_file, title, date, tags,
chunk_index
ID generation:
- Format: {file_stem}_{chunk_index} (e.g., my-blog-post_0,
my-blog-post_1)
- Allows easy identification and updates
3. ./ingest/ - Test Markdown Files
Create sample blog posts for testing:
./ingest/sample-post-1.md:
---
title: Getting Started with Python
date: 2024-01-15
tags: [python, tutorial]
---
# Getting Started with Python
Introduction paragraph about Python...
## Installation
How to install Python...
## Your First Program
Writing hello world...
./ingest/sample-post-2.md:
---
title: Understanding RAG Systems
date: 2024-02-20
tags: [ai, rag, embeddings]
---
# Understanding RAG Systems
What is RAG...
## How Embeddings Work
Explanation of embeddings...
## Vector Databases
Overview of vector DBs...
4. Update pyproject.toml
Add dependency:
dependencies = [
"chromadb>=1.3.5",
"python-frontmatter>=1.1.0",
]
Implementation Steps
1. Create chroma_client.py with factory function
2. Create ingest.py with markdown parsing and chunking logic
3. Create ./ingest/ directory with 2 sample markdown files
4. Update pyproject.toml with python-frontmatter dependency
5. Add a simple CLI to run ingestion: python ingest.py ./ingest
Usage
# Using local persistent storage (default)
python ingest.py ./ingest
# Using cloud storage
export CHROMA_CLIENT_TYPE=cloud
export CHROMA_TENANT=your-tenant
export CHROMA_DATABASE=your-database
export CHROMA_API_KEY=your-api-key
python ingest.py ./ingest
Configuration via Environment Variables
| Variable | Description | Default |
|---------------------|---------------------|-------------|
| CHROMA_CLIENT_TYPE | persistent or cloud | persistent |
| CHROMA_PERSIST_PATH | Local storage path | ./.chromadb |
| CHROMA_TENANT | Cloud tenant ID | - |
| CHROMA_DATABASE | Cloud database name | - |
| CHROMA_API_KEY | Cloud API key | - |
⏺ Let me start implementing the plan.
⏺ Write(chroma_client.py)
⎿  Wrote 45 lines to chroma_client.py
"""ChromaDB client factory with support for persistent and cloud
clients."""
import os
import chromadb
from chromadb import CloudClient, PersistentClient
def get_chroma_client(
client_type: str | None = None,
… +35 lines (ctrl+o to expand)
⏺ Write(ingest.py)
⎿  Wrote 226 lines to ingest.py
"""Markdown ingestion module for ChromaDB RAG system."""
import re
import sys
from pathlib import Path
import frontmatter
from chroma_client import get_chroma_client
… +216 lines (ctrl+o to expand)
⏺ Bash(mkdir -p /Users/sam/Projects/chroma-test/ingest)
⎿  (No content)
⏺ Write(ingest/sample-post-1.md)
⎿  Wrote 53 lines to ingest/sample-post-1.md
---
title: Getting Started with Python
date: 2024-01-15
tags: [python, tutorial, beginner]
---
# Getting Started with Python
Python is one of the most popular programming languages in the world,
known for its simplicity and versatility. Whether you're interested in
web development, data science, automation, or artificial intelligence,
Python is an excellent choice for beginners and professionals alike.
… +43 lines (ctrl+o to expand)
⏺ Write(ingest/sample-post-2.md)
⎿  Wrote 60 lines to ingest/sample-post-2.md
---
title: Understanding RAG Systems
date: 2024-02-20
tags: [ai, rag, embeddings, llm]
---
# Understanding RAG Systems
Retrieval-Augmented Generation (RAG) has emerged as one of the most
practical approaches to building AI applications that can access and
reason over custom knowledge bases. Unlike fine-tuning, which requires
retraining a model, RAG allows you to give language models access to
your data at inference time.
… +50 lines (ctrl+o to expand)
⏺ Read(pyproject.toml)
⎿  Read 10 lines
⏺ Update(pyproject.toml)
⎿  Updated pyproject.toml with 1 addition
6 requires-python = ">=3.13"
7 dependencies = [
8 "chromadb>=1.3.5",
9 + "python-frontmatter>=1.1.0",
10 ]
⏺ Bash(uv sync)
⎿  Resolved 89 packages in 345ms
Prepared 1 package in 154ms
Installed 1 package in 1ms
+ python-frontmatter==1.1.0
⏺ Bash(uv run python ingest.py ./ingest)
⎿  Found 2 markdown file(s) in ingest
Processing: sample-post-1.md
Added 4 chunk(s)
… +8 lines (ctrl+o to expand)
⏺ The ingestion works. Let me also verify the data can be queried:
⏺ Bash(uv run python -c "
from chroma_client import get_chroma_client…)
⎿  Query: How do I install Python?
Top 2 results:
… +20 lines (ctrl+o to expand)
⏺ Implementation complete! Here's what was created:
Files created:
- chroma_client.py - Factory function to switch between persistent/cloud
clients
- ingest.py - Markdown ingestion with section-based chunking
- ingest/sample-post-1.md - Sample blog post about Python
- ingest/sample-post-2.md - Sample blog post about RAG systems
Results of test run:
- 2 markdown files processed
- 9 chunks created (split by ## headers)
- Query test successfully retrieved relevant chunks about "How do I
install Python?" from the correct blog post section
Usage:
# Ingest markdown files (default: persistent local storage)
python ingest.py ./ingest
# Query example
from chroma_client import get_chroma_client
client = get_chroma_client()
collection = client.get_collection('blog_posts')
results = collection.query(query_texts=['your question'], n_results=3)
# To use cloud storage instead:
export CHROMA_CLIENT_TYPE=cloud
export CHROMA_TENANT=your-tenant
export CHROMA_DATABASE=your-database
export CHROMA_API_KEY=your-api-key
python ingest.py ./ingest
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment