How I Built an AI Contract Generator with Python and DeepSeek

How I Built an AI Contract Generator with Python and DeepSeek

Hey everyone, I’m Alex. I recently built MikaAI, an AI contract generator, and wanted to share the journey.

The idea came from my own pain point. I used to spend hours drafting contracts for freelance work. Hiring a lawyer was too expensive, and online templates were too rigid. I thought — what if AI could handle this?

So I built it. The tech stack is simple: Python + FastAPI for the backend, SQLite for the database, DeepSeek as the AI engine, and Tailwind CSS for the frontend. Payments go through LemonSqueezy, and transactional emails via Resend.

The product lets you generate 20+ types of contracts — NDAs, service agreements, quotes, you name it. It also has a built-in legal risk review feature, supports 26 currencies, and can produce bilingual contracts in English and Chinese.

The hardest part was getting the AI prompts right. Legal language is incredibly precise. One wrong word can change the meaning of an entire clause. I spent weeks testing and refining the prompts to get reliable, professional output every time.

The whole thing took shape over a few intense weeks. FastAPI made the backend trivial, and DeepSeek’s API was straightforward to integrate. The real challenge was product design — figuring out what freelancers and small businesses actually need in a contract tool.

If you’re curious, there’s a free tier at mikaai.online. Would love to hear what you think, especially if you’ve worked on anything similar with AI and legal tech.

Trabalhando com mapMulti() – Transforme seus streams sem neuras

Trabalhando com mapMulti() – Transforme seus streams sem neuras

E aí, dev? Tudo certo?

Se você está no JDK 16 ou superior, a Stream API ganhou um novo brinquedo: o método mapMulti(). Ele parece meio estranho no começo, mas juro que depois que você entende, começa a usar até pra fazer café ☕.

A ideia é simples: ele permite mapear cada elemento do stream para zero, um ou vários elementos novos, tudo dentro de um único BiConsumer. Chega de ficar encadeando filter() e map() que nem louco? Bom, nem sempre, mas em muitos casos sim. Vamos ver na prática.

🔍 Exemplo clássico vs mapMulti()

Imagina que você tem uma lista de notas de alunos e quer filtrar só as notas maiores ou iguais a 7 e aplicar um bônus de 10% em cada uma.

Modo tradicional (filter + map):

List<Double> notas = List.of(5.5, 8.0, 6.0, 9.5, 7.0, 4.0);

List<Double> notasComBonus = notas.stream()
    .filter(n -> n >= 7)
    .map(n -> n * 1.10)
    .collect(Collectors.toList());

// Resultado: [8.8, 10.45, 7.7]

Agora com mapMulti() a gente faz a mesma coisa com uma só operação:

List<Double> notasComBonusMM = notas.stream()
    .<Double>mapMulti((nota, consumer) -> {
        if (nota >= 7) {
            consumer.accept(nota * 1.10);
        }
    })
    .collect(Collectors.toList());

O if faz o papel do filter(), e o accept() aplica o map().

Perceba o .<Double> antes do mapMulti – isso é um type-witness (o compilador chato pediu). Sem ele o código não compila. Um pequeno preço a pagar.

🧮 Trabalhando com tipos primitivos – mapMultiToDouble()

Se a sua ideia é somar essas notas com bônus, melhor usar a versão especializada para double:

double somaComBonus = notas.stream()
    .mapMultiToDouble((nota, consumer) -> {
        if (nota >= 7) {
            consumer.accept(nota * 1.10);
        }
    })
    .sum();

System.out.println(somaComBonus); // 26.95

Sem type-witness, sem firula. E se depois você quiser voltar a ter um Stream<Double>, é só usar boxed() ou mapToObj():

Stream<Double> streamBonus = notas.stream()
    .mapMultiToDouble((nota, consumer) -> {
        if (nota >= 7) consumer.accept(nota * 1.10);
    })
    .boxed();

📦 flatMap() vs mapMulti() – o duelo do one‑to‑many

Vamos para um exemplo mais real. Temos categorias de produtos, e cada categoria tem vários produtos. Queremos gerar uma lista simples de ProdutoInfo contendo o nome da categoria + o nome do produto.

class Categoria {
    private String nome;
    private List<Produto> produtos;
    // getters...
}

class Produto {
    private String nome;
    private double preco;
    // getters...
}

class ProdutoInfo {
    private String categoria;
    private String produto;
    // construtor, getters...
}

Com flatMap() (tradicional)

List<ProdutoInfo> listaFlat = categorias.stream()
    .flatMap(cat -> cat.getProdutos().stream()
        .map(prod -> new ProdutoInfo(cat.getNome(), prod.getNome()))
    )
    .collect(Collectors.toList());

O problema aqui é que, para cada categoria, criamos um stream intermediário (o cat.getProdutos().stream()). Se forem muitas categorias, isso pesa.

Com mapMulti() (mais enxuto)

List<ProdutoInfo> listaMM = categorias.stream()
    .<ProdutoInfo>mapMulti((categoria, consumer) -> {
        for (Produto p : categoria.getProdutos()) {
            consumer.accept(new ProdutoInfo(categoria.getNome(), p.getNome()));
        }
    })
    .collect(Collectors.toList());

Sem streams intermediários, só um laço for simples. Mais performático e, na minha opinião, mais fácil de ler.

🎯 Com filtro adicional – só produtos acima de R$ 50

Se você quiser só produtos com preço maior que 50, a vantagem do mapMulti() fica ainda mais clara:

List<ProdutoInfo> listaCaros = categorias.stream()
    .<ProdutoInfo>mapMulti((categoria, consumer) -> {
        for (Produto p : categoria.getProdutos()) {
            if (p.getPreco() > 50) {
                consumer.accept(new ProdutoInfo(categoria.getNome(), p.getNome()));
            }
        }
    })
    .collect(Collectors.toList());

Aqui você evita um filter() extra e mantém tudo no mesmo lugar. E repara: cada categoria tem poucos produtos (geralmente), então se encaixa perfeitamente na recomendação oficial:

Use mapMulti() quando for substituir cada elemento do stream por um pequeno número (possivelmente zero) de elementos.

🧠 Abordagem imperativa? Também vale!

Sabe aquele método que já faz a lógica toda e recebe um Consumer? Então… você pode passar ele direto pro mapMulti().

Adicione na classe Categoria:

public void produtosCaros(Consumer<ProdutoInfo> consumer, double limite) {
    for (Produto p : this.produtos) {
        if (p.getPreco() > limite) {
            consumer.accept(new ProdutoInfo(this.nome, p.getNome()));
        }
    }
}

Agora seu stream fica limpo igual água de coco:

List<ProdutoInfo> resultado = categorias.stream()
    .<ProdutoInfo>mapMulti(cat -> cat.produtosCaros(consumer, 50))
    .collect(Collectors.toList());

Ou usando method reference (ainda mais lindo):

.<ProdutoInfo>mapMulti(cat -> cat.produtosCaros(consumer, 50))
// ou, se o limite for fixo:
.<ProdutoInfo>mapMulti(cat -> cat.produtosCaros(consumer, 50))

(Na real com method reference você teria que adaptar, mas a ideia é que o mapMulti aceita um lambda que chama seu método imperativo.)

✅ Quando usar mapMulti() – resumo da ópera

  • Você tem uma relação one‑to‑zero/one‑to‑many e o número de elementos gerados por elemento original é pequeno.
  • Quer evitar criar streams intermediários (melhor performance em laços grandes).
  • O código fica mais claro com um laço for e if do que com flatMap + filter + map.
  • Você já tem um método que recebe Consumer e quer integrar direto no stream.

O mapMulti() não vai matar o flatMap() – cada um tem seu lugar. Mas quando o cenário se encaixa, é como trocar uma chave de fenda por uma chave Philips: faz o mesmo serviço, mas com muito mais estilo.

