Important
Ethgent has moved to ethosoftai/Ethgent. New work happens there; this repository is kept for its history.
A customisable LLM agent: an orchestrator, sub-agents and tools that you shape to a domain.
Quick start · Make it your own · The agents · Commands · Configuration · Testing
- Standalone. Clone it, run
./run.sh, and you have an orchestrator LLM (BaseModel) that delegates to specialised sub-agents, a terminal to manage it, and its own logs, run history and cost tracking. It is a product you operate, not a framework you assemble. - Configured, not coded. What the agent is for is decided by its configuration: which sub-agents exist, which tools they own, and what their prompts say. Agents and tools live in YAML and in packs, and can be created, edited, pulled from another repository and shared from the terminal.
- Social media by default, any domain by design. It ships tuned for social media and content (X, Instagram, YouTube, image and video content, browser automation, research), but that is a configuration, not a limit. See Make it your own.
It can also be embedded as a library in another Python process (see below), but that is the exception.
| Agents | Content creator (text, images, video), social media (X, Instagram, YouTube), browser, research, system and VLM agents. Two are on by default, see The agents. |
| Build and share | Agent Studio adds, toggles and reconfigures agents and tools from YAML, no code changes. Agent packs are plug-and-play bundles of agents, tools and prompts. |
| Run it | Heartbeat scheduler (cron or interval jobs, APScheduler) and an interactive terminal to create, edit, copy, test and delete agents and tools, schedule jobs and review logs, all without a restart. |
| Observe it | Operations store: persistent logs, per-run history (what a job did, how long it took, what it cost) and LLM token usage, queryable from the terminal. |
| Keep it safe | Risky actions ask for approval. Remote management over Telegram or Discord is limited to allow-listed admins, with remote code upload deliberately blocked. |
You need: Python 3.11+, and Chrome plus an active X (Twitter) session in a Chrome profile if you want the social features.
git clone https://github.com/Ahmet2001/BrowserAgent.git
cd BrowserAgent
chmod +x run.sh
./run.shrun.sh creates a .venv if there is none, installs the packages from requirements.txt, and launches the interactive terminal (python -m MarketingApp.main).
For PNG and video rendering, install the Playwright browser once:
source .venv/bin/activate
playwright install chromiumThen copy .env.example to .env, add your keys (Configuration), and type a message to chat, or /help for the commands.
The shipped agents are social-media ones, but the machinery is general: an agent is a prompt, a model and a set of tools, and a tool is a Python function. To point Ethgent at another domain you write tools, give an agent those tools and a prompt, try it, and, if you like, package it for others. Everything below happens in the terminal (or in config/*.yaml), without touching Ethgent's code.
Note
The names below are illustrations. None of these tools ship with Ethgent.
A tool is a plain function in a Python file. Its docstring (or the --desc you pass) is how the model decides when to call it, so say what it does and what it returns.
# ~/tools/prices.py
def get_price(symbol: str) -> dict:
"""Return the latest price of a symbol, e.g. get_price("AAPL"). Read-only."""
...
return {"symbol": symbol, "price": 123.4}/tool create --file ~/tools/prices.py --all # every public function becomes a tool
/tool create get_price --file ~/tools/prices.py # or just one, named after its function
/tool show get_price # check what was registered
The code is compiled and imported before it is accepted, so a broken tool is rejected immediately (details in Custom tools). Secrets it needs go in with --env NAME=value, which writes to the gitignored .env.model.
Create a sub-agent that owns the tool, with its own prompt and, if you want, its own model.
/agent create trading_agent --tools get_price,bellek_yaz,bellek_oku \
--prompt "You analyse positions and write a short journal entry. Never place orders." \
--desc "Market data and journaling"
- Change it later with
/agent edit trading_agent --add-tools get_candles --prompt "..." --model <name>, copy it with/agent copy, inspect it with/agent show trading_agent. - The same fields live in
config/agents.yaml:name,description,model,system_prompt,tools,enabled. - To change how the orchestrator itself behaves, use
/prompt set "..."(and/prompt resetto go back).
Tip
The orchestrator reaches your tool only through this sub-agent, so giving the agent read-only tools is how you keep it read-only. A tool marked as risky asks for approval before it runs.
Run the one agent directly, before involving the orchestrator, and look at the run it recorded:
/agent test trading_agent "What is the latest price of AAPL?"
/runs 5
/logs 20 --grep trading_agent
When it behaves, ask the same thing in the normal chat and the orchestrator will delegate to it.
Export the agent with its tools and prompt as a pack, put the folder in a GitHub repository, and anyone can install it:
/agent pack export trading_pack --agents trading_agent --out ~/trading_pack # you
# push ~/trading_pack to github.com/you/trading-pack # you
/agent pack install github:you/trading-pack # anyone else
The installer shallow-clones the repository, shows a preview of the agents and tools it would add, and asks before installing. The export lists environment variable names only, never values. More in Sharing a setup.
Warning
A pack carries tool code that runs on your machine. Read the preview and install only packs you trust.
An HR agent would have tools such as list_applicants and read_cv and a prompt about screening against a role description; a support agent would have tools over a ticket system. Only the tools and the prompt change.
Ready-made connectors and workers that join Ethgent to an application or to platform APIs live in the separate open pool Marketing Agent Assets. Ethgent does not require it.
The orchestrator (BaseModel) does not call tools directly: it hands a task to a sub-agent, and each sub-agent owns its own model and its own set of tools. Seven sub-agents ship in config/agents.yaml; two are on by default. A deployment can add more (the MarketingPool example adds two, shown dashed), and so can you with /agent create or a pack.
flowchart TB
U([You: terminal, Telegram, Discord, heartbeat job]) --> O
O[BaseModel<br/>orchestrator] --> SM
O --> CC
subgraph on[On by default]
SM[sosyal_medya_agent<br/>X, Instagram, YouTube]
CC[content_creator_agent<br/>images, video, captions]
end
O -.-> BR
O -.-> CO
O -.-> AR
O -.-> SI
O -.-> VL
subgraph off[Shipped, off by default: /agent NAME on]
BR[browser_agent<br/>Selenium browsing]
CO[computer<br/>empty template]
AR[arastirma_agent<br/>deep web research]
SI[sistem_agent<br/>files, system tasks]
VL[vlm_agent<br/>screen, keyboard, mouse]
end
O -.-> AC
O -.-> PD
subgraph added[Added by a deployment, for example MarketingPool]
AC[asset_collector_agent<br/>an app's approved media]
PD[platform_data_agent<br/>read-only platform data]
end
style added stroke-dasharray: 5 5
| Sub-agent | What it does | Default | Model |
|---|---|---|---|
sosyal_medya_agent |
Publishing, engagement and notification scanning on X, Instagram and YouTube, driven through a signed-in browser session | on | default (follows SUBMODEL_MODEL_NAME) |
content_creator_agent |
Captions, stock photo and video search, website-to-post extraction, HTML/CSS to PNG posts, MP4 reels | on | default |
browser_agent |
Selenium-based navigation, DOM reading and form interaction | off | browser_default (follows BROWSER_AGENT_MODEL) |
computer |
A blank Agent Studio template for computer-related tasks: no tools until you attach some | off | default |
arastirma_agent |
Multi-query web research and report writing | off | Gemini Live (needs GEMINI_API_KEY) |
sistem_agent |
File and workspace management, system status, terminal commands | off | Gemini Live |
vlm_agent |
Screen capture and mouse/keyboard control with a self-verifying vision loop | off | Gemini Live |
Turn one on with /agent <name> on, inspect it with /agent show <name>, and write your own with /agent create (Terminal Commands). Tools marked as risky ask for approval in the terminal before they run.
The two a deployment can add. asset_collector_agent lists, validates and prepares the approved media an application exposes. platform_data_agent asks a separate worker for read-only platform data and never holds the platform's credentials. Both live in the MarketingPool copy of this agent, not in this repository; they show that adding a sub-agent is a configuration step, not a change to the orchestrator.
main.py
├─ background tasks: heartbeat_loop, telegram bot (optional), discord bot (optional)
└─ TerminalManager (foreground)
└─ AutomationCoordinator (mutex: only one caller touches BaseModel/browser at a time)
└─ BaseModel.text_query()
└─ tool-calling loop (≤12 turns, repeat-call guard)
├─ base tools (memory, workspace, search, …)
└─ SubModel agents (each runs its own inner LLM + tool loop)
BaseModel(MarketingApp/llms/BaseModel.py) is the orchestrator: an OpenAI-compatible chat-completions loop that calls tools and sub-agents until it has a final answer.SubModelagents (MarketingApp/llms/SubModels/) are self-contained mini-agents, each with their own model and tool subset, exposed toBaseModelas a single callable tool.AutomationCoordinator(MarketingApp/environments/automation_runtime.py) is a lock ensuring the terminal, heartbeat, and Telegram/Discord triggers never touch the shared browser session concurrently.telemetry(MarketingApp/telemetry.py) is the operations store: arunis opened per unit of work (a chat turn, a heartbeat job) and carried in aContextVar, so every log line and LLM call made inside it is attached to it automatically.Agent Studio(MarketingApp/llms/agent_studio.py) readsconfig/agents.yaml,config/custom_tools.yaml, andconfig/agent_packs.yamlto assemble the runtime — agents and tools can be added, toggled, or reconfigured without touching code.
Copy .env.example to .env and fill in your keys. Settings are loaded in this order (later files override earlier ones): .env → .env.local → .env.model → .env.secrets.
| File | Purpose |
|---|---|
.env |
Base model/provider settings, Telegram token |
.env.local (optional) |
Local overrides, kept out of version control |
.env.model (optional) |
Model-specific overrides |
.env.secrets (optional) |
Additional API keys (Pexels, etc.) |
Key variables
| Variable | Description |
|---|---|
MODEL_PROVIDER |
gemini or an OpenAI-compatible provider |
OPENAI_COMPAT_BASE_URL |
Base URL for the OpenAI-compatible endpoint |
BASE_MODEL_NAME / SUBMODEL_MODEL_NAME / BROWSER_AGENT_MODEL |
Model IDs per role |
GEMINI_API_KEY / GEMINI_API_KEY_SECONDARY |
Gemini API keys (with failover) |
TELEGRAM_TOKEN / DISCORD_TOKEN |
Optional chat platform integrations |
PEXELS_API_KEY |
Stock photo/video search for the Content Creator agent |
TELEGRAM_ALLOWED_USER_IDS / DISCORD_ALLOWED_USER_IDS |
Who may chat with the bot (see Telegram and Discord) |
TELEGRAM_ADMIN_IDS / DISCORD_ADMIN_IDS |
Who may run management commands remotely |
ETHGENT_TELEMETRY_RETENTION_DAYS |
How long logs/runs/usage are kept (default 30, 0 = forever) |
Caution
All API keys and tokens live only in the gitignored .env* files (.env, .env.local, .env.model, .env.secrets). Never commit real credentials.
Once running, type a message to chat with Ethgent, or use a command. /help prints everything below. Destructive commands ask for confirmation unless you pass --yes; --dry-run on agent create/edit and heartbeat add previews the result without saving.
| Command | Description |
|---|---|
/agents, /agent list |
List agents |
/agent <name> on|off|toggle |
Enable/disable an agent |
/agent show <name> |
Type, model, tool list, prompt and any config errors for one agent |
/agent create <name> [flags] |
Create an agent (--model, --tools, --tool-group, --tool-category, --prompt, --desc, --dry-run) |
/agent edit <name> [flags] |
Change one (--add-tools, --remove-tools, --remove-tool-group, --enable/--disable, …) |
/agent copy <from> <to> |
Clone an agent with its resolved tool list |
/agent test <name> "task" |
Run a single agent directly and record it as a run |
/agent delete <name> [--yes] |
Delete a config agent |
/agent pack list|preview|install|export |
Manage agent packs, see Sharing a setup |
/prompt, /prompt default, /prompt set "...", /prompt reset |
Show, set or reset the orchestrator's own system prompt |
| Command | Description |
|---|---|
/tools [query] [--group G] [--category C] [--risk high] |
List and filter tools; --list-groups shows the groups, categories and risk split |
/tool <name> on|off|toggle |
Enable/disable a tool |
/tool create|edit|show|delete|list |
Manage custom tools, see Custom tools |
| Command | Description |
|---|---|
/heartbeat |
Scheduler and job status |
/heartbeat add --cron X --gorev "..." |
Add a scheduled task (startup, */N or HH:MM) |
/heartbeat remove <id>, show <id>, on, off |
Remove/inspect a task, enable/disable the scheduler |
/heartbeat run|pause|resume <id>, reload |
Control jobs, reload the config |
/heartbeat log [id] [n] |
Past runs of a job: when, how long, what it produced |
| Command | Description |
|---|---|
/status |
Model, provider, uptime, channels, config errors, store health and today's token usage |
/logs [n] [--since 24h] [--type T] [--grep text] [--run id] |
Persistent logs (--memory = this process only) |
/runs [n] [--source S] [--status S] [--since 24h] |
Run history across the terminal, heartbeat, Telegram and Discord |
/run <id> |
One run in detail: duration, error, token breakdown by agent/model, its log lines |
/usage [--since 24h] [--by agent|model|source|day|run] |
LLM token usage, plus a cost estimate if config/pricing.yaml exists |
/errors [text] |
Config problems that were previously collected but never shown |
| Command | Description |
|---|---|
/reload |
Reload agent/custom tool config |
/provider |
Show the active provider/model and which agents have a literal (non-default) model pinned in agents.yaml |
/provider set <name> [--base-model M] [--submodel-model M] [--browser-model M] [--base-url URL] [--api-key K] [--reset-pins] [--dry-run] |
Switch provider/model, writing to .env.model; --reset-pins un-pins agents back to the default sentinel so they follow the new provider |
/memory [category] [key], /memory search <text>, /memory delete <category> <key> --yes |
Inspect, search and remove entries from the agent's long-term memory (bellek_yaz/bellek_oku) without going through chat |
/history, /clear, /exit |
Chat history / shut down |
Note
Switching providers. Each agent's model: field in agents.yaml is normally the sentinel default (or browser_default), which is re-resolved from SUBMODEL_MODEL_NAME/BROWSER_AGENT_MODEL on every boot. An agent that was ever given an explicit --model (via /agent create, edit or copy) is pinned to that literal name and does not follow a later /provider set; use --reset-pins to un-pin it. A model name change takes effect immediately via /reload, but a provider swap (--base-url/--api-key) fully applies only after a restart, since the HTTP clients are built once at boot.
/tool create fiyat_getir --file ~/fiyat_getir.py --desc "Returns a price"
/tool create --file ~/kripto.py --all # every public function in the file becomes a tool
/tool create --file ~/kripto.py --names a,b # only these, sharing one copy of the file
/tool create x --code "def x(): return 1" --desc "inline"
The code is compiled, checked for a function named after the tool, and actually imported before it is accepted, so a broken tool is rejected at write time. The file is copied into workspace/custom_tools/; re-run /tool edit <name> --file … to pick up later changes. --env NAME=value writes to the gitignored .env.model; a bare --env NAME only records the requirement.
/agent pack export my_pack --agents my_agent --out ~/my_pack # or --all
/agent pack install ~/my_pack
/agent pack install github:user/repo[@branch][#sub/dir]
export writes plugin.yaml, agents/, prompts/, tools/, a README and an env.example that contains variable names only, never values. install from GitHub shallow-clones the repository, shows the preview, and asks before installing.
- Builtin tools cannot be packaged: they already exist in every install.
- Builtin agents can. One you scaffolded yourself (
/agent create --builtin) travels with its ownsubmodels/<name>.pysource and installs even where it doesn't exist yet. One of the seven agents the app ships with (sosyal_medya_agent,content_creator_agent, …) travels as config only (model, tools, prompt), since the target install already has its code.
Where it is stored, what it records, how cost is estimated
Everything is stored in workspace/runtime/telemetry.sqlite (gitignored) and kept for ETHGENT_TELEMETRY_RETENTION_DAYS days.
- Logs survive restarts (
/logs). - Runs record every chat turn and heartbeat job, including skipped ones and retry attempts, with status, duration, error and a summary of the output (
/runs,/run,/heartbeat log). The heartbeat's ownjob_runtimetable only keeps the last run per job; this keeps them all. - Usage records the tokens of every LLM call, attributed to the run, agent, model and channel it happened in (
/usage).
Cost is estimated only for models you price in config/pricing.yaml (USD per 1M tokens); Ethgent ships no prices because they change and model names are install-specific:
models:
gemini-2.5-flash: {input: 0.30, output: 2.50}Known limits: the Gemini Live agents (arastirma_agent, sistem_agent, vlm_agent) produce one approximate usage row per session, because the SDK does not document whether Live usage_metadata is cumulative; providers that return no usage data are counted as calls with unknown tokens.
Run the management commands from a chat, with an allow-list
The bots can run the management commands above, but who may talk to them is configured by you:
| Variable | Meaning |
|---|---|
<CHANNEL>_ALLOWED_USER_IDS |
Who may chat. If unset, chat is open to everyone (the old behaviour) and startup prints a warning. |
<CHANNEL>_ADMIN_IDS |
Who may run management commands. If unset they are disabled. |
[!WARNING] Set
<CHANNEL>_ALLOWED_USER_IDS. Without it anyone who finds the bot can chat with your agent.
Send /id (Telegram) or !id (Discord) to the bot to learn your numeric ID. Unauthorised users get no reply; each attempt is written to the log (/logs --type remote).
Management commands are /agent, /tool, /heartbeat, /usage, … on Telegram and !agent, !tool, … on Discord. Even for an admin, remote sessions cannot run /tool create|edit|delete or /tool show --code (tool code runs on the server), /agent pack … (filesystem paths) or /agent create --builtin, and agent delete / heartbeat remove require --yes.
An advanced, secondary integration path
Ethgent's primary form is the standalone terminal app described above; nothing here changes that. MarketingApp/agent_api.py exists only for the narrow case where another Python orchestrator (for example an asset-generation pipeline) needs to call Ethgent's BaseModel in-process instead of running it as a separate app: no logs/run-history/telemetry UI, no bots, no terminal, just a single run() call.
from MarketingApp.agent_api import EthgentAgent
agent = EthgentAgent(workspace_dir="/path/to/pool/brandX/workspace")
result = await agent.run("Draft a post about today's topic for X")
print(result.text)EthgentAgent never starts the heartbeat/Telegram/Discord background tasks. Workspace and config directories can be redirected per instance via workspace_dir/config_dir (see the module docstring for the single-process-per-workspace caveat).
Folder layout
MarketingApp/
├── agent_api.py # Embeddable agent wrapper (EthgentAgent)
├── paths.py # Central, overridable workspace/config path resolution
├── main.py # Entry point (python -m MarketingApp.main)
├── telemetry.py # Persistent logs, run history and token usage
├── araclar/ # Tools: browser, search, memory, content creation, workspace, skills
├── config/ # agents.yaml, custom_tools.yaml, agent_packs.yaml, heartbeat_config.yaml
├── environments/ # terminal.py, heartbeat.py, telegram.py, discord_bot.py, automation_runtime.py,
│ # access.py (bot allowlists), remote_commands.py (terminal commands over chat)
├── llms/ # BaseModel orchestrator, Agent Studio, SubModels/
├── legacy/panel/ # Archived FastAPI web panel (superseded by the terminal interface)
└── workspace/ # Runtime data: memory, drafts, assets, custom tools, agent packs
tests/ # unittest suite (isolated temp workspace)
python -m unittest discover -s testsThe suite runs against a temporary workspace and config directory (tests/_env.py), so it never touches your real agents, tools or telemetry. Use discover -s tests rather than tests.<module>: a dependency installs a top-level tests package that shadows it.
Issues and pull requests are welcome. Please keep changes scoped and include a short description of what changed and why.
MIT © 2026 Ahmet Rıfat Öztürk. See LICENSE.