Objective
A grounded answer is only worth as much as your trust that it is grounded. The strongest guardrail against hallucination is a second pass: generate the answer, then have the model check its own answer against the source context and flag anything unsupported. Here you'll build that loop — retrieve, generate, then verify — entirely in one database, so an agent can refuse or revise a draft it can't back up. The same pattern works from any framework or a voice agent — see Use it from your agent at the end.
Step 1: Create the grounding knowledge base
One row per fact the answer must be checkable against, embedded for retrieval.
CREATE TABLE IF NOT EXISTS recipe_selfcheck_facts (
fact_id INTEGER PRIMARY KEY,
content TEXT,
embedding VECTOR(384)
);
Step 2: Load the source facts
The agent may only assert things supported by these rows.
INSERT INTO recipe_selfcheck_facts (fact_id, content) VALUES
(1,'The Pro plan costs $29 per month and includes unlimited projects.'),
(2,'The Pro plan includes email support with a 24-hour response time.'),
(3,'The free plan allows up to 3 projects and community support only.'),
(4,'Annual billing on the Pro plan gives a 20% discount.'),
(5,'There is no phone support on any plan.');
Step 3: Embed the facts
The embedding model runs in-database — your verification index in one line.
UPDATE recipe_selfcheck_facts SET embedding = EMBED(content);
Step 4: Retrieve context and generate a draft answer
Standard RAG: pull the relevant facts into a single context row, then produce a first-pass answer from it. Materializing the retrieved context once lets every later step reuse the exact same source.
CREATE TABLE IF NOT EXISTS recipe_selfcheck_top (
fact_id INTEGER PRIMARY KEY,
content TEXT,
relevance DOUBLE
);
INSERT INTO recipe_selfcheck_top (fact_id, content, relevance)
SELECT fact_id, content,
COSINE_SIMILARITY(embedding, EMBED('What support does the Pro plan include?')) AS relevance
FROM recipe_selfcheck_facts
ORDER BY relevance DESC
LIMIT 3;
CREATE TABLE IF NOT EXISTS recipe_selfcheck_ctx (id INTEGER PRIMARY KEY, source TEXT);
INSERT INTO recipe_selfcheck_ctx (id, source)
SELECT 1, GROUP_CONCAT(content, ' ') FROM recipe_selfcheck_top;
SELECT GENERATE(
'Answer using the context. Context: ' || source ||
' Question: What support does the Pro plan include? Answer:') AS draft_answer
FROM recipe_selfcheck_ctx;
Step 5: Self-check the draft against the source
The second pass: ask the model to verify each claim in the draft is supported, and return a verdict — over the same materialized source.
SELECT GENERATE(
'You are a fact-checker. Given the SOURCE and a DRAFT answer, reply SUPPORTED if every claim in the draft is backed by the source, or UNSUPPORTED followed by the offending claim if not. SOURCE: ' ||
source ||
' DRAFT: The Pro plan includes email support with a 24-hour response time. Verdict:') AS verdict
FROM recipe_selfcheck_ctx;
Step 6: Catch a hallucination in the act
Run the same self-check on a draft that claims something the source contradicts — the checker should reject it. We check against the full fact set, so build a context row over every fact.
CREATE TABLE IF NOT EXISTS recipe_selfcheck_all (id INTEGER PRIMARY KEY, source TEXT);
INSERT INTO recipe_selfcheck_all (id, source)
SELECT 1, GROUP_CONCAT(content, ' ') FROM recipe_selfcheck_facts;
SELECT GENERATE(
'You are a fact-checker. Given the SOURCE and a DRAFT answer, reply SUPPORTED if every claim in the draft is backed by the source, or UNSUPPORTED followed by the offending claim if not. SOURCE: ' ||
source ||
' DRAFT: The Pro plan includes 24/7 phone support. Verdict:') AS verdict
FROM recipe_selfcheck_all;
Step 7: Produce a verified final answer
Combine draft + verdict so the agent only returns an answer it could defend against the source.
SELECT GENERATE(
'Given this fact-check verdict, return the final answer only if SUPPORTED, otherwise return "I can only confirm what is in our documentation: " plus the safe facts. Verdict: SUPPORTED. Draft: The Pro plan includes email support with a 24-hour response time. Final:') AS final_answer;
Cleanup (Optional)
DROP TABLE IF EXISTS recipe_selfcheck_facts;
DROP TABLE IF EXISTS recipe_selfcheck_top;
DROP TABLE IF EXISTS recipe_selfcheck_ctx;
DROP TABLE IF EXISTS recipe_selfcheck_all;
Expected Outcomes
- Step 4 drafts an answer about Pro-plan support, grounded in the retrieved facts.
- Step 5 returns SUPPORTED — every claim in the email-support draft is backed by the source.
- Step 6 returns UNSUPPORTED for the phone-support draft, naming the claim the source contradicts — a hallucination caught before it reaches the user.
- Step 7 emits a final answer only because the verdict was SUPPORTED.
You now have a self-checking agent: it generates, verifies its own answer against the source, and refuses to ship claims it can't back up.
Use it from your agent (framework-agnostic — this is the whole point)
Self-checking is just generate, then verify against the same store, so any agent uses it with no framework lock-in:
- REST / SDK —
POST /v1/query/execute(any language), or@synapcores/sdkclient.executeQuery(...). Your agent runs Step 4 to draft, Step 5 to verify, and only returns the answer when the verdict is SUPPORTED. - MCP (native, on by default) — point any MCP client (Claude Code, Cursor, a custom loop, a voice runtime) at
ws://<your-instance>/mcp?token=<jwt>(JWT from onePOST /v1/auth/login→access_token). Thequerytool runs both the draft and the self-checkGENERATEcalls — grounded, verified answers as tool calls. - Any framework — OpenClaw, LangChain / LlamaIndex evaluators, a custom loop, or a voice agent all run the same retrieve → generate → verify loop. The database is the brain; the framework is swappable.
Key Concepts Learned
- The strongest anti-hallucination guardrail is a second pass that checks the answer against the source.
GENERATE()plays both roles: the generator (draft) and the fact-checker (verdict) over the same retrieved context.- Gating the final answer on a SUPPORTED verdict means the agent ships only defensible claims.
- Because it's plain data ops (retrieve + generate + verify / REST / MCP), self-checking works for any agent — the agent-agnostic backend pattern this cluster builds on.