Projects

Sector: Own IP

Production

cortex-ai — one agentic routing engine, two products in production

The same engine that answers in this site's chat decides campaign budgets in a commercial product.

Try it in the chat
Architecture diagram

cortex-ai was born for Luna Azul legal system. When a decision engine was needed for a campaign-optimization product —a domain with no relation to the previous one— the problem was taking it there without coupling the two products. Copying diverges; sharing without control breaks.

I wrote cortex-ai in Go: it receives the conversation and dispatches it to the right agent by name, without running the model itself. It now powers two products in production: it is the engine answering in this site's chat. The second deployment is not a copy but a one-way propagation, with an allowlist verified against the code's real imports.

On top of it I built the full commercial product: the data architecture (Bronze → Silver → Gold) and a Go microservices backend deployed as AWS Lambdas (go-api, api-read, api-command), with a rules engine already running in production and an explicit trust boundary between what a deterministic rule decides and what an LLM may suggest.

Key metrics

products in production on the same engine, in unrelated domains — one verifiable live on this site
2products in production on the same engine, in unrelated domains — one verifiable live on this site
business rules in the decision engine · 28 green tests, explicit precedence
27business rules in the decision engine · 28 green tests, explicit precedence
knowledge chunks indexed for RAG · validated retrieval
489knowledge chunks indexed for RAG · validated retrieval
of ad spend measured today · 3 of 6 channels — gap documented, not hidden
63%of ad spend measured today · 3 of 6 channels — gap documented, not hidden

Stack

  • Go
  • AWS Lambda
  • Function URL
  • pgvector
  • RAG
  • CQRS

Decisions and trade-offs

  1. Adopt cortex-ai, don’t fork it

    cortex-ai was born for Luna Azul, in a separate repository (agent-platform), as a private source of truth. Instead of copying and diverging, I designed a one-way propagation mechanism: a curation script with a granular allowlist (verified against the code’s real imports, not a whole folder) generates a self-contained, versioned copy inside travel-marketing, which never knows agent-platform exists. The cost: any change to the engine always originates in the private repo and propagates afterward — never the reverse — and that demands process discipline (an anti-drift guardrail that compares before overwriting), not just good intentions.

  2. No fallbacks: if data is missing, it shows as missing

    The rules engine does not fill gaps with a default value: if a profitability threshold does not resolve for a market, the result is null and the rule degrades visibly, with the reason written into the decision’s own record. The reason is not abstract: before this rule, a market without its own threshold fell back to the generic vertical value —and Spain ended up being judged with Peru’s economy without anyone seeing it, because the system was filling in silently. It is the same class of error as the broken JOIN in the finance case: a system that returns an answer instead of admitting it doesn’t have one — here, fixed in the design principle before it produced its own incident.

  3. The LLM suggests, never decides alone

    The conversational agent (brain, in English, a DecisionEnvelope structure; gala, in Spanish, explains the decision to the user) can read the engine’s state and propose an action, but there is a server-side trust boundary: the deterministic blocks of the rules engine cannot be altered by the LLM, and any divergence between what the LLM suggests and what the engine decides is logged, not hidden. The explicit consequence: a decision’s numbers always come from the rules, never from the RAG — semantic search confuses 1.3 with 1.25, and that class of error is not acceptable in a budget decision.

  4. Wave 1 before Wave 2, and we say which is which

    The system was built as a walking skeleton first: real reads, a real rules engine, decisions that are logged and can be approved or deferred — but real execution against the advertising platforms (actually moving budget in Meta/Google/TikTok) is the next wave, not yet built. api-command validates and queues each action, it does not execute it. And the go-api gateway —the piece that in production would validate login and route to the rest— is still a skeleton with no real logic. It is declared so in the code itself (the deployment module literally marks it as "STUB — skeleton, no real logic"), and it is declared the same way here: it is not an omission, it is the right order in which to build a system that can move real money.