DEV Community

Cover image for How I Built a Persona Chat Agent and Fought Hallucination — A RAG Story
Abdullah al Mubin
Abdullah al Mubin

Posted on

How I Built a Persona Chat Agent and Fought Hallucination — A RAG Story

I wasn't building another AI chatbot.

I was building an AI persona.

Someone users could actually have conversations with.

A fictional person with:

  • a backstory
  • opinions
  • psychology
  • memories
  • a unique voice

Imagine chatting with a 46-year-old small business owner named Jane.

  • You ask Jane about a business strategy article.
  • She shouldn't answer like a generic AI assistant.
  • She should answer like Jane.
  • That was the goal.

And for a while...

It worked.

Then the hallucinations started.

Index

  1. The Goal
  2. The Architecture
  3. The First Hallucination
  4. Bug #1 — Retrieval Wasn't Working
  5. Bug #2 — Retrieved Context Was Ignored
  6. Bug #3 — Grounded Facts Mixed With Fiction
  7. Bug #4 — The Right Facts, The Wrong Answer
  8. Bug #5 — Over-Grounding Broke Personal Conversations
  9. The Final Architecture
  10. Lessons Learned
  11. Final Thoughts

1. The Goal

The idea was simple.

A persona shouldn't magically know everything.

Instead, it should discuss content that exists inside a project.

Business Article
        ↓
Knowledge Retrieval
        ↓
Persona Reads Context
        ↓
Persona Replies In Character
Enter fullscreen mode Exit fullscreen mode

The response should be:

  • grounded in the article
  • consistent with the persona
  • natural to read

Sounds straightforward, It wasn't.


2. The Architecture

The system had two major parts.

A preprocessing pipeline

Before the persona ever saw a user message, several lightweight agents prepared it.

Each stage had exactly one responsibility:

  • understand the message
  • check for manipulation
  • extract objectives
  • enrich with context
  • prepare a structured packet
  • validate the result

Only after that did the persona generate a response.


Two separate memories

The system searched two different kinds of knowledge.

The first contained shared project content:

  • articles
  • documentation
  • reports
  • blog posts

The second stored the persona's own long-term memories and previous conversations.

Both were searched in parallel before every reply.


3. The First Hallucination

I asked:

Tell me something about Actionable Steps for Business Leaders.

The article clearly listed five recommendations.

The persona replied:

"Honestly, I think business leaders should focus on hard work, honesty, and setting clear goals..."

It sounded convincing.

There was only one problem.

None of that existed in the article.

The entire answer was improvised.


4. Bug #1 — Retrieval Wasn't Working

The first question was obvious.

Did the model actually receive the article?

Fortunately, every request logged how many knowledge chunks were retrieved.

One request showed:

Project Knowledge Retrieved

0 chunks
Enter fullscreen mode Exit fullscreen mode

That immediately explained everything.

The article wasn't reaching the model.


The Cause

The editor correctly saved every article. But only the primary database was updated. The retrieval index never received those changes. The article existed.

The retrieval system simply couldn't see it.


The Fix

Every content update now performs two operations:

Save Content
      ↓
Update Retrieval Index
Enter fullscreen mode Exit fullscreen mode

I also ran a one-time synchronization job for older articles.

After that:

Project Knowledge Retrieved

4 chunks
Enter fullscreen mode Exit fullscreen mode

Retrieval finally worked.

Or so I thought.


5. Bug #2 — Retrieved Context Was Ignored

Now the system successfully retrieved relevant context.

Yet the persona still answered:

"Honestly, I'm not really sure about that."

The information was there. The model simply ignored it.


The Cause

Earlier in the system prompt was a rule that essentially said:

If you don't know something, admit it honestly.

  • The retrieved knowledge appeared much later in the prompt.
  • The model treated the earlier instruction as more important.
  • So it honestly believed it didn't know.

The Fix

Instead of simply appending retrieved context, I explicitly overrode the earlier rule.

Something like:

The following information has been shared with you. You have read it. Treat it as knowledge you genuinely possess.

That tiny prompt change completely changed the model's behavior.


6. Bug #3 — Grounded Facts Mixed With Fiction

Now the persona finally referenced the article.

But then it added:

"I've been applying these strategies in my own business for years."

The article never said that.

Neither did the persona profile.

The model invented a believable personal experience.

Ironically...

This was the hardest hallucination to catch.

Because it sounded perfectly reasonable.


My First Attempt

I created another stage after generation.

Its job was simple:

