Skip to content
RasaGet a free licence
Guides for AI teams

Guide · AI product engineer

Add Rasa without mixing up customer data or voice replies

Choose a working example for your existing agent or voice framework, and see which jobs Rasa can handle alongside it.

by Rod Rivera

About 7 minutes

Key takeaways (3)
  • Find a runnable Rasa example for the framework your team already uses.
  • Retain useful tools, research and customer preferences in their existing services.
  • Check the information passed between services when a conversation changes.

An Agno adviser starts a new agent for each fictional customer. Both still receive the same saved preference in our test. Creating a fresh object leaves the shared database in place.

Different parts of an assistant need different checks. Match saved preferences to the customer, a delayed answer to its conversation turn, and incoming text to the current connection. The examples below show how to preserve those checks when adding Rasa to your existing agent or voice services.

What changesWhat to checkWhat the runnable test exposes
A customer starts a new chatWhose saved preferences are loadedFresh agents can still load the same preference for two customers
A caller interruptsWhich turn a delayed answer belongs toOld text enters the pipeline after the interruption
A participant reconnectsWhether the message belongs to the current connectionOld text can reach the retained session after reconnecting

Each example includes a test of what happens when conversations change. Saved preferences can reach the wrong customer. An old answer can arrive after a voice interruption. A reconnected participant can receive text from their old connection.

This literal record is from the 11 October offline Agno memory exercise. The fixture deliberately seeds the default owner’s preference, omits user_id, and constructs a fresh Agent for each customer. Run .venv/bin/python demo_memory_scopes.py from the locked Agno project to print it. Both customers’ initial model inputs contain the preference, despite each having just one user message:

{"agent_lifetime": "fresh", "observations": [{"customer": "customer-a", "final_output_present": true, "native_product_tool_seen": true, "preference_in_initial_input": true, "previous_question_in_initial_input": false, "session": "a2", "user_messages_in_initial_input": 1}, {"customer": "customer-b", "final_output_present": true, "native_product_tool_seen": true, "preference_in_initial_input": true, "previous_question_in_initial_input": false, "session": "b1", "user_messages_in_initial_input": 1}], "strategy": "omitted"}

We exercised those cases in three separate native controls. Each retains a reassuring signal while changing the ownership check that matters:

Existing stackSignal that remainsWhat the application must decideWhat the control shows
AgnoA fresh agent with no previous questionWhich customer owns stored memorySeeded default-owner memory still reaches both customers when the user key is omitted
PipecatA native interruption reaches the output sinkWhether the later reply still belongs to the current turnThe same delayed reply is discarded only with the generation check
LiveKitThe participant identity survives reconnectWhether the captured connection is still currentA retained session accepts old reader text through a bare callback; the completion check rejects it

These are constructed application controls, not reported product defects. See the Agno memory test, Pipecat interruption test and LiveKit reconnect test. They use controlled replies or lifecycle boundaries to make the cause visible. Assess model quality, cost and speech performance with separate user-task and connected-audio measurements.

The other two observations have their own commands and literal 11 October scripted native records. Follow each linked lesson’s locked setup first; the commands run in separate selected projects.

From tutorials/frameworks-with-rasa/pipecat:

.venv/bin/python demo_interruption.py
{"generation_guard": "present", "native_interruption_reached_sink": true, "dialogue_requests": 1, "response_text_after_interruption": [], "discarded_responses": 1}
{"generation_guard": "removed", "native_interruption_reached_sink": true, "dialogue_requests": 1, "response_text_after_interruption": ["Synthetic old answer."], "discarded_responses": 0}

From tutorials/frameworks-with-rasa/livekit:

.venv/bin/python demo_late_text.py

The command also prints a changed-identity pair. This separate literal excerpt shows its same-identity reconnect pair:

{"discarded": 0, "guarded": false, "native_linked_participant": "public-a", "reconnect_same_identity": true, "session_replies_after_handoff": ["Synthetic old text."]}
{"discarded": 1, "guarded": true, "native_linked_participant": "public-a", "reconnect_same_identity": true, "session_replies_after_handoff": []}

Each control changes its own completion check while retaining the native lifecycle signal. These scripted text records establish the named handoff outcome; qualify speech, actual rooms and provider models in the combined service.

Adding Rasa gives an existing service a conversation: ask for missing input, handle a correction and explain a result. That connection needs deliberate ownership rules. Keep the preference the customer wants tomorrow; reject the speech they interrupted today.

Give each kind of state its own lifetime

Do not start by asking whether to reuse an agent object. Start by asking what must survive and what must end.

StateWhen to preserve itWhen to retire or exclude it
Saved customer preferenceThe same customer returns in a new conversationAnother customer asks; enforce the memory owner
Conversation historyThe intended conversation continuesA new conversation begins; keep its history separate
Unfinished input or outputIts current connection or turn still owns itThe turn is interrupted or the connection is replaced

The Agno lesson tests useful continuity without yesterday’s question. The voice lessons test accepted current work alongside rejected old completions. Both outcomes belong in the same integration’s acceptance plan.

The Agno demo registry makes two of those lifetimes concrete:

Keep the customer key; change the conversation key
DEMO_SCOPES = {
    "willow-a-chat": CustomerScope("customer-a", "a-chat"),
    "willow-a-voice": CustomerScope("customer-a", "a-voice"),
    "willow-b-chat": CustomerScope("customer-b", "b-chat"),
}
  1. Customer A’s first chat has its own conversation key.
  2. A’s next conversation keeps the customer key, so its stored preference can return with separate history.
  3. B has a different customer key; A’s preference must stay out of B’s model input.

These are public exercise labels. In your service, derive the equivalent keys from the authenticated channel. With a voice adapter, also bind the current turn or connection before accepting unfinished work.

Record three bindings before connecting the systems: the customer who owns long-lived data, the conversation that owns dialogue history, and the current turn or connection that owns unfinished work. Sometimes two bindings share a value. That is a design decision to test, not a reason to treat them as one.

The Agno example maps public demo senders to fixed customer and conversation keys. The voice examples bind public dialogue labels and inspect native lifecycle objects. None of those demo labels authenticates a real customer. Your application must establish the caller’s identity and permissions before deriving keys or permitting access to private data.

The same pinned adviser uses those registry values when calling its existing Agno Agent. After rejecting an unknown sender and an invalid question, it runs:

result = agent.run(question, user_id=scope.user_id, session_id=scope.session_id)

scope.user_id selects customer memory; scope.session_id selects conversation history. The Agent keeps its configured tools, model and database. The complete handoff checks the structured advice before returning it to the conversation.

To repeat the memory control, check out the linked companion revision and create only the Agno environment you need:

cd tutorials/frameworks-with-rasa/agno
uv sync --locked
Any system
make test

These two literal lines come from its 11 October offline qualification output:

test_fresh_agent_preserves_correct_user_memory_across_conversations (test_memory_scopes.MemoryScopes.test_fresh_agent_preserves_correct_user_memory_across_conversations) ... ok
test_fresh_agent_still_loads_seeded_default_preference_for_both_customers (test_memory_scopes.MemoryScopes.test_fresh_agent_still_loads_seeded_default_preference_for_both_customers) ... ok

The first checks intended continuity and exclusion of another customer. The second seeds the default owner’s fact deliberately, omits the owner on both fresh agents, and observes the shared fact. Both pass because they test different behaviours. A green test result needs its asserted outcome beside it.

These are scripted native controls with no provider requests. They confirm memory ownership and continuity; they do not assess model advice or a real customer authentication system.

Connect at the responsibility you need

There are two useful starting points. If the investment produces a useful task result, let Rasa call it through a tool. If it already carries the user’s voice, keep that media path and give it a dialogue adapter.

