nezdali/queenbee-agent

GitHub: nezdali/queenbee-agent

一个运行在 Telegram 上的可自我扩展 LLM Agent,支持用户用自然语言动态生成、审查并注册新的 Python 工具。

Stars: 0 | Forks: 0

# queenbee-agent **一个运行在 Telegram 上的可自我扩展的 LLM agent,内置 runtime 和经过安全审查的** **工具工厂。** 大多数“Telegram + GPT”机器人只是一个简单的封装:接收消息,然后返回 LLM 回复。`queenbee-agent` 在结构上完全不同。该机器人是基于任何兼容 OpenAI 的模型构建的完整 agent loop,并带有模型可以调用的工具注册表。与众不同的是,**注册表不是固定的**: 终端用户可以用简单的英语描述一项新功能(`>> 描述该工具应执行的操作`),机器人就会 编写 Python 代码,通过禁止模式和 LLM 的安全审查,将其交给管理员进行 批准/拒绝,将其持久化为一等公民工具,并使其在下一轮对话中即可调用。 ## 它的功能 - **Telegram 上的 LLM agent loop** — 支持用户级对话历史记录、流式 响应、工具调用、图片输入、群聊 `@mention` 处理。 - **兼容 OpenAI** — 支持 OpenAI、Anthropic Claude(通过 OpenAI 兼容)、Google Gemini、Mistral、Groq、Together、OpenRouter 或本地 Ollama 实例。通过 `LLM_MODEL` / `OPENAI_BASE_URL` 选择模型。 - **Queen Bee 工具工厂** — 从简单的英语描述中在 runtime 生成新工具: 1. 对描述进行禁止意图正则表达式检查(文件列表、shell 执行、机密提取 — 管理员可绕过)。 2. codex 模型编写一个 `async def run(context) -> str` Python 模块 以及包含名称/描述/触发关键字/ 所需权限的 JSON 清单。 3. 扫描生成的代码中是否包含禁止模式 (`subprocess`、`os.system`、`eval`、文件删除等)。 4. 对于非管理员用户,将运行异步 LLM **安全审查**,管理员将获得批准/拒绝按钮。 5. 批准的工具会存入 `tools/.py`,并注册到 支持 RBAC 的工具注册表中,可在下一轮对话中调用。 - **四种调度工具的方式** 1. **LLM 函数调用** — 由模型在聊天过程中决定。 2. **`/runtool [args…]`** — 显式调用。 3. **触发关键字自动调度** — 用户消息的第一个词匹配 工具的 `trigger_keywords`。 4. **智能意图调度** — 如果消息不匹配关键字但 看起来像工具请求,机器人会提供带有 确认/拒绝按钮的工具建议。 - **基于用户的 RBAC** — 每个工具都有一个 `permission` 标签(`public`、 `finance`、`email`、`admin` 等)。用户只能查看和运行其角色允许的 工具。管理员可以看到所有内容。 - **计划任务 / 监控** — 工具可以在类似 cron 的计划上运行 (APScheduler)并将结果推送回给用户。 - **迭代循环** — `/qbtest`、`/qbfix `、`/qbsave`、 `/qbdiscard`、`/edittool `,用于在不离开 Telegram 的情况下完善 工具。 - **操作强化** — 使用 Azure Key Vault 加载器管理机密(包含 AWS Secrets Manager / GCP Secret Manager 方案),对工具生成进行基于用户的速率 限制、输出净化器、群聊允许列表。 ## 内置约 30 个示例工具 CoinGecko BTC 价格、天气、爱沙尼亚燃料/酒精/化妆品价格、 Amazon.de 搜索、Google Shopping 搜索、Booking.com / IKEA / JYSK 抓取器、Spotify 查找、Duolingo 个人资料统计、斯诺克 / FIFA / 足球 状态、Euribor 利率、Cozytouch / eWeLink / Huum 智能家居控制、 yt-dlp 媒体下载器、TLDR 摘要器等等 — 所有这些都在 `tools/` 中, 每个都是一个独立的 `async def run(context) -> str` 模块。 ## 前置条件 在开始之前,请准备好以下三样东西: ### 1. LLM API key 该机器人可与任何**兼容 OpenAI 的 Chat Completions endpoint** 通信,因此您 可以选择您偏好的任何提供商: | 提供商 | 获取密钥地址 | 需设置的 `OPENAI_BASE_URL` | |---|---|---| | **OpenAI**(默认) | | 保持未设置状态(默认为 `https://api.openai.com/v1`) | | **Anthropic Claude** | | `https://api.anthropic.com/v1/`(使用 [Anthropic 兼容 OpenAI 的 endpoint](https://docs.anthropic.com/en/api/openai-sdk)) | | **Google Gemini** | | `https://generativelanguage.googleapis.com/v1beta/openai/` | | **Mistral / Groq / Together / OpenRouter / 本地 Ollama** | 它们各自的仪表板 | 它们兼容 OpenAI 的 base URL | 无论您选择哪种提供商,请将密钥放入 `OPENAI_API_KEY` 中,并(如有需要) 在 `.env` 中设置 `OPENAI_BASE_URL`。您还可以使用 `OPENAI_DEFAULT_MODEL` 覆盖默认模型(例如 `gpt-4o-mini`、`claude-3-5-sonnet-latest`、 `gemini-2.0-flash`、`llama-3.3-70b-versatile` 等)。 ### 2. Telegram bot token + 您的 Telegram 用户 ID - 从 [@BotFather](https://t.me/BotFather) 获取 bot token (`/newbot` → 按照提示操作 → 复制 token)。 - 从 [@userinfobot](https://t.me/userinfobot) 获取您自己的数字 Telegram ID。 ### 3. 一台运行机器人的机器 该机器人是一个长时间运行的 Python 进程 — 它必须保持在线才能接收 Telegram 更新。对于本地测试,您的笔记本电脑即可;对于生产环境,您 需要一个小型始终在线的虚拟机(VM)。 任何小型 Linux VM 都可以(1 vCPU / 1 GB RAM 足矣)。最便宜的选项: | 提供商 | 最小实例层 | 大约费用 | 快速创建 | |---|---|---|---| | **Azure** | `Standard_B1s` Ubuntu 22.04 | 约 $8/月 | `az vm create -g rg -n tgbot --image Ubuntu2204 --size Standard_B1s --admin-username azureuser --generate-ssh-keys` | | **AWS EC2** | `t4g.nano` Ubuntu 22.04 (arm64) | 约 $3/月 | `aws ec2 run-instances --image-id ami-0... --instance-type t4g.nano --key-name mykey` | | **GCP Compute Engine** | `e2-micro` Ubuntu 22.04 | 符合免费层条件(美国区域) | `gcloud compute instances create tgbot --machine-type=e2-micro --image-family=ubuntu-2204-lts --image-project=ubuntu-os-cloud` | | **Hetzner / DigitalOcean / Vultr / Linode** | 共享 CPU 1 GB | 约 $4-5/月 | 使用它们的 Web 控制台 | | **Raspberry Pi / 家庭服务器** | 任意 | 如果您已经拥有则为 $0 | `ssh pi@` | VM 启动后: ``` ssh user@ sudo apt update && sudo apt install -y python3.11 python3.11-venv git git clone cd telegram_chatbot python3.11 -m venv .venv source .venv/bin/activate pip install -r requirements.txt cp .env.example .env && nano .env # paste your tokens python bot.py # smoke-test ``` 为了在您注销后保持机器人运行,请创建一个 systemd unit (`/etc/systemd/system/telegram-bot.service`): ``` [Unit] Description=Telegram Chatbot After=network.target [Service] Type=simple User=youruser WorkingDirectory=/home/youruser/telegram_chatbot EnvironmentFile=/home/youruser/telegram_chatbot/.env ExecStart=/home/youruser/telegram_chatbot/.venv/bin/python bot.py Restart=always RestartSec=5 [Install] WantedBy=multi-user.target ``` ``` sudo systemctl daemon-reload sudo systemctl enable --now telegram-bot journalctl -u telegram-bot -f # follow logs ``` ## 快速开始 ``` git clone cd telegram_chatbot python -m venv .venv source .venv/bin/activate # or .venv\Scripts\activate on Windows pip install -r requirements.txt cp .env.example .env # 编辑 .env:设置 TELEGRAM_BOT_TOKEN, OPENAI_API_KEY, QB_ADMIN_USER_ID python bot.py ``` 从 [@BotFather](https://t.me/BotFather) 获取 bot token。从 [@userinfobot](https://t.me/userinfobot) 获取您自己的 Telegram 数字用户 ID,并 将其放入 `QB_ADMIN_USER_ID` 中。 ## 架构 ``` bot.py — entry point: registers handlers, starts polling config.py — env-var + (optional) Azure Key Vault loading tool_utils.py — fetch_json/text/html/rendered helpers for tools core/ tool_factory.py — Queen Bee: generate / edit / run tools tool_match.py — keyword scoring for auto-dispatch agent_tools.py — built-in LLM tools (list_tools, fetch_url, run_tool) conversation.py — per-user history + model preference output_safety.py — global output sanitiser tool_registry.py — RBAC-aware tool registry user_roles.py — persistent JSON user→roles store handlers/ core.py — /start, /help, /clear, /model, handle_message queen_bee.py — >>, /tools, /runtool, /qbtest, /qbsave, ... admin_users.py — /adduser, /deluser, /listusers, /setrole, ... tool_dispatch.py — thinking phrases + translation helper strava_handlers.py — /stravaconnect, /stravaauth, /stravahelp group.py — auto-leave non-allowed groups utils.py — shared helpers, ConversationManager services/ llm_service.py — OpenAI API wrapper (streaming + tool-calling) model_router.py — picks chat / codex / vision model per task strava_service.py — Strava OAuth + REST wrapper (per-user tokens) tools/ — 29 pre-built tools + your own generated ones (see tools/README.md for the list) ``` ## Queen Bee 工具工厂 在与机器人的私聊中: ``` >> fetch the current Bitcoin price from CoinGecko and format it nicely ``` 机器人将执行以下操作: 1. 根据禁止意图正则表达式列表(文件列表、 shell 执行、机密提取等)验证请求 — 管理员用户可绕过此步骤。 2. 要求 LLM 生成一个 `async def run(context: dict) -> str` Python 模块。 3. 扫描生成的代码中是否包含禁止模式(`subprocess`、`os.system`、 `eval`、文件删除等) — 管理员用户可绕过此步骤。 4. 对于非管理员用户:将工具保存为 `pending_review`,触发异步 LLM 安全审查,并向管理员提供**批准 / 拒绝**按钮。 5. 一旦批准,该工具即可通过以下方式调用: - `/runtool ` — 显式调用 - 首词关键字匹配(每个工具都有 `trigger_keywords`) - LLM 本身,通过 `run_tool` 工具(按用户进行 RBAC 过滤) ### 管理工具 | 命令 | 用途 | |---|---| | `/tools` | 列出已保存的工具 | | `/toolhelp` | 全面的操作指南 + 每个工具的描述 | | `/toolhelp ` | 某个工具的完整详情 | | `/newtool` | 关于如何创建新工具的详细步骤 | | `/runtool ` | 显式运行工具 | | `/deltool ` (管理员) | 删除工具 | | `/edittool ` | 要求 LLM 修改现有工具 | | `/qbtest ` | 生成 + 运行工具但不保存 | | `/qbsave` | 保存上次测试生成的工具 | | `/qbdiscard` | 将其丢弃 | | `/qbfix ` | 要求 LLM 修复上次的测试工具 | | `/qbdebug` | 显示上次生成的代码 | ## RBAC 角色存在于 `config.ROLE_PERMISSIONS` 中: ``` ROLE_PERMISSIONS = { "admin": ["*"], # everything "public": ["public"], # default } ``` 添加您自己的角色(`finance`、`email`、`ops` 等),并通过以下方式分配给用户: ``` /adduser 123456789 finance,email @alice /setrole my_tool finance # only users with the `finance` role may run it ``` 内置工具(`list_tools`、`fetch_url`、`run_tool`)均为 `public`。 生成的工具默认为 `public`;管理员可以通过 `/setrole` 提升其权限。 ## 添加您自己的领域工具 要将自定义工具(例如银行 API)接入 LLM 协调器,请在 `core/tool_registry.py` 中注册它,包含 schema + handler + 权限标签: ``` # 在你的模块的 init 中 import core.tool_registry as tr tr.register( "my_tool", schema={"type": "function", "function": {"name": "my_tool", "description": "...", "parameters": {...}}}, handler=async_handler, permission="finance", # only users with a role mapping to 'finance' see it ) ``` LLM 只能看到调用用户拥有权限的工具。 ## 安全 - 生成的工具通过 `importlib` 在进程内运行;它们没有被沙箱化。 防御措施包括: - 针对非管理员用户的意图正则表达式(LLM 前) + 代码正则表达式(LLM 后) - 后台 LLM 安全审查与管理员批准 - `pending_review` 和 `rejected` 状态会阻止执行 - 每次工具调用有 90 秒的硬性超时 - 禁止列表位于 `core/tool_factory.py` 中 (`_FORBIDDEN_INTENT_PATTERNS`、`_FORBIDDEN_PATTERNS`)。 - 切勿将 `QB_ADMIN_USER_ID` 设置为您不完全信任的用户 — 管理员可绕过 所有检查,且其生成的代码将未经核实直接运行。 - Telegram token、OpenAI 密钥等仅从环境中读取。 `.gitignore` 排除了 `.env`、所有 `*token*.json`、所有 `*credentials*.json` 和所有 `*.pem`/`*.key` 文件。 ## 可选集成 该仓库内置了一些与第三方服务通信的工具和处理程序。**所有这些功能默认关闭** — 将相应的环境变量留空,集成将被静默跳过。设置它们以启用。 | 集成 | 您将获得的功能 | 环境变量 | | --- | --- | --- | | Strava | `/stravaconnect`、`/stravaauth`,以及 4 个 LLM 工具(运动员、活动、单次活动区间、kudos) | `STRAVA_CLIENT_ID`, `STRAVA_CLIENT_SECRET` | | Spotify | `spotify_lookup_tool`(曲目 / 艺术家 / 专辑查找) | `SPOTIFY_CLIENT_ID`, `SPOTIFY_CLIENT_SECRET`, `SPOTIFY_REDIRECT_URI` | | Duolingo | `duolingo_stats_tool`(公开个人资料查找 — 无需认证) | — | | eWeLink | `ewelink_smart_home` 工具(控制 Sonoff 设备) | `EWELINK_EMAIL`, `EWELINK_PASSWORD`, `EWELINK_REGION` | | Huum 桑拿 | `huum_sauna` 工具(状态 / 开启加热 / 关闭加热) | `HUUM_USERNAME`, `HUUM_PASSWORD` | | Cozytouch 热泵 | `cozytouchpump` 工具(温度和模式) | `COZYTOUCH_USERNAME`, `COZYTOUCH_PASSWORD` | | TMDB | `movie_search` 工具(电影元数据) | `TMDB_API_KEY` | | SerpAPI | `amazon_search`、`google_shopping_search`、`booking_com_search` | `SERPAPI_KEY` | | OpenAI 使用情况 | `openai_api_usage_status` 工具(费用报告) | `OPENAI_ADMIN_API_KEY` | | ElevenLabs | `media_creator` 中的可选 TTS | `ELEVENLABS_API_KEY` | | Yandex 翻译 | 可选的翻译助手 | `YANDEX_API_KEY` | ### Strava — 连接用户 1. 访问 → **Create & Manage Your App**。 2. 将 **Authorization Callback Domain** 设置为 `localhost`。 3. 将您的 Client ID + Client Secret 复制到 `.env` 中: STRAVA_CLIENT_ID=12345 STRAVA_CLIENT_SECRET=... 4. 重启机器人并在 Telegram 中运行 `/stravaconnect`。 5. **在外部浏览器中打开链接**(Telegram 的应用内浏览器会被 Strava 阻止)。授权,然后从生成的 URL 中复制 `code=` 的值并运行: /stravaauth 6. token 将保存为 `strava_token_.json`(位置可通过 `STRAVA_TOKEN_DIR` 覆盖)。 连接后,您可以向 LLM 询问诸如*“我上一次骑行怎么样?”*或 *“我昨天在功率区间 5 花了多少时间?”*之类的问题。LLM 会自动选择 正确的 Strava 工具。 要让用户询问彼此的情况(*“Alice 的上一次骑行怎么样?”*),请填写 `KNOWN_USERS` 环境变量: ``` KNOWN_USERS=alice:123456789;al:123456789;bob:987654321 ``` ### Spotify 1. 在 创建一个应用。 2. 添加重定向 URI(例如 `http://localhost:8888/callback`)。 3. 填写: SPOTIFY_CLIENT_ID=... SPOTIFY_CLIENT_SECRET=... SPOTIFY_REDIRECT_URI=http://localhost:8888/callback 4. 运行一次 `spotify_lookup_tool` — 它将打印一个授权 URL 并引导您粘贴回重定向后的 URL。 ### eWeLink (Sonoff) 使用您用于 eWeLink 移动应用的相同电子邮件/密码: ``` EWELINK_EMAIL=you@example.com EWELINK_PASSWORD=... EWELINK_REGION=eu # one of: us | eu | as | cn # 可选:使用手机登录代替邮箱 # EWELINK_PHONE=+3725551234 ``` ### Huum 桑拿 ``` HUUM_USERNAME=you@example.com HUUM_PASSWORD=... ``` 该工具查询 Huum 的 REST API;凭据与 Huum 移动应用相同。 ### Atlantic / Cozytouch 热泵 ``` COZYTOUCH_USERNAME=you@example.com COZYTOUCH_PASSWORD=... ``` 使用 Atlantic / Cozytouch IO 门户;凭据与移动应用相同。 ### Duolingo 无需认证 — 该工具使用 Duolingo 的公开个人资料 API: ``` duolingo ``` ### TMDB (movie_search 工具) 在 获取免费的 v3 API key 并设置: ``` TMDB_API_KEY=... ``` 如果 `yt-dlp` 在 `$PATH` 中,YouTube 预告片提取将使用它;可以选择将 `youtube_cookies.txt` 文件放在 `bot.py` 旁边以绕过机器人检测。 ### OpenAI 使用报告 在 生成一个单独的 **Admin API key**(组织范围, `/v1/organization/usage/*` endpoint 必需),并设置: ``` OPENAI_ADMIN_API_KEY=sk-admin-... ``` ## 可选:密钥管理器(Azure / AWS / GCP) 对于生产环境,您通常不希望在 VM 上的 `.env` 中存放明文 API 密钥。选择以下云密钥管理器之一,将 `config._KV_SECRET_MAP` 中列出的每个值存储在那里,并授予您的 VM 身份读取权限。 ### Azure Key Vault(内置) 设置 `AZURE_KEYVAULT_URL=https://your-vault.vault.azure.net/`,机器人将在 启动时使用 `DefaultAzureCredential`(在生产环境中为 Managed Identity,在 本地为 `az login`)从您的保险库中提取列在 `config._KV_SECRET_MAP` 中的环境变量。保持未设置状态则仅使用 `.env`。 创建保险库: ``` az group create -n tgbot-rg -l northeurope az keyvault create -n tgbot-kv -g tgbot-rg -l northeurope az keyvault secret set --vault-name tgbot-kv --name TELEGRAM-BOT-TOKEN --value '...' az keyvault secret set --vault-name tgbot-kv --name OPENAI-API-KEY --value 'sk-...' # 授予 VM 的 managed identity 读取权限 az vm identity assign -g tgbot-rg -n tgbot VM_PRINCIPAL=$(az vm show -g tgbot-rg -n tgbot --query identity.principalId -o tsv) az role assignment create --role "Key Vault Secrets User" \ --assignee "$VM_PRINCIPAL" \ --scope $(az keyvault show -n tgbot-kv --query id -o tsv) ``` 机器人期望保险库密钥名称带有短划线(例如 `OPENAI-API-KEY`);它们将在启动时映射到带有下划线的环境变量。 ### AWS Secrets Manager(DIY) 该仓库内置了 Azure 加载器,但相同的模式也适用于 AWS — 您只需 将一个小型加载器插入 `config.py`。 创建密钥库: ``` # 1. 创建一个包含所有 key/value 的 JSON secret aws secretsmanager create-secret \ --name tgbot/prod \ --secret-string '{"TELEGRAM_BOT_TOKEN":"...","OPENAI_API_KEY":"sk-...","QB_ADMIN_USER_ID":"123456"}' # 2. 创建一个可以读取它的 IAM role 并附加到你的 EC2 instance ROLE_ARN=$(aws iam create-role --role-name tgbot-secrets-reader \ --assume-role-policy-document file://trust-policy.json --query Role.Arn -o tsv) aws iam put-role-policy --role-name tgbot-secrets-reader \ --policy-name read-tgbot-secret \ --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"secretsmanager:GetSecretValue","Resource":"arn:aws:secretsmanager:*:*:secret:tgbot/prod-*"}]}' aws ec2 associate-iam-instance-profile --instance-id i-... \ --iam-instance-profile Name=tgbot-secrets-reader ``` 然后在 Azure 加载器附近扩展 `config.py`,例如: ``` if aws_secret := os.getenv("AWS_SECRET_ID"): import boto3, json as _json blob = boto3.client("secretsmanager").get_secret_value(SecretId=aws_secret) for k, v in _json.loads(blob["SecretString"]).items(): os.environ.setdefault(k, v) ``` ### Google Cloud Secret Manager(DIY) 相同思路,GCP 风格: ``` # 1. 启用 API 并为每个 key/value 创建一个 secret gcloud services enable secretmanager.googleapis.com printf 'sk-...' | gcloud secrets create OPENAI_API_KEY --data-file=- printf '...' | gcloud secrets create TELEGRAM_BOT_TOKEN --data-file=- # 2. 给予 VM 的 service account 读取权限 SA=$(gcloud compute instances describe tgbot --zone=europe-north1-a \ --format='value(serviceAccounts[0].email)') gcloud secrets add-iam-policy-binding OPENAI_API_KEY \ --member="serviceAccount:$SA" --role="roles/secretmanager.secretAccessor" gcloud secrets add-iam-policy-binding TELEGRAM_BOT_TOKEN \ --member="serviceAccount:$SA" --role="roles/secretmanager.secretAccessor" ``` 然后扩展 `config.py`: ``` if gcp_project := os.getenv("GCP_PROJECT_ID"): from google.cloud import secretmanager client = secretmanager.SecretManagerServiceClient() for key in ("OPENAI_API_KEY", "TELEGRAM_BOT_TOKEN", ...): name = f"projects/{gcp_project}/secrets/{key}/versions/latest" os.environ.setdefault(key, client.access_secret_version(name=name).payload.data.decode()) ``` 使用上述三种方式中的任何一种,VM 都永远不会看到明文 `.env`,轮换 密钥只需在保险库中更新,然后执行 `systemctl restart telegram-bot`。 ## 许可证 MIT — 查看 [LICENSE](./LICENSE)。
标签:LLM Agent, OpenAI兼容, RBAC权限控制, Telegram机器人, 代码生成, 安全审查, 渗透测试工具, 逆向工具