DEV Community

Cover image for Full Agentic Stack - 5 Ideias da Arquitetura 'AI-First' que Vão Mudar a Forma Como Você Desenvolve Software
suissAI
suissAI

Posted on

Full Agentic Stack - 5 Ideias da Arquitetura 'AI-First' que Vão Mudar a Forma Como Você Desenvolve Software

Durante décadas, o título de "Full Stack Developer" representou o auge da competência técnica. Era o profissional capaz de navegar com maestria entre o frontend, o backend, o banco de dados e a experiência do usuário. No entanto, uma mudança fundamental está em curso.

A ascensão dos Modelos de Linguagem (LLMs) adicionou uma "camada cognitiva" à pilha de tecnologia. Integrar Inteligência Artificial deixou de ser um extra opcional e se tornou uma obrigação estrutural, tão essencial quanto um banco de dados. A analogia é direta e poderosa: em 5 a 7 anos, sistemas sem inteligência nativa serão tão obsoletos quanto sites sem design responsivo.

É neste cenário que emerge um novo paradigma: a "Full Agentic Stack". Este é um modelo para arquiteturas que nascem com inteligência, projetadas desde o início para pensar e agir. A seguir, exploraremos as cinco ideias mais impactantes desse novo modelo que estão redefinindo o desenvolvimento de software.


  1. A IA não é um 'plugin', é o novo núcleo operacional

A mudança mais profunda é cultural e arquitetônica. Em sistemas tradicionais, a IA é frequentemente um componente adicionado posteriormente para uma funcionalidade específica. Na abordagem AI-First, a IA é a base sobre a qual todo o sistema opera — ela se torna o núcleo operacional.

Essa nova arquitetura, a Full Agentic Stack, é formalmente definida como:

Full Agentic Stack é um ecossistema completo de software composto por camadas cognitivas, autônomas e reativas que operam de forma coreografada para interpretar, decidir e agir sobre eventos em tempo real.

Para um arquiteto, essa definição se traduz em um conjunto de padrões de engenharia conhecidos, operando em sinergia: Arquitetura Orientada a Eventos (EDA), padrões como CQRS e Event Sourcing, e uma infraestrutura composta por agentes cognitivos coreografados que interagem com uma camada de dados reativa e multimodal.

Ignorar essa camada cognitiva é o novo equivalente a tentar construir uma aplicação moderna sem um banco de dados. Assim como um sistema sem banco de dados não é funcional, um sistema sem camada cognitiva não é competitivo. Ele simplesmente não possui a capacidade de aprender, adaptar-se e realizar a coordenação complexa de tarefas que as arquiteturas tradicionais, com sua lógica fixa, não conseguem alcançar.


  1. A sua próxima API será a Linguagem Natural

No modelo tradicional, a interação com um sistema acontece por meio de requisições HTTP diretas, exigindo que o usuário ou outro sistema conheça a estrutura da API. A Full Agentic Stack inverte essa lógica. A interação começa com linguagem natural, que é então traduzida para uma "Intenção" estruturada.

Modelo Requisição
Tradicional HTTP direta para um endpoint específico (GET /api/orders?client=...)
Full Agentic Stack Linguagem Natural → Intenção Estruturada

Vamos a um exemplo prático de e-commerce. Um usuário digita:

“Quero todos os pedidos do cliente João feitos essa semana.”

Um projeto meu chamado "CogGate" interpreta essa frase, via LLM, e a converte em um comando JSON estruturado que o sistema entende:

{
"intent": "Order.listByClient",
"params": {
"client": "João",
"period": "this_week"
}
}

A principal consequência disso é uma interação radicalmente mais intuitiva. O sistema responde à necessidade do usuário sem que ele precise conhecer SQL, nomes de APIs ou a estrutura interna do banco de dados. A complexidade é abstraída pela camada de inteligência.

O CogGate é um projeto que transforma qualquer sistema legado/existente em um MCP Server Cognitivo que consegue converter prompts em Linguagem Natural para execução de funções pré-existentes, só é necessário adicionar 1 prompt que identifique o uso daquela função e o Schema esperado pela função, nessa v0.1.0 eu usei zod. Com isso basta executar o openintent.forger.ts que irá ler seu coggate.json que é apenas isso:

{
  "$schema": "https://neurohive.dev/schemas/cognitivegateway-config.schema.json",
  "version": "1.0.0",
  "mode": "cognitive",
  "description": "Configuração base do Cognitive Gateway para execução de intents via OIP.",
  "oip": {
    "file": "openintent.json",
    "autoRegenerate": true
  },
  "llm": {
    "provider": "openai",
    "model": "gpt-4o-mini",
    "apiKey": "sk-proj-..."
  },
  "controllers": [
    "src/modules/*/controllers/**/*.ts"
  ],
  "gateway": {
    "promptRoute": "/prompt",
    "port": 8080,
    "logLevel": "info",
    "language": "pt-BR"
  },
  "execution": {
    "sandbox": false,
    "maxTokens": 512,
    "timeout": 10000
  }
}
Enter fullscreen mode Exit fullscreen mode

Nessa v0.1.0 eu fiz em cima da arquitetura modularizada onde cada action de um Controler é 1 arquivo modular e atômico, entenda como Controlle o objeto que contém as funções que são executadas pelas rotas. Na v0.2.0 irei implementar o mesmo mecanismo para arquivos de classe e de Controller que possuam diversas funções no mesmo arquivo, irei pegar os casos que são comuns para que o dev não precise ALTERAR nenhum código, APENAS adicionar o prompt e o schema.

Então o openintent.forger.ts irá gerar o openintent.json que é praticamente o openapi para intenções em linguagem natural, ele liga os prompts que identificam cada função, com seu schema, para que a LLM consiga identificar qual função usar baseada no prompt de entrada.

A LLM não executa NADA, ela apenas diz qual função e quais dados devem ser executados para aquele prompt.

Eu apenas envio isso para a LLM:

const list = Object.entries(intents)
      .map(([id, v]) => `${id}: ${v.prompt}`)
      .join("\n");

//...

{ role: "system", content: "Classifique o prompt abaixo em uma das intenções listadas." },
{ role: "user", content: `${list}\n\nPrompt: ${prompt}` }
Enter fullscreen mode Exit fullscreen mode

onde list é um texto que contém o "[nome_função]: prompt de identificação"

