Cairnloop.Automation.DraftGenerator.Anthropic (cairnloop v0.5.1)

Copy Markdown View Source

Reference Cairnloop.Automation.DraftGenerator backed by Anthropic's Claude (Messages API). Composes a customer-support reply only when grounding is strong, using the canonical Knowledge Base evidence the retrieval layer already gathered.

Fail-closed by construction — it delegates to Cairnloop.Automation.ScoriaEngine (the deterministic default) in every case where it must not let a model guess:

  • grounding is :clarification or :escalation (not :strong) — ask for the missing detail / recommend handoff instead of composing a reply;
  • no API key is configured — degrade gracefully to the deterministic engine (mirrors Cairnloop.Embedder.ExternalApi's no-key behaviour);
  • the Anthropic call errors or returns an unparseable body — never crash the DraftWorker; fall back so a draft still appears for the operator.

The proposal shape, grounding snapshot, evidence, and human-in-the-loop approval are identical to the deterministic engine — only the :strong-grounding customer_reply is model-composed (and still operator-reviewed before any send).

Configuration

# host config
config :cairnloop, :draft_generator, Cairnloop.Automation.DraftGenerator.Anthropic

# runtime.exs — secrets read at boot, never compiled in
config :cairnloop, :anthropic_api_key, System.fetch_env!("ANTHROPIC_API_KEY")

Optional knobs (all have sensible defaults):

  • :anthropic_model — Claude model id (default "claude-sonnet-4-6")
  • :anthropic_max_tokens — reply budget (default 1024)
  • :anthropic_req_options — extra Req options merged into the request (the seam used by tests to inject a stub plug:)
  • :conversation_lookup — how to load the thread for prompt context (default &Cairnloop.Chat.get_conversation!/1; shared with DraftWorker)

The API key is also read from the ANTHROPIC_API_KEY env var when not set in config.