Skip to content

Claude Agent SDK — replace OpenClaw with intelligent triage and dynamic model selection #55

@AnExiledDev

Description

@AnExiledDev

Replace the OpenClaw-based AI backend with the Claude Agent SDK (@anthropic-ai/claude-agent-sdk), adding intelligent message triage and dynamic model selection. This eliminates the dependency on a local proxy, gives cost control via per-query budget limits, and enables model-appropriate responses — cheap Haiku for simple conversation, Sonnet for substantive questions, Opus for complex tasks. The triage layer also offloads anti-abuse detection to more capable models when escalating, and subsumes the chime-in system into a single unified evaluation loop.

Triage System

The triage layer is the core new component. It accumulates messages per channel and periodically evaluates them using a Haiku-model SDK query to classify into one of:

Classification Model Reasoning Tokens Description
ignore No response needed
respond-haiku Haiku 0 (simple) Simple conversation, greetings
respond-sonnet Sonnet 1024 (medium) Questions, substantive discussion
respond-opus Opus 4096 (complex) Complex tasks, nuanced questions (rare)
chime-in Triage decides Varies Organic conversation participation — triage picks the model per chime-in (replaces chimeIn module)
moderate Flags for moderation review (logging only for now)

Dynamic interval (aggressive threshold-based): Triage frequency scales with message queue size:

Queue Size Interval
0–1 messages 10s
2–4 messages 5s
5+ messages 2s

Trigger words: Bot name, @mention, configurable keywords, and moderation keywords bypass the timer for immediate evaluation. Moderation keywords combine existing spam.js patterns with a new triage.moderationKeywords[] config list.

@mentions: Always result in a response (ignore is not possible). Triage still picks the model and evaluates instantly.

Triage context: Triage evaluations see only the accumulated recent message buffer (cheap, fast Haiku calls). When a message is classified for a response, the responding model pulls the full DB-backed conversation history to generate its reply.

Triage verification: When triage escalates to Sonnet or Opus, the selected model first verifies the classification before generating a full response. This catches misclassification and prompt injection that Haiku might miss. If verification downgrades the model (e.g., Opus determines Sonnet is sufficient), the downgraded model is used instead.

Buffer lifecycle: Buffer clears after a response is sent. On ignore or moderate classification, counter resets but buffer is preserved for context continuity.

Requirements

  • Use @anthropic-ai/claude-agent-sdk query() for all AI responses, replacing OpenClaw fetch() calls
  • Dynamic triage interval based on message queue size (aggressive thresholds: 0–1 msgs → 10s, 2–4 → 5s, 5+ → 2s)
  • Triage classifies messages into: ignore, respond-haiku, respond-sonnet, respond-opus, chime-in, or moderate
  • Trigger words (bot name, @mention, configurable keywords) bypass triage timer for immediate evaluation
  • Moderation keywords combine existing spam.js patterns with new triage.moderationKeywords[] config — bypass triage timer
  • @mentions always respond — triage picks model, evaluates instantly
  • Triage verification for Sonnet/Opus escalation — selected model verifies classification before full response
  • Model downgrade if verification determines lower tier is sufficient
  • Triage determines reasoning effort: simple (0 tokens), medium (1024), complex (4096 cap)
  • Triage evaluations use buffer-only context; responding model pulls full DB-backed conversation history
  • Chime-in merged into triage as a unified evaluation loop — triage picks model per chime-in — chimeIn.js deleted
  • moderate classification logs structured warning via Winston (no automated action — deferred to AI Auto-Moderation — intelligent automated moderation powered by Claude SDK #56)
  • SDK tool access restricted to WebSearch only via allowedTools: ['WebSearch']
  • System prompt carries personality/rules; user prompt carries conversation history + latest message
  • Per-query cost logged via Winston (total_cost_usd from SDK results)
  • Per-query budget configurable (default: $0.05 triage, $0.50 responses)
  • SDK response timeout: 30 seconds; triage timeout: 10 seconds; graceful fallback on failure
  • Config updated with SDK-specific options (model defaults, triage interval, trigger words, budget limits)
  • Environment variables: ANTHROPIC_API_KEY replaces OPENCLAW_URL/OPENCLAW_TOKEN
  • Typing indicator shown while waiting for SDK response
  • Threading preserved — triage responses respect existing shouldUseThread() / getOrCreateThread() logic
  • HealthMonitor integration preserved — SDK calls update health status on success/failure
  • Triage timer cleanup added to graceful shutdown sequence
  • Buffer clears after response sent; counter resets (not buffer) on ignore/moderate
  • 80% test coverage maintained

New Config Schema

{
  "triage": {
    "enabled": true,
    "defaultInterval": 10000,
    "maxBufferSize": 30,
    "triggerWords": [],
    "moderationKeywords": [],
    "models": {
      "triage": "claude-haiku-4-5",
      "default": "claude-sonnet-4-20250514"
    },
    "budget": {
      "triage": 0.05,
      "response": 0.50
    },
    "timeouts": {
      "triage": 10000,
      "response": 30000
    },
    "channels": [],
    "excludeChannels": []
  }
}

Out of Scope

  • mem0 integration (deferred — remove from AI pipeline, handle in separate issue)
  • Multi-guild triage customization (single config for all guilds initially; triage state is per-channel)
  • Streaming responses to Discord (SDK streams internally; bot sends complete messages)
  • Custom MCP tools or subagents
  • OpenClaw fallback (clean break)
  • Automated moderation actions from moderate classification (separate issue — AI Auto-Moderation — intelligent automated moderation powered by Claude SDK #56)
  • Cost monitoring dashboard (logging only)

Files Affected

File Impact
src/modules/ai.js Major rewrite — replace OpenClaw fetch with SDK query(), remove mem0 calls, keep conversation history
src/modules/chimeIn.js Delete — subsumed into triage
src/modules/events.js Rewrite — new triage-based message routing
src/modules/config.js Schema changes only (new triage section)
config.json New triage section, remove chimeIn section
src/index.js Remove chimeIn imports, add triage lifecycle (start/stop)
.env.example Replace OPENCLAW_* with ANTHROPIC_API_KEY
package.json Add @anthropic-ai/claude-agent-sdk
tests/modules/ai.test.js Rewrite for SDK
tests/modules/chimeIn.test.js Delete
tests/modules/events.test.js Rewrite for triage routing
New: src/modules/triage.js Triage buffer, timer, classification, verification
New: tests/modules/triage.test.js Triage unit tests

Files Preserved Unchanged

  • src/modules/memory.js — mem0 deferred
  • src/modules/moderation.js — no changes needed
  • src/modules/threading.js — preserved as-is
  • src/modules/spam.js — patterns reused by triage, module unchanged

Dependencies

  • @anthropic-ai/claude-agent-sdk npm package
  • ANTHROPIC_API_KEY environment variable
  • Existing PostgreSQL conversation history system
  • Existing Discord.js v14 event handling

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions