Skip to main content
Version: 1.0.0

Agents and outbound effects

An agent can respect its capabilities while producing incorrect content. Permission to send an email is not business authority to promise a payment. Model instructions therefore do not replace a check before the effect.

Make checks unavoidable​

Use this path: required evidence, agent proposal, deterministic policy, human approval when needed, then the effect. Do not give the agent the sending tool or make the policy check an optional capability it can skip.

This example deliberately uses a simple, testable policy: only one preapproved reply template is allowed. It is not a general prompt-injection detector.

support/outbound.tb
module support.outbound

tool ReadCase
description "Read the submitted case without modifying it."
takes ticket as Text
gives Text

tool SendReply
description "Send an approved reply to the customer."
takes reply as Text
gives Text
requires network

function IsApprovedTemplate
takes draft as Text
gives Boolean
do
give draft == "We received your request and will review it."

agent DraftReply
takes caseText as Text
gives Text
responsibility "Draft a support reply from the case. Do not promise payments."
callable_by strategy Reply

strategy Reply
takes ticket as Text
gives Text
uses ReadCase
uses DraftReply
uses SendReply
do
ReadCase
with ticket as ticket
into caseText
DraftReply
with caseText as caseText
into draft
IsApprovedTemplate
with draft as draft
into allowed
if allowed == false
give "blocked_by_policy"
human.approve
with context as draft
into approval
SendReply
with reply as draft
into sent
give sent

The strategy reads the case before invoking the agent. This guarantees collection, not the model's comprehension. The pure function refuses every other wording, including payment promises. For richer policies, request a typed proposal and validate amounts, recipients and permissions against trusted business data. A word blacklist is not a substitute for that policy.

The ReadCase handler must genuinely have no effect and use effect_contract.kind: "none". SendReply needs a reviewed network grant, an external_write contract and an effect journal. Implement it with the SDK and service-side idempotency. See idempotent effects. The agent cannot choose SendReply or remove steps from the strategy.

Verify the workflow​

  • Approved content and human approval: one expected send for this operation.
  • A payment promise: blocked_by_policy, no send.
  • Allowed content followed by rejection or expiry: no send.
  • Provider attempts to select SendReply directly: authority refusal.
  • Resume the same operation after a crash: check the journal, receipt and service-side deduplication instead of creating a new replacement run.
  • Missing case or read error: never reach sending.

Inspect isolated handler effects, not only run status. Test business rules separately from statistical model quality. For incident runbooks, likewise place required log reads and checks in the deterministic path before the decision and any restart.

Applying these rules to other processes​

ProcessTouba compositionHost responsibility
Expense claimscategory enum, pure threshold function with else if, successive approvals before paymentsigner identity and separation, transactional cumulative budget, idempotent payment
Customer supportnested if for plan/priority rules, Optional of Customer for missing recordscustomer-data quality and content checks before sending
Reviewing a listfor with per-occurrence checks and approvalstrack decisions and external effects, without reusing one approval for the whole list
Incident responsedeterministic reads and policy, restricted diagnostic agent, approval before restarthealth checks, operational authorization and restart reconciliation

Two approval instructions require two decisions, not necessarily two distinct people. A while remains bounded by the iteration budget; composing loops does not allow unbounded execution. Use the SDK testing helpers for success and refusal cases, then independently inspect effects in test services.

Responsibilities by layer​

LayerResponsibility
Modelpropose content or an action; quality remains an empirical question
Business codevalidate content, evidence and business rules
Touba runtimeenforce authority, limits, contracts, human barriers and traces
Host and external serviceauthenticate people, perform effects, implement idempotency and reconciliation

Here human.approve is mandatory. A model-proposed request_human_review is discretionary: it cannot by itself guarantee a review required by policy. See the server API for private approval context and decision/resume correlation.