Show HN: Tadpole – an LLM agent built from scratch then post-trained on 4B model A developer operating under the GitHub handle shakewingo released Tadpole, a tool-using LLM agent written in about 3,500 lines of plain Python with no agent framework, which runs on a local 4B model or any OpenAI-compatible API from a terminal or as a Telegram bot. The project's stage-by-stage post-training pipeline lifted Qwen3-4B from 0.419 to 0.774 pass@1 on task types it never trained on, through supervised fine-tuning, expert iteration and a GRPO pilot. Tadpole requires Python 3.11 or later and ships with downloadable LoRA adapters (sft-1, sft-2, ei-3) for llama.cpp. An LLM agent built from scratch, and grown one stage at a time. A tool-using agent in about 3,500 lines of plain Python, with no agent framework, that you can run around the clock from Telegram, and a stage-by-stage record of how it was built, measured and post-trained. - An agent you can use. Files, shell, web search and fetch, sessions and compaction, on a local 4B model or any OpenAI-compatible API, in a terminal or as a Telegram bot. - A from-scratch tutorial. Each stage adds one mechanism with tests and measured evidence, from the first agent loop to SFT, expert iteration and a GRPO pilot. Post-training lifts Qwen3-4B from 0.419 to 0.774 pass@1 on task types it never trained on results post-training-results . Python 3.11 or later. Run every command from the repository root with the venv active. git clone https://github.com/shakewingo/Tadpole.git cd Tadpole python3 -m venv .venv && source .venv/bin/activate pip install -e . Use a strong model through any OpenAI-compatible API: DeepSeek, OpenAI, OpenRouter, or your own vLLM or LM Studio server. export these for the terminal, or put them in the bot's env file https://github.com/shakewingo/Tadpole/blob/main/agent from scratch/deploy/telegram-bot.env.example : export AGENT BASE URL=https://api.deepseek.com the API root; often ends in /v1 export AGENT MODEL=deepseek-flash the provider's model ID export AGENT API KEY=sk-... export AGENT N CTX=32768 optional: the model's context window Run Qwen3-4B locally, base or post-trained : the model this project trains, for studying the training rather than everyday use | Option | Variables | |---|---| | Ollama, any OS | AGENT BASE URL=http://localhost:11434/v1 , AGENT MODEL=hf.co/unsloth/Qwen3-4B-Instruct-2507-GGUF:Q4 K M , AGENT N CTX=32768 | | llama.cpp in-process, macOS; Qwen2 or Qwen3 GGUFs | leave AGENT BASE URL unset; AGENT MODEL PATH defaults to models/Qwen3-4B-Instruct-2507-Q4 K M.gguf | | A Tadpole adapter download-the-adapters-and-data on llama.cpp, macOS | AGENT MODEL PATH=models/Qwen3-4B-Instruct-2507-Q8 0.gguf , AGENT LORA PATH=models/ei-3-lora-f16.gguf or sft-1, sft-2 ; planning turns off, as in training | Ollama: pull, then serve with a window large enough for the agent ollama pull hf.co/unsloth/Qwen3-4B-Instruct-2507-GGUF:Q4 K M OLLAMA CONTEXT LENGTH=32768 ollama serve llama.cpp: the default AGENT MODEL PATH hf download unsloth/Qwen3-4B-Instruct-2507-GGUF Qwen3-4B-Instruct-2507-Q4 K M.gguf --local-dir models An adapter, on the Q8 0 base it was measured on hf download unsloth/Qwen3-4B-Instruct-2507-GGUF Qwen3-4B-Instruct-2507-Q8 0.gguf --local-dir models hf download Shakewin/tadpole-qwen3-4b-ei-3 ei-3-lora-f16.gguf --local-dir models llama.cpp keeps the model in memory while the agent runs: about 8.5 GB for the Q8 0 base with its 32k window, which slows a 16 GB Mac. python -m agent from scratch.agent 1. Message @BotFather https://t.me/BotFather with /newbot for a bot token , and @userinfobot https://t.me/userinfobot for your numeric user ID . 2. Put both, and your AGENT variables if you use an API, in the env file: cp agent from scratch/deploy/telegram-bot.env.example agent from scratch/deploy/telegram-bot.env 3. Start it, and message your bot: sh agent from scratch/scripts/run telegram bot.sh Warning The agent runs shell commands and edits files with your user's permissions. It answers only TELEGRAM ALLOWED USER ID , but anyone with the token controls the bot: keep the env file private. In Telegram and the terminal: /new starts a session, /reset clears it, /session