Skip to content

Prompt T0.1 — deterministic risk-appetite classifier, rendered as one prompt line #24

Description

@GustavoSena

Prompt T0.1 — Deterministic risk-appetite classifier, rendered as one prompt line

Part of prompt-building Tier 0 (tracking issue links the set). Principle for the whole tier: the model echoes; deterministic code computes. The composer model is qwen/qwen2.5-omni-7b (7B, see packages/arbitration-sdk/src/config.ts) — it has already been caught fabricating chainIds and deadlines (see the comment block in compose.ts around buildComposeMessages), and it gets MAX_COMPOSE_ATTEMPTS = 2 before template fallback, with a user watching a spinner. Anything that can be computed outside the model must be.

Why

Users express risk inclination in their prompt ("keep it safe", "I want max yield, I can take the risk") and today the prompt carries no signal for it — the model either ignores tone or, worse, free-associates on it. Classifying appetite is a task a 7B model should NOT be given (it mixes a classification task into a JSON-emission task, which raises malformed-output rates). It should be classified deterministically and handed to the model as a fact to condition on.

This classifier is also the input for T0.3 (risk-tiered band suggestions) and, later, the Tier 2 candidate-pair windowing — build it as a standalone pure module.

What

  1. A pure module packages/arbitration-sdk/src/appetite.ts:
export type RiskAppetite = "conservative" | "neutral" | "aggressive";
export function classifyRiskAppetite(prompt: string): RiskAppetite;
  • Keyword/phrase lexicon, case-insensitive, English-only for v0.
    • conservative signals (starting set): safe, safely, stable, conservative, cautious, careful, preserve, protect, low risk, don't want to lose
    • aggressive signals (starting set): aggressive, risky, high risk, degen, max yield, maximize, chase, yolo, can stomach, gamble
  • Decision rule v0: count matches per side; more conservative matches → conservative, more aggressive → aggressive; tie or zero matches → neutral. Keep the rule this dumb on purpose — it must be explainable in one sentence on stage.
  • Negation is out of scope for v0 ("not too risky" will misfire toward aggressive). Acceptable: the output only shifts suggestions, it never overrides the budget or any validator invariant, and the user reviews before signing. Note the limitation in the module comment.
  1. One line rendered into the user message of the compose prompt (integration point today: buildComposeMessages in packages/arbitration-sdk/src/compose.ts; see Coordination below), directly under USER PROMPT::
RISK APPETITE (derived deterministically from the user's words — do not re-infer it): NEUTRAL.
conservative = prefer wider bands and lower fees; neutral = balanced; aggressive = tighter bands acceptable when better fee capture justifies them.

The legend stays fixed; only the level varies. The model's job is to condition on the level, never to compute it.

Acceptance criteria

  • classifyRiskAppetite is pure, side-effect free, and table-driven-tested: at least 5 fixture prompts per class, plus ties → neutral, empty string → neutral.
  • The rendered prompt line appears in the compose prompt and contains the classifier output verbatim (snapshot or substring test on the built messages).
  • No inference call is added — zero latency cost.
  • The lexicon and decision rule are recorded on Notion F2 §9 (prompt contract) once merged — repo rule: decisions made while building go back to Notion.

Coordination

A PR refactoring prompt building is in flight. The module itself is safe to build now (pure, standalone). Do the one-line prompt integration against whichever prompt builder that PR leaves standing; if packages/app/src/lib/compose/prompt.ts (the six-section F2 §9 contract) becomes canonical, the line belongs in its REQUEST section and PROMPT_VERSION must be bumped.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions