Agent Architecture

What This Is

A custom-built agent for mutual fund distributors. The orchestration loop is written in-house — not a hosted agent platform. Claude (or any LLM) is just an outbound HTTPS dependency, same as any third-party API. Everything else — memory, routing, tool dispatch, state, policies — lives in our AWS infrastructure.

North star: If the LLM is swapped tomorrow, everything else should stay the same.


System Layers (Top to Bottom)

Advisor / Distributor (UI / Chat)
        │
        ▼
  Agent Orchestrator (our loop, AWS)
        │
        ├── LLM API (outbound HTTPS, inference only)
        │
        ▼
  MCP Layer (domain adapters)
   ├── Fund Domain MCP
   ├── Client Domain MCP
   └── Workflow / Report MCP
        │
        ▼
  Existing Services (untouched)
   ├── Portfolio Analysis (gRPC) → ~10s PDF generation
   ├── Fund Analysis (gRPC)
   └── Client Data Service (gRPC / REST)
        │
        ▼
  Data Platform
   ├── PostgreSQL (funds, clients, derived metrics)
   ├── S3 (generated PDFs, reports)
   └── SQS (async job queue)

Rule: gRPC internally (service-to-service). MCP at the agent boundary. The agent never talks to gRPC or the database directly. MCP is the seam between the agent and our services — it decouples actual business logic from whatever framework or loop runs above it.


Model Configuration

Decisions to make before writing the loop:

Decision Notes
Primary model Decide per route — hard reasoning vs. routine steps don't need the same model or effort level
Fallback model If primary is unavailable (rate limit, outage), fall back to a smaller/cheaper model. Define which routes tolerate degraded quality
Determinism Set temperature=0 for any call that feeds a deterministic downstream step (tool selection, structured extraction). Reserve non-zero temp for response generation only
RPC protocol gRPC internally between services. MCP (HTTP/SSE or stdio) at the agent boundary. The loop itself calls the LLM over HTTPS

Framework Choice

Option Trade-off
LangGraph / CrewAI / AutoGen / Semantic Kernel Faster to start, opinionated about state and loop shape, harder to audit and control precisely
Custom thin loop Full auditability, we own every decision point, more code upfront but no abstraction fighting us later

Decision: Custom thin loop. MCP handles tool discovery and dispatch. The loop is ours. This matters in finance — every tool call, model decision, and client data access needs to be logged explicitly, not inferred from a framework's internals.


Agent Core — 5 Internal Parts

Part Responsibility
Intent Understanding Translate natural language into a goal
Planner Output steps as capabilities, not raw API calls
Execution Engine Map capability steps to existing MCP tools/services
World State Track what happened (report IDs, job status, session facts)
Response Generator Format the final result for the user

What the agent core must never do:


Planning Loop — Decide Per Task

Not every task needs the full agentic loop. Before entering the loop, classify the request:

Request Type Approach
Fixed, deterministic pipeline (e.g. generate scheduled report) Run the pipeline directly. Don't touch the LLM reasoning loop.
Structured query with known tool (e.g. "show fund returns for X") Single tool call, no planning loop needed
Multi-step, ambiguous, or open-ended Enter the planning loop

Iteration cap: Every agentic loop has a hard max (e.g. 10 iterations). If the cap is hit, fail explicitly — don't silently return a partial result.

Deterministic pipelines must not touch the LLM reasoning loop. They're cheaper, faster, and fully auditable. The agent may trigger them, but the computation is entirely outside the model.


Planning Pipeline

Each request flows through these stages in order. Each stage produces structured output, not free text.

1. Intent Analysis       → What is the user actually trying to achieve?
2. Goal Builder          → Translate intent into a concrete goal
3. Data Requirement      → What data is needed? What's known? What must be fetched? What must be asked?
4. Context Builder       → Load relevant session + long-term memory
5. Constraint Analyzer   → Latency budget, token budget, user permissions, data freshness
6. Planner               → Choose capabilities and sequence them
7. Evaluator             → Verify plan is sound before executing
8. Execution Plan        → Deterministic step list handed to the execution engine

