Skip to main content

Tools

OpenCouch tools are app-owned capabilities exposed to model runtimes. Text exposes them as OpenAI Agents SDK function tools. Voice exposes a subset as OpenAI Realtime function schemas. Both call the same backend service functions so memory, lookup, crisis resources, therapeutic skill loading, and guided exercises behave consistently across surfaces.

Tools are deliberately narrow because every tool can change state, surface private context, add latency, or expand the safety surface. A new tool needs a clear owner, a structured result, explicit failure semantics, and tests for the service contract.

Transport bindings

BindingUsed byWhere it lives
SDK function toolsText runtime specialists and safe-turn branchesagent/tools/*.py via @function_tool builders
Realtime function schemasBrowser voice sessionsagent/voice/tools.py
Shared service functionsText and voiceexecute_*_tool(...), memory-control services, guided-exercise services, grounded lookup services

The model-facing schemas differ by transport, but the side effects and state validation stay backend-owned.

TOOLSagent-invokable capabilities
5active
0planned
patternTools in OpenCouch are app-owned. Text binds them as SDK function tools; voice binds a subset as Realtime function schemas. Both transports call the same backend services for state mutation, memory reads, grounded lookup, crisis resources, and exercise progress.
triggerordinary non-crisis therapeutic reply needs response-style guidance

Loads prompt-ready guidance for one response style and optional therapeutic approach. The tool is side-effect-free and gives the model the exact local skill context it should use before drafting the reply.

Pipeline
01Select style
systemText uses specialist/runtime state; voice uses Realtime instructions and tool descriptions.
use_search=falsetemp=0
producesresponse_style + therapeutic_approach
on failurefalls back to ordinary supportive guidance only when locally allowed
02Render skill context
systemRender the response-style prompt fragment and approach overlay from reviewed local sources.
use_search=falsetemp=0
producesTherapeuticResponseSkillToolResult
on failureschema/tool error surfaces to the caller
Writes to
context.tool_results.therapeutic_response_skillresponse_styletherapeutic_approach
On failureThe tool has no side effects. Invalid styles or schema failures surface instead of silently loading unrelated guidance.
agent/tools/therapeutic.py::execute_therapeutic_response_skill_tool
next toolfuture candidates: structured assessment lookup, clinician-reviewed resource directory, session pacing and availability stubs

Tool vs gate vs dependency

Three things often look like "tools" but should be modeled differently in OpenCouch:

TypeExampleLives on
Injected dependencymemory_store, crisis_log_backend, embedding_provider, llm_clientWorkflowContext (frozen dataclass)
Gate / policy stagecrisis guardrail, text turn triage, Realtime voice session policyagent/guardrails/, agent/specialists/triage.py, agent/voice/policy.py
Toollookup_crisis_resources, answer_grounded_lookup, memory-control actions, guided-exercise skill loadingagent/tools/ plus Realtime schemas in agent/voice/tools.py

A capability qualifies as a tool when:

  1. It is conditionally invoked by a model runtime or transport policy.
  2. It returns structured data that the model or runtime uses to continue the turn.
  3. It either reaches outside the local model context, reads private app-owned state, or mutates app-owned state.
  4. It has explicit retry, failure, and empty-result semantics.

If a capability is used on every turn (like the memory store) or fails hard when unavailable (like the database), it's a dependency, not a tool. If it only decides where to route the turn without producing user-facing content, it's a gate or policy stage, not a tool.

Current tool families

FamilyText bindingVoice bindingNotes
Therapeutic response skillsload_therapeutic_response_skillload_therapeutic_response_skillSide-effect-free prompt-local style guidance.
Memory controlSDK memory toolsRealtime persistent-only memory toolsList/status/recall/preference/delete flows with confirmation gates for destructive actions.
Grounded lookupanswer_grounded_lookupanswer_grounded_lookupExplicit factual/current/source-backed requests only.
Crisis resourceslookup_crisis_resources, get_crisis_support_templatelookup_crisis_resourcesSpecific crisis resources must come from verified lookup results.
Guided exercisesDiscovery, skill-load, and progress toolsSame core tool set filtered for voice-suitable exercisesUses the shared exercise catalog and local state validation.

Adding a new tool

  1. Create or extend the shared service function under agent/tools/ or the relevant plain service package. Return a Pydantic result or a plain dict with status fields for empty/no-result cases.
  2. Add an SDK binding when text specialists need the capability.
  3. Add a Realtime schema in agent/voice/tools.py only if voice needs the capability.
  4. Put side-effect rules in the service layer, not in prompt text. Mark non-idempotent actions with clear retry semantics.
  5. Extend runtime state only for data that must survive the turn or be visible in diagnostics.
  6. Add focused unit tests for the shared service and transport binding. LLM-calling behavior belongs in live evals or dogfood checks.