Raw Response
      ↓
Remove Ungrounded Sentences
      ↓
Final Response
Enter fullscreen mode Exit fullscreen mode

It failed. Completely.

The model removed correct information and kept the hallucination.


The Better Solution

Instead of asking the model to judge itself...

I rebuilt the response from scratch.

Every retrieved article was split into individual facts.

Like this:

1. Conduct market research.

2. Define your value proposition.

3. Invest in innovation.

4. Build a flexible business plan.
Enter fullscreen mode Exit fullscreen mode

The grounding stage received:

  • the numbered facts
  • the user's question
  • the persona's original response (only for tone)

Its instructions became:

Rewrite the answer using only these numbered facts.

  • Not summarize.
  • Not invent.
  • Not elaborate.
  • Rewrite.

That single architectural change removed nearly every invented personal story.


7. Bug #4 — The Right Facts, The Wrong Answer

Next question:

What percentage of businesses with a formal plan achieve higher revenue growth?

The article contained two different percentages. The persona picked, the wrong one. Not because it hallucinated. Because both numbers existed in the source.


The Cause

The grounding stage knew the available facts.

It didn't know which fact the user actually wanted.


The Fix

Two improvements solved it.

Pass the user's question

Instead of only seeing the generated response...

the grounding stage also receives the original question.

Now it can match facts against the user's intent.


Retrieve more context

The retrieval step originally returned too few knowledge chunks.

Increasing the retrieval depth ensured the relevant statistic was almost always available.


8. Bug #5 — Over-Grounding Broke Personal Conversations

Then I asked:

Have you personally conducted market research?

The persona suddenly replied:

I don't have information about that.

Technically...

The grounding stage was correct.

The article didn't mention Jane's personal life.

But that wasn't the question. I wasn't asking about the article. I was asking Jane.


The Fix

Before grounding begins, the system now classifies the question.

Personal Question?
        │
   Yes ─────► Leave response unchanged
        │
        No
        ▼
Ground response from retrieved facts
Enter fullscreen mode Exit fullscreen mode

Examples:

Have you ever done market research?

→ Personal
Enter fullscreen mode Exit fullscreen mode
What percentage of businesses achieve higher growth?

→ Factual
Enter fullscreen mode Exit fullscreen mode

This tiny classifier completely changed the behavior.


9. The Final Architecture

Today the pipeline looks like this.

User Message
      │
      ▼

Message Processing

      ▼

Knowledge Retrieval

      ▼

Persona Generates Reply

      ▼

Grounding Stage

• Personal Question?
      │
      ├── Yes → Keep response

      └── No → Rebuild from source facts

      ▼

Final Response

      ▼

Conversation Saved
Enter fullscreen mode Exit fullscreen mode

The result is a persona that can:

  • accurately discuss project content
  • quote facts correctly
  • maintain its personality
  • avoid inventing experiences
  • switch naturally between factual and personal conversations

10. Lessons Learned

1. Log Your Retrieval

The most useful debugging signal wasn't inside the model. It was a simple retrieval count. Without that log, I would have blamed the AI instead of my own architecture.


2. Prompt Order Matters

Earlier instructions often dominate later ones. If one instruction should replace another... say so explicitly.


3. Models Are Bad At Reviewing Their Own Work

  • Asking a model to detect its own hallucinations isn't very reliable.
  • Rebuilding from constrained facts worked far better than asking it to critique itself.

4. Grounding Starts With Classification

  • Not every question should be grounded.
  • Some questions belong to the knowledge base.
  • Others belong to the persona.
  • Treating them the same breaks both.

5. The Most Dangerous Hallucinations Are The Believable Ones

  • Generic hallucinations are easy to spot.
  • Profile-consistent hallucinations aren't.

When Jane said:

"I've been using these techniques in my own business..."

Everyone believed her. Including me.

Those are the hallucinations worth worrying about.


11. Final Thoughts

Before this project, I thought hallucinations were mostly a prompt engineering problem.

I was wrong.

They turned out to be:

  • retrieval problems
  • synchronization problems
  • prompt precedence problems
  • architecture problems
  • validation problems
  • classification problems

Every fix uncovered another hidden weakness.

But that's exactly how production systems improve.

Today the persona can accurately discuss project content, answer factual questions without inventing details, and still hold natural conversations as a believable character.

The biggest lesson wasn't learning how to write a better prompt.

It was learning that reducing hallucinations is an architectural problem not just a prompting problem.

Top comments (0)