
What it is
Source: This site’s own code, for the worked example. Date: Freelance work, 2023 to present. Scope: Appu as deployed for client-facing interactions, and the worked example of its method that runs on this site. Not shown: The deployment and its conversations.
Appu is a conversational chatbot with configurable personality traits, built with LLM APIs and prompt engineering. I developed it in my freelance work, and it was deployed for client-facing interactions. Shipped
What it does
- Conversation. It answers people in conversation, through a large language model called over an API.
- Configurable personality traits. Its personality is a setting, not a fixed part of it.
- A worked example, here. On this site it answers only from the site’s own text, cites the section it read, refuses anything outside that, and shows the trace of each answer. It keeps nothing a visitor types.
How it is built
Appu is built on LLM APIs, with its behaviour shaped by prompt engineering. The worked example on this site is separate: it retrieves passages by keyword scoring (BM25) and sends them, with the question, to a Gemini model on the server.
Stack. LLM APIs, Prompt engineering
Where it stands
Appu was deployed for client-facing interactions. Shipped On this site its evaluation set holds 40 questions, 12 of which it must refuse. It has not been run yet, so the scoreboard shows no score. Method
My part
I developed Appu as a freelance developer.
Appu, observed
| Pass rate | not run | expected behaviour across all scripted questions |
|---|---|---|
| Refusals correct | not run | of 12 questions that must be refused |
| Answers correct | not run | answered and cited an expected section |
| p50 latency | not run | question sent to answer received |
| Cost per answer | not run | estimated from token counts at list price |
Evals not yet run. 40 questions are written, 12 of them must be refused. The harness is evals/appu/run.mjs; until it runs against a deployment, every row above says so.
No question asked yet. The trace of the last answer appears here: model, tokens in and out, latency, the retrieved chunks with their sources, and the estimated cost.
Claim
Source: This page. Date: 2023 to present. Scope: Appu as deployed for client-facing interactions: a conversational chatbot with a configurable personality. What runs here is a worked example of the method on this page’s text. Not shown: The deployed chatbot and its conversations.
Appu is a conversational chatbot with a configurable personality that I shipped for client-facing use, as independent work. Shipped
Here it is a worked example of method, not a feature. Ask it something about this page. It reads only this page’s text, answers from what it finds, cites the section as a plain link, and refuses anything outside that. The scoreboard beside the input reads from the evaluation file. The evals have not yet been run, and the scoreboard says so rather than showing a number.
The scoreboard reads from the evaluation file. Method
Source: evals/appu/run.mjs against /api/ask. Date: not yet run. Scope: 40 scripted questions, 12 of them must be refused; latency and cost from the endpoint's own trace. Not shown: Pass rate, refusals, latency and cost: the harness has not run against a deployment yet.
| Pass rate | not run | expected behaviour across all scripted questions |
|---|---|---|
| Refusals correct | not run | of 12 questions that must be refused |
| Answers correct | not run | answered and cited an expected section |
| p50 latency | not run | question sent to answer received |
| Cost per answer | not run | estimated from token counts at list price |
Evals not yet run. 40 questions are written, 12 of them must be refused. The harness is evals/appu/run.mjs; until it runs against a deployment, every row above says so.
Method
Source: This page’s code. Date: This build. Scope: How the instrument answers, what it refuses, and how it will be scored.
The instrument is small on purpose, so that every part of it can be stated.
Corpus. At build time a script reads this site’s own content files, the instruments on this page, the list of essays and notes, and the notes’ text, and splits them into chunks named for the section they came from. Nothing else is in the index: not the resume, not the essays’ full text on Substack, nothing private.
Retrieval. Keyword scoring (BM25) over those chunks, inside the server function, with no external embedding call. The best chunks go to the model with the question; their ids are what the trace shows.
Answer. A server-side call to a Gemini model, with the key held on the server and never in the page. Output tokens are capped. If the retrieved chunks do not cover the question, the instruction is to say so and stop, not to guess.
Limits. A best-effort token bucket per requesting address, stated beside the input. This site does not log or keep your question.
Evaluation. A question set with expected sections lives beside the instrument’s code; a runner scores the answers against it and writes the results file the scoreboard renders. That runner has not been run yet. Until it has, the scoreboard shows the not-yet-run state and no number appears here.
Raw
Source: This page’s code. Date: This build. Scope: What each answer carries with it, and what is kept. Not shown: Your question once it is answered: this site keeps no copy.
Each answer carries its trace beneath it in the Claim plane: the model name, tokens in and out, latency, the ids of the chunks it retrieved, and the cost of the call. The chunk ids are section ids on this page, so you can check what it read against what it said.
What is kept. The server function holds the model key, applies the rate limit and returns the answer; it keeps no copy of your question or of the answer and writes no log of them. Your question goes to Google’s Gemini to write the answer. The rate limit holds a salted hash of your address, never the address itself, in the server’s memory, at most until that server stops. In this tab, your browser keeps where this site’s server read your connection from and whether Appu has greeted you (session:visitor and session:appu-prompts, listed in the colophon) until the tab closes. This page sets no cookies.
What is in the repository: the question set, the runner, and the results file the scoreboard reads, which at the moment records that the evaluation has not been run.