User question or correction Rasa conversation Existing room or voice pipeline Existing workflow, tools, search or research Checked task result Explanation to the user Application dialogue adapter Rasa dialogue endpoint Existing speech output
  1. Preserve the worker. Rasa collects the question; the existing task system keeps its tools, search, saved run or research roles. Check the result before presenting it.
  2. Preserve the media path. The native session keeps its speech objects. The adapter binds dialogue and checks whether completed work may still enter that session.
FigureKeep the worker or keep the media path

The diagram is a choice of entry point, not a requirement to run both paths. The LiveKit composition recipe shows the second path. Its native construction controls retain supplied speech and turn-detection objects. A connected room and the actual speech providers still need their own acceptance run.

For the first path, start with one read-only operation that already works. Declare its required input, result and unavailable outcome. Let Rasa ask the user for missing input instead of turning an incomplete request into a guessed tool call. Preserve the existing worker’s internal structure; inspect the result at the point where it becomes a user-facing claim.

A source identifier can be genuine while the claim beside it is false. A valid result schema can contain advice based on the wrong customer’s memory. Useful checks depend on the task, which is why the retrieval and memory lessons test different things.

Choose the investment you want to keep

Begin with the useful system you already operate. A retriever does not need to become a voice framework. A room transport does not need to become a research worker. The seven lessons below connect different responsibilities to Rasa.

Existing investmentKeep in that stackRasa’s role in the worked exampleHandoff to inspectStart here
LangGraphWorkflow, checkpoint and paused runCollect missing input and explain the resumed resultResume the intended saved runWorkflow lesson
StrandsRegistered tools and specialist callsCollect input and explain a checked tool observationChoose caller history and specialist context separatelyTool lesson
LangChainDocument retrieval and source metadataCollect the question and explain returned sourcesEnforce document access; distinguish a citation from support for a claimRetrieval lesson
CrewAIResearch roles, tasks and toolsCollect input and explain a checked checklistVerify the task result against its source and task recordsResearch lesson
AgnoAdviser, product tool and saved preferencesExpose the product task across conversationsBind customer memory separately from conversation historyMemory lesson
PipecatTransport, speech services and turn aggregationSupply dialogue replies to the retained media pathReject a callback completed after interruptionPipeline lesson
LiveKitRoom, speech services and turn detectorSupply dialogue replies through the native model interfaceCheck the incoming connection after awaited workRoom lesson

The links lead to individual instructions and pinned companion code. These are application recipes, not a guarantee that every configuration of each framework works with Rasa. Keep your own provider plugins and locks; test the combination you intend to ship.

Test the handoff, then test the user task

Choose one positive control and one negative control from the lesson closest to your system. For a memory-backed adviser, keep one customer’s preference across new chats and exclude it from another customer’s chat. For a voice adapter, accept current input or output and reject the controlled stale completion.

Run the controls using the companion’s selected environment and locked packages. Keep the failing mutation: it demonstrates that the check is doing work. Follow the individual lesson’s setup; there is no reason to install all seven stacks for one integration.

Then exercise the combined service with its real model and, where applicable, its speech providers. Ask an incomplete question, supply the missing detail, correct it and verify the resulting task. Disconnect or interrupt during unfinished work. Inspect both what the user hears or reads and what the service actually records.

What if Rasa accepted the request before the user interrupted?

Discarding its speech does not undo an accepted request. The Pipecat ordering control starts the old request, interrupts, then supplies another question. It checks that both dialogue requests run in order and only the new answer reaches output. Those scripted requests perform no financial action.

For a financial-service task, inspect the underlying operation as well as the explanation. Decide how to look up an accepted operation’s status and explain it after reconnect. Test that recovery with your domain service before adding retries: a local callback cancellation gives you only the local outcome.

You can keep a strong retrieval service, research crew or voice interface and improve how users reach it. Use the Rasa quickstart for the local conversation setup. Start with the investment that already matters, connect one useful task, and verify the boundaries its user journey crosses.