Guava.Agent behaviour (Guava v0.34.0)

Copy Markdown View Source

Behaviour for a voice agent, in the spirit of GenServer/Phoenix.LiveView: use Guava.Agent, give it a persona, and implement only the callbacks you need.

Each call gets its own process; your callbacks thread a per-call state value (like a LiveView socket's assigns) and return standard tuples. Use the Guava.Call handle passed to each callback to drive the conversation (set_task, transfer, get_field, …).

Example

defmodule MyAgent do
  use Guava.Agent, name: "Nova", organization: "Acme", purpose: "Answer questions."

  @impl true
  def init(_call_info), do: {:ok, %{}}

  @impl true
  def handle_question(_call, question, state), do: {:reply, MyKB.answer(question), state}

  @impl true
  def handle_action("sales", call, state) do
    Guava.Call.transfer(call, "+14155550100")
    {:noreply, state}
  end
end

Attach it to a channel with Guava.Channel (see its docs) or Guava.run/1.

Callbacks

All callbacks are optional; a persona-only agent is valid. Callbacks that produce a reply to the Dialog System return {:reply, value, state}; the rest return {:noreply, state}.

  • init(call_info){:ok, state} — build per-call state (default %{})
  • agent_info(call_info) → keyword persona (defaults to the use options)
  • handle_call_received(call_info):accept | :decline (default :accept)

  • handle_start(call, state)
  • handle_caller_speech(call, event, state) / handle_agent_speech(call, event, state)
  • handle_question(call, question, state){:reply, answer, state}
  • handle_action_request(call, request, state){:reply, suggestion, state} where suggestion is a Guava.SuggestedAction, a list of them, or nil
  • handle_action(action_key, call, state) — one callback; pattern-match the key
  • handle_task_complete(task_id, call, state) — one callback; pattern-match the id (a reach_person/3 task completes as handle_task_complete("reach_person", …))
  • handle_search_query(field_key, call, query, state){:reply, {matched, other}, state}
  • handle_dtmf(call, event, state) / handle_escalate(call, state)
  • handle_outbound_failed(call, event, state) / handle_session_end(call, event, state)
  • handle_info(msg, call, state) — external mid-call messages (e.g. results of a spawned Task)
  • terminate(reason, call, state)

Summary

Types

call()

@type call() :: Guava.Call.t()

state()

@type state() :: term()

Callbacks

agent_info(t)

(optional)
@callback agent_info(Guava.CallInfo.t()) :: keyword()

handle_action(t, call, state)

(optional)
@callback handle_action(String.t(), call(), state()) :: {:noreply, state()}

handle_action_request(call, t, state)

(optional)
@callback handle_action_request(call(), String.t(), state()) ::
  {:reply, Guava.SuggestedAction.t() | [Guava.SuggestedAction.t()] | nil,
   state()}

handle_agent_speech(call, t, state)

(optional)
@callback handle_agent_speech(call(), Guava.Events.AgentSpeech.t(), state()) ::
  {:noreply, state()}

handle_call_received(t)

(optional)
@callback handle_call_received(Guava.CallInfo.t()) :: :accept | :decline

handle_caller_speech(call, t, state)

(optional)
@callback handle_caller_speech(call(), Guava.Events.CallerSpeech.t(), state()) ::
  {:noreply, state()}

handle_dtmf(call, t, state)

(optional)
@callback handle_dtmf(call(), Guava.Events.DTMFPressed.t(), state()) ::
  {:noreply, state()}

handle_escalate(call, state)

(optional)
@callback handle_escalate(call(), state()) :: {:noreply, state()}

handle_info(term, call, state)

(optional)
@callback handle_info(term(), call(), state()) :: {:noreply, state()}

handle_outbound_failed(call, t, state)

(optional)
@callback handle_outbound_failed(call(), Guava.Events.OutboundCallFailed.t(), state()) ::
  {:noreply, state()}

handle_question(call, t, state)

(optional)
@callback handle_question(call(), String.t(), state()) :: {:reply, String.t(), state()}

handle_search_query(t, call, t, state)

(optional)
@callback handle_search_query(String.t(), call(), String.t(), state()) ::
  {:reply, {[String.t()], [String.t()]}, state()}

handle_session_end(call, t, state)

(optional)
@callback handle_session_end(call(), Guava.Events.BotSessionEnded.t(), state()) ::
  {:noreply, state()}

handle_start(call, state)

(optional)
@callback handle_start(call(), state()) :: {:noreply, state()}

handle_task_complete(t, call, state)

(optional)
@callback handle_task_complete(String.t(), call(), state()) :: {:noreply, state()}

init(t)

(optional)
@callback init(Guava.CallInfo.t()) :: {:ok, state()}

terminate(term, call, state)

(optional)
@callback terminate(term(), call(), state()) :: any()