I use an AI agent to help me communicate. Not to write for me, but to help me be more efficient and as English is my second language it helps me make fewer mistakes, but the output should always sound like Antonio. It reviews my drafts, helps me write outreach, and prepares me before important conversations.

I built this on top of OpenClaw and on Claude Code which I can access via Telegram, runs persistent sessions, and lets you configure agents with custom behavior files. If you prefer to stay in the terminal, the same patterns work with Claude Code and a CLAUDE.md file and creating skills, more on that at the end.

This was one of the first agents I created, because it was an easy transition from what I was already doing on ChatGPT. It works well. But for a while, it wasn’t reliable enough.

The agent’s behavior was good on average but inconsistent message to message. Paste a draft and sometimes it would review it. Sometimes it would try to rewrite it. Sometimes it would ask clarifying questions when I just wanted a clean copy. The behavior depended on how I phrased the request and what context the agent happened to load that session.

That’s a soft system. Soft systems have good days and bad days.

I wanted something more deterministic.


One Rule Before We Start: The Agent Never Sends or Publishes#

This is worth saying explicitly because it’s easy to get wrong.

My communications agent has exactly one job with external communications: draft, review, and iterate. It never sends anything on its or my behalf. Not an email, not a Slack message, not a LinkedIn DM. Every single thing it produces sits in my hands until I decide what to do with it.

This isn’t just a safety guardrail. It’s a design principle. The agent has no context about timing, relationship dynamics, or what happened in a meeting an hour ago. I do. The agent is good at grammar. I’m responsible for judging several other variables before deciding to send or publish.

The right mental model: the agent is a ghostwriter, not a sender. Everything it writes is a draft until I take action on it.


The Problem with only using LLM Inference#

The agent I use has a configuration file called SOUL.md on OpenClaw and CLAUDE.md in Claude Code. It defines how it should behave: what it does, what it never does, what tone to use, what to watch out for.

One section I added said something like: “When Antonio pastes a message without instructions, automatically review it for grammar, consistency, and typos.”

That worked most of the time. But “most of the time” isn’t deterministic. The agent has to infer that I’m pasting content for review, not asking a question about it. It has to infer I want grammar fixed, not a rewrite. It has to infer I want Portuguese treated as Portuguese, not corrected into English.

I first tried adding a Humanize skill to make the output feel less “agentic” 🤖. It helped a little, but the results were still too generic, they didn’t sound like me. The agent was writing like a human, just not like this human.

Inference is fragile. It’s also invisible, when it goes wrong, you don’t know why.

The fix wasn’t better instructions (I tried). It was removing the inference and keeping it to a minimum.


Slash Commands as a Contract#

Telegram supports native slash commands. When you register them with @BotFather, they autocomplete as soon as you type /. Tap the command, paste your content, and the agent knows exactly what you want before it reads a single word. Claude Code also supports native slash commands that are connected to skills.

No inference. No ambiguity. The command is the intent.

So far I built eight of them, based on what I actually use this agent for.


The Commands#

/review Grammar, clarity, consistency, and typo fix. No rewrites. Returns the corrected version and a short diff of what changed. If the text is in Portuguese, it reviews in Portuguese - no translation.

This is the one I use most. A review should take a few seconds, not a judgment call about what I wanted.

/reply I paste a message I received. The agent drafts two versions: one longer and warmer, one tighter. Both in my voice. It infers the channel (email, Slack, iMessage) from context and adjusts the register accordingly. I pick one, edit it if needed, and send it myself.

/outreach Cold or warm outreach to a specific person. I give a name and brief context. The agent pulls the history from my Personal CRM app and LinkedIn data, then drafts using the patterns I’ve established for outreach.

/announce Org or team communication. Slack post or email, depending on what I specify. It follows the patterns I use for internal announcements: problem before solution, credit collaborators, close with a concrete next step.

/blog Long-form article for this site. I give a detailed prompt about a topic I am working on and add specific areas where I need to find evidence or need more background research. It researches what’s already been written, then drafts 600-1200 words with a structured argument, focusing on readers learning something from my experiences. In the end it suggests several closing approaches and also makes several suggestions for the title based on the overall content. This post was drafted with /blog.

/post Short LinkedIn/Twitter post or repost comment. Character-first, no corporate lingo, don’t over-emoji it. The draft sits in my hands until I post it. I use this command a lot in conjunction with /blog.

/prep Pre-meeting brief. I give a name and context. The agent pulls everything available: CRM history of interactions, LinkedIn profile, prior email threads, any memory from past sessions. Returns a structured brief: who they are, what they care about, our history, what I’m going into the conversation wanting, and what to watch for.

/feedback Strategic tone and framing review. Not grammar verification - is this landing the way I think it is? Returns an overall assessment, what lands well, what might be misread, and one concrete suggestion per issue. If it’s clean, it says “This lands well. Ready to send.” and stops.


What Changed#

Before slash commands, using my communications agent felt like working with a capable colleague who sometimes misread the room. The output was good but I couldn’t predict which version of the agent I was getting.

After slash commands, it’s now deterministic. I know exactly what each command does and exactly what I’ll get back. The agent doesn’t have to guess. I don’t have to re-explain myself every session.

The quality didn’t change. The reliability did.


The Bigger Pattern#

This isn’t really about Telegram or slash commands. It’s about the difference between an AI system that interprets and an AI system that executes.

Interpretation is useful for open-ended work where the problem isn’t well-defined yet - one of my engineers calls this “Online AI interactions”. For everything else - reviews, drafts, briefs - interpretation is overhead. You spend cognitive load managing the agent’s uncertainty instead of doing the work.

Deterministic commands collapse that overhead. The agent knows the workflow, the output format, and the constraints before you paste a single word. Your job is to give it the right input. Its job is to execute the right workflow. Now we have a clean division of labor.

I have a companion file called VOICE.md that defines how I write. It has examples of actual messages I’ve sent, patterns that work, things that don’t. That’s what makes the output actually sound like me rather than a generic professional tone. I plan on writing more about that in a separate post.

For now: if your AI agent’s behavior depends on how you phrase your request, that’s an opportunity to improve it - and native slash commands are a great design option to help you fix it.


How to Do This with Claude Code#

If you’re not using OpenClaw and prefer to work directly in the terminal with Claude Code, the same pattern applies - it just lives in your global CLAUDE.md file instead of SOUL.md.

The setup has three layers: a skill file, routing instructions, and Telegram configuration.

Define the skill#

Create a file at ~/.claude/skills/review/SKILL.md. Placing it in ~/.claude/skills/ makes it global, available in every project. The full docs on creating skills are on Anthropic’s site.

---
name: review
description: Review text for grammar, clarity, and typos. Returns corrected version and a diff of changes.
---

Grammar, clarity, and typo review. Do NOT rewrite - only fix errors.

1. Fix: grammar errors, spelling/typos, consistency issues, clarity issues
2. Do NOT change: tone, sentiment, structure, word choice (unless wrong/unclear)
3. Return the corrected version in full
4. Follow with a bullet diff - max 6 bullets, what changed and why

Output format:
[corrected version]

---
Changes:
  - [change 1 - reason]
  - [change 2 - reason]

The name becomes the slash command in your Claude terminal UI. The description helps Claude load it automatically when relevant. The body is the prompt: what to do, how to format output, what constraints to follow. Be specific. “Review this text” is vague. “Fix errors, don’t rewrite, return a diff” produces consistent results.

Add Telegram routing#

For Telegram, add the same workflow to your ~/.claude/CLAUDE.md under a “Telegram Slash Commands” section. This tells Claude how to handle messages arriving from Telegram that start with /review.

Register with BotFather#

Open @BotFather in Telegram, send /setcommands, select your bot, and paste:

review - Review for grammar, clarity, typos

This makes /review show up in Telegram’s command menu. Same command, same workflow, both surfaces.

The only difference from the OpenClaw approach is persistence. With Claude Code, each new session re-reads CLAUDE.md from scratch. With OpenClaw, the agent carries memory across sessions, which matters for commands like /prep and /outreach where prior context is part of the output quality.

Either way, the principle is the same. Deterministic input, deterministic workflow, deterministic output.

Open Source GitHub project to get you started#

If you want to give this a try, I open-sourced a full agent template as ghostwriter-agent. It includes all eight commands, sample identity files, and setup instructions for both OpenClaw and Claude Code. Clone it, fill in your information and context files, and start writing with your new partner.


This post is part of an ongoing series about how I’m running AI agents in my day-to-day to help me with my work and personal tasks. Earlier posts: How I Use AI Agents to Build Software.