How I stopped an AI astrologer from making things up: ephemeris facts, book grounding and a fact-checker
I'm a solo founder building AstralRead, an AI astrologer. You enter your birth date, time and place; it computes the chart and an LLM writes the reading, then you can ask follow-up questions in a chat. The first version
I'm a solo founder building AstralRead, an AI astrologer. You enter your birth date, time and place; it computes the chart and an LLM writes the reading, then you can ask follow-up questions in a chat.
The first version had one big problem, and it's the same problem every "LLM + domain" product has: the model confidently invented things. Not vague horoscope fluff β concrete, checkable errors. "Your Mars squares your Moon" when there is no such aspect. "Venus in your 7th house" when it sits in the 6th. A retrograde that ended two months ago. To a person who knows their chart, one such sentence kills trust in the whole reading.
Here's what fixed it, in the order it mattered.
1. The model never computes anything
Astrology is, under the hood, astronomy plus a lookup table. Planet positions, house cusps and aspects are deterministic β so the LLM should never be asked to produce them.
Every chart is computed with the Swiss Ephemeris (the same engine professional astrology software uses): longitudes, signs, houses (Placidus by default), angles, and every aspect with its exact orb. Transits are computed day by day, so "when does this start and end" comes from the ephemeris, not from the model's memory of 2023.
The model receives this as a compact fact sheet, e.g.:
Sun 6Β°45' Sagittarius, house 5
Mars square Saturn, orb 1.2Β°, TIGHT
Lilith in Leo, house 12
Transit: Saturn conjunct natal Moon, exact 2026-12-03 (window 11-18 β¦ 12-21)
β¦and the instruction is to interpret these facts, nothing else. That alone removed most invented aspects β but not all. Models still "remember" a classic aspect pattern and slip it in.
2. A fact-checker that reads the answer back
After generation, a separate pass parses the reading and checks every astrological claim against the computed chart:
- every "X in sign Y" / "X in house N" β does it match the chart?
- every aspect named β does it exist, with this aspect type?
- every date β is it inside the computed transit window?
- house rulers β e.g. both the traditional and modern ruler for Scorpio, Aquarius and Pisces are accepted, anything else is not.
A claim that fails is either cut (if the sentence is decorative) or sent back to the model with a targeted correction ("MarsβMoon square does not exist in this chart; the Moon's tightest aspect is a trine to Jupiter, 0.8Β°"). Regex + a small parser does most of this; it doesn't need another LLM.
This is the part I'd recommend to anyone building a domain assistant: compute facts deterministically, then verify the prose against them. The model is the writer, not the source.
3. Grounding in books, not in "the internet"
Generic astrology text online is a soup of contradictions. I wanted the interpretations to come from a defined canon: classic textbooks (Sergey Vronsky's 12 volumes, Howard Sasportas' The Twelve Houses, Robert Hand's Planets in Transit and others), retold, not quoted.
Retrieval is boring and that's fine: chapters are cut by their actual headings ("Lilith in Leo", "Mars in the 12th house"), so the model gets the exact section for this placement rather than a semantic-search neighbour. Two guards run on the output:
- no verbatim quotes β a run of ~7 words from a source fails the check (retell, don't copy);
- tone β old books are often fatalistic ("weak will", "fatal", "complete dependence"). Those verdicts get softened into psychology, never repeated.
4. Splitting the reading into sections
A full natal reading is long. One giant prompt produced shallow coverage: the model spent its attention on the Sun and Moon and skimmed the rest. Splitting the reading into sections (personality, love, career, the Moon's nodes, Lilith, Chironβ¦) β each with its own fact block and book material β made every planet and every tight aspect actually show up, and let the sections run in parallel.
Speed went from 1β2 minutes to ~10β16 seconds per reading once I moved the main model (an open-weights Qwen) to fast inference hardware with a fallback chain of other models behind it. The fallbacks matter more than they sound: providers rename, rate-limit or retire models without much warning.
5. Writing for AI search, not just Google
The second half of the project is a knowledge base: ~5,000 pages like Sun in Leo in the 12th house β each written from the same books and the same guards, with an answer-first definition, FAQ and the sources.
Every page has a clean Markdown twin (append .md: example), there's an llms.txt, and new pages are pushed via IndexNow. A surprise for me: Bing's new AI Performance report shows Copilot already citing these pages around a thousand times in three months β while Google is still slowly deciding whether to index them. If you build content today, look at Bing Webmaster Tools; it's where the AI citations show up first.
What I'd do again
- Never let the LLM produce a fact you can compute.
- Read the answer back and check it β cheaply, with code.
- Ground in a small, named canon and forbid verbatim copying.
- Split long generations into sections with their own context.
- Keep a fallback chain of models; assume your favourite one disappears.
If you want to see how it behaves on a real chart, build yours here β it's free. I'm happy to answer questions about the fact-checker or the ephemeris side in the comments.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.