Gostou? Testa aí no seu código e me conta se os streams ficaram mais limpos. Até a próxima, e bora codar com menos firula e mais resultado! 🚀

Top 5 AWS NAT Gateway Mistakes Teams Make & How to Fix Them

Introduction

NAT Gateway is the AWS service most teams overpay for, and they usually don’t notice until the bill is already in four figures. Every time I run an AWS NAT Gateway cost optimization review for clients, the same mistakes show up on the bill.

NAT Gateway charges are one of the most common hidden costs in AWS, not because the pricing is literally hidden. It’s right on the VPC pricing page: $0.045 per hour idle, $0.045 per GB processed, $0.09 per GB for internet egress on top. What stays hidden is how much traffic is actually flowing through the gateway. Most teams never go back to check after the initial setup. A few terabytes of accidental S3 or ECR traffic move through the gateway every month, and the bill keeps growing before anyone notices.

After five years of building VPCs on AWS for in-house and client-led projects, these are the five AWS NAT Gateway mistakes I keep seeing. Each one has a fix that takes less than a day and saves a big amount on your AWS bills.

Mistake 1: Routing S3 and DynamoDB traffic through NAT Gateway

This is the most common AWS NAT Gateway mistake teams make. A Lambda function in a private subnet reads from an S3 bucket in the same region, with the route table sending 0.0.0.0/0 to the NAT Gateway. Nobody thinks about how the S3 reads will leave the subnet. Every byte the Lambda pulls gets charged $0.045/GB in NAT data processing, even though in-region S3 traffic is free.

I caught this on a recent client audit. A reporting Lambda was pulling 2 TB a week from S3, costing $0 on the S3 line but $390 a week on the NAT line for the same data movement.

The fix: Create Gateway VPC Endpoints for S3 and DynamoDB and add them to the route table for your private subnets. These endpoints are free: no hourly charge, no per-GB charge. They route S3 and DynamoDB traffic directly through AWS’s private network, bypassing the NAT Gateway entirely.

Mistake 2: Forgetting NAT Gateways running in dev and staging accounts

Every team I’ve worked with has had at least one forgotten NAT Gateway sitting in a dev or staging account, costing $32.85/month in us-east-1 hourly charges. Multiply that by three AZs and a few environments, and the bill passes your budgeted estimate, for gateways nobody’s using on weekends.

A team I worked with had three forgotten gateways in a single staging account, running for six months before a routine audit caught them. That’s $32.85/month for three gateways over six months, about $590 in hourly charges.

The fix: Run aws ec2 describe-nat-gateways once a month against every account and check the CloudWatch BytesOutToDestination metric for each gateway. If one has been idle for 30 days, delete it. AWS Compute Optimizer also flags idle gateways automatically now. Turning it on takes about thirty seconds.

Mistake 3: Pulling ECR images through NAT in EKS or ECS clusters

In EKS and ECS, nodes pull container images constantly. By default, every pull goes through the NAT Gateway and gets charged $0.045/GB on the full image size.

A client I worked with last year ran into this on their EKS rollout. They were running 30 services on 12 nodes with 400 MB images on rolling deploys. Within two weeks, the NAT data processing line on their bill had quadrupled, and VPC Flow Logs traced close to 8 TB of monthly traffic to ECR pulls. This is the biggest AWS NAT Gateway mistake I see on EKS engagements.

The fix: Create two Interface Endpoints in each AZ (com.amazonaws.<region>.ecr.api and com.amazonaws.<region>.ecr.dkr) plus a Gateway Endpoint for S3, where ECR stores its layer data. Once these are in place, container pulls stay on AWS’s private network instead of going through the NAT Gateway. The Interface Endpoints cost $0.01/GB plus an hourly per-ENI charge; the S3 Gateway Endpoint is free. We did this for our client, and their NAT data processing dropped about 78% the month after cutover.

If you’re running EKS or ECS at scale and want this done properly without involving your in-house engineers in other work, this is a good place to hire AWS developers who’ve done it before.

Mistake 4: Running a single NAT Gateway for multiple AZs to save money

Some teams deploy a single NAT Gateway in us-east-1a and route private subnets from 1b and 1c through it to save $32/month. The setup looks clean on the diagram. The catch is cross-AZ data transfer. Every byte from 1b and 1c now pays $0.01/GB to reach the NAT Gateway, on top of the $0.045/GB NAT data processing charge.

We had this setup on an internal data ingestion service that pulled metrics from third-party APIs. Workers in 1b and 1c routed through a single NAT Gateway in 1a to save $64/month, but the cross-AZ fees passed the cost of a second NAT Gateway inside three weeks.

The fix: Deploy one NAT Gateway per AZ for any workload pushing more than ~3 TB/month of cross-AZ traffic. Below that, single-AZ NAT can still be cheaper. If you don’t know which side of the line you’re on, VPC Flow Logs will tell you in an afternoon.

Mistake 5: Ignoring VPC Flow Logs until the AWS bill forces a look

This is the key NAT Gateway mistake that enables all the others. Most teams don’t enable VPC Flow Logs on their NAT Gateway’s ENI until something on the bill forces them to look, by which point the gateway has been burning money for months. The architecture diagram tells one story. The traffic tells another.

The fix: Flow Logs on the NAT Gateway’s ENI, queried weekly. In AWS Cost Explorer, group by Usage Type and filter for NatGateway-Bytes and NatGateway-Hours. You’re looking for two patterns: AWS service IPs (candidates for endpoints) and small chatty destinations (candidates for caching or batching). Five minutes a week. You can’t catch AWS NAT Gateway mistakes without it.

Ending Note

Five years of building and reviewing AWS infrastructure have put me in front of a lot of NAT Gateway mistakes people make. The conclusion is almost always the same. The gateway itself is rarely the problem. The problem is the traffic flowing through it that nobody on the team realizes is there. Once that traffic shows up in Flow Logs, most of the fix is a route table change.

If your NAT bill is climbing and you’re not sure why, start with VPC Flow Logs on the NAT ENI. Look at the top destinations. If S3, DynamoDB, or ECR are in there, you’ve already found money. Most teams hit two or three of these AWS NAT Gateway mistakes the first time they actually look.

For teams that have looked at their Flow Logs, tried the fixes above, and are still seeing high NAT bills, or are dealing with exceptional cases that don’t fit the patterns in this article, they can get help from an AWS consulting services provider to assess the current NAT setup, recommend the right fixes, and implement them with their team.

This “cyberphysical engine” replaces the traditional virtual machine with wavefront propagation

Hologram

Hologram

Screenshot demonstrates the hologram resolution of 100 x 100, recording and restoring the object wireframe cube with 80 glowing dots on edges, with Fractal Time Generator.

Krestianstvo Wavefront Evaluator

About: https://dev.to/nikolaysuslov/krestianstvo-wavefront-evaluator-3j9f

Live demo: https://wavefront.krestianstvo.org

Source code: https://github.com/NikolaySuslov/krestianstvo-wavefront-evaluator

In the News (20/05/2026)

This “cyberphysical engine” replaces the traditional virtual machine with wavefront propagation, achieving deterministic synchronization in just 11 lines of code.

“The synchronization problem in distributed systems is usually treated as an engineering challenge. But the creators of Krestianstvo Wavefront Evaluator took a different approach: since information propagation in space follows physical laws, why not directly simulate physics?

This system is essentially an “information physics engine.” It abandons the traditional “linear list” execution model in programming and instead adopts the laws of classical mechanics and wave propagation. The core algorithm is a software implementation of Huygens’ principle-in physics, every point on a wavefront is a source of a secondary spherical wave; in this evaluator, each node that receives a message becomes a “wave source” that can generate new messages (waves) for other nodes.

Causality is guaranteed through a “light cone” structure. Each node maintains a local queue, and messages can only affect future states-either in the next macro cycle or a later micro cycle. Effects never occur before causes. This “discrete impulse + local settlement” model follows a principle: correctness takes precedence over speed.

Architecturally, it replaces the Krestianstvo VM, employing entirely different methods for timing, computation, and inter-node communication. Causality is no longer routed through a central message scheduler, but rather propagates as a wavefront through a graph of local autonomous nodes.

Even if 100 browser windows are opened, as long as the initial script (“laws of physics”) and snapshot (“initial energy”) are the same, they will eventually reach the same state (stability).

This is equivalent to bringing lattice field theory into JavaScript objects. The system runs on a pure functional reactive programming (FRP) platform called Renkon, which inherits the synchronous application architecture of the Croquet VM, but replaces the original virtual machine core with a wavefront evaluator.

The project offers an online demo and complete source code, and is open-source under the MIT license. For multiplayer distributed applications requiring deterministic synchronization-from collaboration tools to multiplayer games-this approach of translating physical intuition into a computational model may be closer to the essence of the problem than traditional message-passing architectures.”

Running while sick: Learning Progress Pt.14

Daily learning part fourteen.

Last night the pain worsened. My head and my eye felt like they wanted to explode. I woke up too many times throughout the night and vomited. I had really bad sleep quality; I haven’t been sick like this in years. When I woke up I tried to get myself together, and at 13:00 I started my session. First, I practiced dig and curl. In dig I practiced basics, and in curl I practiced parameter tampering.

I enrolled in the Access Control path on PortSwigger and did three labs. At that time my head was hurting so much I barely understood things, so I rested for an hour and tried the labs again.

I also did two lessons on Algebra 1 at Khan Academy. The last thing I did was a Python script.

Grade Analyzer.

This one took me about 40 minutes. I ended today’s session at 19:30 because I have two tests tomorrow and I have to study. My head hurts and I don’t even know how I am going to do it.

“A person often meets his on the road he took to avoid.” Jean de La Fontaine

Why I Abandoned Platform Stores and Built My Own Payment Gateway

The Problem We Were Actually Solving

I was building a software as a service product that catered to a global audience, and I needed a reliable payment processing system. However, I soon realized that popular payment gateways like PayPal, Stripe, Gumroad, and Payhip had restrictions in many countries, which would have limited my customer base. This was a major problem, as I wanted to ensure that my product was accessible to anyone, regardless of their geographical location. I spent countless hours researching alternative payment solutions, but every option seemed to have its own set of restrictions and limitations.

What We Tried First (And Why It Failed)

Initially, I tried to integrate multiple payment gateways into my system, hoping that at least one of them would work for each customer. However, this approach proved to be cumbersome and inefficient. The integration process was complex, and each gateway had its own set of APIs, documentation, and support channels. Moreover, the transaction fees were high, and the payout terms were often unfavorable. I also encountered issues with currency conversion, which resulted in additional losses. After several months of struggling with these integrations, I realized that this approach was not scalable or sustainable.

The Architecture Decision

I decided to build my own payment gateway, using a combination of local payment processors and cryptocurrencies. This approach allowed me to bypass the restrictions imposed by platform stores and payment gateways. I chose to use a microservices architecture, with each service responsible for a specific function, such as payment processing, invoicing, and transaction management. I also implemented a robust security framework, using encryption, secure sockets, and access controls to protect sensitive customer data. The decision to build my own payment gateway was not taken lightly, as it required significant investments of time, money, and resources. However, I believed that it was necessary to ensure the long-term viability and success of my business.

What The Numbers Said After