Replanning triggers (loop back to planner when):


Tool Anatomy

Every tool, whether domain-specific or generic, must define all five of these:

{
  "name": "get_fund_returns",              # snake_case, unique
  "description": "...",                   # when to call this, not just what it does
  "function": get_fund_returns,            # the actual callable
  "parameters": {                          # JSON Schema
    "type": "object",
    "properties": {
      "scheme_code": { "type": "string", "description": "..." },
      "period":      { "type": "string", "enum": ["1y","3y","5y"] }
    },
    "required": ["scheme_code"]
  }
}

Input validation happens inside the tool's execution wrapper before calling the backend — not in the model's output. Schema adherence from the model is not a guarantee. Validate, then call.

Per-call timeouts are set at the tool layer, not globally. A database query gets 2s. A PDF generation gets 30s. An async job submission gets 5s.


Tool Catalog

Domain Tools — Growthvine MCP

All wrapped behind MCP. The agent sees these as capabilities, not gRPC methods.

Tool What It Returns
compare_funds Side-by-side metrics for 2–10 schemes
get_category_stats Pre-aggregated stats for a fund category
get_factsheet_history Historical factsheet snapshots for a scheme
get_fund_details Full profile of a single scheme
get_fund_holdings Current portfolio holdings of a fund
get_fund_overview Summary metrics (AUM, rating, category)
get_fund_returns CAGR / absolute returns over standard periods
get_fund_risk_metrics Sharpe, Sortino, SD, Beta, Alpha
get_fund_sectors Sector allocation breakdown
get_holdings_history How a fund's holdings changed over time
get_nav_history Raw NAV time series
get_nav_history_compare NAV curve comparison across schemes
get_portfolio_analysis Full analysis for a client's portfolio
get_returns_history Rolling returns over time
get_sector_history Sector allocation changes over time
list_categories All available fund categories
list_schemes All schemes (filterable)
search_schemes Text / attribute search across schemes
screen_clients Filter clients by AUM, risk, drift, etc.
get_client_analysis_context Client facts, goals, current holdings

Generic Tools

Tool Notes
web_search For live fund news, regulatory updates, market context
python_exec Sandboxed. For ad-hoc calculations the deterministic services don't cover
file_read / file_write Scoped to designated paths only. Never arbitrary filesystem access
database_query Read-only. Parameterized queries only — no string interpolation
calculator Thin wrapper to avoid asking the LLM to do arithmetic

Document Loading Tools

Used when an advisor uploads a document for the agent to read:

Library Use Case
PyPDF2 / pdfplumber / pypdf PDF text extraction
Unstructured Multi-format parsing (PDF, DOCX, HTML, images)
python-docx Word document loading
beautifulsoup4 HTML document parsing

Document content is extracted, chunked if needed, and injected into context — never stored as raw PII.


Memory Architecture

Five distinct memory types. Never conflate them.

In-Context Memory

Just the conversation. Managed by the loop. Expires when the session ends.

Session Memory (Short-Term)

What happened in this conversation that the planner needs to reference mid-task. Stored server-side in a session store, not in the prompt. Includes: which tools were called, what they returned, what the user confirmed.

Not yet fully defined — to be designed.

Long-Term Memory

Durable, persists across sessions. Promoted deliberately by a memory manager, not written directly by the LLM.

Not yet fully defined — to be designed.

Episodic Memory

Append-only. Timestamped. Retrieved chronologically.

Records of significant advisor-client interactions:

This is a compliance-adjacent record. The advisor conclusion is drafted by the agent, then the advisor gives explicit confirmation, after which it is immutable. Corrections are new entries — not edits to history. Same principle as a client communication log.

Semantic Memory

Current facts about a client — the live state.

Includes: goals, goal progress, risk profile, portfolio composition preferences, agreed benchmarks. This is the "what is true right now" layer. Unlike episodic, it can be updated when facts change (new goal, updated risk profile). One owner per truth — the client data service.

