Crawl4AI RAG in my Electron AI assistant
I added Crawl4AI to my Electron and Vue 3 assistant so it can answer from crawled websites. Playwright failed inside the FastAPI server on Windows, so the crawler moved into Docker.
My "Desktop AI Assistant," an Electron and Vue 3 app, already handled Ollama, Kokoro TTS and Whisper. Its knowledge was limited to the pre-trained models it used. The next step was to let it learn from custom sources, such as websites, sitemaps or local text files, and run Retrieval Augmented Generation (RAG) against that knowledge. This is where the Crawl4AI library came in.
The challenges
The goal was to integrate Crawl4AI's Python crawling and processing into my existing FastAPI backend, which already served Kokoro TTS. Three hurdles stood out.
- Playwright and asyncio on Windows. Crawl4AI uses Playwright for browser automation. Running Playwright inside asyncio, as FastAPI does with Uvicorn, failed on Windows with a persistent NotImplementedError from asyncio's subprocess creation. We tried global event loop policies and separate threads. We also tried separate processes via multiprocessing, but the issue remained in the main FastAPI process tree.
- Architectural complexity. The pipeline runs from the Electron app to the main FastAPI server, to a specialized crawler service, back to the main FastAPI server for chunking, embedding and RAG, and then to the Electron app.
- Backend modularity. The new RAG logic had to stay organized, with clear separation between API routing, orchestration and core services such as ChromaDB, chunking and LLM interaction for RAG.
The outcome
We built the RAG backend and frontend. The fix for the Playwright issue was to Dockerize the crawling component.
-
Dockerized crawler service. A separate, minimal
FastAPI service runs in a Docker container
(
crawl_service_docker). It houses Crawl4AI and its Playwright dependency in a stable Linux environment, which bypasses the Windows asyncio issues. It exposes a simple/do_crawlendpoint. -
Main FastAPI server (
fastapi_server). It is refactored into modular APIRouters for TTS and the new RAG features.-
The RAG ingestion endpoint
(
/rag/ingest) calls the Dockerized crawler over HTTP with httpx to fetch web content. -
The main server's
ingestion_orchestrator.pyhandles the scraped Markdown. It runs chunking (chunker_service.py) and metadata extraction (metadata_service.py), and stores the data in a local ChromaDB instance managed bychroma_service.py. -
A RAG query endpoint (
/rag/query) usesretrieval_service.pyto fetch relevant context from ChromaDB.answer_generation_service.pyformulates the answer with a configured LLM, which currently defaults to local Ollama.
-
The RAG ingestion endpoint
(
- The backend ingests data from URLs and sitemaps, and it answers queries based on that data.
The next phase built the "Crawl & RAG" page in the Electron and Vue 3 app, which gives these backend capabilities a user interface.
Key learnings
- Docker isolates problematic dependencies. When a library has deep environmental conflicts, such as Playwright with asyncio on Windows, Docker runs it in a more compatible environment, such as Linux.
- Treat the crawler as a microservice. Even when it runs locally in Docker, a separate service simplified the main FastAPI app and decoupled the problematic dependency.
- APIRouters are essential for a growing FastAPI app. They split endpoints into logical modules.
- Multiprocessing helps with long tasks. The attempts to use it for Playwright-based ingestion, before Docker, showed its value for offloading work that might block the main FastAPI event loop or conflict with its async requirements. Inside the Docker container, Playwright runs in its own process.
- Clear API contracts matter. Pydantic models for requests and responses between the Electron app, the main FastAPI server and the Dockerized crawler were crucial for smooth integration.
- Asyncio issues can persist. Explicit event loop policies are sometimes not enough. A library's internals or the hosting framework, such as Uvicorn on Windows, can have their own rules for managing loops for subprocesses.
The AI approach
My LLM assistant worked through the integration with me. It helped:
- Outline the initial architecture for integrating Crawl4AI into FastAPI.
-
Refactor the
crawl4AI-agent-v2Python scripts into modular services. - Draft Pydantic models for API request and response bodies.
- Brainstorm solutions for the Playwright and asyncio NotImplementedError, including event loop policies and process isolation.
-
Generate the initial Dockerfile and the minimal
FastAPI app structure for
crawl_service_docker. - Debug import errors, indentation issues and logic flaws across the Python backend.
Getting Playwright to run smoothly was hard. The Dockerized solution works, and the backend now supports RAG for the Electron desktop app. Next I want to use the frontend UI in my daily local LLM workflows.