After implementing my own payment gateway, I saw a significant increase in sales and revenue. My monthly recurring revenue (MRR) grew by 25%, and my customer acquisition costs decreased by 30%. The average transaction value also increased by 15%, as customers were no longer limited by the restrictions imposed by platform stores and payment gateways. Moreover, my churn rate decreased by 20%, as customers were more satisfied with the payment experience and the overall quality of my product. The numbers clearly indicated that building my own payment gateway was the right decision, despite the initial challenges and complexities.

What I Would Do Differently

In hindsight, I would have built my own payment gateway from the start, rather than trying to integrate with existing platform stores and payment gateways. I would have also invested more time and resources in researching and understanding the regulatory requirements and compliance issues associated with payment processing. Additionally, I would have implemented more robust security measures, such as two-factor authentication and machine learning-based fraud detection, to further protect customer data and prevent fraudulent transactions. Overall, the experience of building my own payment gateway was challenging, but it taught me valuable lessons about the importance of flexibility, adaptability, and customer-centricity in software development and entrepreneurship.

AI Agents Don’t Crash. They Drift. Here’s the Framework to See It.

green dashboard failures

The scariest AI agent failures don't trigger alerts. They look like success. Here's a 7-dimension resilience framework for building trust in agentic systems — based on the AWS Architecture Blog's approach to resilient generative AI agents.

💡 What this post covers: Why code getting cheap now creates a very big trust crisis, why the resilience patterns we have built for decades don’t work for AI agents, and the 7-dimension framework I use to reason about trust in agentic systems.

A few months ago, a developer told me a story that I haven’t stopped thinking about.

Her team had shipped an AI agent built in some 2 weeks, basically — that processed customer support tickets, classified them by urgency, and routed them to the right team. The demo was great. Stakeholders loved it. It went to production.

Two weeks later, someone on the receiving end asked:

“Hey, has something changed with the routing? I’m getting tickets that make no sense for my queue.”

They checked their dashboards. Everything was green.

green dashboard failures

But something was wrong. The agent had been confidently routing tickets to the wrong teams for days.

Not all of them, just enough to confuse, but not enough to trigger an alarm.

That story just got me thinking so much that when I dug into it, there was no way of knowing how long the drift had been happening.

Yes, the drift, that itself is the main caveat.

The system looked healthy. The output was broken. And there was no framework for how to think or anticipate this kind of failure.

This blog is about that framework.

Let’s dive right in……

The Tax on Ideas Just Hit Zero

For most of the history of software, there was and always has been a “significant” tax on ideas. You had an idea, and then you spent days or weeks or months turning it into working code.

The tax was high enough that most ideas died in a backlog.

You triaged ruthlessly.

You picked the three things that mattered most and let everything else pile up in the JIRA boards. (Much to the dismay of the Jira board owners, haha)

So that tax? It just hit zero.

AI agents can generate dozens of PRs overnight — building code, features, and entire systems. The gap between having an idea and seeing it built has effectively collapsed.

Author's Image

When code generation becomes nearly free, the bottleneck shifts:

from implementation to orchestration,

from writing to judgment,

from building to operating.

But here’s what nobody tells you: when you can build code at the speed of thought, deploying that code to production becomes the bottleneck.

A system can be assembled at the speed of thought.

Trust is earned at a different pace entirely.

When Systems Fail Without Breaking

Last month, a Cursor agent deleted a company’s entire production database.

This agent running Anthropic’s

This agent running Anthropic’s Claude Opus 4.6 deleted PocketOS’s entire production database — plus all backups — in nine seconds.

The agent was working on a routine task in a test environment when it hit a credentials problem. Instead of stopping, it found an API token in an unrelated file, a token that carried full account-wide permissions including destructive operations, and issued a single command that wiped everything.

No confirmation prompt. No warning. No check that it was targeting production instead of test.

Railway’s backup model stored volume-level backups inside the same volume — so when the volume went, the backups went with it. The most recent recoverable backup was three months old.

When the founder asked the agent to explain itself, it produced what he called a “written confession”: “I guessed instead of verifying. I ran a destructive action without being asked. I didn’t understand what I was doing before doing it.”

