1. What Alexandria is, and what it isn't
"Autonomous" here means it runs every day without me: it wakes on a schedule, does the same five stages in the same order, and stops. It is not an open-ended agent that decides its own next step. That choice makes it cheap, testable, and easy to replay when a pick goes wrong.
| Requirement | What it means in practice |
|---|---|
| F1 Taste registry | My tastes live in a database with their history. I add, reweight or pause a taste from a web form on this site; Telegram commands come later. |
| F2 Discover | Crawl the bookstore and publisher sites I track. I add sites from the same web form; a seed list comes preloaded. |
| F3 Shortlist | Filter and score candidates against my taste; plan which taste gets today's slot. |
| F4 Review check | Gather ratings and reviews from the tracked sites and critics' pages, and write an honest digest with sources. |
| F5 Recommend | One book a day, published as a page on this site. That page is the whole channel for the proof of concept; Telegram comes later. |
| F6 Reactions | Record four signals on that page: Want it, Not for me, Finished, and a comment. |
| F7 Learn | Turn reactions into rewards that change future picks, the way RL does, at a data rate of one label a day. |
| N1 Truthful | Every rating, quote and price comes from a fetched page with a date. No source, no claim. |
| N2 Polite | The crawler obeys robots.txt and crawl delays, says who it is, and keeps a site link-only when its terms forbid robots. |
| N3 Affordable and Quiet | No paid data APIs. The only paid service is the AI model, and free tiers can cover the proof of concept. No server to babysit; one status line a day on the console page. |
| N4 Model-agnostic | Every AI call goes through one gateway, so changing the model is a config edit, and models can be compared on my own data. |
2. The taste registry
Taste is data with a timeline. Every change is an event, so Alexandria can answer "what did I like in March?" and the learning loop can tell a real shift in taste from a bad week.
| Facet | Starting priority | Notes the scorer uses |
|---|---|---|
| History and stories of mathematics | 1.00 | Narrative over textbook; biographies of mathematicians count |
| History and stories of physics | 1.00 | Same; includes cosmology and the bomb-era histories |
| History of other sciences | 0.70 | Biology, chemistry, medicine, computing history |
| AI and computer science | 0.70 | Both technical and narrative; seeded from my reading list |
| Philosophy | 0.55 | Philosophy of science gets a bonus from the science facets |
| Psychology | 0.55 | Seeded by Thinking, Fast and Slow (rated 4★) |
| Pulitzer Prize winners | 0.50 | A quality signal that boosts any facet, plus its own slot |
| Classics | 0.40 | Rotates with new releases |
| Hot new releases | 0.40 | Published in the last 12 months, from the tracked sites' new-release pages |
Register a new taste
A web form on this site first: a console page lists the current facets and lets me add, reweight or pause one. Only requests carrying my owner key count. A free-text box ("more books like The Code Breaker") is parsed into a proposed change that I confirm with one click. Telegram commands come later.
Kept time by time
taste_event is append-only (add, reweight, pause, remove, note). The current profile is a view over it. A monthly snapshot gives a taste timeline I can read later.
Cold start
Seed from the 8 books already on my reading list (titles, ratings, notes), a short form (10 favorites, 5 books I didn't enjoy), and a Goodreads CSV export if I have one. Day 1 picks use these priors.
3. Stage 1: Discover
Alexandria crawls the sites I track: bookstores and publishers I register in a web form, the same way I register tastes. A wide crawl every Sunday builds the candidate pool; a small daily crawl picks up new releases. No paid data APIs.
The form takes a site URL, its kind (bookstore, publisher, prize, review), the pages to watch (new releases, a category), and the language. Before the first crawl, Alexandria reads the site's robots.txt and sets its mode: crawl, or link-only (no fetching; store search links only). The seed list below is preloaded, with modes from robots.txt checked on Nov 2, 2026.
| Seed site | What it watches | Mode | Why |
|---|---|---|---|
| Penguin Random House | New releases; science, history and philosophy categories | crawl | robots.txt allows catalog pages (blocks only search) |
| Tiki | Book categories and new arrivals; Vietnamese and imported editions with VN prices | crawl, slow | robots.txt allows product and category pages, and publishes a sitemap |
| Fahasa | Book categories; Vietnamese editions and prices | crawl | robots.txt blocks only account, cart and checkout pages |
| Nhã Nam | Vietnamese translations of the books I'm offered | crawl, 10 s apart | robots.txt asks for a 10-second crawl delay |
| Pulitzer Prize | Winners by category | crawl, yearly | allowed |
| The Guardian books | Critics' reviews (used in Stage 3) | crawl | allowed |
| Amazon | — | link-only | robots.txt allows many pages, but Amazon's Conditions of Use forbid robots and data extraction |
| Shopee | — | link-only | until its terms are checked; a search link is enough to buy |
| O'Reilly | — | link-only for now | its robots.txt returned 403 to a bot request |
Read the rules first
robots.txt, crawl-delay, and the site's terms decide the mode. A site that forbids robots stays link-only, however useful it would be.
Find pages
Start from sitemaps and the watched listing pages. Never crawl search result pages, and never log in.
Extract
Read schema.org Book / Product data embedded in the page when it's there. Otherwise a small model behind the AI gateway extracts title, author, ISBN, price and rating into a typed record.
Be gentle
At least 10 seconds between requests to one site, a daily page cap per site, conditional requests (ETag) so unchanged pages cost nothing, and a user agent that names Alexandria and links back here.
Everything is stored per work (one book, many editions), keyed by ISBN-13 or normalized title and author, so the same book found on five sites is one candidate.
4. Stage 2: Shortlist by taste
From a few hundred candidates to five, in three passes: hard filters, a cheap score, then an LLM judge on the top twenty.
Hard filters
- Not already read, recommended or rejected
- Author cooldown: 30 days
- Buyable in Vietnam, or a Vietnamese edition exists
- Enough ratings to judge, except for releases under 3 months old
Cheap score
score = taste_match + quality + freshness + novelty − penalties, each weighted. Taste match is embedding similarity between the book and each facet. Quality is a Bayesian average of the ratings found on tracked pages, so a 4.6 from 12 readers doesn't beat a 4.3 from 40,000, plus a prize bonus.
LLM judge
The judge model, called through the AI gateway, reads my profile and the top 20 book cards and scores each against a fixed rubric (fit per facet, depth, readability, why now), with a one-line reason. Output is typed JSON, so the reasons are stored and auditable.
Which taste gets today? A slot planner picks the facet before the book. It uses Thompson sampling: each facet keeps a Beta distribution of "how often I liked its picks", seeded from my priorities, and the planner samples from each and takes the highest. Math and physics history start ahead, but a facet that keeps getting "Not for me" loses slots on its own. One day a week is a labeled wildcard outside my top facets, so the loop doesn't shrink my taste to what it already knows.
5. Stage 3: Review check
The top three of the shortlist get a full review check. The best of the three after reviews becomes today's pick; the other two are kept as runner-ups.
Gather evidence
- Ratings and review counts on the tracked store and publisher pages
- Critics' reviews on tracked review sites, such as The Guardian
- Optional: the model's own web search, when the provider chosen in the gateway offers it, with every result cited
Each piece is stored with its URL and fetch time.
Write the digest
The digest model writes a structured summary: consensus, strengths, common criticisms, who it's for, how much math it assumes, and red flags (dated science, disputed claims). Every sentence carries the ID of the evidence it came from.
Check the digest
A validator drops any sentence whose evidence ID doesn't exist or whose source text doesn't support it. Ratings are copied from evidence, never written by the model. If too little survives, the book goes back to the pool and the runner-up moves up.
The comparison of the three books is stored too: which one won and why. That gives a ranked group every day, the same group-relative signal GRPO learns from in the Wikipedia agent tutorial.
6. Stage 4: Recommend, one book a day
At 07:00 ICT the pick is published as a page on this site. For the proof of concept that page is the only channel, and it is also where I react. Telegram comes later.
Daily pick page (mock-up)
Mock-up only: ratings, links and the digest are placeholders.
How the page works
- One page a day in a new "Book suggestions" list on this site, with a short introduction, the review digest with sources, and links to find the book.
- Reactions on the page: three buttons and a comment box. A small edge function stores the click. Only requests carrying my owner key count, saved once in my browser; visitors' clicks are kept apart and never train anything.
- Later: a Telegram bot with the same buttons, sending the same stored recommendation.
Publishing. The page is built overnight and goes public at 07:00. From the console page I can hold today's page or skip a day.
Site change needed. One page a day would flood the blog index, so suggestions get their own list (content/book-suggestions.json, rendered on Books), and the blog index shows only the latest three.
7. Stage 5: Recognize my reaction
Only four signals, all on the daily page. Each becomes a row in reaction, then a number in reward. Rewards can be revised: finishing a book weeks later updates the reward of the day it was recommended.
| Signal | Where | Reward |
|---|---|---|
| ✅ Finished | Button on that day's page, any time later | 1.0 |
| 👍 Want it | Button on the daily page | 0.6 |
| 👎 Not for me | Button on the daily page | 0.0 |
| 💬 Comment | Comment box on the daily page | Parsed into sentiment and aspects; moves the reward by up to ±0.2 and can propose a taste change |
A comment such as "great story but too equation-heavy for a morning read" is parsed by a small model behind the gateway into structured fields: sentiment, aspects (narrative +, math density −), and a suggested change ("lower math depth for weekday picks"). Suggestions never change the registry by themselves; they wait on the console page for one-click confirmation.
Nothing else is recorded: no purchase tracking, no reading-time guesses, and silence is not treated as a reaction.
8. The learning loop, like RL but sized for one reader
One reader produces about 365 labels a year. Full RL on an LLM would starve on that. So Alexandria climbs in levels, and each level only switches on when there is enough data to beat the one below it on the offline eval.
Priors and rules
Starting priorities, cold-start seed books, the judge's rubric. Works with zero reactions.
Slot bandit
Thompson sampling over taste facets, updated the moment a reaction arrives. Decides which kind of book I get.
Learned scorer
A small logistic model on book features and embeddings, refit weekly on my rewards, replacing the hand-set weights in pass 2. Decides which book.
Post-trained judge
SFT on accepted picks plus DPO or GRPO on my preferences, trained on Tinker like the Wikipedia agent. Only if the Level 2 eval shows the off-the-shelf judge is the bottleneck.
Weekly reflection. Every Sunday, the reflection model reads the week's reactions and comments and proposes a profile diff in plain words ("you liked narrative physics history more than technical treatments; lower math depth on weekdays"). I approve or edit it on the console page, and it becomes a new profile_version.
| Wikipedia agent tutorial | Same idea in Alexandria |
|---|---|
| Eval first: lock a test set | Offline replay: books I've already rated, held out. Metric: how many I rated ≥ 4★ land in the shortlist's top 5. Online: 30-day Want-it rate and finish rate |
| Harness: the same loop for eval and training | The five stages are one pipeline; replaying a past day runs the same code |
| SFT on its own correct answers | Picks I wanted or finished, with the judge's reasons, become few-shot examples (and training data for Level 3) |
| GRPO: compare a group of attempts | Each day's three reviewed books are a group, ranked by the judge; my reaction scores the one I was shown |
| Ablation with confidence intervals | Monthly report: turn off reviews, the bandit, or embeddings on the replay set, with bootstrap intervals |
9. Tech stack
A scheduled Python pipeline, one Postgres database, a few small edge functions, an AI gateway, and pages on this site. Nothing to keep running except the database.
The AI gateway
Every AI job (extract, judge, digest, write, parse a comment, reflect) goes through one function: gateway.run(task, input, schema). It returns typed output plus the model used, tokens, cost and latency. Which model serves which task lives in one config file, so switching providers is an edit, not a code change.
judge: { model: gemini-3.8-flash, fallback: [deepseek-v4-pro, claude-sonnet-5.5] }
digest: { model: gemini-3.8-flash, fallback: [deepseek-v4-pro] }
write: { model: gemini-3.8-flash, fallback: [claude-haiku-5.5] }
extract: { model: gemini-3.1-flash-lite, fallback: [openrouter/free] }
parse: { model: gemini-3.1-flash-lite, fallback: [openrouter/free] }
reflect: { model: claude-sonnet-5.5 }
budget: { usd_per_day: 0.50, on_exceed: free_routes_only }
shadow: { judge: deepseek-v4-pro, share: 0.2 }Adapters
Anthropic, OpenAI, Google Gemini, DeepSeek and OpenRouter. DeepSeek and OpenRouter speak the OpenAI-compatible API, so they share one adapter; OpenRouter also reaches the free models. A local model can be added later.
Same shape every time
Each task has a JSON schema. The gateway validates the answer, retries once with the error, and otherwise moves to the next model in the fallback list.
Fallback and budget
Rate limits, server errors, refusals and timeouts fall through to the next model. When the daily budget is spent, only free routes run.
Compare on my data
Shadow mode runs a second model on a share of a task and stores both answers. The replay eval scores both against my past reactions, so choosing a model is a measured decision. Every call is logged in llm_call.
| Build or buy | Verdict |
|---|---|
| Thin gateway in the pipeline's code | Recommended. A few hundred lines; no extra server; full control of routing, schemas and logs |
| LiteLLM proxy | More features, but it is a server to deploy and keep running |
| OpenRouter alone | One key for many models, but every call depends on one company. Used as one adapter instead |
| Cloudflare AI Gateway | Logging and caching in front of providers; can be added later without changing the code |
Choices and why
| Decision | Choice | Why not the alternative |
|---|---|---|
| Orchestration | Fixed pipeline in code | An agent framework could run this on a schedule, but the order of steps never changes. Code is cheaper, and every day can be replayed exactly |
| Data | Polite crawler over tracked sites | Paid data APIs are what this design avoids; crawling the sites I choose also covers Vietnamese stores that have no API |
| Scheduler | GitHub Actions cron (private repo) | A VPS would sit idle 23 hours a day. A run takes minutes |
| Database | Supabase Postgres with pgvector | One store for rows and embeddings, reachable from Actions and from edge functions. Free projects pause after a week of inactivity; the daily run keeps it awake |
| AI | AI gateway with per-task routes | Hard-wiring one provider makes cost and quality comparisons impossible |
| Channel | A page on this site; Telegram later | The site already builds and deploys itself, and a page can carry the reaction buttons |
Data model
| Table | Key columns |
|---|---|
taste_facet, taste_event | facet, priority, status · event type, payload, source, time |
profile_version | profile text, embedding, facet snapshot, approved_at |
tracked_site, page_cache | URL, kind, watched pages, mode (crawl / link-only), crawl delay, health · URL, ETag, content hash, fetched_at |
work, edition, offer | title, authors, year, subjects, description, embedding · ISBN-13, language, format · store, price seen, URL, checked_at |
evidence | work, source URL, rating, count, excerpt, fetched_at |
candidate, recommendation | run ID, work, slot facet, scores, rank · date (unique), work, digest, judge reasons, runner-ups, exploration flag |
reaction, reward | kind (want, not, finished, comment), comment text · value, components, updated_at |
bandit_state, model_version | facet α/β · scorer params and eval metrics |
llm_call, run_log | task, provider, model, tokens, cost, latency, ok · stage, status, pages crawled, error |
10. Guardrails
What it may do alone
- Crawl tracked sites in crawl mode
- Score and store
- Publish the daily page
What needs me
- Any change to the taste registry
- Adding or removing a tracked site
- Raising the spend cap
Hard limits
- Daily caps on AI spend and pages crawled per site; the run stops cleanly when hit
- One recommendation per date (unique key), so a rerun never double-posts
- Pause, Skip today and Why this book on the console page
Honest content
- No claim without evidence; ratings copied, never generated
- Prices show their check time
- Pages in my own words, at most one short quote
Polite crawler
- robots.txt and crawl-delay obeyed
- Names itself and links back here
- Link-only when terms forbid robots
Private by default
- Reactions and taste history stay in the database
- The public page shows the recommendation, not my reaction
- Only my owner key can change tastes or react
11. Cost estimation
Crawling and scheduling run inside free tiers, so the only real cost is the AI model. Depending on the route through the gateway, that is $0 on free tiers or about $3–8 a month on paid models.
Daily AI workload
| Job | Kind | Calls / day | Tokens in / out |
|---|---|---|---|
| Extract new books from crawled pages, parse comments | light | ~10 (batched) | 80k / 15k |
| Judge the top 20 | heavy | 1 | 25k / 6k |
| Review digests for 3 books | heavy | 3 | 27k / 4.5k |
| Write the daily page | heavy | 1 | 6k / 2.5k |
| Weekly reflection, averaged per day | heavy | 1 a week | 6k / 0.7k |
| Heavy total | ≈ 6 | 64k / 13.7k |
AI API comparison for this workload
Monthly cost of the heavy jobs (30 days × 64k in / 13.7k out) at each model's standard price. "Tier" is where the model sits in its own provider's line-up; how well a model actually picks books for me is measured by the gateway's shadow runs, not by this table.
| Model | Tier in its family | Price in / out per 1M | Free tier | Heavy jobs / month |
|---|---|---|---|---|
| Claude Opus 5.5 | Top | $4 / $20 | No | $15.90 (batch $7.95) |
| Claude Sonnet 5.5 | Middle | $2 / $10 | No | $7.95 (batch $3.98) |
| GPT-6.1 Sol | Middle (GPT-6 Astra is the top) | $2 / $10 | No | $7.95 (batch $3.98) |
| Gemini 4 Argon | Newest generation | $2 / $10, then $4 / $20 from Dec 1 | No | $7.95, then $15.90 |
| Kimi K3.1 Pro | Top; leads open-weight models | $2 / $10 | No | $7.95 |
| Gemini 3.8 Flash | Middle | $0.75 / $3.75, then $1.50 / $7.50 from Jan 1 | Yes | $2.98, then $5.96 |
| Mistral Large 4 | Top | $0.70 / $2.10 | No | $2.21 |
| DeepSeek V4 Pro (off-peak) | Top | $0.66 / $1.98 | No | $2.08 |
| Gemini 3.1 Flash-Lite | Small | $0.25 / $1.50 | Yes | $1.10 |
| DeepSeek Flash (off-peak) | Small | $0.15 / $0.60 | No | $0.53 |
| Claude Haiku 5.5 | Small | $0.10 / $0.50 | No | $0.40 |
| GPT-6 Luna | Small | $0.10 / $0.50 | No | $0.40 |
| OpenRouter free models (e.g. Inkling, Nemotron 3 Ultra, Gemma 4 31B) | Varies | $0 | Yes | $0 |
Gemini free tier
Covers Gemini 3.8 Flash, 3.6 Flash, 3.1 Flash-Lite, 2.5 Pro and Gemma 4, among others. Google doesn't publish per-model daily limits in its docs (AI Studio shows them per account), and third-party reports range from about 20 to 1,500 requests a day. Under Google's terms, free-tier prompts can be used to improve its products: fine for public book data, so keep personal notes on paid routes if that matters.
OpenRouter free models
17 free models in its catalog on Nov 2, 2026. Limits: 20 requests a minute and 50 a day, or 1,000 a day after buying at least $10 of credits. The list changes often, so they are a fallback, not the main route.
Quality, roughly
Public leaderboards in October 2026 put Gemini 4 Carbon, GPT-5.6 Sol and Claude Opus 5.5 at the top, with Kimi K3.1 Pro leading open-weight models, and the order shifts between index versions. Judging fit, summarizing reviews and writing a short page are mid-tier work, so a middle-tier model is likely enough; shadow runs will show whether the top tier earns its price.
Does free fit?
Yes for a proof of concept: about 6 heavy and 10 light calls a day. Route heavy jobs to Gemini 3.8 Flash (free) and light jobs to Flash-Lite (free), with OpenRouter free models as the fallback.
Four routes
| Route | Heavy jobs | Light jobs | ≈ per month |
|---|---|---|---|
| A. Free POC | Gemini 3.8 Flash, free tier | Gemini 3.1 Flash-Lite, free tier | $0 |
| B. Budget | DeepSeek V4 Pro, off-peak | DeepSeek Flash, off-peak | $2.71 |
| C. Balanced | Claude Sonnet 5.5 or GPT-6.1 Sol, batch | Claude Haiku 5.5 | $4.44 |
| D. Best quality | Claude Opus 5.5, batch | Claude Haiku 5.5, batch | $8.18 |
Plan: start on route A, shadow-run route B or C on 20% of judge calls (well under $1 a month), and let the replay eval decide after a month. DeepSeek's off-peak price applies to the nightly run: 00:00 ICT is 17:00 UTC, outside its peak hours.
Everything else: GitHub Actions (about 10 minutes a day, inside the free monthly minutes), Supabase free tier, GitHub Pages, and embeddings on Gemini's free tier. No paid data APIs.
Prices as of Nov 2, 2026: Google and DeepSeek from their own pricing pages, Anthropic from its price list, and OpenAI, Moonshot (Kimi) and Mistral from OpenRouter's public catalog. Gemini prices above change on Dec 1, 2026 and Jan 1, 2027, as noted. Check before switching routes.
12. Open decisions
Decided items are recorded so the design stays honest about what changed. Open items have a recommended default.
- Decided Channel. The proof of concept publishes a page on this site and collects my reactions there. Telegram comes later.
- Decided Editions. Show both the original and the Vietnamese edition when a translation exists.
- Decided Models. Through the AI gateway, compared by shadow runs and the replay eval.
- Decided Formats. Any format; no filter on print, ebook or audiobook.
- Open Starting route. A (free), B, C or D from section 11? Recommended: A, with B shadowed.
- Open Seed sites. Keep the nine seed sites and their modes, or add others now? Recommended: keep, add more from the form later.
- Open Where pages live. A separate "Book suggestions" list plus the latest three on the blog index, or every page in the blog index? Recommended: separate list.
- Open Publishing. Public at 07:00 with a Hold button on the console, or a private preview I approve first? Recommended: public at 07:00 with Hold.
- Open Monthly cap. Recommended: $10 hard cap, which only matters once a paid route is on.
- Open Delivery time. Recommended: 07:00 ICT.