Skip to main content

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.

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 for what’s on the other side of the handoff. chat agent diagram

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 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.
{
  "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:
{
  "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

ActionDone by
Validate the plan against the user’s rulesPolicy engine
Sign or broadcast transactionsSession keys
Decide when to re-evaluate a positionDeterministic loop (scheduler)
Move USDC across chainsCircle Gateway
Write to the audit trailThe 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

The deterministic loop

What happens on the other side of the handoff.

Policy engine

Every chat-generated plan goes through this gate.

Audit trail

How chat-originated plans are tagged and recorded.

System overview

Back to the full three-layer picture.