DEV Community

Cover image for Build a Self-Improving AI Agent in Rust with Garudust — Daily Briefing Bot in 10 Minutes
Garudust
Garudust

Posted on

Build a Self-Improving AI Agent in Rust with Garudust — Daily Briefing Bot in 10 Minutes

Most AI agent frameworks feel like they were designed for Python developers who love ceremony. You write adapters, glue code, orchestrators, memory stores — and by the time your agent actually does something useful, you've got a monorepo and a headache.

Garudust Agent takes a different approach: a single ~10 MB statically-linked Rust binary that gives you persistent memory, cron scheduling, multi-platform gateways (Telegram, Discord, Slack, Matrix), and MCP tool support — out of the box, no runtime dependencies.

In this article I'll show you how to build a Telegram Daily Briefing Bot that:

  • Wakes up every morning at 9 AM and sends you a personalised briefing
  • Remembers your preferences across sessions (no repeating yourself)
  • Reads files from your server and summarises them
  • Uses a custom Skill to keep its briefing format consistent

By the end you'll understand the core Garudust concepts well enough to build your own agents.


Why Garudust?

Before diving in, here's what sets it apart from other agent runtimes:

Feature Garudust LangChain/LangGraph AutoGen
Language Rust Python Python
Binary size ~10 MB N/A (pip install) N/A (pip install)
Cold start < 20 ms 1–5 s 1–5 s
Persistent memory Built-in Plugin needed Plugin needed
Platform adapters Built-in Plugin needed Plugin needed
Self-hosted Yes, trivially Needs extra infra Needs extra infra

The self-improvement angle is genuinely interesting: the agent notices when it keeps doing something wrong and patches its own Skill files. We'll see that in action below.


Prerequisites

  • A Linux server or Mac (the binary is statically linked — no Rust required to run it)
  • An API key for any LLM provider: Anthropic, OpenRouter, Ollama, vLLM, etc.
  • A Telegram bot token (free — create one via @BotFather)

Step 1 — Install Garudust

Download the pre-built binary from GitHub Releases.

# Linux x86_64
curl -LO https://github.com/garudust-org/garudust-agent/releases/latest/download/garudust-x86_64-unknown-linux-musl.tar.gz
tar -xzf garudust-*.tar.gz
sudo mv garudust garudust-server /usr/local/bin/

# Verify
garudust --version
Enter fullscreen mode Exit fullscreen mode

For macOS (Apple Silicon):

curl -LO https://github.com/garudust-org/garudust-agent/releases/latest/download/garudust-aarch64-apple-darwin.tar.gz
tar -xzf garudust-*.tar.gz
sudo mv garudust garudust-server /usr/local/bin/
Enter fullscreen mode Exit fullscreen mode

Or build from source if you have Rust 1.75+:

git clone https://github.com/garudust-org/garudust-agent
cd garudust-agent
cargo build --release
export PATH="$PATH:$(pwd)/target/release"
Enter fullscreen mode Exit fullscreen mode

Step 2 — Configure Your LLM Provider

Run the setup wizard:

garudust setup
Enter fullscreen mode Exit fullscreen mode

This walks you through picking a provider and saving your key. Under the hood it writes to ~/.garudust/config.yaml.

You can also set environment variables directly:

# Anthropic
export ANTHROPIC_API_KEY="sk-ant-..."

# OpenRouter (gives you access to 200+ models)
export OPENROUTER_API_KEY="sk-or-..."

# Self-hosted vLLM (like the Qwen3-8B-AWQ setup I run locally)
export VLLM_BASE_URL="http://localhost:8000/v1"
export GARUDUST_MODEL="Qwen/Qwen3-8B-AWQ"

# Ollama (fully local, no key needed)
export OLLAMA_BASE_URL="http://localhost:11434"
export GARUDUST_MODEL="llama3.2"
Enter fullscreen mode Exit fullscreen mode

Let's verify everything is working:

garudust doctor
Enter fullscreen mode Exit fullscreen mode

You should see all green checkmarks. Now try a quick chat:

garudust "hello, what can you do?"
Enter fullscreen mode Exit fullscreen mode

Step 3 — Create a Telegram Bot

  1. Open Telegram and message @BotFather
  2. Send /newbot and follow the prompts
  3. Copy your bot token (looks like 7123456789:AAHxxx...)

Set it in your environment:

export TELEGRAM_TOKEN="7123456789:AAHxxx..."
Enter fullscreen mode Exit fullscreen mode

Step 4 — Create a Briefing Skill

This is where Garudust gets interesting. Skills are Markdown files that the agent automatically loads when relevant. They live in ~/.garudust/skills/ and are hot-reloaded on every call — no restarts needed.

Create the directory and your first skill:

mkdir -p ~/.garudust/skills/daily-briefing
Enter fullscreen mode Exit fullscreen mode

Now create ~/.garudust/skills/daily-briefing/SKILL.md:

---
name: daily-briefing
description: Instructions for generating a personalised morning briefing via Telegram
version: 1.0.0
---

## Daily Briefing Format

When asked to generate a morning briefing, always follow this structure:

1. **Greeting** — Use the user's name and current local time
2. **Today's Focus** — 2–3 bullet points on what to prioritise (infer from memory or files)
3. **Quick Stats** — If any log files or data files are available in ~/briefing-data/, summarise key numbers
4. **Reminder** — One motivational sentence, varied each day, never generic

## Style Rules

- Keep the total message under 300 words
- Use Telegram-compatible formatting (bold with *, italic with _)
- Never use HTML tags
- If no data files are found, skip the Quick Stats section gracefully
- Always end with: "Have a great day, Garudian! 🦅"
Enter fullscreen mode Exit fullscreen mode

That last line — "Have a great day, Garudian!" — is a nod to the Garudust contributor community name.


Step 5 — Set Up Cron Scheduling

Garudust's cron scheduler uses standard cron expressions. You configure it via the GARUDUST_CRON_JOBS environment variable:

export GARUDUST_CRON_JOBS="0 9 * * *=Generate a morning briefing using the daily-briefing skill and send it to me on Telegram"
Enter fullscreen mode Exit fullscreen mode

The format is "cron_expression=natural language task". The agent handles the rest — it knows who "me" is from the user profile it builds over time.

You can also add multiple jobs separated by commas:

export GARUDUST_CRON_JOBS="0 9 * * *=Generate a morning briefing and send via Telegram,0 18 * * 5=Send a weekly summary of tasks completed this week"
Enter fullscreen mode Exit fullscreen mode

Step 6 — Start the Server

Create a simple .env file to keep everything together:

# ~/.garudust/.env
ANTHROPIC_API_KEY=sk-ant-...
TELEGRAM_TOKEN=7123456789:AAHxxx...
GARUDUST_MODEL=anthropic/claude-sonnet-4-6
GARUDUST_CRON_JOBS="0 9 * * *=Generate a morning briefing using the daily-briefing skill and send it to me on Telegram"
GARUDUST_APPROVAL_MODE=auto
RUST_LOG=info
Enter fullscreen mode Exit fullscreen mode

Now start the server:

# Load env and start
set -a; source ~/.garudust/.env; set +a
garudust-server --port 3000
Enter fullscreen mode Exit fullscreen mode

Or with Docker (the docker-compose.yml in the repo is ready to use):

cp .env.example .env
# Edit .env with your keys
docker compose up -d
Enter fullscreen mode Exit fullscreen mode

You should see logs like:

INFO garudust_server: Starting Garudust server on port 3000
INFO garudust_platforms::telegram: Telegram adapter connected
INFO garudust_cron: Scheduled job: "0 9 * * *" → Generate a morning briefing...
Enter fullscreen mode Exit fullscreen mode

Step 7 — Teach the Agent About You

Open Telegram and message your bot. The first time, introduce yourself:

Hi! I'm Alex. I'm a backend developer working on Rust microservices.
I usually check logs in ~/briefing-data/app.log and ~/briefing-data/errors.log.
I prefer short, direct messages with no fluff.
Enter fullscreen mode Exit fullscreen mode

The agent saves this to persistent memory automatically. You'll see something like:

Got it, Alex! I've saved your preferences:
- Name: Alex
- Role: Backend developer (Rust microservices)
- Briefing data path: ~/briefing-data/
- Communication style: short and direct

I'll use these tomorrow morning at 9 AM for your briefing.
Enter fullscreen mode Exit fullscreen mode

This is the self-improvement loop: you teach it once, and it remembers forever. On the next session — or the next day's cron job — the context is already loaded.


What Happens at 9 AM

At 9 AM, the cron job fires. The agent:

  1. Loads the daily-briefing Skill — sees the format rules
  2. Reads persistent memory — knows your name, preferences, file paths
  3. Calls read_file on ~/briefing-data/app.log and errors.log
  4. Generates the briefing using the Skill's format
  5. Sends it to you on Telegram

A real message might look like:

*Good morning, Alex!* It's 9:00 AM.

*Today's Focus*
• 3 errors in errors.log since yesterday — worth a look before standup
• PR #142 still open — you had this on your radar
• Dependency audit due this sprint

*Quick Stats (from app.log)*
• 14,203 requests last 24h
• p99 latency: 187 ms
• 3 ERRORs, 0 WARNings

_Have a great day, Garudian!_ 🦅
Enter fullscreen mode Exit fullscreen mode

Step 8 — Test the HTTP API

You don't have to wait until 9 AM. The HTTP gateway is always running:

# Trigger a briefing right now
curl -X POST http://localhost:3000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "Generate my morning briefing now and send it to me on Telegram"}'
Enter fullscreen mode Exit fullscreen mode

For streaming responses (great for long tasks):

curl -X POST http://localhost:3000/chat/stream \
  -H "Content-Type: application/json" \
  -d '{"message": "Summarise my error logs from the last 7 days"}'
Enter fullscreen mode Exit fullscreen mode

Step 9 — Run as a systemd Service

For production use on a Linux server:

sudo nano /etc/systemd/system/garudust.service
Enter fullscreen mode Exit fullscreen mode
[Unit]
Description=Garudust Agent Server
After=network.target

[Service]
Type=simple
User=youruser
WorkingDirectory=/home/youruser
EnvironmentFile=/home/youruser/.garudust/.env
ExecStart=/usr/local/bin/garudust-server --port 3000
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode
sudo systemctl daemon-reload
sudo systemctl enable --now garudust
sudo journalctl -u garudust -f
Enter fullscreen mode Exit fullscreen mode

The Self-Improving Part

Here's something that makes Garudust distinct from a static chatbot: if the briefing format stops being useful, the agent fixes itself.

Suppose you message your bot:

The briefing is too long. Cut the Quick Stats section to just one line.
Enter fullscreen mode Exit fullscreen mode

The agent updates ~/.garudust/skills/daily-briefing/SKILL.md immediately — while the server is running, without a restart. The next morning's briefing will already follow the updated format.

You can also ask it to create new skills on the fly:

Write a skill for when I ask you to review a Rust PR  check for unwraps, missing error handling, and doc comments.
Enter fullscreen mode Exit fullscreen mode

It creates ~/.garudust/skills/rust-pr-review/SKILL.md automatically. From that point on, whenever you paste a PR and ask for a review, it loads that skill without being told.


Architecture at a Glance

Your Telegram ──► garudust-server
                       │
                  ┌────┴────────────────────┐
                  │   daily-briefing SKILL  │  ← hot-reloaded markdown
                  │   Persistent Memory     │  ← ~/.garudust/memory/
                  │   Session DB (FTS5)     │  ← SQLite full-text search
                  └────┬────────────────────┘
                       │
                  LLM Transport
          (Anthropic / OpenRouter / vLLM / Ollama)
                       │
                  Tool Registry
         (read_file, write_skill, memory, web_search…)
Enter fullscreen mode Exit fullscreen mode

Every piece is a separate Rust crate. If you want to add a new platform or tool, you touch exactly one crate and typically write under 100 lines.


What to Build Next

Now that you have the basics, here are some natural extensions:

Add web search to the briefing
Set BRAVE_SEARCH_API_KEY and update your Skill to include: "Always search for the top 3 tech news headlines and include them under a 'News' section."

Multi-platform: Run Telegram + Slack simultaneously

export SLACK_BOT_TOKEN="xoxb-..."
export SLACK_APP_TOKEN="xapp-..."
# garudust-server handles both in the same process
Enter fullscreen mode Exit fullscreen mode

Connect to your database via MCP
Add to ~/.garudust/config.yaml:

mcp_servers:
  - name: postgres
    command: npx
    args: ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"]
Enter fullscreen mode Exit fullscreen mode

The briefing can now query your own database directly.

Delegate to sub-agents
For heavy tasks, use the delegate_task tool — it spawns parallel sub-agents that run concurrently and report back. Great for summarising large codebases or doing multi-step research.


Wrapping Up

Garudust isn't trying to be the most featureful agent framework — it's trying to be the most practical one. A single binary, sensible defaults, and just enough structure to let you build real things without ceremony.

The daily briefing bot we built today shows the core loop:

  1. Install — one binary, no dependencies
  2. Configure — env vars or YAML
  3. Teach — talk to it, it remembers
  4. Extend — write a Skill in Markdown

The project is actively developed and welcoming contributors. If you want to add a new platform adapter, a tool, or improve the TUI — check out the CONTRIBUTING.md and look for "good first issue" labels.

Star the repo, join the community, and become a Garudian 🦅

github.com/garudust-org/garudust-agent


Have questions or want to share what you built? Drop a comment below — I read everything.

Top comments (0)