> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corti.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a conversational agent

> Forward every utterance to a Corti agent without a wake phrase, using speculative prefetch for fast responses

<Warning>
  **Beta.** This is an experimental pattern under active development. The speculative prefetch approach chains several real-time behaviors — interim results, debounced finalization, and stateless prefetch calls — so response quality and timing can vary. Treat it as a starting point for exploration rather than a production-ready recipe, and [reach out](mailto:help@corti.ai) to discuss your use case or share feedback.
</Warning>

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/0I2LVQsyemg?start=224&end=403" title="Build a Conversational Agent — walkthrough" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen />

<Info>
  **Follow along with the runnable example.** This guide walks through the [conversational agent example](https://github.com/corticph/corti-examples/tree/main/sdk/typescript/applets/src/applets/conversational-agent) in `corti-examples` — bootstrap your build from it or use it as reference.
</Info>

Unlike the [wake-command agent](/stt/guides/agent-wake-command), a **conversational agent** needs no trigger phrase. The mic runs continuously and every completed utterance is forwarded to the agent. The core UX technique is **speculative prefetch**: the agent fires a stateless request while the user is still speaking so the response is often ready the instant they stop.

<Note>
  This builds on the agent lifecycle from [On-Demand Agent](/stt/guides/agent-on-demand) and the threading pattern from [Wake-Command Agent](/stt/guides/agent-wake-command).

  The prompts used here are mere examples - be sure to test and refine the prompt to support your specific needs.
</Note>

***

## Configure for always-on capture

Enable interim results so transcripts arrive while the user is still speaking. Disable automatic punctuation to reduce noise in partial text, and enable audio events so the session can detect silence. See the [conversational agent example](https://github.com/corticph/corti-examples/tree/main/sdk/typescript/applets/src/applets/conversational-agent) in `corti-examples` for the full `dictationConfig` and event wiring.

***

## Fire a speculative request on interim text

On the first interim of each turn, schedule a short debounce (200 ms) then fire a **stateless** agent call — no `contextId`, so the real conversation thread is never touched. If the user keeps speaking, the timer resets and a fresh call fires with the updated text.

```ts title="JavaScript" theme={null}
let speculativeTimer: ReturnType<typeof setTimeout> | null = null;
let heldResponse: string | null = null;

function handleInterim(text: string) {
  if (speculativeTimer) clearTimeout(speculativeTimer);
  speculativeTimer = setTimeout(() => {
    speculativeTimer = null;
    void fireSpeculative(text);
  }, 200);
}
```

`fireSpeculative` sends the text to the agent without a `contextId` and stores the result in `heldResponse`. See the [conversational agent example](https://github.com/corticph/corti-examples/tree/main/sdk/typescript/applets/src/applets/conversational-agent) in `corti-examples` for the full implementation.

<Tip>
  The speculative call is intentionally stateless. Sending partial interim text (e.g. "patient April") with a `contextId` would corrupt the real thread — the provisional response is shown for perceived speed, then replaced in-place by the real contextual response.
</Tip>

***

## Debounce finals and flush the turn

Buffer consecutive final segments and restart a silence timer on every new transcript event. When speech genuinely pauses — the timer fires without interruption — treat it as a complete turn and call `handleFinal`.

```ts title="JavaScript" theme={null}
const RESPONSE_DEBOUNCE_MS = 1500; // configurable; 0.5–3.0 s range works well
let finalBuffer: string[] = [];
let debounceTimer: ReturnType<typeof setTimeout> | null = null;

dictation.addEventListener("transcript", (e: CustomEvent) => {
  const { text, isFinal } = e.detail.data;
  if (!isFinal) {
    handleInterim(text);
    resetDebounce();
  } else {
    finalBuffer.push(text);
    resetDebounce();
  }
});

function resetDebounce() {
  if (debounceTimer) clearTimeout(debounceTimer);
  debounceTimer = setTimeout(flushFinal, RESPONSE_DEBOUNCE_MS);
}

function flushFinal() {
  const text = finalBuffer.join(" ").trim();
  finalBuffer = [];
  if (text) void handleFinal(text);
}

// Also flush immediately when recording stops — no artificial delay at end of session.
dictation.addEventListener("recordingstop", () => {
  if (debounceTimer) clearTimeout(debounceTimer);
  flushFinal();
});
```

***

## Commit the turn with the real context

When `handleFinal` fires, cancel any pending speculative timer. If `heldResponse` is set, display it optimistically and then replace it in-place with the real contextual response. If not set, show a thinking spinner while the contextual call completes. The real `messageSend` always uses `contextId` to keep the thread coherent — pass the current one and update it from the response. See the [conversational agent example](https://github.com/corticph/corti-examples/tree/main/sdk/typescript/applets/src/applets/conversational-agent) in `corti-examples` for the full implementation.

<Note>
  See [Context & Memory](/agentic/context-memory) for how threads work in the Agentic Framework. A `404` on `messageSend` means the agent or context was deleted — recreate the agent and retry the turn without the stale `contextId`.
</Note>

***

## Reset the conversation

To start over, call `client.agents.deleteContext(agentId, contextId)` to remove the server-side thread, then clear `contextId`, `heldResponse`, `finalBuffer`, and the message list.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Wake-Command Agent" icon="mic" href="/stt/guides/agent-wake-command">
    Gate the agent behind a spoken wake phrase instead of forwarding every utterance.
  </Card>

  <Card title="On-Demand Agent" icon="robot" href="/stt/guides/agent-on-demand">
    The agent lifecycle and message API, for a single isolated pass.
  </Card>

  <Card title="Context & Memory" icon="brain" href="/agentic/context-memory">
    How conversation threads and memory work in the Agentic Framework.
  </Card>

  <Card title="Example code" icon="github" href="https://github.com/corticph/corti-examples/tree/main/sdk/typescript/applets/src/applets/conversational-agent">
    The complete, runnable conversational agent example in `corti-examples`.
  </Card>
</CardGroup>

<Note>Please [contact us](mailto:help@corti.ai) for help or questions.</Note>
