- Building a tool-using agent with
instruct()andModelOption.TOOLS - Enforcing structured output with requirements and a retry budget
- Inspecting
SamplingResultto understand failures - Detecting harmful outputs with
guardian.guardian_check - Grounding safety checks against retrieved context
pip install mellea, Ollama running locally with granite4.1:3b downloaded.
Step 1: A simple tool-using agent
Start with two tools — a search stub and a calculator — and wire them into aninstruct() call:
Sample output
Note: LLM output is non-deterministic. The model may call one or both tools; the answer will reference whichever tool results it received.
ChatContext is required here so tool result messages persist between the first
instruct call and the follow-up. Without it, the session uses SimpleContext,
which discards history between calls and the model answers as if no tools ran.
Step 2: Adding output requirements
Require the agent to format its answer as a short structured response:Sample output
Note: With the 50-word requirement enforced, the response will be concise and answer both questions. Wording varies by model and temperature.Requirements are placed on the synthesis call (the follow-up
instruct) rather
than the tool-call step — at the tool-call step the model returns structured
tool invocations, not the final answer, so applying requirements there would
validate the wrong output. The word-count check runs deterministically. The
“answer both questions” requirement falls back to LLM-as-a-judge. If either
fails, Mellea retries with the failure reason embedded in the repair request.
Step 3: Inspecting failures and handling a retry budget
UseRejectionSamplingStrategy with return_sampling_results=True to observe
what happens when requirements fail:
Sample output
Sample output
sampling.success is True when at least one attempt satisfied all requirements.
sampling.sample_generations gives you every attempt in order — useful for
debugging or for choosing the best available output when the budget runs out.
Step 4: Adding Guardian harm detection
Guardian intrinsics evaluate the output against specific risk criteria. Run them after your agent responds to flag outputs before they reach downstream code.Sample output
Note: Guardian intrinsics load LoRA adapters and requireScores are floats between 0.0 (safe) and 1.0 (risk detected); 0.5 is the threshold. The available criteria are:LocalHFBackend. They cannot run againstOllamaModelBackend. The main agent and the Guardian checks can use different backends — only the Guardian calls needLocalHFBackend.
"harm", "jailbreak", "social_bias",
"profanity", "violence", "unethical_behavior", "groundedness",
"answer_relevance", "context_relevance", and "function_call".
Note: If you were usingGuardianRisk.SEXUAL_CONTENTfrom the old API, there is no direct equivalent key inCRITERIA_BANK. Use a custom free-text criteria string instead — see Custom criteria in the Guardian how-to guide.
Step 5: Running multiple Guardian checks with a shared backend
When running several Guardian criteria checks, instantiateLocalHFBackend once
and pass it to each call to avoid reloading the model weights for every check:
Sample output
Note: Guardian scores are deterministic for a given response string, but the agent’s answer varies by model and temperature, so exact scores will differ across runs.The available criteria are:
"harm", "jailbreak", "social_bias",
"profanity", "violence", "unethical_behavior", "groundedness",
"answer_relevance", "context_relevance", and "function_call".
Note: If you were usingGuardianRisk.SEXUAL_CONTENTfrom the old API, there is no direct equivalent key inCRITERIA_BANK. Use a custom free-text criteria string instead — see Custom criteria in the Guardian how-to guide.
Step 6: Groundedness checks with retrieved context
When your agent retrieves documents before answering, userag.flag_hallucinated_content to confirm the response is faithful to what was
retrieved rather than hallucinated:
Sample output
Tip: Pass the same text as bothgrounding_contexttoinstruct()and as theDocumenttoflag_hallucinated_content. This ensures the faithfulness check evaluates the response against exactly what the agent was given.
Step 7: A ReACT agent with Guardian checks
For goal-driven agentic loops, combinereact() with Guardian validation. The
react() function is an async built-in that runs the Reason-Act loop until the
goal is reached or the step budget is exhausted:
Sample output
Note: LLM output is non-deterministic. Thereact()loop runs sequentially — each turn awaits the model before proceeding, soChatContextis safe to use here despite the async execution. The agent callsweb_searchto ground its answer, then calls the built-infinal_answertool to signal completion. The Guardian harm check runs after the loop usingLocalHFBackendwith theguardian-coreLoRA adapter. Your exact wording will vary.
Advanced:react()implements the Reason + Act loop: the LLM alternates between producing a reasoning step (“Thought”) and invoking a tool (“Action”) until it determines the goal is satisfied or the step budget runs out. You can inspect the intermediate steps via the second return value (the trace list). For fine-grained control over each reasoning step, build a custom loop usingm.instruct()withModelOption.TOOLSdirectly.
What you built
A progression from a basic tool-using agent to a safety-validated, grounded agentic system:See also: The Requirements System | Tools and Agents