Procedural Memory

General portfolio composition rules, evaluated deterministically in code.

These are rules like "equity exposure should not exceed X% for a conservative profile" or "no single fund should exceed Y% of allocation." They are not evaluated by the LLM — they run as code against a client's holdings and return pass/fail with a reason.

Fund tracking alerts are not memory. They are the output of a background job that compares live fund data against client holdings and composition rules. The agent only retrieves and explains alerts — it never evaluates the rule itself.


Portfolio Analysis Tool — Detailed Design

Core Call

generate_portfolio_report(
    client_ref: str,
    holdings_override: list[HoldingChange] | None = None
)

HoldingChange = {
    "remove": fund_id,   # optional
    "add": fund_id       # optional
}

holdings_override is None by default — run the standard analysis. When set, run the exact same pipeline but pretend the client holds the overridden set. This enables "what if" scenarios ("run the same analysis but pretend the client holds C and D instead of A and B") without changing any computation logic.

The agent's job is to figure out the holdings_override from the conversation — the pipeline itself doesn't change.

What Portfolio Analysis Covers

Output

Two things always returned together:

  1. PDF — formatted client-facing report
  2. Structured metrics payload — the same computed values in JSON, for the agent to reason about or surface in UI

The agent gets back a report_url (S3) and the metrics object. It never reads the PDF content — just knows the URL.

Snapshot vs. Live

Once generated, the holdings and data used are frozen as a snapshot. We can always look up: "this is what we told the client on date X, computed against state Y." Re-reviewing always means pulling fresh data into a new report — never mutating an existing one.


Policy Engine (Separate from Agent Core)

The policy engine enforces rules the agent core is not allowed to decide on its own:

The agent core says what it wants to do. The policy engine says whether it can.

Data access control is enforced server-side, not prompt-side. Telling the model "don't show client X's data to advisor Y" is not access control — it's a suggestion. Access scoping is enforced at the tool layer: each tool call carries the authenticated advisor's context, and the backend rejects requests outside their permitted client scope.


Security and Compliance

Concern Approach
Raw PII in memory Never stored in any memory layer — session, long-term, or episodic. Facts about clients are stored, not verbatim chat containing PII
Access scoping Enforced at tool layer per authenticated session. Not via prompt instructions
Encryption All client data at rest (PostgreSQL, S3) encrypted. In transit: TLS everywhere
Retention policy To be defined per data type. Episodic records (advisor conclusions) are likely long-retention compliance records. Session memory is short-retention
Audit trail Every tool call, every model decision, every client data access is logged with inputs, outputs, latency, and actor. In finance this is a compliance requirement, not optional

Observability and Audit Trail

Every action logged:

{
  "event": "tool_call",
  "tool": "generate_portfolio_report",
  "input": { "client_ref": "C123" },
  "output": { "report_url": "s3://...", "metrics": {...} },
  "duration_ms": 9820,
  "status": "success",
  "advisor_id": "A456",
  "session_id": "sess_789",
  "timestamp": "2026-07-29T10:23:11Z"
}

Also log: every model call (prompt token count, completion token count, model used), every planning decision (which capability was selected and why), every policy decision (allowed or rejected and why).


Retries and Idempotency


Capabilities Layer

The agent thinks in capabilities, not APIs. Each capability is an atomic action with metadata:

{
  "name": "generate_portfolio_report",
  "cost": "medium",
  "freshness_slo": "data as of last scheduled run",
  "permissions": ["advisor", "admin"],
  "required_inputs": ["client_ref"],
  "optional_inputs": ["holdings_override"],
  "returns": "report_url + metrics_payload",
  "timeout_s": 30
}

Build a capability graph (not a flat tool list) so the planner knows relationships, not just endpoints. The governance layer enforces, but the planner needs to see constraints (cost, freshness, permissions, timeout) before deciding. Capabilities are the language the agent thinks in.


Workflow Management

Workflows compose capabilities into reusable sequences.

