Guide · AI product engineer
Help customers prepare a claim with CrewAI and Rasa
Use CrewAI to research the documents needed for an insurance claim, and Rasa to ask customers questions and explain the checklist.
Key takeaways (3)
- Let CrewAI research the checklist and Rasa explain it to the customer.
- Check the source and task results before accepting a checklist.
- Return tool failures in the form CrewAI can recognise.
A customer wants to know which documents they need for an insurance claim. This tutorial uses an existing CrewAI research crew to prepare the checklist. Configure Rasa to ask which kind of claim they have and explain the checked result.
What happens when the crew cannot read its source? In our test, the tool returns
“Synthetic source temporarily unavailable (code: source_unavailable)” as plain text. The crew still finishes,
and this application accepts its checklist. Selecting the RAISE failure policy
leaves that result unchanged.
The missing piece is the tool’s return type. A plain error string supplies no
native failure record. Return the same wording as a ToolFailure and WARN
records it; RAISE aborts. The six-case native exercise
keeps the source error wording fixed and varies representation and policy.
| Source tool returns | Policy | Recorded failures | Native outcome | This application’s handoff |
|---|---|---|---|---|
| Plain error string | IGNORE | 0 | Both tasks finish | Ready |
| Plain error string | WARN | 0 | Both tasks finish | Ready |
| Plain error string | RAISE | 0 | Both tasks finish | Ready |
| Declared failure | IGNORE | 0 | Both tasks finish | Ready |
| Declared failure | WARN | 1 | Both tasks finish | Incomplete |
| Declared failure | RAISE | No returned result | ToolExecutionFailedError | No crew result |
Each case observes one actual native source-tool call. The error wording and scripted source/checklist replies stay fixed. The experiment inspects SDK execution and this application’s acceptance rule. Its narrow public fixture makes the source failure repeatable without a provider request.
Before giving existing tools another customer channel, check how they report business failures. A readable error message alone leaves this failure flag empty. Keep the useful handler, adapt its unsuccessful outcomes to the native failure type, and then apply your evidence checks.
If you already have a useful research crew, keep it. This example adds a Rasa conversation around a HarborCover document checklist. CrewAI keeps the research roles, registered source tool and model. Mantle asks which public claim type the user needs, passes their question to the crew and explains the checked result. The fictional sources contain vehicle and travel document lists.
Keep the researcher and checklist writer
The existing crew
has two native Agent objects and two sequential Task objects. The source
researcher calls read_claim_source. The checklist writer receives the source
task as context and returns the required document names. Each task has a typed
JSON output.
| Responsibility | Owner | What stays there |
|---|---|---|
| Research and document list | CrewAI | Agents, tasks, source tool, configured model and task records |
| Missing input and explanation | Rasa Mantle | Ask for the public claim type, call the application tool and explain the checked result |
| Accepted evidence | Application | Bind the selected type, inspect task records and failures, validate the returned source and checklist |
The live worker uses the crew’s configured model. Native controls replace its
responses with a scripted BaseLLM, so the failure is repeatable without a
provider request. Both paths construct the same research crew.
- A final answer can exist alongside a failed source read. Inspect the native records before accepting its checklist.
- Incomplete research remains a conversation outcome. The user can retry or stop instead of receiving an invented document list.
Inspect the records, then accept the checklist
The native representation control returns either the failure object’s message or the object itself. It replaces both the agent and task tool registrations, then observes the invoked handler. The observations confirm that each matrix case invokes its selected handler once, with the same error wording.
The conversation adapter uses declared failures with WARN. The native record
survives completion, allowing the application to withhold incomplete research.
The matrix’s RAISE arm aborts with a native exception. If your crew uses
that policy, keep the worker’s exception-to-incomplete mapping. With a legacy error string, first repair the tool’s
failure signal: a guard reading an empty failure list has nothing to reject.
The acceptance function first checks the selected public type and recorded failures:
Prefer: Intact application guard
if claim_type not in PUBLIC_SOURCES or result.has_tool_failures:
return incompleteThe failed run returns incomplete.
Avoid: One guard removed by the control
if claim_type not in PUBLIC_SOURCES:
return incompleteThe same failed run returns ready.
It then requires exactly the source and checklist task records. Both must
parse into their expected schemas, with extra fields refused. The source ID,
revision and document list must match the selected public fixture. The
checklist must preserve that same claim type and document list.
This contract is deliberately small. If you adapt an existing crew, define the evidence your application needs from each task. A customer-service handoff might need source references, a timestamp and explicit missing evidence. Keep those checks in application code before the result reaches the reply.
The mutation test
removes only or result.has_tool_failures from this application’s function.
The failed native result still passes the other checks and becomes ready.
That makes the failure check’s role visible. It also shows why adding another
instruction to the conversation would leave the acceptance rule unchanged.
Keep the two runtimes separately pinned
These selected releases need different PyJWT versions. CrewAI requires
>=2.15,<3; Rasa Pro requires >=2.13,<2.14. The combined dependency lock
cannot satisfy both. The example therefore has two locked Python environments.
Mantle imports a small standard-library process bridge. It starts a fixed crew worker and sends the claim type and question as JSON on stdin. The worker executes the native crew in its own environment and returns one JSON result. The bridge checks the selected public source and exact checklist again before passing it to Mantle.
Customer text stays in the JSON input. The command, worker file and interpreter are application settings. The worker’s subprocess timeout is shorter than the Mantle tool allowance. Errors, malformed output and a timeout return an incomplete result.
The source protocol controls exercise a fixed command, invalid inputs, malformed output and timeout handling. Two controls also start the actual native fixture worker, including its failed-source path.
Let Rasa collect the missing choice
The Mantle skill
asks for vehicle or travel and calls select_claim_type. Application state
binds that choice to the conversation. The actual decorated binding controls
reach the worker, keep mocked caller selections separate and withhold the
recorded source failure. The research tool then accepts only the
question; it gets the selected type from that mapping.
If the choice is missing, the tool returns needs_claim_type. If a different
type is selected later, it returns claim_type_changed. Start another
conversation for the other public example. A ready result supplies the source
ID, revision and required document names for Mantle to explain. An incomplete
result gives the conversation a recovery path.
The example uses process-local sender mapping. It does not authenticate an insurance customer. Add your identity and access controls before connecting private claim data; the supplied corpus is public and synthetic.
Keep filing, approval and payment operations outside this read-only research handoff. If your existing crew has write tools, give those operations their own authorisation and confirmation rules before exposing them to customers.
The failure signal is a typed native result in the pinned
ReadClaimSource._run:
def _run(self):
if self.unavailable:
return ToolFailure(
message="Synthetic source temporarily unavailable",
code="source_unavailable",
retryable=True,
)
return PUBLIC_SOURCES[self.claim_type].model_dump_json()
Return the ToolFailure object so the framework records the failed tool.
Returning only its message changes the native signal to ordinary text.
The ready-result acceptance check still checks the final checklist separately.
The Mantle tool looks up the previously selected type through the current sender. Its research argument contains only the question:
selected = SESSIONS.get(sender())
if selected is None:
return ToolResult(llm_response={"status": "needs_claim_type"})
return ToolResult(
llm_response=await asyncio.to_thread(WORKER.checklist, selected, question)
)
This is an excerpt of research_claim_documents. The public exercise stores
its selected vehicle or travel type outside the model-supplied research input.
The existing worker retains its crew, tools and task sequence.
Run the crew on its own, then add the conversation
Start with the Rasa quickstart if Mantle is new to you. Use Python 3.12 and the supplied locks. From a fresh checkout:
git clone https://github.com/RasaHQ/rasa-community-resources.git
cd rasa-community-resources
git fetch origin 413e46b3b596c2a96b8495ad6730020a90bb747f
git checkout 413e46b3b596c2a96b8495ad6730020a90bb747f
python3 scripts/workspace.py start tutorials/frameworks-with-rasa/crewai --owner claim-crew-demo
cd tutorials/frameworks-with-rasa/crewai
make test
.venv/bin/python demo_completion.py
.venv/bin/python demo_failure_signals.py
The 11 October scripted qualification ran the core suite from the
crewai-mantle directory, using the separate CrewAI interpreter. This is the
recorded command; set up both projects as described here before repeating it:
CREWAI_TELEMETRY_DISABLED=true OTEL_SDK_DISABLED=true ../crewai/.venv/bin/python -m unittest discover -s ../crewai/tests -p 'test_*.py'
Its literal recorded summary was:
Ran 25 tests in 0.216s
OK
The same dated scripted run continued from crewai-mantle with the transport
protocol controls. This is a separate literal command and output excerpt:
CREWAI_TELEMETRY_DISABLED=true OTEL_SDK_DISABLED=true .venv/bin/python -m unittest discover -s ../crewai-transport/tests -p 'test_*.py'
Ran 7 tests in 3.081s
OK
Finally, it exercised the actual decorated Mantle binding:
CREWAI_TELEMETRY_DISABLED=true OTEL_SDK_DISABLED=true .venv/bin/python -m unittest discover -s tests -p 'test_*.py'
Ran 4 tests in 4.555s
OK
These offline tests make zero provider model requests. Each excerpt retains its own recorded timing; warnings and per-test output lie outside the excerpts.
Expected output from demo_completion.py, as defined by the pinned fixture:
these are fixture expectations, not an independently preserved command receipt.
{"final_output_present": true, "handoff_status": "ready", "recorded_tool_failures": 0, "source_unavailable": false, "tasks": ["source", "checklist"]}
{"final_output_present": true, "handoff_status": "incomplete", "recorded_tool_failures": 1, "source_unavailable": true, "tasks": ["source", "checklist"]}
From the crew directory, inspect the native failure code yourself:
.venv/bin/python - <<'PY'
from claim_crew.native_fixture import create_fixture
result = create_fixture("vehicle", unavailable=True).kickoff()
print(result.tool_failures[0].failure.code)
PY
The native control checks this same code: source_unavailable. The completed
tasks remain present; it is the recorded failure that makes research incomplete.
The failure-signal exercise prints six JSON records matching the opening table.
Inspect tool_calls and observed_results as well: each case ran the source
tool once, with the same error wording. The plain-string raise case still
finishes and records zero failures.
Start the selected Rasa environment from the repository root while keeping the crew environment available for its worker:
cd ../../..
python3 scripts/workspace.py start tutorials/frameworks-with-rasa/crewai-mantle --owner claim-conversation-demo
cd tutorials/frameworks-with-rasa/crewai-mantle
make test
make validate
Export your own RASA_PRO_LICENSE and OPENAI_API_KEY using the companion
.env.example as the variable list. CREW_MODEL selects the existing crew’s
model; Mantle uses its separate integrations.yml configuration. Each model
makes its own provider requests. Then train and start the local REST channel:
make train
make run
In another terminal, try this reader exercise:
curl -sS http://localhost:5019/webhooks/rest/webhook \
-H 'Content-Type: application/json' \
-d '{"sender":"claim-demo","message":"What documents do I need?"}'
curl -sS http://localhost:5019/webhooks/rest/webhook \
-H 'Content-Type: application/json' \
-d '{"sender":"claim-demo","message":"Use the vehicle example"}'
Keep the sender when supplying the missing choice. Check for HC-V-101, its
revision and the two vehicle document names. Use another sender for travel and
check HC-T-201. Record whether an incomplete result leads to a retry or stop,
and whether the response stays within the read-only checklist task.
Qualification covers the native crew, process bridge and actual decorated Mantle bindings. Licensed project validation, training and a cold archive load also passed. The native tests use scripted responses; run and assess your configured models when adapting the reader exercise.
Stop your own server and finish worker operations. From the repository root, retire both selected environments with the same owners used at creation:
python3 scripts/workspace.py retire tutorials/frameworks-with-rasa/crewai-mantle --owner claim-conversation-demo --inactive
python3 scripts/workspace.py retire tutorials/frameworks-with-rasa/crewai --owner claim-crew-demo --inactive
For a worker built as a graph, the LangGraph conversation guide shows how to keep its workflow state across follow-up questions.