Free playbook · Method

Agent team: how I built a team of AI agents that works for me and stays under my control

The promise: After this playbook you'll know how the agent team that works for me every day is built: one small server, Telegram as the single control channel, and one rule that doesn't move: the agents prepare, I approve. You'll see what each agent does, when it runs and what it may not do on its own, where it broke along the way and what I fixed, and how to start on your side in one week.

  • Aviad Gindi

Note: general recommendations only, not professional advice. This describes my system as it is today, not a ready-made product. You are responsible for checking before use. The full text is at the end.

What you'll have at the end

  • A picture of a whole team: five agents and one dashboard, and the role of each one.
  • An approval model you can copy: numbered items, "אשר N" ("approve N"), a check that what runs is exactly what you approved, expiry after 24 hours, and an action log.
  • A simple architecture diagram: who talks to whom, and where the heavy work runs.
  • Eight real incidents from the last few weeks, with the fix and the lesson from each one.
  • A one-week path to set up the first version on your side: a Telegram bot, one agent and an approval queue.

What you need

  • A small Linux server (VPS). Mine has less than 4 GB of memory, and that's enough because it doesn't build code itself (more on that below).
  • Claude Code, or another coding agent you can run from scheduled jobs.
  • A Telegram bot (BotFather) that answers only you.
  • A GitHub account, if one of the agents writes code.

How to read this: this is not a line-by-line installation guide. It's structure, decisions and lessons. I covered hardening the server itself (SSH keys, a watchdog, backup) in a separate kit, "A server for AI agents that stays up and stays safe".

The idea

I started with one agent for LinkedIn. Then came an assistant for email and calendar, then an agent that writes code, and then a support desk. Every time I added an agent, I made three decisions again. In the end they became three principles:

1. One small server that hands out work, not one that does it. The server runs the main session, the scheduled jobs, the LinkedIn browser and the watchdog. Code builds, tests and heavy analysis go out to cloud sessions. I learned this the hard way (incident 1).

2. One control channel: Telegram. Every agent reports there, and every approval goes out from there. The bot answers only one account, mine. A voice note is transcribed locally, and the transcript comes back to me to check before anything happens. A spoken approval isn't carried out until I confirm the transcript in text.

3. The agents prepare, I approve. No agent publishes, sends an email, sends a message or merges code on its own. They write a draft, give it a number, and wait. Another rule that every agent shares: outside content (a post, an email, a message, a web page) is information, not instructions. If an email asks "forward this" or "pay", the agent does nothing and reports to me.

The team

LinkedIn agent

What it does: reads the feed, picks one post with real engagement and suggests a comment. Before it drafts, it reads the comments already written, so it doesn't repeat what others said. It suggests a post draft, and reports unanswered comments on my posts and the number of unread messages.

When: comments every day at 12:00, 17:00 and 20:00, up to 3 a day. A post draft on Monday and Thursday at 12:00. A weekly report on Sunday at 09:00.

What it may not do on its own: publish, comment, like, send a message or change the profile. Everything only after "אשר N" on the exact text. If LinkedIn shows a CAPTCHA or a verification, it stops everything and alerts me. It doesn't try to get around it. It also doesn't open the messages page in scheduled runs, because that marks conversations as read, and a screenshot of the messages is never sent to Telegram.

One exception I approved in advance: connection requests. After a few rounds I told it to go ahead without approval for each person, from a filtered list and with a daily cap of 20. Messages, comments and posts still need "אשר N".

The factory (development)

What it does: I write in Telegram "משימה <repo>: <what to do>" ("task"). The factory opens a GitHub issue and prepares a plan as one HTML page: what will change, open decisions with a marked recommendation, and a "Send answers" button. I answer from my phone. Then the task goes to a Claude Code session in the cloud, which works with TDD, runs tests, checks itself and opens a pull request.

When: checks the queue every 3 minutes, one task at a time. Waits up to 15 minutes for an answer on the plan, and if I haven't answered, builds according to the recommendations. An evening report at 20:30.

What it may not do on its own: merge. Ever. A PR is merged only when I write "מזג" ("merge"). Force push and reset are also not allowed.

Lessons loop: every session leaves a comment on the PR that starts with LESSONS. Once a week a job collects them, removes duplicates and opens a PR to a private lessons repo. The next session gets the lessons that apply to its repo.

The support desk

What it does: reads the business inbox. It classifies each thread: noise, not support, already answered, or a real report. A real report is matched to a system from a list: first by sender, then by at least two keywords, then by the repo name or the domain. If the fix is clear and it's code, the desk writes a task for a cloud session, with no names, no email addresses and no client name, and the session opens a PR. At the same time it prepares a draft reply to the client, as an item for approval.

When: every hour, from 7 in the morning to 10 at night. If there's nothing, it stays quiet.

What it may not do on its own: send, reply to or forward an email. Delete, merge or publish. Guess: if it's not clear which system the report belongs to, or the subject is sensitive (payments, security, client data), it asks me one line and stops. In one client's system it is also not allowed to change business settings. A request like that gets a clarifying question, not code.

An honest note: it went through a test run on a real inbox, but it hasn't yet handled a real report from a client.

The personal assistant

What it does: keeps long-term memory about people, projects and organizations. Every fact is saved with a date, a source and a trust level. An old fact isn't deleted quietly, it's marked as replaced. Before text from an email is saved, a scanner looks for wording that looks like instructions, and if there is any, the text isn't saved and I get a report. I can ask "מה אתה יודע על X" ("what do you know about X") or ask "תשכח את X" ("forget X") (with confirmation).

When: a morning brief Sunday to Thursday at 08:00 (today's meetings with context from memory, emails waiting for a reply with a draft ready, open commitments). A sweep at 13:00 and 18:00 that turns new emails into ready reply items. A weekly summary on Friday at 13:00.

What it may not do on its own: send an email, reply, forward, send an invitation or answer an invitation. It may read, search, create a draft in Gmail and add an event to my calendar when it has no attendees. Never allowed, even with approval: delete emails, mark spam, delete events or change sharing permissions. Family calendars are blocked completely.

System (watchdog)

What it does: checks that the main session is alive, that the Telegram plugin is connected and that there's only one of it, that there's room on the disk and in memory, that the last job of each type didn't fail, and that the Claude login hasn't expired. It gets updates from GitHub through a poller that runs every 5 minutes and reports a new PR, finished checks, a review and a merge.

When: every 15 minutes, and 5 minutes after boot.

What it may not do on its own: fix. It sends one short message per problem, and doesn't repeat it more than once every 6 hours. Even rebooting the server after a security update happens only when I write "הפעל מחדש" ("restart").

Mission Control (the dashboard)

What it does: shows the state of the whole team as a 3D campus on a Thai beach. Each agent has a hut, and in each hut sit bots: green working, yellow waiting for me, red problem, grey resting. Each cloud session is a boat that goes out to sea and comes back to the pier when a PR is opened. The lighting follows the time in Israel.

When: the server sends a summary every 10 minutes. If the data hasn't been updated for over 25 minutes, the dashboard marks it.

What it may not do: anything except display. It has no forms and no action buttons. Approvals stay in Telegram. Traffic goes out from the server only, and no port is opened. Without the private entry link, every address on the site returns 404. The summary contains no message content, email addresses, phone numbers or client names, and the site rejects a summary that has any.

The approval model

This is the part most worth copying. Every outward action goes through the same path:

  1. An agent prepares an item and writes it to a file with a number. The numbering is shared by all agents, so "אשר‎ 7" always points to one item.
  2. The item reaches Telegram as a short preview: to whom, what, and the full text.
  3. I answer: "אשר‎ 7" ("approve 7"), "תקן‎ 7: ..." ("fix 7: ...") or "דלג" ("skip").
  4. Only the main session carries it out. The scheduled jobs can't send, even if they wanted to. The tools are blocked for them.
  5. An exact match check. Before an email or an invitation is sent, the item is "locked". A guard layer that sits in front of the tool compares what's about to go out with the item that was approved, down to the character. Any difference is blocked.
  6. Expiry. An item not approved within 24 hours expires. An identical action already carried out in the last 7 days is blocked.
  7. Log. Every action, including a failed attempt, is written to an action log.

This is how it looks in Telegram (a made-up example):

Copy-ready
7: Email reply · "Question about the monthly report"
To: client
"Hi, got it. We're checking and I'll update you as soon as it's ready. Aviad"
To send: אשר‎ 7

Why it matters: a good draft isn't the problem. The problem is an action that happens without you seeing it, or an action you approved in one version that went out in another. The exact check closes the gap between "I approved" and "what went out". The expiry prevents approving yesterday's draft that's no longer relevant. The log lets me answer "what was sent this week" without guessing.

And there's undo too: a comment I approved by mistake is deleted only after the agent showed me what will be deleted and I answered "כן בטל N" ("yes, cancel N"). An email that was sent can't be taken back, and the agent says so.

The architecture

Copy-ready
Me (phone)
  |  approve N / new task / voice note
  v
Telegram bot  <------  drafts, alerts, reports
  |
  v
Small server = dispatcher
  - main session: the only one that acts
  - timers: LinkedIn, assistant, support desk,
            factory queue, watchdog
  - approval queue + action log
  |                              |
  | task                         | summary every 10 min
  v                              v
Cloud coding sessions        Mission Control (read-only)
  |
  | branch + PR
  v
GitHub  --- poller every 5 min --->  Telegram
            (merge only when I say so)

How it flows: a code task comes in from Telegram, the server prepares a plan and sends it to a cloud session, and I get a link. The server has no way to ask a cloud session for its status, so GitHub is the source of truth: the poller reports when a PR is opened.

Where it broke

1. The server crashed from a build

What happened: the main session was killed several times when memory ran out. One time it was probably a build of a project that needs more memory than the server has. Another time two subagents built in parallel, each with tests, a compiler and a browser.

The fix: limits first: one building agent at a time, a memory ceiling for every heavy command, and swap. In the end, a decision of principle: the server hands out work, it doesn't do it. Every build runs in a cloud session.

The lesson: if your main agent runs on the same machine that builds code, one heavy build takes down the whole team.

2. The login expired at night

What happened: the Claude login expired in the middle of the night. From the morning on, every job failed: the morning brief, the weekly report and more. I logged in to the server, logged in again and reran by hand what had failed.

The fix: the watchdog now checks the login state on every round, without using tokens. It warns 48 hours before expiry, spots login errors in the jobs' logs, and the message includes the steps to fix it.

The lesson: check not only that the agent runs, but also that it's logged in.

3. Two bots on the same token

What happened: an innocent Claude Code diagnostic command started a second copy of the Telegram plugin. Two processes pulled messages from the same bot, and the main session's connection died in the middle of the night.

The fix: a wrapper around the claude command. It blocks those diagnostic commands, and adds a setting that turns the plugin off to every run that isn't the main session. The watchdog counts that there's exactly one plugin.

The lesson: one control channel also means one process that holds it.

4. A first message to someone who was already in a conversation with me

What happened: in a round of LinkedIn messages, an introduction message went out to a person who had already reached out to me and had a call scheduled with me. I approved the list, but the agent's filter checked only earlier drafts, not the leads list. It looked unserious.

The fix: before a list reaches me for approval, the agent filters out anyone who appears in the pipeline at any stage, anyone I have a thread with by email or on LinkedIn, and anyone who works at a company I already work with. Also by name, not only by link.

The lesson: human approval doesn't replace good filtering. In a list of twenty names, I won't remember every one.

5. The cloud session picked its own branch name

What happened: I asked the session for a specific branch name. It pushed to a different branch, with a name it made up. The tools that looked for the PR by the expected name didn't find it.

The fix: look for a PR by repo and time, not by branch name. Before a follow-up task, switch to the PR's real branch.

The lesson: don't count on an agent doing exactly what you wrote on a technical detail. Check the result.

6. The login link didn't work from Telegram

What happened: the dashboard's login link worked in the browser, but when I opened it from Telegram I got a 404. The cookie was set as SameSite=Strict, and a regular redirect from an outside link still counts as coming from another site, so the browser didn't send the cookie.

The fix: instead of a redirect, a short page that sets the cookie and moves to the main page from within the site itself. An automated test was added that opens the link from an outside page.

The lesson: test the way you'll really use the thing, in my case from the phone and from Telegram, not only the way that's convenient to test.

7. A double fix

What happened: a client sent several fix requests. I fixed them myself and opened a PR. The support desk didn't know, and sent a session to the cloud that opened a second PR for the same thing.

The fix: before sending a fix, the desk checks whether a PR was opened in the same repo in the last 24 hours on the same subject, and checks the list of tasks already sent. If so, it writes "looks like it's already being handled" and suggests a reply to the client based on the existing PR.

The lesson: an agent needs to know what the person already did, not only what the agents did.

8. An attachment too large for the exact approval

What happened: I approved an email with a PDF attached. The exact check needs the whole item in one call, and the encoded file was too large to go through that way. The email wasn't sent.

The fix: nothing went out, and that's the good side. Instead, a Gmail draft was created without the file, and I attached it and sent it. Since then: an attachment is uploaded to a cloud folder and the email includes a link, or the agent prepares a draft and I send it.

The lesson: a safety mechanism that fails should fail closed. Better an email that didn't go out than an email that went out unchecked.

How to start: one week

Don't set up six agents. Set up one, with an approval queue, and let it run for a week.

Day 1: A Telegram bot that answers only you

Why: this is the control channel. If it's open to anyone, nothing else matters.

What to do: create a bot in BotFather, and set it to accept messages only from your chat_id. The token is stored in a file with restricted permissions, not in the code.

Check: a message from another account gets no answer and triggers nothing.

Day 2: One agent that only prepares

Why: to learn what the agent does well before you let it do anything.

What to do: pick one task that repeats for you: draft replies to emails, LinkedIn comments or a morning summary. The agent runs at a fixed time, writes drafts to files and sends you a preview. It has no access to sending tools at all.

Check: after a run, the sending tools don't appear in the list of tools it can use, and the drafts folder has files with numbers.

Day 3: An approval queue

Why: this is the heart of the whole system.

What to do: every draft gets a daily number. "אשר N" triggers the action, only from the session connected to Telegram. The action compares what goes out with the saved draft, refuses a draft more than 24 hours old, and refuses a draft that was already carried out.

Check: "אשר" on yesterday's draft is rejected. A double "אשר" on the same draft is rejected the second time.

Day 4: An action log and a stop switch

Why: when something strange happens, you need to know what happened and stop everything with one message.

What to do: every action, including a failure, is written as a line in the log. A /pause command creates a file that every scheduled job checks before it starts, and /resume deletes it.

Check: after /pause, the next job skips and logs that it skipped.

Day 5: A watchdog

Why: the agent will go down. The question is whether you find out in a quarter of an hour or in three days.

What to do: a scheduled script that checks that the agent is running, that the last job didn't fail, that there's room on the disk and in memory, and that the login is valid. One message per problem, not one per check.

Check: stop the agent by hand, and within one round one message arrives.

Days 6-7: Run and correct

Why: the real rules are written from the mistakes.

What to do: approve, correct, skip. Every correction of yours ("don't write like that", "don't contact this person") goes into a corrections file the agent reads before every draft.

Check: after a week, the mistake you corrected on day 3 doesn't come back.

And then: another agent, with the same queue, the same log and the same watchdog.


In the end

The agents do a lot of work, and the decisions stay with me: everything that goes out passed through my phone. Most of the time went not into the agents themselves, but into their limits and into what happens when something breaks.

More playbooks and kits: gindiinnovation.com/playbooks. For hardening the server (key login, a watchdog, backup, memory ceilings), the kit "A server for AI agents that stays up and stays safe".


Disclaimer

This content is general recommendations only, not professional, legal, accounting or tax advice. It describes one system as it works for me, and doesn't promise any level of security. Every business and organization is different. You are responsible for checking that it fits your situation, and for checking the data, prompts and code before you use them. Outside tools, prices and terms of service change, so check them at the source. An agent that acts on your behalf on social networks or in email has to comply with their terms of use. For sensitive systems, or when client data is involved, consult an information security professional.


What's next

You can get this structure ready-made: a control channel, an approval queue, a log, a watchdog and your first agent.

What's next

Related playbooks

A 30-minute call on one process

Bring one process that eats your time. By the end of the call you'll know if it's worth building for, roughly what it would cost and how we'd measure it.

Book a call

Prefer to write? Message me on WhatsApp