Example: portfolio_review = generate_report → risk_analysis → comparison → (optional) email_client

Workflow Lifecycle

draft → observed → candidate → reviewed → approved → production

Workflow vs. Service

Services Workflows
Primitive capabilities Compose services into sequences
Own how (email service sends emails) Decide when (orchestration)
Stateless / deterministic Can pause for user input, branch, retry
Never change per workflow Reusable across multiple workflows

Tool Design Rules

Return references, not raw files. A portfolio PDF tool returns report_url and a metrics payload — not a base64 blob. The agent knows the report exists and can surface structured data; it doesn't read the PDF.

Async for long-running work. If a task takes minutes:

Never block a tool call for more than the tool's defined timeout.

Idempotent calls. Every MCP tool is safe to retry. Pass a request_id from the orchestrator.

Tool count discipline. Too many tools in context degrades capability selection. Plan for dynamic tool discovery (model asks what tools are available for a goal, rather than receiving the full list every turn) before the surface grows large.


Bootstrap Context

Before the first turn, the agent is loaded with static domain context (stable, not per-user):

Dynamic context (per-user, per-task) is fetched on demand — not stuffed into the system prompt. The system prompt is frozen text; interpolating session state or timestamps into it kills prompt cache hit rate.


Evaluation Suite

Define before building, not after. A fixed set of realistic scenarios:

For each scenario: define expected tool-call sequence, expected policy decisions, and acceptable response shape. Check automatically — not just "does the final text look right?"

Run the eval suite before launch.


Gaps to Address

  1. Reasoning strategies — Different request types need different thinking modes (report generation vs. anomaly detection vs. hypothesis testing). The planner should classify reasoning type before building a plan.

  2. Planning under constraints — The plan should adapt to latency budgets, token budgets, mobile vs. server context.

  3. Verification step — Plans should be checked for invariants before execution, not just outputs afterwards.

  4. Evolving user intent — Handle mid-conversation goal shifts, not just single-turn prompts.

  5. Failure as knowledge — Operational history (which capabilities fail, which inputs are frequently missing) should feed back into planning heuristics.

  6. Short-term and long-term memory design — Both are noted as needed but not yet defined. Need storage schema, retrieval strategy, and promotion rules.

  7. Retention policy per data type — Episodic records, session memory, and raw logs all have different legal and operational lifetimes. Needs definition before launch.


Key Architecture Principles

Principle What It Means
Every dependency points toward stability Infrastructure knows about business logic, not the reverse
Data outlives code Design the data layer to be stable; services come and go
One owner per truth One service decides what a value means; everyone else reads it
Stable contracts, not stable internals Refactor freely inside a service; version the interface
Make illegal states unrepresentable Use state machines for workflows, not boolean flags
Prefer ownership of behavior, not just data Services expose capabilities, not tables
Eventual consistency with explicit bounds Define staleness SLOs per field; expose as-of timestamps
Failures are normal control flow Transient: retry with backoff + idempotency. Permanent: fail fast with context
Access control is infrastructure, not prompt Enforced at the tool layer, not by telling the model what to do
Advisor conclusions are immutable records Draft, confirm, freeze. Corrections are new entries

What to Document Before Building

The architecture thinking is solid; the next step is turning intent into specs with contracts, invariants, and failure semantics. That is what lets multiple engineers build without drifting.

Five documents needed before a single line of production code:

1. ADR Collection

Every major decision gets a one-page Architecture Decision Record. Six months later, ADRs are the map of why we did this. Start with:

ADR Decision
ADR-001 Why MCP instead of direct gRPC at the agent boundary
ADR-002 Why a custom orchestrator instead of LangGraph / CrewAI
ADR-003 Why immutable episodic memory
ADR-004 Why reasoning packages instead of raw tool results
ADR-005 Why workflow commit semantics gate memory writes
ADR-006 Why planner and executor are separate processes
ADR-007 Why capability graph instead of flat tool list

Each ADR: context → decision → consequences → alternatives considered. One page.