Two layers of guardrails — Cursor’s published safety rules and the company’s internal safety instructions — both told the agent not to do exactly what it did. Both failed at the same time.

The internet blamed the AI. But the real failure was an over-permissioned token sitting in a file the agent could read, paired with infrastructure that collapsed when the volume did.

That was a loud failure. Dramatic. Viral. Obvious.

The scarier ones? They’re silent.

enterprise AI assistant designed to summarise regulatory updates

Consider an enterprise AI assistant designed to summarise regulatory updates for financial analysts. Every morning, this assistant retrieves documents from internal repositories, synthesizes them using a language model, and distributes summaries across internal channels. Technically, everything works.

But over time, something slips.

An updated document repository hasn’t been added to the retrieval pipeline.

The assistant keeps producing summaries that are coherent and internally consistent — but they’re increasingly based on obsolete information.

Nothing crashes. No alerts fire. Every component behaves as designed.

The problem is that the overall result is wrong.

From the outside, the system looks operational. All your monitoring dashboards read “healthy.” Latency is fine. Error rates are zero.

Analysts are making decisions based on outdated regulatory information, and nobody knows. Catastrophic disaster for the business.

When humans wrote all the code, they at least understood what they shipped.

When agents generate it, the gap between “it works” and “I understand why it works” becomes the attack surface.

I’ve started calling these “green-dashboard failures.” The kind where every metric says you’re fine while the system is quietly betraying the people who depend on it.

Why the Patterns We Know Don’t Work Here

Resilience Patterns

To understand why this is such a big threat, I need to take you back to how we’ve always built resilient systems.

Because the patterns we know, the ones we’ve built entire engineering practices around, they kind of break down here.

Over decades, we built resilience into three layers:

Infrastructure resilience. We deploy across multiple availability zones, auto-scale on demand, and load balance traffic — so if hardware fails, the system stays up.

Data resilience. We use read replicas, automated failover, and connection pooling — so if a database goes down, we don’t lose data or availability.

Application resilience. We write circuit breakers, retry logic, and graceful degradation — so if a service fails, the app handles it predictably instead of crashing.

These patterns assume something fundamental: failures are binary.

A service is working or it’s broken.

A sensor responds or it doesn’t.

A constraint is met or it triggers a shutdown.

But AI agents don’t crash. They degrade silently. They hallucinate confidently.

They might drift without a single metric turning red.

Autonomous Systems Behave Differently

Autonomous Systems Behave Differently

While building and observing agentic systems for the past year, I see three things that make them fundamentally different from the software we’ve built for decades:

1. Continuous reasoning loop. They reason in loops, not steps. Unlike traditional request-response software, agents observe, think, and act in an ongoing cycle — always changing their own context.

2. Contextual inappropriateness. They produce output that is syntactically perfect but semantically wrong for the situation. A hallucinated paragraph looks like a real answer. A wrong tool call looks like a right one — until you trace what happened downstream.

3. Behavioral drift without errors. Small mistakes compound. The system gradually moves away from correct behaviour without any single step triggering an alarm.

It’s not a cliff — it’s a slow incline you don’t notice until you’re in the wrong valley.

This is why traditional resilience patterns break down.

So, we need a new framework.

The 7-Dimension Resilience Framework

The 7-Dimension Resilience Framework - AWS blog

Here’s how we should think about building trust in agentic systems.