Para que a LLM a partir do prompt de identificação ela retorne apenas qual é o Intent (nome da função). Esse é um exemplo como fica no openintent.json:

 "intents": {
    "update": {
      "prompt": "Use esta intenção quando o usuário quiser **atualizar dados existentes**.\n\nExtraia o identificador (nome ou id) e os campos a atualizar.\nRetorne **somente JSON** no formato:\n{\n  \"identifier\": string | number,\n  \"fields\": {\n    \"name\"?: string,\n    \"email\"?: string,\n    \"phone\"?: string,\n    \"role\"?: string\n  }\n}",
      "examples": [],
      "behavior": {
        "controller": "modules.user.controllers.update",
        "operationId": "update"
      },
      "context": {
        "domain": "users",
        "language": "pt-BR"
      }
    },
Enter fullscreen mode Exit fullscreen mode

Perceba que o segredo está no prompt, é nele que o dev precisa declarar o schema que ele espera, não precisa enviar JSON, schemas, nem nada.

Esse é o arquivo User/Controller/update.ts

import { z } from "zod";
import { UserService } from "../services/index.js";

/**
 * @intent update
 * @example Mude o email da Maria para teste@teste.com
 * @context domain:users, language:pt-BR
 */
export const prompt = `Use esta intenção quando o usuário quiser **atualizar dados existentes**.

Extraia o identificador (nome ou id) e os campos a atualizar.
Retorne **somente JSON** no formato:
{
  "identifier": string | number,
  "fields": {
    "name"?: string,
    "email"?: string,
    "phone"?: string,
    "role"?: string
  }
}
`;

export const schema = z.object({
  identifier: z.union([z.string(), z.number()]),
  fields: z.object({
    name: z.string().optional(),
    email: z.string().email().optional(),
    phone: z.string().optional(),
    role: z.enum(["admin", "user", "guest"]).optional()
  })
});

export async function update(req, reply) {
  try {
    const { identifier, fields } = req.body;
    const service = new UserService();
    const updated = await service.update(identifier, fields);
    reply.send({ success: true, data: updated });
  } catch (error) {
    reply.status(400).send({ success: false, error: error.message });
  }
}
Enter fullscreen mode Exit fullscreen mode

Essa arquitetura será a canonica pois eu gostaria de evangelizar essa forma atômica-modular de criar suas funções pois se tovê perceber eu posso fazer:

//actions/controller.update.ts

Enter fullscreen mode Exit fullscreen mode

Isso porque a função que executa na rota não deve possuir regras de negócio, deve apenas validar o contrato/schema para a camada do Domínio poder usar, a estrutura MAIS SIMPLES que conheço é:

routes(gateway de entrada/saída) -> Controller(validação dos dados) -> Service(execução das regras de negócio) -> Repository(persistência dos dados)

Você concorda que não tem como ficar mais simples sem colocar mais responsabilidades do que deve em 1 camada?

Então, as ações das rotas deveriam ser todas genéricas e você usaria apenas 1 factory de routes actions passando apenas o nome da Entidade e o nome da ação para aquela rota.

app.post("/users", RoutesActionsFactory.forge("User", "create")
Enter fullscreen mode Exit fullscreen mode


ts

mais nada, pois você teria o schema em no Data Plane /src/entities/user/schema.json perceba que o nome dos arquivos não deve conter o nome da entidade, apenas o que ele é, pois ele já entá dentro da pasta da Entidade, pois assim facilita a geração dinamica dos códigos, exemplo do JSON Schema:

{
  "$schema": "https://json-schema.org",
  "title": "User",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "username": {
      "type": "string"
    },
    "cpf": { 
      "type": "string", 
      "pattern": "^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$" 
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "phone": {
      "type": "string"
    },
    "password": {
      "type": "string"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    },
    "deletedAt": {
      "type": [
        "string",
        "null"
      ],,
    "default": null
    "format": "date-time"
    }
  },
  "required": [
    "name",
    "username",
    "email",
    "cpf",
    "password",
  ]
}

Enter fullscreen mode Exit fullscreen mode

Você deve se perguntar:

Cadê a função de validar cpf?

E eu respondo:

Com SemanticTypes todo tipo possui 1 validate, isso é padrão.

Mas vamos usar o caso comum: você cria 1 arquivo /src/entities/user/cpf.validate.ts

Para quando for gerar o zod schema e o schema do seu ORM/ODM qualquer propriedade que tenha um arquivo com .validate.ts seja adicionada com um require/import simples sem ter que escrever essa função em cada camada que for usar, não sei você mas o mínimo de validações são 2: route e repositoy. Na minha arquitetura a função validate do SemanticType é executada automaticamente quando recebe 1 valor, o dev não precisa escreve nada. Vai que o valor é modificado no meio do caminho aí você só saberá no final do fluxo, não custa nada importar a função e só chamar

// src/routes/user.routes.ts
import { RoutesActionsFactory } from "../shared/factories/RoutesActionsFactory.js";

export async function userRoutes(app: any) {
// Rotas geradas de forma dinâmica e limpa usando o Factory
app.post("/users", RoutesActionsFactory.forge("User", "create"));
app.put("/users", RoutesActionsFactory.forge("User", "updateBy"));
app.delete("/users", RoutesActionsFactory.forge("User", "deleteBy"));
app.get("/users", RoutesActionsFactory.forge("User", "findAll"));
app.get("/users/:identifier", RoutesActionsFactory.forge("User", "findBy"));
}

Mas não vou me extender nisso, prepararei 1 artigo apenas ensinando a escrevercódigos genéricos que se montam em tempo de execução.


  1. Sistemas que 'pensam' em vez de apenas executar scripts

Arquiteturas convencionais dependem de controladores com lógica fixa e fluxos predefinidos. Se A acontecer, execute B. Na Full Agentic Stack, esse modelo é substituído por uma coreografia de "agentes" autônomos. Cada agente é responsável por um domínio (preços, reclamações, marketing) e eles negociam e se coordenam para cumprir uma tarefa complexa, permitindo um "comportamento emergente".

Considere este cenário avançado: um gestor solicita ao sistema:

“Aumente o desconto dos produtos que tiveram mais de 10 mensagens de reclamação nas últimas 48h.”

Em vez de um script único, uma sequência de eventos e ações coordenadas acontece:

  1. O ComplaintAgent (Agente de Reclamações) detecta os produtos com alta taxa de queixas e publica um evento HighComplaintRate(product_id).
  2. O PricingAgent (Agente de Precificação) consome esse evento, analisa outros fatores (como tendência de vendas) e decide aplicar um novo desconto.
  3. A decisão é registrada e aciona o MarketingAgent (Agente de Marketing) para atualizar o catálogo online e talvez notificar os clientes.

Não há um controlador central ditando cada passo. O resultado final emerge da interação entre agentes especializados, uma "coreografia autônoma" que se adapta ao contexto, habilitada por barramentos de eventos robustos como RabbitMQ, NATS ou Kafka.


  1. O desenvolvedor evolui de programador para coreógrafo de fluxos cognitivos

Essa nova arquitetura exige uma nova identidade para o desenvolvedor. A transição é do "Full Stack Developer" para o "Full Agentic Developer".

Enquanto o primeiro domina as camadas de frontend e backend, o segundo domina os "fluxos cognitivos". A tarefa principal muda. Não se trata mais apenas de escrever código para implementar funcionalidades fixas, mas de projetar, treinar e gerenciar a interação entre agentes inteligentes. O foco se desloca do versionamento de código, como o versionamento semântico (SemVer) que conhecemos, para o versionamento de comportamentos ("Behavioral Versioning"), garantindo que as interações entre os agentes evoluam de forma controlada e previsível.


  1. O futuro é declarativo: sistemas que se autocompõem

A visão final da Full Agentic Stack aponta para uma mudança de paradigma de "como" para "o quê". Em vez de programar os passos exatos que o sistema deve seguir (o como), o desenvolvedor declarará o resultado esperado (o quê), permitindo que agentes autônomos componham micro-serviços em tempo de execução.

Isso se materializa na possibilidade de descrever um sistema inteiro em um arquivo declarativo, como um .tyflow.yaml. Por exemplo:

agents:

  • name: Client events: [ClientCreated, ClientUpdated] storage: postgres
  • name: Order storage: mongodb consumes: [ClientCreated] produces: [OrderPlaced]
  • name: Analytics storage: weaviate listens: [OrderPlaced]

Ao executar tyflow run, o sistema cria filas, APIs, eventos e dashboards automaticamente — sem código manual. Isso aponta para um futuro de "engenharia sem código" — não no sentido de simplicidade, mas de alta abstração, onde a arquitetura se torna auto-adaptativa, reconfigurando fluxos com base no contexto.


O "Full Agentic Stack" não é apenas um novo conjunto de ferramentas; é a evolução lógica do desenvolvimento de software. Ela reconhece que a inteligência não é mais um acessório, mas um requisito fundamental e estrutural. Assim como a nuvem e o design responsivo se tornaram padrões indispensáveis, a inteligência nativa será o próximo pilar do software moderno.

Estamos entrando em uma era onde o objetivo final muda drasticamente. Afinal, o futuro do desenvolvimento não é apenas programar sistemas inteligentes, mas sistemas que programam a si mesmos.

Estamos prontos para essa nova era de desenvolvimento?

Top comments (0)