Skip to content

Overview

Loquent agents use tools in two contexts: voice tools run during live phone calls via the realtime API, and task tools run after calls when the todo execution agent processes approved tasks.

Used by the realtime voice model during a live call. Registered at session start via collect_agent_tools, dispatched through handle_tool_call. Results are spoken to the caller.

ToolWhen RegisteredParameters
end_callAlwaysNone
query_knowledgeAgent has linked knowledge basesquery: String
lookup_callerOrg has client_lookup_url configuredNone (phone injected)
transfer_callAgent has configured transfer numbersphone_number: String (enum-restricted)

Definition type: AgentToolDefinition — provider-agnostic, converted to OpenAI/Gemini formats at session start.

pub struct AgentToolDefinition {
pub name: String,
pub description: String,
pub parameters: serde_json::Value, // JSON Schema
}

Used by the todo execution AI agent (GPT-5) when processing approved todos. Registered per-todo based on the TodoType’s assigned tools. Run in a standard chat completion agentic loop, not a realtime session.

ToolWhat It Does
send_email_to_callerSend a follow-up email to the caller (recipient auto-resolved from contact)
send_emailSend an email to any recipient (AI determines recipient from task context)

Definition type: aisdk::Tool — includes an executable closure, capped at 10 tool-calling steps per todo.

Session start → collect_agent_tools(agent_id, org_id, tool_config)
→ build_end_call_tool_definition() → (definition, prompt_section)
→ build_knowledge_tool_definition(agent_id) → Option<(definition, prompt_section)>
→ build_client_lookup_tool_definition(org_id) → Option<(definition, prompt_section)>
→ build_transfer_call_tool_definition(tool_config) → Option<(definition, prompt_section)>
→ Assemble all prompt sections into "## Available Tools" markdown
→ Return (Vec<AgentToolDefinition>, prompt_addendum)
During call → Model invokes a tool
→ handle_tool_call routes by function_name
→ Handler executes and returns result string
→ Result sent back to realtime model as tool response

Each tool builder returns a tuple: (AgentToolDefinition, String). The AgentToolDefinition is the structured schema sent to the AI provider. The String is a markdown section with behavioral rules for that tool.

collect_agent_tools assembles all prompt sections into a single ## Available Tools block with ### Subsection per tool, then appends it to the agent’s system instructions. This gives the AI model clear behavioral guidance alongside the tool definitions.

Example prompt section (from end_call):

### Ending the Call
- When the conversation is naturally over, say your goodbye and IMMEDIATELY call `end_call`.
- Do NOT wait for the caller to respond after your farewell.
- Do NOT announce that you are ending the call — just say goodbye and invoke the tool.

Each tool defines its own rules: lookup_caller instructs proactive use at call start, query_knowledge requires the tool before answering knowledge questions, and transfer_call requires caller confirmation before transferring.

Todo approved → execute_todos_job picks it up (every 30s)
→ execute_todo(todo_id)
Load TodoType → get assigned SystemTools
Build aisdk::Tool for each
Run GPT-5 agentic loop (max 10 steps)
AI decides which tools to call and with what arguments

Both systems convert errors to strings and return them to the AI model. The conversation or task continues even if a tool fails. Errors are logged via tracing::error!.

  1. Create a builder returning Option<AgentToolDefinition>
  2. Call it in collect_agent_tools and push to the tools vector
  3. Add a match arm in handle_tool_call
  4. Implement the handler function
  1. Add a variant to the SystemTool enum
  2. Implement display_name(), description(), as_str(), from_str()
  3. Add a match arm in execute_todo to build the aisdk::Tool
  4. Implement the execution function

Current SystemTool variants: SendEmailToCaller, SendEmail.

  • Agent — owns the voice tool pipeline
  • Todos — owns the task tool pipeline