Skip to main content

The agent

Matilda is a Strands agent driven by a Kimi model through the OpenAI-compatible API. This page describes the moving parts.

Models​

Two model roles, both defaulting to kimi-k2.6:

  • Reply model (KIMI_MODEL) handles the conversation and calls tools.
  • Extraction model (KIMI_EXTRACT_MODEL) runs after each turn to update the contact's profile and memory.

Any tool-calling Kimi model works. Replies are capped by KIMI_MAX_TOKENS and truncated gracefully.

System prompt​

The prompt in matilda/prompts.py defines Matilda's identity, communication style, reaction policy, the per-turn procedure, the intake flow, and her limits. Operators can read it verbatim on the dashboard at /dashboard/prompt. It cannot be edited from the dashboard.

Tools​

ToolPurpose
send_imessageSend the text reply, via BlueBubbles or the web chat
react_to_messageAdd an iMessage tapback: love, like, dislike, laugh, emphasize, question
get_contactRead a contact's profile
update_contactSave qualification fields and status
search_leadsQuery the pipeline
set_reminderSchedule a follow-up nudge
get_my_remindersList a contact's reminders
cancel_reminderCancel one

Per turn, Matilda sends at most one text reply and optionally one tapback. A tapback alone is acceptable only for pure acknowledgements.

The turn pipeline​

  1. Load the contact and their recent conversation.
  2. Build the known contact block: name, captured fields, memory, and a still-missing list. This is prepended to the incoming message and treated as the source of truth.
  3. Run the reply model with the tools.
  4. Deliver the reply and any reaction.
  5. Run extraction on the exchange and merge results into the contact.
  6. Re-score qualification and maybe promote.

Qualification​

matilda/qualification.py defines exactly five milestones: name, intent, budget, timeline, pre-approval. The score is the count of captured milestones times 20, so it is always one of 0, 20, 40, 60, 80, or 100.

  • maybe_promote() flips the status to qualified when all five are present.
  • fire_handoff_if_newly_qualified() fires the handoff exactly once, on the edge from an earlier status to qualified.

Memory​

matilda/memory.py builds the per-turn context and runs extraction. Extracted facts are merged into the memory column as curated long-term notes. The block is rebuilt fresh on every turn, so a fact captured in one message is available in the next.

Handoff​

On the first transition to qualified, lead_page.py generates a share token and renders the lead sheet and PDF, and mailer.py sends the notification through Resend if configured.

Scheduler​

matilda/scheduler.py runs an APScheduler job every 15 minutes. It looks for reminders that are due, respects each contact's timezone and reminders toggle, and sends the nudge through the same delivery path as a reply.

Sessions​

Agent session state is written under MATILDA_SESSIONS_DIR. Deleting a session directory resets the agent's short-term context for that contact without touching the database.