4.6 KiB
Odin → General Assistant (Phase 2+) — Forward-Looking Notes
Date: 2026-06-08 · Status: NOT scheduled — design notes only, recorded so Phase 1 choices don't box us in.
The vision: Odin grows from "chat + invoice import" into a general assistant that can query system data (invoices, projects, attendance, vehicles…) and take actions (create an order, mark paid, draft a quote…). This note assesses whether the Phase-1 architecture supports that, and what to carry forward deliberately.
Bottom line
Phase 1 sets us up well; it does not box us in. Two things to carry forward on purpose:
- Structured message storage (content blocks, not a plain string) — cheap to plan now, costly to retrofit later.
- The assistant acts strictly as the authenticated user — every tool runs through the existing permission/ownership layer.
Everything else (conversations, budget/usage, thin services) is already pointing the right way.
What Phase 1 already gets right
- Conversations — multi-turn, persisted, per-user, isolated. Exactly the context substrate an agent needs.
- Budget + usage tracking +
ai.usegate — the cost/access spine. Tool loops spend more, so this matters more, not less. - Thin service layer (
{ data } | { error, status }, ownership-checked) — ideal to wrap as tools without rewriting business logic. - The extract → review → save flow — a perfect template for "propose action → human confirms → execute." The
canSave/invoices.creategate added in Phase 1 is the one-tool version of per-action authorization.
The one schema thing to note now
ai_chat_messages.content is a plain string. Tool use requires persisting content blocks (text + tool_use + tool_result), because the model must see the full tool-call history to continue. When Phase 2 lands, store the raw content-block array (JSON column, e.g. content_json/blocks) and keep a derived plain-text for display/search. Don't change it now — just design Phase 2 persistence as "store the blocks," treating the current string as a lossy projection.
The core shift (Phase 2)
- Tool use over the existing services. Define tools (
list_invoices,get_project,create_order, …) whose handlers call the same service functions the routes use. The SDK tool-runner loops; we execute. Reuses all validation/business logic and — critically — permissions. - Authorization is the hard part. Every tool MUST run as the user, through
requirePermission/ service ownership checks. The assistant must never do what the user can't. It is the user's delegate, not a privileged actor. - Read vs write. Read/query tools may run autonomously. Write/destructive tools propose → confirm → execute (generalize the invoice review card into a generic "action card"). Aligns with the assistant safety rules (confirm side-effecting actions).
- Audit. Every action the assistant takes should
logAuditlike a manual action, attributed to the user with a marker (e.g.via: "odin"), for a real trail. - Cost. Tool loops are multi-round-trip → 3–10× the tokens of a chat turn. The $50 cap bites faster; consider per-conversation cost display and per-action budget re-checks (the inter-file budget re-check in
extract-invoicesis the pattern).
Data access: tools-over-services, NOT RAG
Recommendation: function-calling against the real services, not embeddings/RAG. The data is structured, live, and permissioned — tools give correct, current, authorized answers. RAG over a snapshot would be stale, would leak across permission boundaries, and would duplicate logic. Reserve embeddings for genuinely unstructured document search if it ever comes up.
Other forward notes
- Model / effort: a true agentic assistant wants a stronger model and possibly
efforttuning; Phase-1 Sonnet single-shot is right for now. - Streaming: multi-step tool use feels slow without it — add SSE when Phase 2 lands.
- System prompt: describe the user's role + available tools + the confirm-before-write rule.
- Managed Agents vs self-hosted loop: Anthropic's Managed Agents host the loop, but for a self-hosted business app with our own services + permissions, Claude API + tool use with a self-hosted loop is the better fit — authorization stays in our stack.
What NOT to do prematurely
- Don't add a tool framework, embeddings, or streaming now — Phase 1 is plain chat + a fixed-prompt extractor, and that's the right scope.
- Don't widen
ai.useto non-admins until the per-action authorization story (point 2) is in place.