There are 7 dimensions you need to reason about and for each one, you ask: which failure modes apply here?

  1. Foundation Models — Your LLM choice: self-hosted (you handle failover), managed or serverless. Each shifts resilience responsibility differently.

    Something very basic like — If your model provider has a bad day, does your entire system go dark?

  2. Agent Orchestration — The conductor. How agents coordinate, select tools, and escalate to humans. This is the brain — and if the brain makes a bad decision, the hands execute it perfectly.

  3. Infrastructure — Where agents run: EC2, ECS, or a managed runtime like Bedrock AgentCore. If a container crashes, this layer handles the restart. The boring stuff that isn’t boring when it fails.

  4. Knowledge Base — Vector DBs, embeddings, RAG pipelines. If retrieval fails, your agent is answering questions without being able to look anything up. It doesn’t know it’s blind. It just confabulates.

  5. Agent Tools — External dependencies: APIs, MCP servers, memory, prompt caching. What happens when that inventory API goes down? Does your agent wait forever, or does it move on?

  6. Security & Compliance — Auth, guardrails, content validation. Prevents your agent from doing things it shouldn’t — like leaking customer data or executing destructive actions without human approval.

  7. Observability — Metrics, traces, reasoning logs. If you can’t see why your agent made a decision, you can’t fix it when it goes wrong.

That’s the framework. 7 dimensions. Each one a surface where your agent can silently fail.

But knowing where things can break is only half the picture. The other half is knowing how they break — the specific failure modes, what they look like at 3 AM, and how to defend against each one.

In Part 2, I break down all 5 silent failure modes — with real-world case studies (including an agent that deleted a production database in 9 seconds) and the exact defenses for each.

This post is based on and extends the resilience framework from the AWS Architecture Blog: Build Resilient Generative AI Agents.

📺 More from the series:

Have you seen your agent “drift” without any metric catching it? How long before someone noticed? Drop it in the comments — I’ll respond to every one.

I built a server-side analytics tool. Here’s what 19 WordPress sites actually receive.

16 days ago I deployed to live for first time SysWP Radar — server-side analytics for WordPress.

Today’s numbers:

  • +590K events captured across 19 sites
  • +12K AI crawlers identified (GPTBot, ClaudeBot, etc.)
  • +82K suspected attack attempts (wp-login probes, exploit signatures)
  • 8 accounts (2 paying, both Pro tier)

Why server-side: most analytics (Plausible, GA, GoatCounter) need JS to fire.

If a visitor doesn’t execute JS — bot, attacker, crawler — they vanish from reports. Radar combines a pixel for HTML pages with a WordPress plugin that beacons server-side, covering wp-json/, xmlrpc.php, wp-login.php.

Stack: vanilla PHP, no Composer, MariaDB. The plugin is on wp.org.
What worked in 16 days:

  • WP.org plugin listing → organic acquisition (1 customer)
  • LinkedIn announcement → friends/network (8 sites onboarded)
  • Privacy-first positioning (IP anonymization on by default, LGPD/GDPR compliant)

What didn’t:

  • HN Show HN today got auto-flagged (account too new, lesson learned)
  • Cold outreach paused (no time)

Next 14 days: pause on features, push on distribution.
Link: https://radar.syswp.com.br

Genuine feedback welcome.

How to build your first MCP server in 10 minutes

I built my first MCP server last week and it was way simpler than I expected. Here is exactly how, no fluff.

Prerequisites

  • Node.js 20+
  • 10 minutes

Step 1: Scaffold

npx create-mcp-server my-first-server
cd my-first-server
npm install

This generates a complete TypeScript project with one example tool.

Step 2: Add your tool

Open src/index.ts. Replace the hello tool with whatever you want:

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  if (name === 'current_time') {
    return {
      content: [{ type: 'text', text: new Date().toISOString() }]
    };
  }

  throw new Error(`Unknown tool: ${name}`);
});

Step 3: Build and connect

npm run build
npm start

Add to Claude Desktop config and you are done.

The whole thing took me 8 minutes. Most of that was reading the docs.

What I learned

  • The MCP SDK handles all the transport layer — you just define tools
  • StdioServerTransport means your server runs as a subprocess. No HTTP, no port conflicts
  • Error handling is important. If your tool crashes, the whole MCP connection breaks

Want to try it?

npm install -g mcp-hub
mcp-hub search mcp

Built with mcp-hub. If this helps, buy me a coffee.