A Discord companion for the PiEEG / BCI community. It answers questions about PiEEG hardware, software and setup using a lightweight RAG (retrieval-augmented generation) pipeline over the existing PiEEG documentation and website markdown — and always links back to the source pages.
PiEEG-docs + PiEEG-com ──git clone──▶ chunk by heading ──embed──▶ data/index.json
│
Discord / REST ──ask──▶ cosine top-k ──▶ one LLM call (grounded) ─────┘
No vector database. The corpus is a few dozen markdown files, so the whole index is a single JSON file loaded into memory and searched with cosine similarity in NumPy. Embeddings are computed locally with fastembed (ONNX on CPU — free, no API key), and answers are generated by Claude Haiku from the retrieved excerpts only. If the docs don't cover a question, the bot says so and points to the community Discord instead of guessing.
Sources indexed (see pieeg_bot/config.py):
- PiEEG Docs —
PiEEG-docs/content→https://pieeg.com/docs/... - PiEEG News —
PiEEG-com/content/news→https://pieeg.com/news/...
Adding another repo is a one-line entry in SOURCES.
Requires Python 3.10+ and git on the PATH.
pip install .
cp .env.example .env # fill in ANTHROPIC_API_KEY and DISCORD_TOKEN
# 1. Build the index (clones the docs repos, embeds locally, writes data/index.json)
# No API key needed — embeddings run on-device via fastembed.
pieeg-bot ingest
# 2. Try it locally — no Discord required (reads ANTHROPIC_API_KEY from .env)
pieeg-bot ask "How do I attach the electrodes?" # one-shot
pieeg-bot ask "..." --scores # also print retrieval scores
pieeg-bot chat # interactive REPL
# 3. Run the bot on Discord
export ANTHROPIC_API_KEY=sk-ant-...
export DISCORD_TOKEN=...
pieeg-bot runask and chat exercise the exact same retrieval + prompt pipeline the Discord
bot uses (pieeg_bot/engine.py), so they're the fastest way to sanity-check the
index, tune PIEEG_TOP_K / PIEEG_MIN_SCORE, or demo answers without a token.
All commands auto-load a local .env, so exporting the variables by hand is
optional.
In Discord:
/ask <question>— slash command, works anywhere the bot is installed.@PiEEG-bot <question>— mention it in any channel it can read.
The Fly deployment exposes the same answer engine through POST /ask. The
FastAPI server and Discord client run in one process and share one in-memory
index, embedding model, and Anthropic client; requests from either transport use
the same retrieval and grounding pipeline.
Set a separate API key for applications that call the REST endpoint:
export ANTHROPIC_API_KEY=sk-ant-...
export DISCORD_TOKEN=...
export PIEEG_API_KEY=replace-with-a-long-random-secret
uvicorn pieeg_bot.api:app --host 0.0.0.0 --port 8080This command starts both the HTTP server and the existing Discord bot. The
pieeg-bot run command is still available when only Discord is needed.
Ask a question from another server-side application:
curl -X POST http://localhost:8080/ask \
-H "Content-Type: application/json" \
-H "X-API-Key: replace-with-a-long-random-secret" \
-d '{"question":"How do I attach the electrodes?"}'The response is structured JSON so clients do not need to parse Discord Markdown:
{
"answer": "...",
"grounded": true,
"sources": [
{"title": "...", "url": "https://docs.pieeg.com/..."}
]
}GET /health is public for Fly health checks. POST /ask requires the API key
in the X-API-Key header. Keep this key on the calling application's server;
the endpoint does not enable browser CORS and is not intended to expose a secret
from frontend JavaScript.
All settings have sensible defaults; override via environment variables
(see .env.example):
| Variable | Default | Purpose |
|---|---|---|
ANTHROPIC_API_KEY |
— (required) | Chat answers (Claude) |
DISCORD_TOKEN |
— (required for Discord) | Discord bot token |
PIEEG_API_KEY |
— (required for REST) | Secret accepted by POST /ask |
PIEEG_CHAT_MODEL |
claude-haiku-4-5 |
Answer model |
PIEEG_MAX_TOKENS |
1024 |
Max answer length |
PIEEG_EMBED_MODEL |
BAAI/bge-small-en-v1.5 |
Local embedding model (fastembed) |
PIEEG_TOP_K |
5 |
Chunks retrieved per question |
PIEEG_MIN_SCORE |
0.30 |
Minimum cosine score to keep a chunk |
pieeg-bot ingestneeds no API key — embeddings are computed locally.
A one-time setup to create the bot account and invite it to your server.
- Go to the Discord Developer Portal and click New Application. Name it
PiEEG-bot. - Open the Bot tab → Add Bot.
- Under Privileged Gateway Intents, enable Message Content Intent. This is required for the
@PiEEG-botmention trigger; without it only/askworks. - Click Reset Token, copy the token, and put it in your
.envasDISCORD_TOKEN. Treat it like a password — never commit it.
- Open OAuth2 → URL Generator.
- Under Scopes, tick
botandapplications.commands. - Under Bot Permissions, tick Send Messages, Read Message History, and Use Slash Commands.
- Copy the generated URL, open it in a browser, pick your server, and Authorize. You need Manage Server permission on that server.
With data/index.json built (pieeg-bot ingest) and .env filled in:
pieeg-bot runOn startup the bot registers its slash commands and connects to the Discord
gateway. Give slash commands a minute to propagate the first time, then try
/ask what sample rate does the PiEEG board use? or @PiEEG-bot in any channel
it can see. To keep it online continuously, deploy to Fly.io (below).
The nightly GitHub Action rebuilds
data/index.json, commits it when it changes, and (optionally) redeploys to
Fly.io. Ingestion embeds locally, so no API key secret is required — only
FLY_API_TOKEN if you want auto-deploy. You can also trigger it manually via
Run workflow.
One always-on 512mb Fly machine runs both the HTTP service and Discord gateway
client. The extra headroom is for the fastembed ONNX runtime (see
fly.toml). The committed index is baked into the image at build
time, and Fly checks the public /health endpoint.
fly launch --no-deploy # first time only
fly secrets set ANTHROPIC_API_KEY=sk-ant-... DISCORD_TOKEN=... PIEEG_API_KEY=...
fly deployThe production endpoint is https://pieeg-bot.fly.dev/ask unless the Fly app
name or custom domain is changed. Generate PIEEG_API_KEY with a password
manager or python -c "import secrets; print(secrets.token_urlsafe(32))" and
give it only to applications allowed to consume the API.
pip install -e ".[dev]" # editable install with pytest + pytest-asyncio
python -m pytest -vThe retrieval tests run offline (local embeddings, no key). The live grounded-
answer test in tests/test_pipeline.py calls Claude
and runs only when ANTHROPIC_API_KEY is set (loaded from .env automatically);
it is skipped otherwise, so CI stays green without secrets.
CC BY-NC 4.0 — see LICENSE.