2. Capability Specification Schema

Not just examples — a formal schema that becomes the contract between the planner and the execution engine:

{
  "name": "...",
  "purpose": "...",
  "inputs": { "required": [], "optional": [] },
  "outputs": { "schema": {}, "reasoning_package": {} },
  "side_effects": [],
  "idempotency": "full | idempotent-with-request-id | not-idempotent",
  "cost": "low | medium | high",
  "latency_slo_ms": 0,
  "freshness_slo": "...",
  "permissions": [],
  "failure_modes": [],
  "dependencies": []
}

Every capability in the system fills this schema. No exceptions.

3. State Machine Spec

For workflows, async jobs, memory promotion, and approvals — define every legal state and every legal transition. Illegal states are structurally impossible. Covers:

4. Failure Model Document

Assume everything fails. For each failure scenario, define the response:

Failure Behaviour
MCP server down Which capabilities degrade? Is there a fallback?
LLM API timeout Retry? Cancel? Return partial?
Partial workflow success Which steps are committed? How do we resume?
Human approval never comes Expiry policy, notification, cancellation
Tool returns stale data Freshness check fails — replan or surface to advisor?
Policy rejects mid-workflow Rollback? Hold? Escalate?

5. Planner Spec

The planner is the brain. Everything else hangs off it. See the full planner design section below.


Design Order

Capabilities first. Services second. Planner third.

Start by defining domain capabilities as business surfaces — what problem does each solve, what is its contract, what are its SLOs. Then, behind each capability, design or refine the RPC services that back it. That way services conform to the agent's needs, not the other way around.

Domain sequence (each depends on the one before): Client → Portfolio → Fund → Recommendation → Monitoring → Workflow.


Fund Domain — Capability Catalog

Fund Profile

Static facts about a fund. Name, AMC, category, launch date, AUM, fund manager, benchmark. Read-only. Freshness: daily.

Fund Analytics

Returns (absolute, CAGR), risk metrics (Sharpe, Sortino, SD, Beta, Alpha), ratios, drawdown. The computation backend owns this. The agent receives a structured analytics object — it does not compute.

Fund Composition

Current holdings and exposure. Sector breakdown, market-cap tilt, top holdings, overlap with other funds. Freshness: monthly (or at factsheet publish).

Fund Comparison

Side-by-side comparison of 2–10 funds across any metric set. This is one of the highest-frequency advisor tasks. Returns a structured comparison object the agent can explain.

Fund Discovery

Search and filter across the full fund universe. Inputs: category, AMC, AUM range, Sharpe minimum, rating, expense ratio cap, etc. Returns a ranked list. Deterministic — does not go through LLM reasoning.

Fund Ranking

Advisor gives a category and a set of parameters with weights. System ranks funds in that category and returns an ordered list with scores and explanations. The advisor can then open a session to argue with the ranking, adjust weights, and re-rank. The LLM explains the ranked output — it does not do the ranking computation.

Fund Events

Significant changes: manager change, category reclassification, SEBI rating change, exit load change, AUM spike/drop. Append-only event log. The monitoring system produces these; the agent only retrieves and explains them.

Fund Knowledge Layer

Curated summaries of static facts and derived insights — not computed live, maintained by a background job. Purpose: the agent can explain a fund without triggering a live fetch. Think of it as a pre-digested briefing per fund.

Brokerage and Tax Data

Per-fund brokerage structure and exit load schedule. Per-redemption tax implication (STCG / LTCG thresholds). This data is needed whenever the recommendation service evaluates a fund switch. It is reference data, not analytics. Freshness: updated when regulation or fund terms change.


Workflow Domain — Catalog

Workflows are reusable business processes, not API sequences. Group into four buckets:

Bucket Examples
Advisor-initiated Replace fund, review portfolio, prep for client meeting
Event-driven Monitoring alert, portfolio drift, fund manager change
Scheduled Monthly reviews, weekly summaries, data refreshes
Human-approval Anywhere execution explicitly pauses for sign-off

