Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

AMIQ — Deep Agent Layer

A portfolio writeup of the deep-agent architecture I designed and shipped for the AMIQ platform (Ads & Marketing IQ). AMIQ is Sobhan Bahrami's agentic burn-detection and campaign-analytics platform. This repo documents the deep agent layer — ReAct, Plan-and-Execute, the Strategy Agent with human-in-the-loop, and the pending-actions API — that I added to it.

⚠️ All code lives in the upstream AMIQ repo. This repo is a writeup, not a fork. The point is to explain the architecture and the design decisions, with pointers to the source files.


TL;DR

In May 2026 I shipped a 3,693-line deep-agent layer on top of AMIQ's existing audit pipeline. The addition:

  • Two agent architectures — ReAct (single-agent, multi-turn tool use) and Plan-and-Execute (decompose → execute → reduce)
  • A Strategy Agent with confidence-gated action generation
  • A human-in-the-loop pending-actions API — every strategy proposal is stored, surfaced, and requires explicit human approval before any writeback to Meta/Google APIs
  • A streaming protocol so the dashboard can show LLM tokens, tool calls, and tool observations in real time

All built on LangChain 1.2.x's create_agent API (which returns a CompiledStateGraph directly — no AgentExecutor needed) and AMIQ's existing FastAPI + Supabase + LangGraph stack.


