The Cratis community's docs assistant — living on Discord, grounded in the docs, allergic to making things up.
In theater, the prompter sits just offstage with the script and quietly feeds the line to anyone who blanks mid-scene. That's the whole idea. The script is the published documentation at cratis.io; the stage is the Cratis Discord; and when someone forgets their line — "wait, how do I append an event again?" — Prompter whispers the answer, with citations, or admits honestly when the docs don't cover it.
It is built to stick to the script: answers come from the retrieved docs, with citations — and when the docs don't cover something, it says so instead of improvising.
Three reasons, and they all line up:
- The theater prompter feeds you the line you forgot from just offstage — never stealing the scene, always working from the script. That's exactly the role: the docs are the script, and the bot only speaks when someone needs the line.
- The LLM double meaning — Prompter prompts a language model for a living. The name wears its machinery on its sleeve.
- The Cratis storytelling family — Cratis names its products after telling a story: Chronicle records what happened, Arc shapes the plot, Narrator, Lens, Studio… Prompter joins the cast.
you · #help
How do I define a command in Arc?
🎭 Prompter · replies to you
In Arc a command is a record marked [Command]; its Handle() method
returns the event(s) that happened — one file, backend to event:
[Command]
public record RegisterAuthor(AuthorId Id, AuthorName Name)
{
public AuthorRegistered Handle() => new(Name);
}
Arc then generates the TypeScript proxy so React calls it type-safe.
📚 Sources
• Cratis — One feature, one slice, typed end to end (cratis.io)
👍 👎 ← tell us if that helped
Ask it about something the docs don't cover and it won't improvise — it tells you it doesn't know and points you at a human. That honesty is the feature.
- @mention
@Prompteranywhere it can see — it replies in-thread, right where you asked. /ask— the slash command, for a clean one-off question; it shows a "thinking…" indicator while it looks things up, then delivers the cited answer.#ask-ai— a dedicated channel where every message is treated as a question, no mention needed.- Help forum — open a new thread in the help forum and Prompter takes the first swing automatically, so you're never waiting on the timezone gods for a first answer.
- 👍 / 👎 buttons — one click under any answer; the verdict is logged so the docs (and the bot) get better.
/issue— turn what you are describing into a GitHub issue on the right Cratis repository: Prompter drafts it, shows it to you privately, and files it only when you say so. Bugs, missing APIs, feature requests, ideas, docs gaps — anything worth tracking.
Prompter never barges into normal conversation — it only speaks when spoken to, and it rate-limits each person to a handful of questions per window so no one can spam it.
A small, honest RAG pipeline — hybrid retrieval (keyword and meaning), then a grounded answer with citations:
flowchart LR
Docs["📖 cratis.io docs<br/>(markdown mirrors)"] -->|"chunk + embed"| Voyage["Voyage AI<br/>embeddings"]
Voyage --> PG[("Postgres<br/>+ pgvector")]
Q["❓ your question"] --> Hybrid{"Hybrid search<br/>BM25 + vector · RRF"}
PG --> Hybrid
Hybrid -->|"top passages"| Claude["🤖 Claude"]
Claude -->|"cited answer<br/>or honest refusal"| You["🎭 you, on Discord"]
- Ingest — walk cratis.io's sitemap, fetch each page's markdown mirror, strip the MDX noise, and split into heading-aware chunks. Only changed chunks are re-embedded, so re-indexing is cheap.
- Retrieve — one SQL query fuses lexical (BM25 via
tsvector) and semantic (cosine over pgvector) hits with Reciprocal Rank Fusion. - Answer — Claude gets the top passages and a system prompt that demands grounding and citations, and refuses when the score says the docs don't have it.
Built with C# / .NET 10, NetCord, Microsoft.Extensions.AI, Claude (Anthropic), Voyage AI embeddings, and Postgres + pgvector.
Bring up Postgres (with pgvector):
docker compose up -dIndex the documentation, then ask a question straight from your terminal:
cd Source
dotnet run -- index # ingest cratis.io into the corpus
dotnet run -- ask "How do I append an event in Chronicle?" # answer from the CLI (add --verbose for the passages)Run it as the Discord bot — bot mode also serves GET /healthz and the shared-secret POST /reindex webhook,
and sweeps expired interactions on a daily retention job:
dotnet runMeasure answer quality against the golden question set (from the repo root; needs the keys plus an indexed corpus):
dotnet run --project Eval # groundedness, citation, and refusal scoresYou'll need a (free) Voyage API key to index and an Anthropic key to answer — see the table below.
Configuration binds to the Cratis:Prompter section (environment variables use __ as the delimiter):
| Setting | Environment variable | Default |
|---|---|---|
| Postgres connection string | Cratis__Prompter__ConnectionString |
localhost, db/user/pass prompter |
| Docs site to ingest | Cratis__Prompter__DocsSiteUrl |
https://cratis.io |
| Embedding batch size | Cratis__Prompter__Voyage__BatchSize |
128 |
| Discord bot token | Cratis__Prompter__Discord__Token |
— |
| Ask channel (mention-free questions) | Cratis__Prompter__Discord__AskChannelId |
— |
| Help forum channel (auto-reply) | Cratis__Prompter__Discord__HelpForumChannelId |
— |
| Rate limit — questions per window | Cratis__Prompter__Discord__RateLimit__MaxQuestions |
5 |
| Rate limit — window length (minutes) | Cratis__Prompter__Discord__RateLimit__WindowMinutes |
10 |
| Answer timeout (seconds) | Cratis__Prompter__Discord__AnswerTimeoutSeconds |
60 |
| Anthropic API key | Cratis__Prompter__Anthropic__ApiKey (or ANTHROPIC_API_KEY) |
— |
| Answer model | Cratis__Prompter__Anthropic__Model |
claude-sonnet-5 |
| Refusal threshold (min top-passage score) | Cratis__Prompter__Answering__MinScore |
0.02 |
| Voyage API key | Cratis__Prompter__Voyage__ApiKey |
— |
| Interaction retention (days) | Cratis__Prompter__RetentionDays |
90 |
| Re-index webhook secret | Cratis__Prompter__ReindexSecret |
— |
| GitHub token (file issues) | Cratis__Prompter__GitHub__Token |
— |
| GitHub webhook secret (answer issues) | Cratis__Prompter__GitHub__WebhookSecret |
— |
| Repositories whose issues may be answered | Cratis__Prompter__GitHub__AnsweringRepositories__0 |
— (none) |
| Channel new issues are announced in | Cratis__Prompter__GitHub__NotifyChannelId |
— |
| Label put on issues filed from Discord | Cratis__Prompter__GitHub__IssueLabel |
from-discord |
| Label that opts an issue out of answers | Cratis__Prompter__GitHub__OptOutLabel |
no-prompter |
API keys are never committed — use environment variables or a git-ignored Source/appsettings.Development.json.
Deployment/README.md— the Pulumi stack that runs Prompter on the Cratis UpCloud cluster.Documentation/concepts/architecture.md— how ingestion, retrieval, and answering fit together.
dotnet build --configuration Release # zero warnings, zero errors (warnings are errors in Release)
dotnet test --configuration Release # all specs greenAnd one more gate that's unusual for a bot: answer quality is measured, not vibed. A golden-question eval harness (milestone M4) scores groundedness, citation accuracy, and refusal behavior — and gates prompt and retrieval changes the same way specs gate code.
This project is part of Cratis — free, MIT-licensed tools for building event-sourced and CQRS applications.
- Chronicle — event-sourcing database and runtime. Orleans-based kernel, pluggable storage (MongoDB default; PostgreSQL, SQL Server, SQLite, in-memory), language-agnostic gRPC contracts. Docs
- Chronicle clients — first-class .NET SDK, plus TypeScript, Kotlin/Java, and Elixir; Python coming soon (pre-alpha). AI agents connect through the Chronicle MCP server.
- Arc — opinionated CQRS framework for ASP.NET Core with commands, queries, validation, authorization, and TypeScript proxy generation. Works without event sourcing. Docs
- Components — React components aligned with Arc patterns. Docs
- CLI + Workbench — inspect and diagnose Chronicle from the terminal or the browser. Docs
- Model-first layer (experimental) — Studio, Screenplay, Stage, Scene, Prologue
- Supporting — Fundamentals, Specifications, Synopsis, Lens, Narrator, and free AI tooling (preview); Ensemble coming soon (pre-release)
- Samples — runnable event sourcing and CQRS samples for the whole stack
Everything Cratis publishes today is MIT licensed and free to use.
Part of the Cratis platform · Licensed under the MIT license