Workflow Template

Every workflow follows this structure. No exceptions.

Purpose         — one sentence, what business outcome does this achieve
Trigger         — what starts this workflow
Steps           — ordered, each mapped to a capability
Decision Points — where the workflow branches based on data or advisor choice
Approvals       — which steps require explicit human confirmation before proceeding
Failures        — what happens at each failure point (retry, cancel, escalate)
Audit           — what is written to the audit log and when
Version         — every workflow is versioned; audits record which version ran

Workflow: Replace Fund

  1. Advisor specifies fund to replace and constraints (same category? risk profile? tax sensitivity?)
  2. Recommendation service generates ranked replacement candidates with tax, exit load, and overlap data
  3. Agent explains trade-offs in a session — advisor can ask questions, adjust constraints, request re-ranking
  4. Advisor selects replacement
  5. Policy check (approval required for any switch above threshold amount)
  6. On approval: update semantic memory (new holding), append episodic memory (fund replaced, reason, advisor sign-off), emit event

Workflow: Portfolio Construction / Investment Proposal

  1. Advisor inputs: amount to invest, composition constraints (X% large cap, Y% debt, Z% hybrid)
  2. System generates a small set of candidate portfolios (not one — always a set)
  3. Each candidate is scored and explained — risk, expected return, overlap, cost
  4. Advisor tweaks constraints and regenerates until satisfied
  5. Advisor selects a portfolio
  6. System generates a client proposal document (versioned, savable object)
  7. Proposal is stored — not just the PDF, also the structured inputs so it can be re-explained later

Key design: this is construction from scratch, not a recommendation against an existing portfolio. Proposals are first-class objects — savable, versioned, retrievable.

Workflow: Fund Watch List / Monitoring

Advisor marks specific funds for daily monitoring. A background job fetches fund data each day and compares against watch criteria (NAV drop, AUM change, rating change, manager change). Alerts are generated by the job. The agent only retrieves and explains alerts — it never evaluates the monitoring rule itself. The agent can help an advisor set up a watch, but the watch runs entirely outside the agent loop.


Reasoning Packages

Services compute. Agent reasons.

Every capability response carries a reasoning package — a structured object containing the data, assumptions, and freshness metadata the agent needs to continue reasoning without re-fetching.

{
  "capability": "get_portfolio_analysis",
  "computed_at": "2026-07-30T09:00:00Z",
  "freshness_valid_until": "2026-07-30T21:00:00Z",
  "holdings_snapshot": [...],
  "metrics": { ... },
  "assumptions": { "benchmark": "Nifty 50", "period": "1Y" },
  "summary": "Portfolio is overweight large-cap growth, underweight debt."
}

The planner checks freshness before reusing a reasoning package. If it is still fresh, it reuses — no additional capability call. If stale, it re-fetches.

Effect: the agent builds up a context of structured reasoning packages through the conversation. Follow-up questions ("why is the Sharpe low?") are answered from existing packages, not new fetches. This makes the system faster and gives it a clear identity — it reuses reasoning, it does not constantly recompute.


Planner Spec — Foundation

Purpose: Convert structured intent into the lowest-cost executable capability plan that satisfies all constraints.

Design Goals

Goal What It Means
Deterministic Same inputs produce same plan
Explainable Every step in the plan has a reason
Cheap No unnecessary capability calls or loop iterations
Recoverable Can replan from any point in partial execution

Responsibilities

The planner does:

The planner does not:

Inputs (Structured, Not Raw Text)

{
  "intent": { "type": "...", "goal": "...", "entities": {} },
  "session_context": { "conversation_history": [], "active_reasoning_packages": [] },
  "world_state": { "completed_steps": [], "pending_approvals": [] },
  "capability_graph": { ... },
  "available_workflows": [ ... ],
  "constraints": {
    "latency_budget_ms": 5000,
    "token_budget": 4000,
    "permissions": ["advisor"],
    "freshness_required": true
  },
  "policy_hints": [ ... ]
}

