AI-Powered Chief of Staff: Scaling Agent Architecture from Monolith to Distributed System

outropy · Tech · 2024 · source Agentic: yes · promotional bias: low · source-backed · extracted with prompt 0.5.0 (schema v2)

Generated from extraction.json — do not edit by hand. Every claim cites verbatim source evidence; stated means the quote directly asserts the claim, implied means it supports it indirectly.

Problem

Domain: Agent platform architecture for engineering leadership assistants

Outropy needed to scale an AI chief-of-staff assistant from a Slack-bot style product into reliable agent infrastructure that could handle long-running stateful workflows, user memory, tool calls, and growing customer load.

Constraints:

Architecture

Outropy started with a single FastAPI/asyncio ECS monolith containing a Slack-facing Chief of Staff assistant, productivity-tool connectors, inference pipelines, and stateful agent objects. Agent memory moved from mixed ORM tables to a uniform CQRS/event-sourcing model in Postgres, with a semantic event bus letting each agent decide which domain events mattered. Natural-language messages were converted into propositions for retrieval/routing instead of sending every message to every agent. As usage grew, the team added organization-based sharding, a GPTProxy for Azure GPT quota/load balancing, separate connector and agent worker components, and finally Temporal workflows plus activities for durable agent execution and LLM/tool calls.

Diagram confidence: reconstructed (reconstructed from prose).

flowchart LR
  chief-of-staff-assistant[Chief of Staff assistant] --> productivity-connectors[Productivity connectors]
  productivity-connectors --> semantic-event-bus[Semantic event bus]
  semantic-event-bus --> agent-entities[Agent entities]
  agent-entities --> cqrs-event-store[CQRS event store]
  agent-entities --> inference-pipelines[Inference pipelines]
  inference-pipelines --> proposition-generator[Proposition generator]
  organization-sharded-ecs-app[Organization-sharded ECS app] --> chief-of-staff-assistant
  organization-sharded-ecs-app --> gptproxy[GPTProxy]
  connector-worker[Connector worker] --> semantic-event-bus
  agent-entities --> temporal-workflows[Temporal workflows]
Drag to pan · scroll or use +/− to zoom · reconstructed from the source's described component topology

Components

Component Role Interacts with
chief-of-staff-assistant User-facing AI assistant for engineering leaders that unified team-tool information and tracked project developments. productivity-connectors, agent-entities, inference-pipelines
productivity-connectors Slack, GitHub, Google Docs, calendar, email, and related integrations that produced events and fetched source data from team tools. chief-of-staff-assistant, semantic-event-bus, connector-worker
inference-pipelines Deterministic AI processing chains used for specific transformations such as proposition generation, decisions, and tool calling. agent-entities, gptproxy
agent-entities Stateful object-like agents with identity, lifecycle, memory, tools, and decision-making methods. chief-of-staff-assistant, inference-pipelines, cqrs-event-store, semantic-event-bus, temporal-workflows
cqrs-event-store Postgres-backed command log plus query models used to reconstruct and read agent state efficiently. agent-entities, semantic-event-bus
semantic-event-bus Event publication and subscription layer where agents independently filter relevant state changes; initially SQLAlchemy observer events, later Postgres pub/sub. productivity-connectors, agent-entities, cqrs-event-store, connector-worker
proposition-generator LLM-backed conversion of natural-language messages into AMR-inspired factoids for cheaper relevance routing and retrieval. inference-pipelines, agent-entities
organization-sharded-ecs-app FastAPI/asyncio single-component deployment on AWS ECS, later sharded by organization so large accounts did not block smaller ones. chief-of-staff-assistant, gptproxy, connector-worker
gptproxy Dedicated remote service for GPT calls that managed shared Azure GPT quotas and enabled load balancing across deployments. organization-sharded-ecs-app, inference-pipelines
connector-worker Extracted service for productivity-tool data pulling, connected to the rest of the system through Postgres-backed pub/sub. productivity-connectors, semantic-event-bus, organization-sharded-ecs-app
temporal-workflows Durable orchestration substrate for distributed agents: workflows hold side-effect-free agent logic; activities perform tool/API/model calls with retries and durability. agent-entities, inference-pipelines

Data flow

Slack, GitHub, Docs, calendar, and other productivity events entered the assistant through connectors and a semantic event bus. Agents subscribed to state-change events, filtered what mattered, and persisted memory as CQRS commands/query models. Natural-language messages were first converted into propositions, which made retrieval/routing cheaper than invoking every agent pipeline on every message. User-facing agent work ran in the FastAPI/ECS application at first, then split into GPTProxy and connector workers, and finally into Temporal workflows where agent logic stayed in workflows while tool and model calls ran as activities.

4 supporting quotes
“Following this model, all state-change events were posted to an event bus that agents could subscribe to. Each agent filtered out irrelevant events independently, removing the need for external systems to know what they cared about.”article, stated
“Our solution was to use an LLM to generate propositions for every message, structured according to a format inspired by [Abstract Meaning Representation]”article, stated
“We kept our assistant as a single component, with a single code base and a single container image that we deployed using AWS Elastic Container Service.”article, stated
“We implemented our agents’ main logic as Workflows, while tool and API interactions—like AI model calls—became Activities.”article, stated

Why this architecture

The architecture keeps conventional software where it fits and reserves agent machinery for components that need state, tool use, memory, and long-running decision processes. The team rejected stateless microservice assumptions for core agents because agent state, nondeterminism, poor data locality, and unreliable LLM/tool dependencies made fine-grained service extraction brittle. The later decomposition therefore used coarse components, explicit DTOs, shared quota services, and Temporal durability rather than pretending remote agent objects behave like local calls.

4 supporting quotes
“Could we have built our Slackbot and productivity tool connectors using agents? Sure, but the traditional design patterns worked perfectly well, and our limited resources were better spent elsewhere.”article, stated
“Unfortunately, the more we went down the path, the more obvious it became that stateless microservices and AI agents just don’t play nice together.”article, stated
“The problem with the distributed objects craze of the 90s was its promise that fine-grained operations—like iterating through a list of user objects and setting is_enabled to false—could work transparently across processes or servers.”article, stated
“Temporal’s core abstractions mapped perfectly to our object-oriented agents. It splits work between side-effect-free workflows and flexible activities.”article, stated

Models used: LLMs for proposition generation · LLMs for agent reasoning · embedding models

Scale

Alternatives considered (roads not taken)

Stateless service-layer/microservice agents

Rejected because: The model conflicted with agents needing state, nondeterministic execution, heavy context, and slow unreliable model/tool dependencies.

“Initially, we implemented agents as a service layer with traditional request/response cycles:”article, stated
“Agents must maintain rich context across interactions, including conversation history and planning states. This fundamentally conflicts with microservices’ stateless nature and complicates scaling and failover.”article, stated

An all-knowing event router that dispatches every state change to the correct agents

Rejected because: Only each agent knew what mattered; a central router would become a God object with domain logic spread across it.

“Rather than building an all-knowing router to dispatch events to the right agents—risking the creation of a [God object]”article, stated

Sending every Slack/GitHub/Docs/calendar item to every agent pipeline

Rejected because: It would create performance and cost problems because most content was irrelevant to most agents yet would still trigger LLM calls.

“each agent would need to run its inference pipeline for every piece of content. This would offer all sorts of performance and cost issues due to frequent calls to LLMs and other models”article, stated

Simpler ML feature extraction for message routing

Rejected because: The team tried it but found proposition-based retrieval better for this open-ended product, while noting simpler extraction can still work in constrained domains.

“We wrestled with this problem for a while, exploring some initially promising but ultimately unsuccessful attempts at Feature Extraction using simpler ML models instead of LLMs.”article, stated

Airflow, Lambda, or serverless orchestration for distributed agents

Rejected because: Those systems were optimized for stateless, scheduled, or short-lived tasks, while these agents needed durable long-running stateful workflows.

“We first looked at ETL tools like Apache Airflow. While great for data engineering, Airflow’s focus on stateless, scheduled tasks wasn’t a good fit for our agents’ stateful, event-driven operations.”article, stated
“while serverless has evolved significantly, it’s still optimized for stateless, short-lived tasks—the opposite of what our agents need.”article, stated

Patterns

agent-memory (context)

Outropy used CQRS and event sourcing so agents could reconstruct and query state from recorded changes.

“After some experimentation, we adopted CQRS with Event Sourcing.”article, stated

durable-execution (orchestration)

Agent work was moved into Temporal workflows so long-running stateful computations could get retries, durability, and scalability.

“We migrated our agents to run all their computations through Temporal workflows. Temporal’s core abstractions mapped perfectly to our object-oriented agents.”article, stated

rag-retrieval (retrieval)

The team converted structured messages into natural-language propositions before embedding to make vector retrieval more effective.

“Our solution built on Tong Chen’s Proposition-Based Retrieval research.”article, stated

tool-use-orchestration (orchestration)

Outropy modeled agents as stateful objects exposing tools and decision methods through inference pipelines.

“Agents naturally align with OOP principles: they maintain encapsulated state (their memory), expose methods (their tools and decision-making capabilities via inference pipelines), and communicate through message passing.”article, stated

Tradeoffs

How to model agents inside the application.

Chose stateful object-like agents over stateless microservice-style service layers.

Rationale: Agents needed memory, planning state, and tool orchestration that did not match small stateless request/response services.

“Agents must maintain rich context across interactions, including conversation history and planning states. This fundamentally conflicts with microservices’ stateless nature and complicates scaling and failover.”article, stated

How to scale personalized daily briefings quickly.

Chose midnight batch processing plus organization-based sharding over per-user reports generated around each user's login time.

Rationale: The team sacrificed personalization timing to get a practical scaling path while demand kept growing.

“we had to abandon this personalization and batch process everything at midnight, hoping reports would be ready when users logged in.”article, stated

Which workflow infrastructure to use for stateful agents.

Chose Temporal workflows over Lambda, serverless, or Airflow-style stateless scheduled tasks.

Rationale: Temporal matched the need for long-running stateful workflows, durability, retries, and multi-language support.

“it’s still optimized for stateless, short-lived tasks—the opposite of what our agents need. I’d heard great things about Temporal from my previous teams at DigitalOcean.”article, stated

Whether to keep both ORM-backed simple agents and CQRS-backed complex agents.

Chose convert all agents to CQRS style while retaining ORM for non-agentic components over maintaining mixed ORM and CQRS persistence styles across agents.

Rationale: The dual model made refactorings and shared agent logic cumbersome; a uniform event-sourced agent model reduced architectural friction.

“Managing both the ORM approach for simpler objects and CQRS for more complex needs grew increasingly cumbersome. Small refactorings or shared logic across all agents became harder than necessary.”article, stated

How to manage shared GPT quota at scale.

Chose extract GPT calls into GPTProxy and load-balance across Azure GPT deployments over adding distributed locks around model calls in the monolith.

Rationale: Azure per-deployment quotas gave a scalable quota unit; a dedicated proxy centralized the shared quota concern without spreading locking through the app.

“This let us scale by load-balancing across multiple deployments. To manage the shared quota, we extracted our GPT calling code into a dedicated service rather than adding distributed locks”article, stated

Gotchas (what bit them)

Microservice architecture assumptions broke down for agents.

Root cause: Agents carry state, behave non-deterministically, process large context, and depend on slow external AI APIs.

Resolution: Model agents as stateful objects and run long-lived work through durable workflow infrastructure.

“Unfortunately, the more we went down the path, the more obvious it became that stateless microservices and AI agents just don’t play nice together.”article, stated

Scaling daily briefings created account-level contention.

Root cause: Large organizations could block smaller ones when all users' reports had to be generated overnight.

Resolution: Shard by organization, dedicate resources to large accounts, and prioritize active users.

“This simple change gave us breathing room by preventing larger accounts from blocking smaller ones. We also added priority processing, deprioritizing inactive users and those we learned were away from work.”article, stated

Temporal solved orchestration but introduced Python integration friction.

Root cause: The Python SDK did not fit common libraries and favored data classes over the team's Pydantic-heavy stack.

Resolution: Build converters and exception wrappers around Temporal's Python SDK.

“Temporal’s Python SDK felt like a second-class citizen—even using standard libraries like Pydantic was a challenge, as the framework favors data classes.”article, stated

Evaluation

The post reports production scaling lessons from a live assistant with 10,000 users, plus qualitative architecture lessons from failed and successful implementation attempts.

Metrics: 10,000 users within a year · personal daily briefings went from minutes to hours per user before sharding · large organizations received dedicated resources

“Within a year, we attracted 10,000 users, outperforming even deep-pocketed incumbents such as Salesforce and Slack AI.”article, stated
“Our personal daily briefings—our flagship feature—went from taking minutes to hours per user.”article, stated

Outcomes

Practitioner-authored post with unusually candid architecture lessons; some business claims are still company-authored.

“This led us to pivot to Outropy, a developer platform that enables software engineers to build AI products.”article, stated
“We were so impressed that Temporal became core to both our inference pipelines and Outropy’s evolution into a developer platform!”article, stated