The architecture

                    ┌─────────────────────────────┐
                    │  AMIQ core audit pipeline   │
                    │  (Sobhan's original work)   │
                    │                             │
                    │  INGEST → AUDIT → REPORT    │
                    │       (LangGraph state)     │
                    └─────────────┬───────────────┘
                                  │ audit_results
                                  ▼
                    ┌─────────────────────────────┐
                    │   Strategy Agent  ◄────┐    │
                    │   (this repo)        LLM    │
                    │                       │    │
                    │  decision_confidence ─┘    │
                    │     < 0.50 → DATA_FIX only  │
                    │    >= 0.50 → full actions  │
                    └─────────────┬───────────────┘
                                  │ N proposals
                                  ▼
                    ┌─────────────────────────────┐
                    │   pending_actions table     │
                    │  (Supabase, RLS-protected)  │
                    └─────────────┬───────────────┘
                                  │
                                  ▼
                    ┌─────────────────────────────┐
                    │   Dashboard  (Next.js)      │
                    │   "Review N actions" card   │
                    └─────────────┬───────────────┘
                                  │ human PATCHes status
                                  ▼
                    ┌─────────────────────────────┐
                    │   APPROVED / REJECTED       │
                    │   (logged; Phase 4+ writes  │
                    │    back to Meta/Google APIs)│
                    └─────────────────────────────┘

   ────────────────────────────────────────────────────

   ┌─────────────────┐    ┌──────────────────┐
   │  ReAct Agent    │    │  Plan-and-Execute│
   │  (deep_agents/  │    │  (deep_agents/   │
   │   react_agent)  │    │   plan_execute)  │
   └────────┬────────┘    └────────┬─────────┘
            │     same tool layer   │
            └────────────┬──────────┘
                         ▼
            ┌─────────────────────────┐
            │  deep_agents/tools.py   │
            │  9 tools, 502 lines:    │
            │   query_campaigns       │
            │   query_metrics         │
            │   query_audit_results   │
            │   query_reports         │
            │   get_company_settings  │
            │   calculate_burn_score  │
            │   compare_campaigns     │
            │   generate_burn_alert   │
            └─────────────────────────┘

The five components I built

1. src/deep_agents/react_agent.py — ReAct single-agent

Multi-turn tool-use reasoning via LangChain 1.2.x's create_agent which returns a CompiledStateGraph directly runnable with .stream() / .invoke() — no AgentExecutor needed.

from src.deep_agents.react_agent import build_react_agent

agent = build_react_agent(company_id=uuid, plan="starter")
result = await agent.ainvoke({"messages": [{"role": "user", "content": "..."}]})
# result["messages"] contains the full conversation including tool calls

Streaming protocol yields {"type": "token"|"tool"|"obs"|"final", "content": ...} events so the dashboard can render LLM output in real time.

Design choice: I deliberately did not roll my own ReAct loop. LangChain 1.2.x's create_agent is the canonical ReAct implementation and is what every team will recognize. The value of this file is the streaming protocol and the integration with AMIQ's LLM router (src.llm.router.get_llm), not a custom agent loop.

2. src/deep_agents/plan_execute.py — Plan-and-Execute

Three sub-agents:

  • Planner — single LLM call that emits a structured JSON plan
  • Executor — a ReAct-style agent that runs each step with tools
  • Reducer — single LLM call that synthesizes the step results

The pattern is the classic Wang et al. 2023 "Plan-and-Execute" but implemented in <400 lines by reusing the ReAct agent for the executor sub-agent.

When to use which:

  • ReAct for single, focused investigations ("which campaigns are burning the most this week?")
  • Plan-and-Execute for multi-step analyses ("compare Q1 vs Q2 across all channels, identify the worst 3 campaigns, and propose reallocations")

3. src/deep_agents/tools.py — the tool layer (502 lines)

Nine tools, each one a thin wrapper around AMIQ's existing services:

Tool What it returns
query_campaigns(company_id) List of campaigns with status, channel, spend
query_campaign_metrics(company_id, date_range, ...) Daily metrics with filters
query_audit_results(company_id, run_id) Latest audit findings
query_reports(company_id) Historical natural-language reports
get_company_settings(company_id) Account config, integrations, plan
calculate_burn_score(company_id, ...) Burn score (0–100) with breakdown
compare_campaigns(company_id, campaign_ids) Side-by-side metric comparison
generate_burn_alert(company_id, severity, ...) Insert a Slack/email alert
_interpret_score(score, severity, details) Score → human language

Design choice: every tool takes company_id as a string and validates RLS context internally — agents cannot accidentally query across companies. The tool layer is the trust boundary.

4. src/agents/strategy.py — Strategy Agent with HITL

This is the part I'm most proud of. The Strategy Agent:

  1. Reads audit results
  2. Calls the deep ReAct agent with the audit context
  3. Proposes N strategy actions (e.g., PAUSE_CAMPAIGN, BUDGET_SHIFT)
  4. Confidence-gated:
    • If decision_confidence < 0.50 → only DATA_FIX actions
    • If >= 0.50 → full strategy actions
  5. Writes each proposal to the pending_actions table with status=PENDING

The human-in-the-loop contract:

Strategy Agent → pending_actions table (status=pending)
                              ↓
                  Dashboard surfaces "Review N actions"
                              ↓
              User PATCHes /pending-actions/{id} → approve or reject
                              ↓
                  status=approved|rejected (logged)
                              ↓
              Phase 4+ → write back to Meta/Google APIs

Why the gate at 0.50: Below that, the agent doesn't have enough signal to propose real changes; instead it should be raising tracking issues (DATA_FIX) so that future audits are more reliable. The "refuse to act below 0.50" rule is the single most important piece of operational discipline in the whole system.

5. src/api/routes/pending_actions.py — REST API (323 lines)

Five endpoints:

Method Path What
POST /pending-actions/generate Trigger Strategy Agent, return proposed actions
GET /pending-actions/ List pending actions for the current company
GET /pending-actions/{id} Get one action's full context
PATCH /pending-actions/{id} Approve or reject
GET /pending-actions/stream SSE stream of strategy generation events

Pydantic-validated request/response, Supabase RLS on every query, full audit trail in the pending_actions table.


What I deliberately did NOT do

  • No writebacks to Meta/Google APIs in Phase 3. Every approved action is logged with status=APPROVED but nothing is pushed. The system proposes; the human disposes. The writeback layer is the next phase and needs a separate round of safety review.
  • No custom agent loop. I used LangChain 1.2.x's create_agent rather than rolling my own. The point of this layer is the AMIQ- specific orchestration, not re-implementing ReAct.
  • No fine-tuning. Routing is by plan (starter/pro/enterprise), not by model selection at the agent level. Fine-tuning is a 2027 problem, not a 2026 one.

Lessons from shipping it

  1. Confidence gating is non-negotiable. Without the 0.50 gate, the agent will propose plausible-looking actions on thin evidence. With the gate, it correctly refuses to act on weak audits and instead raises data-quality issues.
  2. The tool layer is the trust boundary. Every tool validates RLS context, every tool returns enough context to be auditable. Tools are the API; the agent is just a router over them.
  3. HITL is the right default for every write action. "Just approve it later" means "approvals are skipped". The pending- actions table is the only way to make review the path of least resistance.
  4. Streaming the LLM tokens is a UX feature, not a perf feature. Users tolerate long agent runs only if they can see what's happening. Without the SSE stream, "the agent is thinking" for 30 seconds looks like a hang.
  5. Don't over-engineer the tool layer. Nine tools is enough. Resist the urge to add query_everything_as_json — it encourages the agent to dump data instead of using focused tools.

File-level reference

All in the live repo at sobhanb-eth/ads-marketing-iq:

File Lines What
src/deep_agents/__init__.py 22 Public exports
src/deep_agents/react_agent.py 344 ReAct single-agent + streaming
src/deep_agents/plan_execute.py 380 Plan-and-Execute three-stage agent
src/deep_agents/tools.py 502 9 tools with RLS-safe context
src/agents/strategy.py 376 Strategy Agent + confidence gating
src/api/routes/pending_actions.py 323 5 REST endpoints for the HITL flow
src/models/pending_action.py 127 Pydantic models + 8 ActionTypes + 5 statuses
supabase/migrations/003_deep_agent_layer.sql 204 pending_actions table, RLS, indexes

Total: 2,278 lines of agent code + 1,415 lines of supporting infrastructure (anomaly scoring, memory service, routing cache, DB migration) = 3,693 insertions in the single feat(amiq): deep agent layer commit.


Tech stack

Layer Choice
Agent framework LangChain 1.2.x create_agent (returns CompiledStateGraph)
LLM routing AMIQ's src.llm.router — Qwen 3.5-9B local (Ollama) → MiniMax M2.7 cloud → OpenRouter fallback
LLM tool-calling schema LangChain @tool decorator on each tools.py function
Streaming Server-Sent Events (SSE) on the FastAPI endpoint, AsyncGenerator on the client
State Supabase PostgreSQL + Row-Level Security (RLS)
Validation Pydantic v2 everywhere
Logging Structured logger.info(..., key=val) style — no f-strings in hot paths

What I'd do differently next time

  • Make confidence gating tunable per-company. Hard-coded 0.50 is fine for the first 5 customers, but a company with $10M/month spend should probably have a 0.70 gate. A setting on the companies table.
  • Add a "show me the plan" preview. Currently the agent goes straight to action proposals; surfacing the ReAct thought process before the action list would make review faster.
  • Per-tool rate limits. Right now a single agent run can hammer query_campaign_metrics 30 times. A per-tool rate limit per audit run would cap the worst case.

License & attribution

This writeup is MIT. The code it describes is proprietary to AMIQ AI and lives upstream at sobhanb-eth/ads-marketing-iq. The architecture described here was designed and shipped by Pouya Zargar in May 2026, on top of the original AMIQ platform by Sobhan Bahrami.

About

Portfolio writeup of the deep-agent layer I shipped for the AMIQ platform (sobhanb-eth/ads-marketing-iq): ReAct + Plan-and-Execute, Strategy Agent with confidence-gated HITL, and the pending-actions API. 3,693 lines of agent code, all upstream.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors