- Published on
Agentic writer: a journey into agentic systems
- Authors

- Name
- Mattia Peretti
- @mattperetti
How I've built an agentic agency of agents to write and publish articles: https://github.com/foxbit19/agentic-writer

This is a build journal for an end to end writing pipeline that I wanted to be useful, not a demo that produces pretty nonsense. The system, agentic writer, coordinates specialized agents to research, draft, edit, and publish with a human approval gate in the middle. Orchestrating multiple agents matters because a single monolithic prompt tends to collapse into filler. The tradeoff is that orchestration introduces failure modes you have to catch: context loss, token limits, and cost drift.
I built it in TypeScript on Mastra to keep the stack aligned with how I ship backend code. The thread that runs through this piece is the writer and editor loop, how I kept it grounded with notes and operating instructions, and the bug that made the writer invent details after a rejection.
The choice of the right framework
Since my background is in node + ts I prefer mastra over langgraph. Mastra is an open source TypeScript framework for building AI agents and applications. It gives you typed agents with instructions, models, and tools, workflows you compose as a graph, and memory with thread awareness and semantic recall. It ships observability with traces, metrics, logs, and evals, and it routes to many model providers through one interface. It also supports MCP so you can expose agents and tools via the Model Context Protocol.
The architecture
Starting from 2 workflows. The article workflow turns operating instructions and an optional author draft into a researched, written, human approved Markdown article. The social media workflow takes the approved article and turns it into platform native posts and a hero image, then saves the outputs to disk.
In the article workflow, the Researcher agent runs first to gather and structure context. The Writer produces a complete draft, then the Editor enforces the style guide and flags any rejections. A human approval gate sits after the Editor. Nothing moves to social until a person gives the green light. For cost and quality, I tiered models by role: the Researcher uses DeepSeek for efficient breadth, the Writer uses GPT-5 to keep coherence in long drafts, and the Editor uses GPT-5-mini for quicker passes.
Once an article is approved, the social media workflow takes over. The Strategist plans the distribution, the Content Creator adapts the article into platform specific posts, and the Graphic Designer produces a header image with a small vision capable model. In this setup the Strategist runs on GPT-5-nano, the Content Creator on GPT-5-mini, and the Graphic Designer on a GPT-4.1-nano tier. The workflows are available through an MCP server that exposes start, list, and approve tools at localhost:4111. Local profile customization lives alongside the app in user-profile.local.json, so I can swap author details, tone, and model choices without code changes.
The difficulties to train the writer
The writer is a strange beast. It sustains on its shoulders the entire workflow's core. It needs enough freedom to structure a piece and enough constraint to follow a style that serves a real reader, not a demo. Early on it struggled with mode switching. It knew how to output a rough plan and it knew how to output finished prose, but not always when to do which.
At a point it produced an article composed by a list of bullet points. That forced me to harden the instructions about flowing prose, section rhythm, and limited use of lists. Tightening those without suffocating the model was the main iteration loop in the first days.
The severity of the editor
The editor reads from the same article style guide as the writer. Its job is not to rewrite every sentence. It holds the line on structure, voice, and the rules that keep the piece readable for engineers. When the writer drifts into checklist mode, skips source attribution, or forgets the closing section, the editor blocks and sends back specific revision notes. When a draft hits the bar in the first pass, the editor clears it and the workflow prompts the human for approval.
Severity here earns its keep. It is cheaper to run one extra light pass than to publish something that feels like tool output. The editor carries that tradeoff.
The need for two inputs: notes and author instructions
The workflow needs two inputs to stay grounded: the high level notes that name the topic and constraints, and the author operating instructions that fix format, voice, and link rules. Without both, the writer improvises too much. With both, the writer can make local decisions without losing the shape of the piece.
Model tuning follows the same idea. I route research to DeepSeek so the system can afford breadth. I keep the writer on GPT-5 to maintain long form coherence. Lighter roles sit on GPT-5-mini and GPT-5-nano. That mix kept token costs sane and prevented the worst regression, which was the writer flattening sources into generic filler.
The customization
You can customize the author profile, the article style, and the models used by the agents. I keep these in user-profile.local.json so I can nudge tone and defaults per project without touching the core agents. For example, swapping models for a cheaper run or tightening the voice during an edit pass is a config change, not a code change.
Adding skills
Adding skills from skills.sh (http://skills.sh) keeps agents inline with reusable capabilities. It is a directory of composable skills you can wire into tools, so I could add functions instead of rewriting prompts. It helps the researcher and editor share stable building blocks across runs.
The problem with writer
The agentic writing pipeline had a strange bug: draft 001 always reproduced the author draft I provided faithfully but every revision after a rejection came back with an invented things and rewritten links.
The hypothesis was simple: the revision step must be dropping the author draft from the Writer's context. The model wasn't lying; it just couldn't copy what it couldn't see.
The TokenLimiterProcessor capped at 16,000 tokens as a cost guard. Two details mattered: it runs at every step of the agentic loop, and it trims whole messages oldest-first. At step one, the ~11k-token revision prompt fit, and the Writer read it and called its skill tool. At step two, the skill's large tool result filled the budget from the newest end, and the oldest message got trimmed. The oldest message was the revision prompt itself. One step after reading it, the model had lost it, and it rebuilt the article from observational-memory summaries. Draft 001 had survived only because ~6k tokens with no history needed no trimming.
The fix: raise the limit to 64k (a cost guard that can't fit one self-contained turn isn't guarding costs, it's corrupting output), tell the Writer explicitly that the author draft is the verbatim source of truth for quoted material, and put the prompt assembly under unit tests so it can't silently regress.
Adding the writer-editor cycle
With the last version, writer and editor keep talking up to 3 times cycle in order to produce a better draft. If the editor finds an acceptable draft from first interaction, it stops the cycle and alerts the human for approval.
The purpose here is to provide the human with the most acceptable draft without burning too many tokens.
After the human approval or refusal, the cycle never happens. Only isolated writer calls.
What I took from building on Mastra
Mastra moved fast enough that I could ship a real multi agent system without dropping into a Python stack. Typed agents, graph workflows, memory, observability, and human in the loop support were all in one place. The weekly cadence of events and updates made it feel safe to bet on, even if some site dates read like placeholders. Most important, the framework made it possible to build a writer editor loop that behaves like a real production tool instead of a demo prompt.
If you are exploring agentic systems in TypeScript, Mastra is worth watching: https://mastra.ai/