Skip to content

feat: add parish-rag demo — RAG-enhanced NPC knowledge#486

Draft
dmooney wants to merge 3 commits into
mainfrom
claude/rag-npc-knowledge-demo-h2fxt
Draft

feat: add parish-rag demo — RAG-enhanced NPC knowledge#486
dmooney wants to merge 3 commits into
mainfrom
claude/rag-npc-knowledge-demo-h2fxt

Conversation

@dmooney
Copy link
Copy Markdown
Owner

@dmooney dmooney commented Apr 20, 2026

Summary

Standalone parish-rag crate demonstrating how retrieval-augmented generation can ground NPC dialogue in the parish's own lore (world locations, NPC bios, festivals) instead of relying on the model's training data or the 4 hand-written knowledge bullets in npcs.json.

The crate is a pattern demo, not a replacement for the existing keyword recall in parish-npc::memory. No existing code was modified.

What's in it

  • LoreIndex — in-memory vector store with cosine top-k search.
  • AnyEmbedder — unified handle over two backends:
    • HashEmbedder — deterministic hashing-trick embedder. Zero network, zero dependencies, reproducible test output.
    • OllamaEmbedder — real /api/embeddings client (e.g. nomic-embed-text) for semantic retrieval.
  • build_rundale_corpus(path) — chunks mods/rundale/{world,npcs,festivals}.json into ~280 single-fact passages.
  • format_recall_block(hits) — formats retrieved hits as a "KNOWLEDGE YOU RECALL" block to append to an NPC's system prompt.
  • npc_knowledge_demo binary — runs a scripted set of questions, printing retrieved passages + baseline vs. RAG system prompts. With --llm, calls an OpenAI-compatible chat endpoint for both and prints the responses side by side.

Try it

# Offline — works anywhere, no Ollama needed
cargo run -p parish-rag --bin npc_knowledge_demo

# Live — semantic embeddings + real LLM responses
cargo run -p parish-rag --bin npc_knowledge_demo -- \
    --embedder ollama --embed-model nomic-embed-text \
    --chat-model qwen2.5:7b --llm

# Another NPC / custom question
cargo run -p parish-rag --bin npc_knowledge_demo -- \
    --npc "Siobhan Murphy" \
    --question "Who should I see about renting farmland?"

Sample offline output (Padraig, top-4):

Q: Tell me about the crossroads — is there anything strange about it?
  [0.250] location:The Crossroads — folklore: Crossroads hold power in Irish folklore — a place between places, where the veil is thin.
  [0.250] npc:Siobhan Murphy — Siobhan Murphy is a Farmer (45 years old).
  ...
Q: What is Lughnasa and when is it celebrated?
  [0.213] festival:Lughnasa — Lughnasa is a festival held on the 1st of August. Start of autumn — the harvest festival
  ...

Test plan

  • cargo test -p parish-rag — 31 tests, all deterministic, offline, pass.
  • cargo clippy -p parish-rag --all-targets -- -D warnings — clean.
  • cargo fmt -p parish-rag — formatted.
  • cargo check --workspace --exclude parish-tauri — clean. (Tauri excluded only because of a pre-existing gdk-pixbuf-sys system-library issue in this sandbox, unrelated to this change.)
  • cargo run -p parish-rag --bin npc_knowledge_demo — demo runs end-to-end against shipped Rundale mod.
  • Live mode with Ollama — needs a machine with Ollama + nomic-embed-text installed.

Files changed

  • Cargo.toml — added crates/parish-rag to workspace members.
  • Cargo.lock — regenerated for the new crate.
  • crates/parish-rag/ — new crate (lib + bin + README + tests).

No changes to parish-core, parish-npc, parish-inference, or any existing code.

https://claude.ai/code/session_01Npr5LoSFHrwKoYsQJVUzd7

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c1c6f591f2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

user: &str,
max_tokens: u32,
) -> Result<String, String> {
let url = format!("{}/v1/chat/completions", base_url.trim_end_matches('/'));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize chat base URL before appending the v1 route

Constructing the chat URL with format!("{}/v1/chat/completions", base_url.trim_end_matches('/')) breaks whenever callers pass a common OpenAI-style base URL that already ends in /v1 (for example http://localhost:11434/v1), producing /v1/v1/chat/completions and consistent 404s. This makes --llm fail for a realistic configuration; stripping a trailing /v1 before concatenation (as done elsewhere in the repo) avoids that regression.

Useful? React with 👍 / 👎.

.map(|d| (cosine_similarity(query, &d.embedding), d))
.collect();
scored.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
scored.truncate(k);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return no hits when retrieval scores have no signal

The search path always truncates to k documents even when every cosine score is 0.0, which happens for empty/stopword-only hash queries; this injects arbitrary lore into the prompt despite no semantic match. In practice, queries like "the and of" return the first indexed docs with zero scores, degrading answer quality and making RAG output misleading. Filtering out non-positive scores (or short-circuiting when query magnitude is zero) would prevent false recalls.

Useful? React with 👍 / 👎.

@dmooney dmooney added the enhancement New feature or request label Apr 25, 2026
Standalone crate showing how retrieval-augmented generation can ground
NPC dialogue in the parish's own lore (world locations, NPC bios,
festivals) instead of relying on hallucinated facts.

- Chunks mods/rundale/{world,npcs,festivals}.json into ~280 passages.
- Ships two embedders: a deterministic hashing-trick embedder for
  offline/test runs, and an Ollama /api/embeddings client for semantic
  retrieval.
- Cosine top-k search with a small LoreIndex.
- npc_knowledge_demo binary prints retrieved passages + baseline vs.
  RAG-augmented system prompts; with --llm, calls an OpenAI-compatible
  chat endpoint for both and prints the responses side by side.
- 31 unit tests; all deterministic and run without a network.

https://claude.ai/code/session_01Npr5LoSFHrwKoYsQJVUzd7
@dmooney dmooney force-pushed the claude/rag-npc-knowledge-demo-h2fxt branch from c1c6f59 to 0da2490 Compare May 2, 2026 01:21
dmooney and others added 2 commits May 2, 2026 22:21
The corpus tests load mods/rundale/world.json using a relative path. The
original path joined only 2 levels up (from parish-rag -> crates), but the
crate is at parish/crates/parish-rag, requiring 3 levels to reach the repo
root. Tests failed with 'No such file or directory' because the resolved
path was incorrect during CI.
The send button was missing its aria-label attribute which provides
contextual information to screen readers: "Waiting for response..." when
streaming is active, "Type a message to send" when empty, or "Send message
(Enter)" otherwise. This fixes the missing attribute from the PR.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@dmooney dmooney marked this pull request as draft May 3, 2026 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants