Retrival Augmented Generation (RAG) enabled anime recommendation system using LangChain, ChromaDB Vectorstore, Model Context Protocol, and OpenAI GPT-5.
The Shoko anime management system by default uses a SQLite database on the backend. Extracting the AniDB information from that database with a simple SQL query allows the user to create an input file for this RAG enabled LLM service. Queries can be executed either as a one-shot or via a REPL/Chat interface, and if the RAG doesn't have enough information to satisfy a question, it will use a custom MCP server to fetch specific show information from AniDB to fill in the gap.
This service was built using Python 3.13, and uses standard tooling like poetry, pytest, ruff, mypy, and bandit as the dev stack, and uses a pre-commit configuration to ensure proper code hygine.
- 🔍 Vector-based semantic search - Find anime using natural language queries
- 🎯 Comprehensive metadata - 21 fields per anime including ratings, episodes, dates, and relationships
- 📊 Efficient ingestion - Batch processing with progress indicators (1,458 anime records)
- 💬 Multiple query modes - Web UI, interactive REPL, single questions, file input, or stdin
- 🎨 Beautiful CLI - Rich formatting with tables, colors, and progress bars
- 📤 JSON output format - Structured output for programmatic usage and API integration
- ⚙️ Flexible configuration - JSON config with environment variable overrides
- 🤖 GPT-5 integration - Responses API with configurable reasoning effort
- 🔌 MCP fallback - Automatic AniDB integration via Model Context Protocol for comprehensive coverage
- 🏗️ Modular architecture - Auto-loading CLI commands with dependency injection
- ✅ Type-safe - Full Pydantic validation and mypy strict mode
- 📝 Well-documented - Comprehensive guides and architecture documentation
- Python 3.12+
- Poetry (for build system) or uv (for dependency management)
- OpenAI API key
- mcp-server-anime (optional, for AniDB fallback)
# 1. Clone and setup
git clone https://github.com/yourusername/shokobot.git
cd shokobot
# 2. Configure environment
cp .env.example .env
# Edit .env and add your OPENAI_API_KEY
# 3. Start with Docker Compose
docker-compose up -d
# 4. Access web UI
open http://localhost:7860See DOCKER.md for detailed Docker deployment guide.
# Run the setup script
./setup.sh
# Edit .env and add your OpenAI API key
export OPENAI_API_KEY='your-key-here'
# Verify configuration
poetry run shokobot info
# Ingest anime data
poetry run shokobot ingest
# Start querying
poetry run shokobot repl# Install poetry if not already installed
curl -sSL https://install.python-poetry.org | python3 -
# Install dependencies
poetry install
# Activate virtual environment (optional)
poetry shell# Install uv if not already installed
curl -LsSf https://astral.sh/uv/install.sh | sh
# Create virtual environment and install dependencies
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv pip install -e ".[dev]"- Copy the example environment file:
cp .env.example .env- Edit
.envand add your OpenAI API key:
OPENAI_API_KEY='your-api-key-here'- (Optional) Override config.json settings via environment variables:
# Pattern: SECTION_KEY (e.g., OPENAI_MODEL overrides openai.model)
OPENAI_MODEL='gpt-5-nano'
OPENAI_REASONING_EFFORT='high'
CHROMA_COLLECTION_NAME='my_anime'Edit resources/config.json to customize:
{
"chroma": {
"persist_directory": "./.chroma",
"collection_name": "tvshows"
},
"data": {
"shows_json": "input/shoko_tvshows.json"
},
"openai": {
"model": "gpt-5-nano",
"embedding_model": "text-embedding-3-small",
"reasoning_effort": "medium",
"output_verbosity": "medium",
"max_output_tokens": 8192
},
"mcp": {
"enabled": true,
"cache_dir": "data/mcp_cache",
"fallback_count_threshold": 3,
"fallback_score_threshold": 0.5,
"timeout": 30,
"servers": {
"anime": {
"command": "/path/to/mcp-server-anime/.venv/bin/python",
"args": ["-m", "mcp_server_anime.server"],
"cwd": "/path/to/mcp-server-anime",
"env": {
"PYTHONPATH": "/path/to/mcp-server-anime/src"
}
}
}
},
"ingest": {
"batch_size": 100
},
"logging": {
"level": "INFO"
}
}MCP Configuration (Optional):
- Set
mcp.enabledtofalseto disable AniDB fallback - Adjust
fallback_score_threshold(0.0-1.0) to control when fallback to MCP triggers - A score of 0.0 is a perfect match, while a score of 1.0 is no match
- Lower thresholds = stricter (more MCP calls), higher = more lenient
- See MCP Integration Guide for detailed setup
poetry run shokobot infoDisplays current configuration, including ChromaDB settings, OpenAI model, and data paths.
# Use default settings (1,458 anime records)
poetry run shokobot ingest
# Dry-run: validate data without ingesting
poetry run shokobot ingest --dry-run
# Custom input file and batch size
poetry run shokobot ingest -i custom.json -b 200
# Use AniDB_AnimeID as primary identifier
poetry run shokobot ingest --id-field AniDB_AnimeIDOptions:
-i, --input PATH- Path to JSON file (overrides config)-b, --batch-size INTEGER- Documents per batch (overrides config)--id-field [AnimeID|AniDB_AnimeID]- Primary ID field--dry-run- Validate mappings and show statistics without ingesting
Dry-Run Mode:
Use --dry-run to validate your data before ingestion. This mode:
- Validates all document mappings
- Shows total document count and batch statistics
- Displays year range and episode statistics
- Lists sample titles (first 10)
- Reports any validation errors
- Does NOT insert data into the vector store
# Single question
poetry run shokobot query -q "What anime are similar to Cowboy Bebop?"
# With context display
poetry run shokobot query -q "Best mecha anime?" -c
# From file (batch processing)
poetry run shokobot query -f questions.txt
# From stdin
echo "What is Steins;Gate about?" | poetry run shokobot query --stdin
# Interactive mode
poetry run shokobot query -iOptions:
-q, --question TEXT- Single question to ask-f, --file PATH- File with questions (one per line)--stdin- Read questions from stdin-i, --interactive- Start interactive REPL mode-c, --show-context- Display retrieved context documents--k INTEGER- Number of documents to retrieve (default: 10)--output-format [text|json]- Output format (default: text)
# Start REPL mode (recommended for multiple queries)
poetry run shokobot repl
# With context display
poetry run shokobot repl -c
# With custom retrieval count
poetry run shokobot repl --k 15Options:
-c, --show-context- Display retrieved context documents--k INTEGER- Number of documents to retrieve--output-format [text|json]- Output format (default: text)
REPL Commands:
- Type your question and press Enter
exit,quit, orqto leave
# Start on default port (7860)
poetry run shokobot web
# Start on custom port
poetry run shokobot web --port 8080
# Create a public shareable link
poetry run shokobot web --share
# Enable debug logging
poetry run shokobot web --debugFeatures:
- 💬 Chat Interface - Natural language conversation with your anime collection
- ⚙️ Customizable Settings - Adjust retrieval count (k) and context display
- 📚 Context Display - See which anime were used to generate recommendations
- 🎯 Example Queries - Click pre-made examples to get started quickly
- 📱 Mobile Responsive - Works on desktop, tablet, and mobile devices
Options:
--port INTEGER- Port to run the server on (default: 7860)--share- Create a public shareable link via Gradio (expires after 72 hours)--debug- Enable debug mode with verbose logging
Access:
Once started, open your browser to http://localhost:7860 (or your custom port).
The interface provides an intuitive chat experience with:
- Real-time responses from the RAG system
- Adjustable number of documents to retrieve (1-20)
- Optional context display showing source anime
- Example queries to help you get started
Sharing:
Use the --share flag to create a temporary public URL that you can share with others.
This is useful for demos or remote access. The link expires after 72 hours.
- Questions are processed with cached RAG chain for efficiency
For programmatic usage and service integration, both query and repl commands support JSON output:
# Single question with JSON output
poetry run shokobot query -q "What is Frieren about?" --output-format json
# With context metadata
poetry run shokobot query -q "Recommend a sci-fi anime" --output-format json --show-context
# Interactive mode with JSON
poetry run shokobot repl --output-format json
# From file (batch processing)
poetry run shokobot query -f questions.txt --output-format json
# From stdin (pipeline integration)
echo "What is Cowboy Bebop about?" | poetry run shokobot query --stdin --output-format jsonJSON Response Structure:
{
"question": "What is Frieren about?",
"answer": "Frieren (Sousou no Frieren) is an action-adventure fantasy...",
"context": [
{
"title": "Sousou no Frieren",
"anime_id": "17617",
"year": 2023,
"episodes": 28
}
]
}Use Cases:
- Building APIs or microservices on top of ShokoBot
- Automating batch processing of queries
- Integrating with other services that expect structured data
- Parsing and storing responses in databases
- Creating custom frontends or chatbots
Programmatic Example:
import subprocess
import json
result = subprocess.run(
["poetry", "run", "shokobot", "query",
"-q", "Recommend a sci-fi anime",
"--output-format", "json"],
capture_output=True,
text=True
)
data = json.loads(result.stdout)
print(f"Answer: {data['answer']}")
if 'context' in data:
print(f"Found {len(data['context'])} relevant anime")ShokoBot includes full Docker support for easy deployment:
# Quick start
make docker-up
# View logs
make docker-logs
# Run commands
docker-compose exec shokobot shokobot query -q "Best mecha anime"
# Ingest data
make docker-ingest FILE=input/shoko_tvshows.json
# Stop services
make docker-downAvailable Docker Make commands:
make docker-build- Build Docker imagemake docker-up- Start servicesmake docker-down- Stop servicesmake docker-logs- View logsmake docker-shell- Open shell in containermake docker-test- Run testsmake docker-backup- Backup vector databasemake docker-clean- Remove containers and volumes
For local development (non-Docker):
make install-dev- Install dependenciesmake test- Run tests locallymake format- Format codemake lint- Lint code
See DOCKER.md for comprehensive Docker deployment guide including:
- Production deployment with Nginx
- SSL/TLS configuration
- Resource limits and scaling
- Backup and restore procedures
- CI/CD integration
Replace poetry run with uv run:
uv run shokobot info
uv run shokobot ingest
uv run shokobot repl
uv run shokobot query -q "..."# Install with dev dependencies
poetry install --with dev
# Install pre-commit hooks
pre-commit install# Format code with ruff
poetry run ruff format .
# Lint code with ruff
poetry run ruff check .
poetry run ruff check . --fix # Auto-fix issues
# Type checking with mypy (strict mode)
poetry run mypy . --strict
# Security scanning with bandit
poetry run bandit -r services models utils cli
# Run all pre-commit hooks
poetry run pre-commit run --all-files# Run all tests
poetry run pytest
# Run with coverage
poetry run pytest --cov
# Run specific test file
poetry run pytest tests/config/test_config_service.py
# Verbose output
poetry run pytest -v
# Generate HTML coverage report
poetry run pytest --cov-report=html
open htmlcov/index.html
# Enforce coverage threshold (90%)
poetry run pytest --cov --cov-fail-under=90Test Coverage: 96.75% (380 tests)
- MCP JSON Parser: 91.14%
- MCP Client Service: 98.36%
- RAG Service: 92.97%
- All other services: 95%+
The project uses GitHub Actions for automated testing and quality checks:
Workflows:
- Tests - Runs on Python 3.12 and 3.13, enforces 90% coverage threshold
- Lint - Checks code formatting and linting with ruff
- Security - Runs bandit security scans weekly
Pull Request Checks: All PRs must pass:
- ✅ All tests (380+ tests)
- ✅ Coverage ≥ 90%
- ✅ Ruff formatting and linting
- ✅ Type checking with mypy
- ✅ Security scan with bandit
Setup for Contributors:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Ensure all checks pass locally
- Submit a PR using the template
The project uses pre-commit hooks for automated quality checks:
- ruff - Code linting with auto-fix
- ruff-format - Code formatting (matches GitHub Actions)
- mypy - Type checking
- bandit - Security scanning
- pytest - Run test suite
- trailing-whitespace - Remove trailing whitespace
- end-of-file-fixer - Ensure files end with newline
- check-yaml - Validate YAML files
- check-json - Validate JSON files
Hooks run automatically on git commit. To run manually:
# Run all hooks
pre-commit run --all-files
# Update hook versions to match GitHub Actions
pre-commit autoupdate
# Run specific hook
pre-commit run ruff-format --all-filesshokobot/
├── cli/ # Modular CLI commands (auto-loaded)
│ ├── __init__.py # Main CLI group with auto-loader
│ ├── info.py # Configuration display
│ ├── ingest.py # Data ingestion
│ ├── query.py # Natural language queries
│ └── repl.py # Interactive REPL mode
├── services/ # Business logic (dependency injection)
│ ├── app_context.py # Application context
│ ├── config_service.py # Configuration management
│ ├── ingest_service.py # Data ingestion logic
│ ├── rag_service.py # RAG chain with GPT-5 and MCP fallback
│ ├── vectorstore_service.py # ChromaDB operations
│ ├── mcp_client_service.py # MCP client for AniDB integration
│ ├── mcp_anime_json_parser.py # MCP response parser
│ └── showdoc_persistence.py # ShowDoc caching
├── models/ # Pydantic data models
│ └── show_doc.py # ShowDoc model (21 fields)
├── prompts/ # LLM prompt templates (versioned)
│ ├── __init__.py # Prompt exports
│ ├── anime_rag.py # Anime RAG prompts
│ ├── title_extraction.py # Title extraction prompt (for MCP)
│ └── README.md # Prompt engineering guide
├── utils/ # Utility functions
│ ├── batch_utils.py # Batch processing helpers
│ └── text_utils.py # Text cleaning utilities
├── docs/ # Documentation
│ ├── README.md # Documentation index
│ ├── USER_GUIDE.md # Complete usage guide
│ ├── MCP_INTEGRATION.md # MCP fallback guide
│ ├── MODULAR_CLI_ARCHITECTURE.md
│ ├── APPCONTEXT_USAGE.md # Dependency injection guide
│ ├── TESTING_STRATEGY.md # Testing approach
│ └── SHOWDOC_JSON_EXAMPLE.md # Data format reference
├── resources/ # Configuration files
│ └── config.json # Main configuration
├── input/ # Data files
│ └── shoko_tvshows.json # Anime data (1,458 records)
├── data/ # Runtime data
│ └── mcp_cache/ # MCP response cache
├── tests/ # Test suite
│ ├── config/ # Config service tests
│ ├── ingest/ # Ingestion tests
│ └── models/ # Model tests
├── .env.example # Environment template
├── pyproject.toml # Poetry configuration
├── setup.sh # Automated setup script
├── README.md # This file
├── SETUP_GUIDE.md # Detailed setup instructions
└── QUICK_REFERENCE.md # Command reference
The ShowDoc Pydantic model includes 21 comprehensive fields:
Identifiers:
anime_id- Unique Shoko anime identifieranidb_anime_id- AniDB anime identifier
Titles:
title_main- Primary anime titletitle_alts- Alternate titles (auto-cleaned)
Content:
description- Anime descriptiontags- Tags and genres (auto-cleaned)
Episodes:
episode_count_normal- Number of regular episodesepisode_count_special- Number of special episodes
Dates:
air_date- Initial air dateend_date- Final air datebegin_year- Year began airingend_year- Year finished airing
Ratings:
rating- AniDB rating score (0-1000)vote_count- Number of votesavg_review_rating- Average review ratingreview_count- Number of reviews
External IDs:
ann_id- Anime News Network IDcrunchyroll_id- Crunchyroll IDwikipedia_id- Wikipedia page identifier
Relationships:
relations- JSON string of related animesimilar- JSON string of similar anime
All config.json settings can be overridden via environment variables using the pattern SECTION_KEY:
# ChromaDB
CHROMA_PERSIST_DIRECTORY='./.chroma'
CHROMA_COLLECTION_NAME='tvshows'
# Data
DATA_SHOWS_JSON='input/shoko_tvshows.json'
# OpenAI
OPENAI_MODEL='gpt-5-nano'
OPENAI_EMBEDDING_MODEL='text-embedding-3-small'
OPENAI_REASONING_EFFORT='medium' # low/medium/high
OPENAI_OUTPUT_VERBOSITY='medium' # low/medium/high
OPENAI_MAX_OUTPUT_TOKENS='8192'
# Ingestion
INGEST_BATCH_SIZE='100'
# Logging
LOGGING_LEVEL='INFO' # DEBUG/INFO/WARNING/ERROR/CRITICALShokoBot uses OpenAI's GPT-5 Responses API with reasoning capabilities:
Supported Models:
gpt-5-nano(default)gpt-5-minigpt-5
Reasoning Effort:
low- Faster responses, less reasoningmedium- Balanced (default)high- More thorough reasoning, slower
Output Verbosity:
low- Concise responsesmedium- Balanced (default)high- Detailed explanations
┌─────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ ┌──────┐ ┌────────┐ ┌───────┐ ┌──────┐ │
│ │ info │ │ ingest │ │ query │ │ repl │ │
│ └──┬───┘ └───┬────┘ └───┬───┘ └───┬──┘ │
└─────┼─────────┼───────────┼──────────┼──────────────────────┘
│ │ │ │
└─────────┴───────────┴──────────┘
│
┌──────────────▼──────────────────────────────────────┐
│ Application Context │
│ ┌────────────────┐ ┌────────────────────┐ │
│ │ ConfigService │ │ VectorStoreService │ │
│ └────────────────┘ └────────────────────┘ │
└─────────────────────────────────────────────────────┘
│
┌──────────────┴──────────────────────────────────────┐
│ Service Layer │
│ ┌────────────────┐ ┌──────────────────┐ │
│ │ IngestService │ │ RAGService │ │
│ │ │ │ (with MCP │ │
│ │ │ │ fallback) │ │
│ └────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────┘
│
┌──────────────┴──────────────────────────────────────┐
│ Data & External │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────┐ │
│ │ ChromaDB │ │ OpenAI │ │ ShowDoc │ │ MCP │ │
│ │ │ │ │ │ │ │ │ │
│ │ (Vector │ │ (GPT-5 + │ │ (Pydantic│ │(Ani-│ │
│ │ Store) │ │Embedding)│ │ Model) │ │ DB) │ │
│ └──────────┘ └──────────┘ └──────────┘ └─────┘ │
└─────────────────────────────────────────────────────┘
MCP Fallback Flow:
- User query → RAG Service
- Vector store search (ChromaDB)
- If results insufficient or poor quality → MCP fallback
- MCP fetches from AniDB → Caches locally
- Combined results returned to user
- Modular CLI - Auto-loading command discovery from
cli/directory - Dependency Injection - Services injected via
AppContext - Rich Formatting - Professional CLI with Rich-Click
- Pydantic Validation - Type-safe data models with automatic validation
- Batch Processing - Efficient chunked operations for large datasets
- CLI Layer - Rich-Click commands with auto-loading
- Application Context - Centralized service management
- Service Layer - Business logic with dependency injection
- Data Layer - ChromaDB vector store and OpenAI embeddings
- Model Layer - Pydantic models with validation
- README.md - This file (project overview)
- SETUP_GUIDE.md - Detailed setup instructions
- QUICK_REFERENCE.md - Command reference and examples
- docs/USER_GUIDE.md - Complete usage guide with examples
- docs/README.md - Documentation index
- docs/MODULAR_CLI_ARCHITECTURE.md - CLI design patterns
- docs/APPCONTEXT_USAGE.md - Dependency injection guide
- docs/MCP_INTEGRATION.md - MCP fallback configuration and usage
- docs/TESTING_STRATEGY.md - Testing approach and best practices
OpenAI API Key not set:
export OPENAI_API_KEY='your-key-here'
# Or add to .env fileChromaDB permission issues:
rm -rf ./.chroma
poetry run shokobot ingestImport errors:
poetry install
poetry run shokobot --helpModule not found:
# Ensure you're using poetry run or uv run
poetry run shokobot info
# Or activate the virtual environment
poetry shell
shokobot info- Ingestion: ~40-50 seconds for 1,458 anime records
- Query Response: ~3-6 seconds (including LLM processing)
- Interactive Mode: Cached RAG chain for efficiency
- Batch Size: 100 documents per batch (configurable)
- Use
replmode for multiple queries (avoids reloading RAG chain) - Adjust
--kparameter to control context size (default: 10) - Increase batch size for faster ingestion on powerful machines
- Use environment variables for quick configuration changes
- Ensure
input/shoko_tvshows.jsonis properly formatted JSON - Check logs for validation errors during ingestion
- Use
--id-fieldto control primary identifier selection
- Be specific in questions for better results
- Use natural language (e.g., "romance anime with strong characters")
- Try different phrasings if results aren't satisfactory
- Use context display (
-c) to understand retrieval quality
- Run
poetry run shokobot infoto verify configuration - Use
--show-contextto debug RAG retrieval - Check
docs/for architecture details before modifying - Follow Python 3.12+ type hints and Pydantic patterns
- Fork the repository
- Create a feature branch
- Make your changes
- Run quality checks:
pre-commit run --all-files - Run tests:
pytest - Submit a pull request
MIT