Inspiration

Odin had two ravens: Huginn, who flew out over the world to think, and Muninn, who remembered what he saw. Every morning they went out, and every evening they came back with what mattered. That's the shape of what a good marketing agent should be too. Not a tool that generates an ad and forgets it ever existed, but something that watches what happened last time, carries that forward, and gets sharper with every cycle instead of repeating the same mistakes forever.

Most "AI marketing" tools are stateless. Ask for an ad, get an ad, ask again tomorrow and it has no idea what happened yesterday. We wanted to build the opposite: an agent whose memory is the whole point, not a bolt-on. That's why CockroachDB isn't just where we happen to store rows, it's the thing that makes the agent an agent instead of a script that calls an LLM in a loop.

What it does

Humin runs a full Perceive, Remember, Think, Act, Learn loop for every ad campaign it manages:

  • Perceive: pulls the campaign's own recent performance and a live external trend signal.
  • Remember (Muninn): embeds the current situation and runs a vector similarity search across every past campaign, in every region, stored in CockroachDB, to recall what actually worked in similar situations, not just this campaign's own history.
  • Think (Huginn): hands performance data, recalled precedent, and the trend signal to Claude on Amazon Bedrock, which decides whether to keep the current angle, tweak it, pivot the message or channel, or kill the direction entirely, and drafts new ad copy and a visual creative direction to match.
  • Act: writes the new ad back into CockroachDB. If the campaign is set to autonomous mode it publishes immediately; if it's set to review mode, a human sees the draft first and can approve it as is, edit it, send it back with feedback for a genuine redraft, or discard it outright.
  • Learn: a simulated outcome closes the loop, and that outcome becomes memory for the next cycle.

Guardrails are enforced in code, not just requested in a prompt. A kill recommendation needs at least three cycles of sustained evidence before it's allowed to fire. A bold pivot gets capped back down to a smaller move when the evidence behind it is thin. A budget cap that's nearly spent forces the agent to stop starting new experiments. None of that is the model's choice to make, on purpose.

How we built it

The backend is FastAPI, deployed as two AWS Lambda functions behind the same codebase: one serves the REST API behind a Function URL, the other runs one full agent cycle for every active campaign on an Amazon EventBridge schedule. The frontend is a Next.js dashboard deployed on Netlify.

CockroachDB does two jobs that both count toward the hackathon's tool requirement, and neither is decorative:

  • Distributed Vector Indexing: every piece of ad copy gets embedded (Amazon Titan) and stored in a native VECTOR column with a real vector index. Every Remember phase, for every campaign, queries that index across the entire cluster.
  • Cloud Managed MCP Server: the same similarity search is also routed through CockroachDB's managed MCP server as a genuine second read path, with the direct driver as a fallback if MCP is ever unavailable. We verified this is actually happening in production, not just configured, by checking CockroachDB's own query statistics and finding real queries tagged with the MCP server's application name.

Amazon Bedrock does the actual reasoning (Claude) and the embeddings (Titan), and also generates a visual creative direction for each ad, then attempts to render an actual image for it. The previous cycle's real creative image gets sent back into the next cycle's reasoning call as an actual vision input, so a pivot decision can weigh the creative itself, not just the copy.

Every mock switch has a matching real path. The whole agent loop runs fully offline with zero cloud credentials, using a dependency-free in-memory repository and a deterministic mock reasoning engine that makes the same kind of conditional decisions a real LLM would, so the core logic is testable and demoable without needing live infrastructure, and then flips over to the real CockroachDB cluster and real Bedrock calls with the same code path.

Challenges we ran into

The Cloud Managed MCP Server integration looked done and wasn't. Once we had real credentials against a live cluster, we found and fixed five separate bugs in a row: a missing required header for selecting which cluster a request targets, an async context manager bug that only surfaces as a cryptic "cancel scope in a different task" error, an SDK version mismatch in how the underlying transport hands back its read and write streams, a required argument the query tool needed that nothing was supplying, and a tool-selection heuristic that confidently picked a schema-editing tool instead of the actual read-only query tool because it matched on the wrong signal. Every one of those had to be diagnosed by reproducing the exact failure locally against the real cluster, not guessed at from documentation, because CockroachDB's actual MCP tool surface isn't something we had a live endpoint to verify against until credentials were in hand.

AWS Lambda Function URLs quietly require two separate permission statements to accept public invocations, not one, and the second one isn't obvious from the error message you get without it. A non-ASCII character in AI-generated ad copy came back corrupted in every API response until we traced it to how Mangum hands the response body back to the Function URL runtime. And more than once, logging.basicConfig() silently did nothing on Lambda specifically, because the runtime attaches its own handler to the root logger before application code ever runs, so a log line we were relying on to debug a live issue simply never appeared until we set the logger's level directly instead.

What we learned

The gap between "the client code compiles and looks right" and "this actually works against the real managed service" was much wider than expected, especially for the newest CockroachDB Cloud AI tooling, which doesn't have a large body of working examples yet. Reproducing failures locally against real production credentials, one at a time, was the only way through, not reading the tool's schema more carefully.

We also learned that guardrails belong in code, not prompts. It's tempting to just ask the model nicely to be cautious. It listens most of the time. Code doesn't have "most of the time."

What's next for Humin

The two CockroachDB tools we didn't get to yet, ccloud CLI and the Agent Skills Repo, are the most obvious next step. ccloud CLI fits naturally as an ops layer for provisioning and monitoring the cluster the agent depends on, and the Agent Skills Repo is a real opportunity to package the reasoning patterns we hand-built here (the guardrail logic, the plateau detection, the evidence scoring) as reusable, versioned skills other agents could pull in instead of reimplementing.

Right now, the "Learn" phase closes the loop with a simulator, grounded by a real external trend signal but not real ad spend. The natural next step is a real ad platform integration (Meta or Google Ads) so Act actually publishes somewhere real and Learn reads back real performance instead of a modeled outcome. That's a meaningfully bigger scope, real OAuth flows, real spend, real approval processes, which is exactly why we scoped it out for the hackathon rather than half-build it.

Image generation is implemented end to end (prompt generation, storage, upload, and feeding the previous cycle's actual creative back into the next reasoning pass as a real vision input) but currently blocked in production by an AWS policy issue on the specific image model we targeted, unrelated to our code. Getting that reactivated, or switching to another Bedrock image model, would make the visual side of the agent as live as the copy side already is.

Longer term, the most interesting direction is letting the agent reason across campaigns, not just within one. Right now, each campaign's Think phase optimizes for itself. A fleet-level agent that reallocates budget across campaigns based on which ones are actually converting, using the same memory layer, is a natural extension of an architecture that already treats CockroachDB as one shared brain instead of per-campaign silos.

Built With

  • amazon-bedrock
  • amazon-eventbridge
  • amazon-nova-canvas
  • amazon-titan
  • apscheduler
  • aws-lambda
  • boto3
  • claude
  • cockroachdb
  • cockroachdb-cloud
  • fastapi
  • git
  • github
  • mangum
  • model-context-protocol
  • netlify
  • next.js
  • psycopg2
  • python
  • pytrends
  • react
  • recharts
  • typescript
  • vector-search
Share this project:

Updates

Submission history