Output (Pure Data Structure, No Prose)

{
  "plan_id": "plan_abc123",
  "reasoning_strategy": "comparison",
  "workflow": "replace_fund | null",
  "steps": [
    {
      "step_id": 1,
      "capability": "get_fund_details",
      "inputs": { "scheme_code": "INF200K01150" },
      "reason": "Need current fund profile before replacement analysis",
      "reuse_from_package": null
    }
  ],
  "missing_info": [],
  "estimated_cost": "medium",
  "estimated_latency_ms": 1200
}

Planner Pipeline — Ordered Stages

1. Intent Analysis          → classify the request type
2. Goal Builder             → translate intent into a structured goal
3. Context Builder          → load session memory, active reasoning packages
4. Data Availability Check  → which data is already fresh? what must be fetched?
5. Missing Info Resolver    → what must be asked from the advisor before planning?
6. Reasoning Strategy       → lookup / comparison / simulation / screening / construction
7. Workflow Selector        → does a known workflow match this goal? use it.
8. Capability Planner       → if no workflow, compose capability steps
9. Constraint Optimizer     → minimize cost and latency while satisfying all constraints
10. Plan Verifier           → check invariants, permissions, policy hints
11. Handoff                 → structured plan to execution engine

Key principle: minimize capability calls, maximize reuse of existing reasoning packages. The planner checks freshness at step 4 before selecting capabilities at step 8.

Reasoning Strategies

Strategy When Used
Lookup Single entity retrieval — fund profile, client details
Comparison Side-by-side analysis of 2+ entities
Simulation What-if — holdings override, scenario analysis
Screening Filter a large set to a ranked short-list
Construction Build something new — investment proposal, portfolio
Explanation Advisor wants to understand an existing result

Replanning Triggers

Trigger Action
Capability call fails (transient) Retry with backoff, then replan if repeated
Capability call fails (permanent) Replan without that capability, surface to advisor if no alternative
Missing data discovered mid-plan Pause, ask advisor, resume
Policy engine rejects a step Replan around the constraint
Advisor changes intent mid-conversation Rebuild plan from current world state
Freshness check fails on reuse Re-fetch and continue

Memory Design — Lifecycle and Ownership

Memory is not a storage question. It is a lifecycle and ownership question.

Core principle: Nothing becomes memory by accident. Every write to semantic or episodic memory is an intentional, policy-governed act triggered by a committed workflow.

Memory Types and Lifecycle

Type Lifecycle Owner Mutable
Conversation Ephemeral — expires end of session Session manager Yes (trimmed)
Reasoning Packages Short-lived — TTL-based cache Execution engine No (replaced on re-fetch)
Semantic Memory Durable — current truth Client data service Yes (updates replace)
Episodic Memory Durable — permanent history Memory manager No (append-only)
Operational Logs Durable — infrastructure record Observability layer No

Invariants

  1. Episodic memory is append-only. No edits, no deletes. Corrections are new entries.
  2. Semantic memory holds current truth. Each fact has exactly one owner.
  3. Every memory write has exactly one owning workflow that authorized it.
  4. Operational logs are not business history. A failed attempt never enters client history.
  5. Reasoning packages are temporary. They expire. They are never permanently stored.

Memory Promotion Flow

When a workflow completes with advisor approval and successful execution:

Workflow reaches COMMITTED state
         │
         ▼
Policy check: is this an event or a fact change?
         │
    ┌────┴────┐
    ▼         ▼
 EVENT      FACT CHANGE
    │         │
    ▼         ▼
Append to   Update semantic
episodic    memory (current
memory      truth replaced)
(who, what, when, why)
         │
         ▼
Log the memory write (audit record)
         │
         ▼
Expire reasoning package (no longer needed)

Only the decision and rationale summary are stored. The full reasoning package is not persisted — it expires.


Workflow Commit Semantics — One-Pager

Purpose: Define when a workflow becomes business truth and what is permitted to enter long-term memory.

Only committed workflows can modify business state or write to semantic and episodic memory. Everything else is an execution attempt and is never treated as business history.

State Machine

DRAFT
  │
  ▼
VALIDATED          ← input schema and policy pre-check
  │
  ▼
PLANNING           ← planner generates execution plan
  │
  ▼
READY              ← plan verified, awaiting execution
  │
  ▼
EXECUTING          ← steps running
  │
  ├── (if human approval required)
  │         ▼
  │     AWAITING_APPROVAL
  │         │
  │    ┌────┴────┐
  │    ▼         ▼
  │  APPROVED  REJECTED / EXPIRED
  │    │
  │    ▼
  └── RESUMED
  │
  ▼
EXECUTION_COMPLETE
  │
  ▼
COMMIT_VALIDATOR   ← checks: all steps done, approvals present, policy still valid, data still fresh
  │
  ├── fail → COMMIT_FAILED (logged, surfaced to advisor)
  │
  ▼
COMMITTED          ← only this state writes to memory or business state
  │
  ▼
COMPLETED

Failure branches from any state: VALIDATION_FAILED / PLANNING_FAILED / EXECUTION_FAILED / CANCELLED / EXPIRED

Commit Responsibilities (Atomic)

All of these happen together or none of them do:

  1. Persist business state change
  2. Update semantic memory (current facts)
  3. Append to episodic memory (event record)
  4. Write audit record
  5. Emit domain events (for downstream consumers)
  6. Mark workflow COMPLETED

Memory Rules

Memory Layer Written By When Mutable
Semantic Commit phase only On COMMITTED Yes — updates replace current truth
Episodic Commit phase only On COMMITTED No — append-only
Reasoning Package Execution engine On each capability response Replaced on re-fetch, expires by TTL
Operational Log Every state transition Always No

Commit Validator

Before entering the COMMITTED state, the commit validator checks:


Architecture Document Index

Target: 9 parts.

Part I — Foundation

Part II — Domain Model

One chapter per domain. Each chapter contains: purpose, responsibilities, non-responsibilities, capability catalog, capability contracts, data ownership, freshness SLOs, permissions, failure modes, sequence diagram, state diagram, examples.

Domains: Client · Portfolio · Fund · Recommendation · Monitoring · Workflow

Part III — Agent Core

Planner : Intent classification, goal builder, context builder, data availability and freshness check, missing info resolver, reasoning strategy selector, workflow selector, capability planner, constraint optimizer, plan verifier, replanner.

Execution Engine : Execution model, state machine, retry semantics, idempotency, parallel execution, human approval, cancellation, resume, timeouts, context propagation, reasoning packages.

Context Builder : The agent does not go from tool result → answer. It goes from tool result → reasoning package → continued conversation. That is a major architectural idea that deserves its own chapter. Reasoning package schema, freshness management, compaction rules, handoff to planner on subsequent turns.

Part IV — Memory

Memory lifecycle, promotion rules, ownership, TTL, freshness, compaction, retrieval strategy, embedding strategy, conflict resolution, memory manager service, memory APIs, memory schema per type.

Part V — Policies

Policy engine design, capability policies, human approval flows, access control, compliance rules, data retention, audit requirements, workflow commit policy, policy DSL or schema.

Part VI — Capability Registry

Every capability in the system gets its own contract using the standard schema (see Capability Specification Schema above). No undocumented capabilities in production.

Part VII — Infrastructure

AWS architecture, MCP server design, gRPC service contracts, Redis (caching layer), PostgreSQL schema principles, S3 usage patterns, SQS job queue design, event bus, observability stack, model routing, prompt cache strategy.

Part VIII — Evaluation

Benchmark scenarios, regression suite, golden conversations, tool-call sequence verification, planner accuracy metrics, latency budgets, cost benchmarks, reasoning quality rubric, memory quality checks, failure injection tests, chaos testing plan.

Part IX — ADRs

Every major decision. Target 30–40 ADRs by v1.0. See ADR index in Part I.