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

# Chat agent

> The LLM-facing layer — what it does, what it doesn't, and how Compass keeps it from making things up.

> The chat agent translates natural language into a structured plan, asks the user for any missing detail, and hands off. It never signs transactions, never bypasses the policy engine, and is constrained by a fabrication detector that catches plans referencing things that don't exist.

This is the only component in Compass that calls a language model. Everything
else — scheduling, evaluation, policy checks, signing — runs on deterministic
code. See [The deterministic loop](/architecture/deterministic-loop) for
what's on the other side of the handoff.

<img src="https://mintcdn.com/compassai/Qq7UDvjWweIau4hP/images/chat-agent-diagram.svg?fit=max&auto=format&n=Qq7UDvjWweIau4hP&q=85&s=deead29a2acc172fd5e89a4beb614c95" alt="chat agent diagram" className="rounded-lg" width="1500" height="800" data-path="images/chat-agent-diagram.svg" />

## What the chat agent does

The chat agent has three jobs, in order:

1. **Parse the user's intent.** Take a free-text message like `"move 5 USDC
   to the best lending venue on an L2"` and identify the action, amount,
   target protocol, and target chain.
2. **Fill in missing detail.** If the user named a protocol that's
   whitelisted on multiple chains, ask which chain. If the amount is
   ambiguous, ask. The agent never guesses on safety-relevant fields.
3. **Emit a structured plan.** Once everything is unambiguous, produce a
   JSON `Plan` object and hand it to the deterministic layer.

The agent stops at step 3. It does not call any contract, does not validate
the plan against policy (that's [policy engine](/architecture/policy-engine)
work), and does not write to the audit trail directly.

## The Plan schema

The chat agent's output is constrained to a fixed JSON schema. The LLM is
called with structured-output enforcement — it cannot return free-form text
when generating a plan, only valid `Plan` JSON or a clarification question.

```json theme={null}
{
  "action": "supply",
  "source_chain": "arc_testnet",
  "target_chain": "<l2_chain>",
  "target_protocol": "<lending_protocol>",
  "amount_usdc": "5000000",
  "user_message": "move 5 USDC to the best lending venue on an L2",
  "requires_user_confirmation": true
}
```

Three properties of this schema matter:

* **All enum fields are whitelisted.** `target_chain`, `target_protocol`,
  and `action` can only contain values the system recognizes. The LLM
  cannot fabricate a protocol name — the structured-output layer rejects
  it before it reaches the deterministic engine.
* **Amounts are strings, in micro-USDC.** No floating-point parsing risk;
  the LLM has to commit to an exact integer the user can verify.
* **`requires_user_confirmation` is always true for routes that move
  funds.** The chat agent cannot generate a "silent execute" plan even if
  the user asks for one.

## How fabrication is caught

Structured output prevents most LLM hallucination by construction — the
model can't return a protocol name that isn't in the enum. But it can still
generate plans that are *structurally valid but semantically wrong*. For
example:

* A plan to supply more USDC than the user has.
* A plan targeting a `(protocol, chain)` pair the user's policy doesn't
  whitelist.
* A plan with an `amount_usdc` that doesn't match what the user typed.

These pass schema validation but fail real-world checks. Compass has two
defenses:

### Fabrication detector

Between the LLM output and the handoff to the deterministic layer, a
detector runs a small set of semantic checks:

* **Reference check.** Every entity in the plan (protocol, chain, action)
  is verified against the live registry. Pairs that exist in the schema
  but not in the current deployment are flagged.
* **Quantity check.** `amount_usdc` is checked against the user's balance
  and against any number explicitly mentioned in `user_message`. A plan
  for "5 USDC" that emits `5000000000` (5000 USDC) is rejected.
* **Action check.** The `action` field is checked against what the
  `target_protocol`'s facet actually supports.

A failed detector check does not surface as an error to the user. Instead,
it triggers a **corrective re-prompt**.

### Corrective re-prompt

When the detector rejects a plan, the original user message is re-sent to
the LLM with the detector's failure injected as an additional instruction:

```
USER: move 5 USDC to the best lending venue on an L2
SYSTEM (correction): Previous output had amount_usdc=5000000000 which
       represents 5000 USDC, not 5. User asked for 5. Retry.
```

If the corrected plan still fails the detector, the agent surfaces the
problem to the user as a clarification question rather than guessing again.
The loop is capped at three attempts. This prevents the agent from quietly
producing wrong plans when the LLM is confused.

## Clarification turns

When the user's intent is incomplete, the chat agent does not fill in
defaults. Instead it emits a `Clarification` rather than a `Plan`:

```json theme={null}
{
  "type": "clarification",
  "asking_about": "target_chain",
  "options": ["<l2_chain_a>", "<l2_chain_b>"],
  "user_message_context": "move 5 USDC to the best lending venue on an L2"
}
```

The chat UI renders this as a question with selectable options. The user's
selection is fed back into the next turn and the agent retries generating a
`Plan` with the additional context.

The asymmetry here is intentional: the chat agent is encouraged to ask
clarifying questions and discouraged from guessing. Guessing wrong on
`target_chain` could send USDC to the wrong place; asking adds one turn but
guarantees the user agreed.

## What the chat agent doesn't do

| Action                                     | Done by                                                                                   |
| :----------------------------------------- | :---------------------------------------------------------------------------------------- |
| Validate the plan against the user's rules | [Policy engine](/architecture/policy-engine)                                              |
| Sign or broadcast transactions             | [Session keys](/contracts/session-keys)                                                   |
| Decide *when* to re-evaluate a position    | [Deterministic loop](/architecture/deterministic-loop) (scheduler)                        |
| Move USDC across chains                    | [Circle Gateway](/cross-chain/circle-gateway)                                             |
| Write to the audit trail                   | The component performing the action; the chat agent's output is logged by the layer below |

The clean separation is the point. A compromised LLM, a prompt-injection
attack inside `user_message`, or a model that simply gets confused cannot
escalate beyond producing a `Plan` that the policy engine will check and the
session key will validate again on-chain.

## When does the chat agent run

Unlike the deterministic loop, the chat agent runs **only when the user
sends a message**. It is not part of any scheduled or event-driven path.
For a typical account:

* The deterministic loop ticks dozens of times a day on yield-source
  updates.
* The chat agent runs zero to a few times — only when the user actively
  chats.

In aggregate across all accounts, fewer than 5% of `EvaluatorThought`
records originate from a chat-generated plan. The other 95%+ come from the
event-driven scheduler with no LLM involvement at all.

## Next steps

<CardGroup cols={2}>
  <Card title="The deterministic loop" icon="repeat" href="/architecture/deterministic-loop">
    What happens on the other side of the handoff.
  </Card>

  <Card title="Policy engine" icon="shield-check" href="/architecture/policy-engine">
    Every chat-generated plan goes through this gate.
  </Card>

  <Card title="Audit trail" icon="scroll" href="/architecture/audit-trail">
    How chat-originated plans are tagged and recorded.
  </Card>

  <Card title="System overview" icon="layers" href="/architecture/system-overview">
    Back to the full three-layer picture.
  </Card>
</CardGroup>
