
Agent Matchbox 面向 Agent 开发而生,是一个可独立运行、可嵌入应用的大模型路由与配额控制中心。它不要求宿主项目提供特定的 core 包,也不预设某个 Agent 或业务用途。宿主可以通过集成回调注入自己的数据库工厂、请求上下文、默认用途和扩展密钥迁移逻辑。
该项目的设计目标是支持从个人开发、调试到多用户生产环境的多种复杂场景,并提供了一个图形化界面来简化核心配置的管理。
💡 为什么叫“火柴”? 火柴是点燃智能之火的原材料。在这个 AI 普惠的时代,构建各类 AI 应用的站长们就像是一个个“卖火柴的小女孩”(
Token滞销,请帮帮我们)。
专门的外置网关(如 NewAPI、LiteLLM 等)虽然强大,但在与复杂的应用直接结合时往往存在体验断层。本管理器作为内置网关,具备以下独特优势:
matchbox().get_user_llm(user_id, usage_key="fast") 获取对应用户/用途的 LLM 实例。reasoning_content 和 <think> 标签)统一转化为持续的推理流,确保深度思考模型在运转时前端依然能拥有极佳的纯流式体验。required: [],兼容 Grok 等严格校验实现;该规则属于通用 OpenAI Compatible 协议层,不按域名或模型名分支,也不占用或改写 extra_body。matchbox_cfg.yaml) 统一管理,用户平台数据则存储在数据库中。LLM_AUTO_KEY 选项,允许在用户未提供密钥时,自动降级使用服务器的密钥(需谨慎使用)。sys_paid(消耗站长托管 Key)和 self_paid(消耗用户自己的 Key)。single(一次性,用完即废)和 per_user(每用户可用一次,全服福利)。probe_platform_models),可以探测任何兼容OpenAI接口的平台所支持的模型列表。
reasoning_content、usage 或 billing 相关字段,可直接在日志中查看。Flet (0.28.3) 的现代化自适应 GUI 工具(matchbox_cfg_gui.pyw,Windows 下支持无控制台直接双击自启),完全无需依赖前端配置,直接操作数据库,支持添加/编辑/软禁用平台与模型、拖拽排序、加密存储 API Key、探测和测试模型,以及从本地 YAML 重置数据库或将数据库导出为 matchbox_cfg.yaml + matchbox_key.yaml。AGENT_MATCHBOX_DATABASE_URL 切换到 PostgreSQL。.
├── __init__.py # 包入口,导出 initialize_matchbox / matchbox / create_matchbox
├── database.py # 独立数据库 Engine 工厂
├── integrations.py # 宿主集成回调契约
├── manager.py # AIManager 核心类(组合所有 Mixin)
├── config.py # 配置加载与全局常量 (USE_SYS_LLM_CONFIG, LLM_AUTO_KEY 等)
├── models.py # SQLAlchemy 数据库模型
├── security.py # 安全与加密 (SecurityManager)
├── admin.py # 平台与模型管理 Mixin (AdminMixin)
├── builder.py # LLM 实例构建 Mixin (LLMBuilderMixin)
├── user_services.py # 用户服务 Mixin (UserServicesMixin)
├── quota_services.py # 配额配置/统计/拦截 Mixin (QuotaServicesMixin)
├── usage_services.py # 用量统计 Mixin (UsageServicesMixin)
├── redeem_code_services.py # 兑换码管理 Mixin (RedeemCodeServicesMixin)
├── tracked_model.py # LLMClient/LLMUsage/UsageTrackingCallback
├── estimate_tokens.py # Token 用量估算工具
├── hf_mirror.py # Hugging Face 镜像发现、区域判断与可达性探测
├── utils.py # 工具函数 (probe_platform_models, parse_extra_body 等)
├── matchbox_cfg.yaml # 系统平台结构配置(仅用于初始化/导出,运行时以数据库为准)
├── matchbox_key.yaml # 系统平台 API Key(应被 git 忽略,禁止提交)
├── matchbox_cfg_gui.pyw # 图形化配置管理工具(双击直接自启入口,实际代码在 gui/ 子目录)
├── gui/ # GUI 模块(拆分自 matchbox_cfg_gui)
│ ├── __init__.py
│ ├── main_window.py # 主窗口 LLMConfigGUI 类(平台配置、用户总览、模型管理)
│ ├── platform_panel.py # 平台管理 Mixin
│ ├── model_panel.py # 模型管理 Mixin
│ ├── dialogs.py # 对话框 Mixin(添加/编辑模型、系统用途槽、用户配额)
│ ├── key_manager.py # 密钥管理 Mixin
│ ├── probe.py # 探测功能 Mixin
│ ├── dpi.py # 高分屏适配与窗口尺寸策略
│ └── theme.py # GUI 主题、配色与字体跨平台(Windows/Ubuntu)适配
└── README.md # 本文档
manager.py: 包含 AIManager 类,通过 Mixin 模式组合了 AdminMixin、LLMBuilderMixin、UserServicesMixin、QuotaServicesMixin、UsageServicesMixin 等功能模块。这是与程序交互的主要入口。quota_services.py: 配额服务模块,集中处理 sys_paid/self_paid 两条计费口径的配额配置、周期用量统计、总量统计与调用前拦截。usage_services.py: 用量统计模块,除单用户汇总外,也提供面向 GUI 的全用户调用总览聚合能力。matchbox_cfg.yaml: 初始化配置文件。用于定义初始的"系统平台"。首次启动时,管理器会将此文件中的平台同步到数据库。后续启动仅增量添加新平台,不会覆盖已有配置。运行时权威数据源是数据库,而非此文件。matchbox_cfg_gui.pyw: GUI 入口文件,实际逻辑拆分在 gui/ 子目录中。直接操作数据库,支持平台/模型增删改(删除为软禁用)、API Key 加密存储、模型探测与测试、全用户调用总览、双击用户查看详情,以及从本地 YAML 重置数据库或将数据库导出为 matchbox_cfg.yaml + matchbox_key.yaml。注意: 包内提供的配置文件 (matchbox_cfg.yaml) 适用于快速迁移或者分享模型配置。platform_key 是平台配置和密钥映射的稳定身份,数据库运行时引用使用 platform_id;base_url 仅是允许重复的上游连接地址。API Key 单独存放在 matchbox_key.yaml 中并按 platform_key 索引;旧版按 URL 索引的密钥只会在该 URL 唯一时兼容读取。该文件被 .gitignore 忽略,禁止提交到版本库。
首次使用时,你需要运行配置工具,填入你自己的 API Key。
设置主加密密钥 (LLM_KEY):
LLM_KEY 加密你的 API Key和所有用户自定义的API Key。你可以设置环境变量,或者直接运行 GUI 工具,它会提示你输入并自动保存。matchbox_key.yaml 中携带了仓库作者或其他环境生成的加密 Key,它们在你的机器上本来就不可用。此时你只需要设置自己的 LLM_KEY,并按提示选择清理这些不可恢复密钥即可。清理不会删除平台与模型结构,只会清空这些不可用的托管 Key。启动配置工具:
matchbox_cfg_gui.pyw,或在命令行运行 python matchbox_cfg_gui.pyw。替换并激活平台:
验证模型:
检查用途绑定:
main (主模型)、fast (快速模型)、reason (推理模型) 绑定的模型是你刚刚配置过 Key 的有效模型。最终测试:
理解网关的运行模式至关重要,这直接影响到功能的表现和二次开发。
为兼顾稳定性、可维护性和扩展自由度,火柴网关采用两阶段初始化 + 双通道标准设计:
initialize_matchbox(ensure_defaults=True),仅完成数据库引擎初始化和默认配置同步。此阶段不会加载 langchain_openai 等重运行时依赖。warmup_matchbox_runtime(blocking=False),在后台线程中预加载 ChatUniversal、LLMClient 等运行时模块,与应用启动并行执行,避免首个请求阻塞。matchbox() 获取管理器,再调用 get_user_llm(...) / get_user_embedding(...)。create_quick_llm(...) / create_quick_embedding(...) 快速创建客户端。reset_matchbo(),避免导入即初始化的副作用。set_default_mgr_home(path) 设置自己的默认目录;部署环境仍可通过优先级更高的 AGENT_MATCHBOX_HOME 显式覆盖。from agen_matchbox import initialize_matchbox, warmup_matchbox_runtime, matchbox
# 1) 轻启动:数据库引擎 + 默认配置同步(毫秒级)
initialize_matchbox(ensure_defaults=True)
# 2) 异步预热:后台线程加载 langchain_openai 等重运行时依赖
# blocking=False(默认)立即返回,与应用启动并行;首个请求到达时模块已就绪
warmup_matchbox_runtime(blocking=False)
# 3) 在业务请求中按需获取(默认 required=True)
client = matchbox().get_user_llm(user_id="user_123", usage_key="main", agent_name="your_agent")
# 4) 像普通 LLM 一样使用
result = client.invoke("请给我一个赛博朋克世界观种子")
# 5) 流式同样可用,且会自动完成用量归档
for chunk in client.stream("继续扩展成三幕结构"):
print(chunk.content, end="")
SYSTEM_USER_ID = "-1")这是一个特殊的虚拟用户ID。当代码中使用 matchbox().get_user_llm() (不带 user_id 参数) 或 matchbox().get_user_llm(user_id="-1") 时,管理器会进入系统模式。
LLM_AUTO_KEY 规则回退到系统后备 Key。当前实现中,系统后备 Key 来自 DEFAULT_PLATFORM_CONFIGS,由 matchbox_cfg.yaml 提供平台结构、matchbox_key.yaml 提供平台 API Key,并解析环境变量占位符。在 config.py 中有两个重要的全局开关:
USE_SYS_LLM_CONFIG = True (多用户固定平台模式)
matchbox_cfg.yaml 中定义的系统平台。llm_sys_platform_keys 表中,与用户ID关联。USE_SYS_LLM_CONFIG = False (多用户自定义平台模式)
AIManager 的 add_platform, add_model 等方法来创建自己的私有平台和模型。LLM_AUTO_KEY)系统在获取 API Key 时遵循 “用户私有 > 系统后备” 的原则:
LLM_AUTO_KEY。LLM_AUTO_KEY = True
True,管理器会自动回退并使用系统后备 Key(当前实现来自 DEFAULT_PLATFORM_CONFIGS,由 matchbox_cfg.yaml + matchbox_key.yaml 解析而来)作为后备 API Key。False。LLM_AUTO_KEY = False
ValueError,提示用户需要配置API Key。推荐设置:
LLM_AUTO_KEY = True,并通过 GUI 为系统默认平台配置 API Key(由管理员支付)。LLM_AUTO_KEY = False,并在前端或用户设置中要求用户填写他们的 API Key。当前版本已把所有调用按实际命中的密钥来源拆成两条计费/配额口径:
sys_paid:系统平台 + 站长托管 Keyself_paid:用户自己的 Key
这样做的目的是:
sys_paidsys_paid 用完,只要切到自己的 Key,仍然可以继续使用 self_paid配额配置存储在 user_quota_policies 表中,支持两条口径分别配置:
*_window_hours*_window_token_limit*_window_request_limit*_total_token_limit*_total_request_limit所有字段都允许为空;为空表示该项限制未启用。
运行时拦截逻辑如下:
matchbox().get_user_llm(...) / matchbox().get_spec_sys_llm(...) 先解析本次调用实际命中的 Key。sys_paid 或 self_paid。QuotaExceededError。main(主模型)、fast(快速模型)、reason(推理模型)三个槽位,并在注册时绑定默认平台/模型。POST /api/ai/user-selection/usage 或 AIManager.create_user_usage_slot(...) 可以新增任意 usage_key,并指定初始模型。GET /api/ai/user-selection?usage_key=fast 可查询指定用途;响应中还会包含 usage_selections 列表以展示所有用途的当前绑定。POST /api/ai/user-selection 支持传入 usage_key 字段来更新特定用途的模型。matchbox().get_user_llm(user_id, usage_key="reason") 会直接返回该用途绑定的模型实例;若参数省略,则默认为主模型。当前仓库按源码组件提供,不包含 pyproject.toml。请把 agen_matchbox 目录放入宿主项目的 Python 导入路径,并由宿主依赖文件统一声明版本。直接开发时可显式安装所需依赖:
pip install langchain-core langchain-openai sqlalchemy tiktoken cryptography pyyaml requests python-dotenv
# 按需:PostgreSQL / GUI / Alembic
pip install "psycopg[binary]" "flet>=0.28.3,<0.29.0" alembic
推荐方式:在 Windows 下直接双击 matchbox_cfg_gui.pyw 启动,或使用命令行操作数据库,无需手动编辑 YAML。
python matchbox_cfg_gui.pyw
说明:
matchbox_cfg.yaml仅在首次启动时将预置平台写入数据库(增量同步,不覆盖已有配置)。 运行时平台/模型选择以数据库为准;系统后备 Key 解析仍会使用DEFAULT_PLATFORM_CONFIGS(来自matchbox_key.yaml/ 环境变量占位符)。 若仓库分发的密钥文件中包含其他环境加密的ENC:密钥,而当前站点无法解密,系统会跳过导入这些无效托管 Key,但仍然会正常同步平台与模型结构,随后由站长在本地 GUI 中填写自己的 Key。
GUI 操作步骤:
模型类型不再通过 is_embedding 或一组不断扩张的分类能力位保存。数据库、YAML 和 API 只使用两个事实字段:
input_modalities:当前支持 text、image。output_modalities:当前支持 text、image、embedding;embedding 与其他输出互斥。常见组合如下:
| 模型用途 | input_modalities | output_modalities |
|---|---|---|
| 文本模型 | [text] | [text] |
| 视觉文本模型 | [text, image] | [text] |
| 文生图 | [text] | [image] |
| 图生图 / 图片编辑 | [text, image] | [image] |
| 文本与图片统一输出 | [text, image] | [text, image] |
| 向量模型 | [text] | [embedding] |
Web 管理页和 CustomTkinter GUI 只显示“视觉 / 生图 / 向量”三个复选框,文本输入默认隐含。勾选向量会自动取消其他选项。模型列表使用紧凑标签:T 表示文本输出,I 表示图片输出,V 表示接收图片输入,E 表示向量输出;Web 端通过 Tooltip 解释标签。
models:
可编辑生图模型:
model_name: provider-image-model
input_modalities: [text, image]
output_modalities: [image]
image_generation_adapter: openai_images
extra_body:
quality: high
image_generation_adapter 是网关协议选择的唯一真相源。它由用户明确选择,不根据 base_url 域名、模型名或 extra_body 猜测,也不会写入或转发到上游请求。extra_body 仍专门承载供应商参数;适配层只过滤网关自己的内部控制键,其他参数会按对应协议尽力透传。生图模型未显式选择时使用 openai_images 默认值,不读取任何旧版嵌套 adapter。
| 值 | 上游协议 | 配置中的 model_name | 参考图传递方式 |
|---|---|---|---|
openai_images | /images/generations、/images/edits | GPT Image 或兼容生图模型 | multipart image[] |
openai_responses_image | /responses 的 image_generation 工具 | 支持该工具的主线文本模型 | 单次请求内的 input_image data URL,不经 Files API 持久上传 |
openai_chat_image | /chat/completions 兼容网关 | 网关暴露的生图模型名 | 多模态消息中的 data URL;兼容 Markdown/data URI 图片结果 |
gemini_generate_content | Gemini models/*:generateContent | Gemini / Nano Banana 模型 | inline_data |
gemini_interactions | Gemini Interactions | Gemini / Nano Banana 模型 | input 图片 part |
xai_images | xAI /images/generations、/images/edits | Grok Image 模型 | JSON data URL,编辑最多 3 张参考图 |
默认值仍为 openai_images,因为它最接近当前图片 API 的公共最小集合。openai_responses_image 不是 GPT Image 直连协议:它会让 model_name 对应的主线模型调用生图工具,因此还会产生主线模型 token 成本。网关不会为了传参考图自动调用供应商 Files API;宿主项目图片只在本地持久保存,调用时才按所选协议随请求发送。
直接编辑 matchbox_cfg.yaml 文件,下次启动时新增的平台会被增量同步到数据库。
api_key: 可使用占位符(如 {OPENAI_API_KEY}),系统启动时会自动从环境变量解析。
也可留空,后续通过 GUI 在数据库中填写加密 Key。在运行你的主应用之前,请确保在系统中设置了你在 matchbox_key.yaml 中引用的环境变量。
例如,如果你的配置是 api_key: '{GEMINIX_API_KEY}',你需要:
Windows:
$Env:GEMINIX_API_KEY="your_real_api_key"
(为了永久生效,请在系统属性中设置)
Linux/macOS:
export GEMINIX_API_KEY="your_real_api_key"
(为了永久生效,请添加到 .bashrc 或 .zshrc)
提示:GUI 工具的“保存 API Key”会将 Key 加密写入数据库(不是直接写入 YAML)。如果你希望将当前数据库配置回写到本地文件,请使用工具栏的“导出DB到YAML”,它会同时生成 matchbox_cfg.yaml(结构)和 matchbox_key.yaml(密钥)。
大模型管理器现已重构为组件化结构,通过 Mixin 模式集成管理、构建和统计能力。
推荐写法是:导入 initialize_matchbox、warmup_matchbox_runtime 和 matchbox,并在应用启动阶段显式执行两阶段初始化(建议放到 lifespan / startup 钩子中)。
from agen_matchbox import initialize_matchbox, warmup_matchbox_runtime, matchbox
# 建议在应用启动时执行(进程级,两阶段)
# 阶段一:轻启动——数据库引擎、表结构、默认配置同步(不加载 langchain_openai)
initialize_matchbox(ensure_defaults=True)
# 阶段二:异步预热——后台线程加载 ChatUniversal / LLMClient 等重运行时模块
# blocking=False(默认)立即返回,不阻塞应用启动
warmup_matchbox_runtime(blocking=False)
# --- 场景1: 获取指定用户的LLM ---
# 管理器会自动处理该用户的模型选择、API Key等所有配置
try:
user_llm = matchbox().get_user_llm(user_id="user_123")
fast_llm = matchbox().get_user_llm(user_id="user_123", usage_key="fast")
# response = user_llm.invoke("你好")
# for chunk in user_llm.stream("你好"):
# print(chunk.content, end="")
except ValueError as e:
# 可能是API Key未配置等问题
print(f"获取LLM失败: {e}")
# --- 场景2: 在后端服务或无用户场景下使用 ---
# 使用特殊的 SYSTEM_USER_ID,密钥来自 matchbox_key.yaml 中配置的加密 Key
try:
system_llm = matchbox().get_user_llm() # user_id=None 默认为系统用户
# response = system_llm.invoke("写一个Python的Hello World")
except ValueError as e:
print(f"获取系统LLM失败: {e}")
# --- 场景3: 轻量入口,直接指定系统模型(适用于本地测试/调试脚本)---
# 显示名称必须与配置中完全一致,调用期间不可修改
try:
qwen_llm = matchbox().get_spec_sys_llm(
platform_name="阿里云百炼",
model_display_name="通义flash"
)
# response = qwen_llm.invoke("介绍一下通义千问")
except ValueError as e:
print(f"获取指定LLM失败: {e}")
系统平台配置支持两种数据源,各有不同的使用场景:
| 数据源 | 存储位置 | 生效方式 | 适用场景 |
|---|---|---|---|
| 数据库 (推荐) | llm_config.db | 修改即时生效 | 生产环境、Web 前端管理、动态修改 |
| YAML | matchbox_cfg.yaml(结构)matchbox_key.yaml(密钥) | 需重启服务 | 初始化部署、配置分享、版本控制 |
首次启动 (First Initialization)
增量同步 (Incremental Sync)
强制重置 (Force Reset)
GUI 配置工具 (matchbox_cfg_gui.pyw) 直接操作数据库,修改即时生效,无需重启服务。
matchbox_cfg.yaml + matchbox_key.yaml 为准重置数据库中的系统平台;YAML 中不存在的平台会被软禁用,用户 API Key 会保留。适合恢复标准状态。matchbox_cfg.yaml(结构)和 matchbox_key.yaml(密钥),用于版本控制或分发。管理员可通过 REST API 直接管理数据库中的系统平台:
GET /api/ai/admin/sys-platforms # 获取所有系统平台
POST /api/ai/admin/sys-platform # 添加系统平台
PUT /api/ai/admin/sys-platform # 更新系统平台
DELETE /api/ai/admin/sys-platform # 软禁用系统平台
POST /api/ai/admin/sys-platform/api-key # 更新平台 API Key
POST /api/ai/admin/reload-from-yaml # 从配置文件强制重置数据库
数据库是运行时权威源
API Key 安全性
matchbox_key.yaml 或 .env 文件提交到公共代码仓库(如 GitHub)。.gitignore:请确保项目根目录下的 .gitignore 文件中包含 *.env,以防止意外泄露。.gitignore 忽略了敏感配置,为什么还需要加密解密这种多此一举的操作呢?”
实际上,这一设计的主要目的是在发生意外泄露的情况下,提升被破解的成本。目前互联网上 99.9% 的 API 密钥泄露,都是由恶意脚本使用正则表达式自动扫描明文搜出来的。
如果发生了泄露,且主密钥(LLM_KEY)和加密后的密钥文件同时暴露,真人或有针对性的 AI 确实能够破解它。但这已经极大地增加了攻击者的成本,能够完美规避绝大多数普通泄露被自动化机器人直接扫出明文的情况。数据库文件
llm_config.db。宿主可在初始化前调用 set_default_mgr_home(path) 设置默认目录,或通过优先级更高的 AGENT_MATCHBOX_HOME 显式指定运行目录。该 SQLite 文件包含所有用户数据和同步后的系统平台数据,请妥善保管。AGENT_MATCHBOX_DATABASE_URL=postgresql+psycopg://user:password@host:5432/dbname。该变量属于 Agent Matchbox 组件本身,方便在不同项目中复用。模型探测失败?
base_url:确保URL正确,并且末尾是否需要 /v1。base_url。extra_body 的使用
extra_body 提供了一个强大的机制来传递模型提供商的专有参数。ChatOpenAI 的 extra_body 或 model_kwargs 中。matchbox().get_user_llm() 返回 LLMClient:
invoke/stream 等调用)。.usage 子对象访问(如 client.usage.get_usage_last_24h())。from agen_matchbox import matchbox
# 获取客户端(默认可直接当作 LLM 用)
client = matchbox().get_user_llm(user_id="user_123", agent_name="agent_muse")
# 正常使用,用量会自动记录到数据库
result = client.invoke(messages)
# 流式输出也会在结束后自动记录
for chunk in client.stream(messages):
print(chunk.content, end="")
# 如果流式中断(客户端断开/取消),
# 系统会按“已输出的 token”估算 completion_tokens 并立刻入库(success=0)
# 如需查询用量,使用 .usage 子对象
usage_24h = client.usage.get_usage_last_24h()
print(usage_24h)
# 查询过去 24 小时消耗站长额度的用量
sys_paid_24h = client.usage.get_sys_paid_usage_last_24h()
# 查询过去 24 小时消耗用户自有 Key 的用量
self_paid_24h = client.usage.get_self_paid_usage_last_24h()
通过 client.usage 查询当前模型在当前用户维度下的用量:
# client = matchbox().get_user_llm(user_id="user_123")
# 获取过去 24 小时的用量
usage_24h = client.usage.get_usage_last_24h()
print(f"过去24小时: {usage_24h['total_tokens']} tokens, {usage_24h['requests']} 次请求")
# 获取过去 7 天的用量
usage_week = client.usage.get_usage_last_week()
# 获取过去 30 天的用量
usage_month = client.usage.get_usage_last_month()
# 获取所有时间的总用量
usage_total = client.usage.get_usage_total()
# 获取所有时间消耗站长额度的总用量
sys_paid_total = client.usage.get_sys_paid_usage_total()
# 获取所有时间消耗用户自有 Key 的总用量
self_paid_total = client.usage.get_self_paid_usage_total()
# 获取指定时间范围的用量
from datetime import datetime
usage = client.usage.get_usage_by_range(
start_time=datetime(2026, 1, 1),
end_time=datetime(2026, 1, 31)
)
返回的字典格式:
{
"total_tokens": 12345, # 总 Token 数
"prompt_tokens": 8000, # 输入 Token 数
"completion_tokens": 4345, # 输出 Token 数
"requests": 50, # 请求次数
"errors": 2, # 失败次数
}
matchbox() 返回的管理器提供了更丰富的用量查询接口:
from datetime import timedelta
from agen_matchbox import matchbox
mgr = matchbox()
# 获取用户过去 24 小时的总用量
usage = mgr.get_user_usage_last_24h(user_id="user_123")
# 获取用户过去 24 小时消耗站长额度的用量
usage = mgr.get_user_sys_paid_usage_last_24h(user_id="user_123")
# 获取用户过去 24 小时消耗自有 Key 的用量
usage = mgr.get_user_self_paid_usage_last_24h(user_id="user_123")
# 获取用户过去 7 天的总用量
usage = mgr.get_user_usage_last_week(user_id="user_123")
# 获取用户所有时间消耗站长额度的总用量
usage = mgr.get_user_sys_paid_usage_total(user_id="user_123")
# 获取用户所有时间消耗自有 Key 的总用量
usage = mgr.get_user_self_paid_usage_total(user_id="user_123")
# 按口径查询(sys_paid / self_paid / total)
usage = mgr.get_user_usage_by_scope(
user_id="user_123",
quota_scope="sys_paid",
)
# 获取用户的所有模型使用统计(按模型分组)
stats = mgr.get_user_usage_stats(
user_id="user_123",
since=timedelta(days=7) # 可选,限制时间范围
)
# 返回: [{"model_name": "gpt-4", "tokens": 5000, ...}, ...]
# 按 Agent 分组查看用量
by_agent = mgr.get_usage_by_agent(
user_id="user_123",
since=timedelta(hours=24)
)
# 返回: [{"agent_name": "agent_muse", "tokens": 1234, "requests": 10}, ...]
# 获取时间线数据(用于图表)
timeline = mgr.get_usage_timeline(
user_id="user_123",
granularity="hour", # 或 "day"
since=timedelta(hours=24)
)
# 返回: [{"time": "2026-01-01 10:00", "tokens": 500, "requests": 5}, ...]
# 清理旧日志(建议定期执行)
deleted = mgr.purge_old_usage_logs(older_than=timedelta(days=90))
print(f"已清理 {deleted} 条旧日志")
用量数据存储在 usage_log_entries 表中,每次 LLM 调用会创建一条记录,包含:
user_id 和 model_idquota_scope(sys_paid 或 self_paid)prompt_tokens, completion_tokens, total_tokenssuccess (1=成功, 0=失败)agent_name (调用的 Agent 名称)created_at (时间戳,用于时间范围查询)注意: 旧的
ModelUsageStats表已废弃,不再写入数据。如需查询历史汇总,请使用新的时序日志表进行聚合查询。
除用量查询外,管理器还提供了用户配额策略的读写与状态汇总能力:
from agen_matchbox import matchbox
# 获取当前用户的配额策略
policy = matchbox().get_user_quota_policy(user_id="user_123")
# 保存/更新配额策略
policy = matchbox().save_user_quota_policy(
user_id="user_123",
sys_paid_window_hours=24,
sys_paid_window_token_limit=100000,
sys_paid_window_request_limit=200,
sys_paid_total_token_limit=None,
sys_paid_total_request_limit=None,
)
# 查询配额策略 + 当前使用状态 + 剩余额度摘要
status = matchbox().get_user_quota_status(user_id="user_123")
其中:
sys_paid_*:限制站长承担费用的调用self_paid_*:限制用户自己承担费用的调用None,则表示该项配额未启用在 GUI 中点击“测试模型”后,内部调用 test_platform_chat(..., return_json=True),并在日志区打印响应 JSON(过长会截断)。
reasoning_content(或兼容字段),可在日志 JSON 中直接看到。usage、token_usage 或 billing 相关字段,也会原样出现在日志 JSON 中。为了保证跨平台兼容性和统计的一致性,管理器采用“优先真实 usage,缺失时本地估算”的混合策略:
优先使用 API 返回的 usage:若响应包含标准字段(如 prompt_tokens / completion_tokens,或 input_tokens / output_tokens),优先使用真实值。
缺失时降级到本地估算:若平台未返回 usage(常见于部分流式或非标准实现),使用 estimate_tokens 对输入与输出文本估算。
推理内容参与估算:流式回调会累积 reasoning_content(含部分第三方平台扩展),在无真实 usage 时计入 completion 估算,尽量减少低估。
按次与成功状态记录:每次调用都会落库,包含 success=1/0 与 token 字段;流式中断会记录已产出的估算结果并标记失败。
说明:当前内置统计聚焦 token/request/error 维度,不直接输出“金额”。如需金额计费,请按各平台单价在业务层做二次换算。
虽然本组件目前深度集成了 LangChain,但其核心逻辑(数据库管理、安全加密、用量统计)设计得非常独立。如果你需要将 matchbox 迁移到其他主流 Agent 框架,可以参考以下步骤:
AutoGen v0.4+ (python-v0.7+) 引入了 model_client 模式,不再强制依赖 llm_config 字典。
迁移核心:在 LLMBuilderMixin 中增加一个返回 model_client 的方法。
示例代码:
from autogen_ext.models.openai import OpenAIChatCompletionClient
def get_autogen_client(self, user_id, usage_key="main"):
# 1. 调用 resolved 获取底层的 base_url 和 api_key
resolved = self._resolve_user_choice(...)
# 2. 返回 AutoGen 兼容的客户端
return OpenAIChatCompletionClient(
model=resolved["model"].model_name,
api_key=resolved["api_key"],
base_url=resolved["base_url"]
)
CrewAI 仍然高度兼容 LangChain 对象,但它也提供了原生 LLM 类来直接处理 OpenAI 格式接口。
快速接入:get_user_llm() 返回的 LLMClient 已代理 LangChain 常用方法,可直接传入 Agent(llm=client) 或使用 client.invoke()/client.stream()。
原生接入:如果你想彻底去掉 LangChain,可以利用 CrewAI 的 LLM 类:
from crewai import LLM
# 从 matchbox 获取配置并实例化
crew_llm = LLM(
model=f"openai/{model_name}", # CrewAI 习惯使用 provider/model 格式
base_url=base_url,
api_key=api_key
)
如果你想做一个完全不依赖 LangChain 的通用后端,推荐使用 LiteLLM 作为中间件:
litellm 替换 langchain-openai。tracked_model.py 中的 LLMClient/UsageTrackingCallback,使其改为直接包装 litellm.completion 方法(保留 .usage 查询能力)。manager.py 和 usage_services.py,它们负责的数据库和统计逻辑是 100% 通用的。通过这种“两层架构”(管理层 + 适配层),你可以非常轻松地将 matchbox 接入任何新的 AI 生态。
火柴 Agent 网关按本目录内 LICENSE 以 Apache License 2.0 单独授权,可作为独立组件复用。
49 commits
Python
100.0%

Agent Matchbox 面向 Agent 开发而生,是一个可独立运行、可嵌入应用的大模型路由与配额控制中心。它不要求宿主项目提供特定的 core 包,也不预设某个 Agent 或业务用途。宿主可以通过集成回调注入自己的数据库工厂、请求上下文、默认用途和扩展密钥迁移逻辑。
该项目的设计目标是支持从个人开发、调试到多用户生产环境的多种复杂场景,并提供了一个图形化界面来简化核心配置的管理。
💡 为什么叫“火柴”? 火柴是点燃智能之火的原材料。在这个 AI 普惠的时代,构建各类 AI 应用的站长们就像是一个个“卖火柴的小女孩”(
Token滞销,请帮帮我们)。
专门的外置网关(如 NewAPI、LiteLLM 等)虽然强大,但在与复杂的应用直接结合时往往存在体验断层。本管理器作为内置网关,具备以下独特优势:
matchbox().get_user_llm(user_id, usage_key="fast") 获取对应用户/用途的 LLM 实例。reasoning_content 和 <think> 标签)统一转化为持续的推理流,确保深度思考模型在运转时前端依然能拥有极佳的纯流式体验。required: [],兼容 Grok 等严格校验实现;该规则属于通用 OpenAI Compatible 协议层,不按域名或模型名分支,也不占用或改写 extra_body。matchbox_cfg.yaml) 统一管理,用户平台数据则存储在数据库中。LLM_AUTO_KEY 选项,允许在用户未提供密钥时,自动降级使用服务器的密钥(需谨慎使用)。sys_paid(消耗站长托管 Key)和 self_paid(消耗用户自己的 Key)。single(一次性,用完即废)和 per_user(每用户可用一次,全服福利)。probe_platform_models),可以探测任何兼容OpenAI接口的平台所支持的模型列表。
reasoning_content、usage 或 billing 相关字段,可直接在日志中查看。Flet (0.28.3) 的现代化自适应 GUI 工具(matchbox_cfg_gui.pyw,Windows 下支持无控制台直接双击自启),完全无需依赖前端配置,直接操作数据库,支持添加/编辑/软禁用平台与模型、拖拽排序、加密存储 API Key、探测和测试模型,以及从本地 YAML 重置数据库或将数据库导出为 matchbox_cfg.yaml + matchbox_key.yaml。AGENT_MATCHBOX_DATABASE_URL 切换到 PostgreSQL。.
├── __init__.py # 包入口,导出 initialize_matchbox / matchbox / create_matchbox
├── database.py # 独立数据库 Engine 工厂
├── integrations.py # 宿主集成回调契约
├── manager.py # AIManager 核心类(组合所有 Mixin)
├── config.py # 配置加载与全局常量 (USE_SYS_LLM_CONFIG, LLM_AUTO_KEY 等)
├── models.py # SQLAlchemy 数据库模型
├── security.py # 安全与加密 (SecurityManager)
├── admin.py # 平台与模型管理 Mixin (AdminMixin)
├── builder.py # LLM 实例构建 Mixin (LLMBuilderMixin)
├── user_services.py # 用户服务 Mixin (UserServicesMixin)
├── quota_services.py # 配额配置/统计/拦截 Mixin (QuotaServicesMixin)
├── usage_services.py # 用量统计 Mixin (UsageServicesMixin)
├── redeem_code_services.py # 兑换码管理 Mixin (RedeemCodeServicesMixin)
├── tracked_model.py # LLMClient/LLMUsage/UsageTrackingCallback
├── estimate_tokens.py # Token 用量估算工具
├── hf_mirror.py # Hugging Face 镜像发现、区域判断与可达性探测
├── utils.py # 工具函数 (probe_platform_models, parse_extra_body 等)
├── matchbox_cfg.yaml # 系统平台结构配置(仅用于初始化/导出,运行时以数据库为准)
├── matchbox_key.yaml # 系统平台 API Key(应被 git 忽略,禁止提交)
├── matchbox_cfg_gui.pyw # 图形化配置管理工具(双击直接自启入口,实际代码在 gui/ 子目录)
├── gui/ # GUI 模块(拆分自 matchbox_cfg_gui)
│ ├── __init__.py
│ ├── main_window.py # 主窗口 LLMConfigGUI 类(平台配置、用户总览、模型管理)
│ ├── platform_panel.py # 平台管理 Mixin
│ ├── model_panel.py # 模型管理 Mixin
│ ├── dialogs.py # 对话框 Mixin(添加/编辑模型、系统用途槽、用户配额)
│ ├── key_manager.py # 密钥管理 Mixin
│ ├── probe.py # 探测功能 Mixin
│ ├── dpi.py # 高分屏适配与窗口尺寸策略
│ └── theme.py # GUI 主题、配色与字体跨平台(Windows/Ubuntu)适配
└── README.md # 本文档
manager.py: 包含 AIManager 类,通过 Mixin 模式组合了 AdminMixin、LLMBuilderMixin、UserServicesMixin、QuotaServicesMixin、UsageServicesMixin 等功能模块。这是与程序交互的主要入口。quota_services.py: 配额服务模块,集中处理 sys_paid/self_paid 两条计费口径的配额配置、周期用量统计、总量统计与调用前拦截。usage_services.py: 用量统计模块,除单用户汇总外,也提供面向 GUI 的全用户调用总览聚合能力。matchbox_cfg.yaml: 初始化配置文件。用于定义初始的"系统平台"。首次启动时,管理器会将此文件中的平台同步到数据库。后续启动仅增量添加新平台,不会覆盖已有配置。运行时权威数据源是数据库,而非此文件。matchbox_cfg_gui.pyw: GUI 入口文件,实际逻辑拆分在 gui/ 子目录中。直接操作数据库,支持平台/模型增删改(删除为软禁用)、API Key 加密存储、模型探测与测试、全用户调用总览、双击用户查看详情,以及从本地 YAML 重置数据库或将数据库导出为 matchbox_cfg.yaml + matchbox_key.yaml。注意: 包内提供的配置文件 (matchbox_cfg.yaml) 适用于快速迁移或者分享模型配置。platform_key 是平台配置和密钥映射的稳定身份,数据库运行时引用使用 platform_id;base_url 仅是允许重复的上游连接地址。API Key 单独存放在 matchbox_key.yaml 中并按 platform_key 索引;旧版按 URL 索引的密钥只会在该 URL 唯一时兼容读取。该文件被 .gitignore 忽略,禁止提交到版本库。
首次使用时,你需要运行配置工具,填入你自己的 API Key。
设置主加密密钥 (LLM_KEY):
LLM_KEY 加密你的 API Key和所有用户自定义的API Key。你可以设置环境变量,或者直接运行 GUI 工具,它会提示你输入并自动保存。matchbox_key.yaml 中携带了仓库作者或其他环境生成的加密 Key,它们在你的机器上本来就不可用。此时你只需要设置自己的 LLM_KEY,并按提示选择清理这些不可恢复密钥即可。清理不会删除平台与模型结构,只会清空这些不可用的托管 Key。启动配置工具:
matchbox_cfg_gui.pyw,或在命令行运行 python matchbox_cfg_gui.pyw。替换并激活平台:
验证模型:
检查用途绑定:
main (主模型)、fast (快速模型)、reason (推理模型) 绑定的模型是你刚刚配置过 Key 的有效模型。最终测试:
理解网关的运行模式至关重要,这直接影响到功能的表现和二次开发。
为兼顾稳定性、可维护性和扩展自由度,火柴网关采用两阶段初始化 + 双通道标准设计:
initialize_matchbox(ensure_defaults=True),仅完成数据库引擎初始化和默认配置同步。此阶段不会加载 langchain_openai 等重运行时依赖。warmup_matchbox_runtime(blocking=False),在后台线程中预加载 ChatUniversal、LLMClient 等运行时模块,与应用启动并行执行,避免首个请求阻塞。matchbox() 获取管理器,再调用 get_user_llm(...) / get_user_embedding(...)。create_quick_llm(...) / create_quick_embedding(...) 快速创建客户端。reset_matchbo(),避免导入即初始化的副作用。set_default_mgr_home(path) 设置自己的默认目录;部署环境仍可通过优先级更高的 AGENT_MATCHBOX_HOME 显式覆盖。from agen_matchbox import initialize_matchbox, warmup_matchbox_runtime, matchbox
# 1) 轻启动:数据库引擎 + 默认配置同步(毫秒级)
initialize_matchbox(ensure_defaults=True)
# 2) 异步预热:后台线程加载 langchain_openai 等重运行时依赖
# blocking=False(默认)立即返回,与应用启动并行;首个请求到达时模块已就绪
warmup_matchbox_runtime(blocking=False)
# 3) 在业务请求中按需获取(默认 required=True)
client = matchbox().get_user_llm(user_id="user_123", usage_key="main", agent_name="your_agent")
# 4) 像普通 LLM 一样使用
result = client.invoke("请给我一个赛博朋克世界观种子")
# 5) 流式同样可用,且会自动完成用量归档
for chunk in client.stream("继续扩展成三幕结构"):
print(chunk.content, end="")
SYSTEM_USER_ID = "-1")这是一个特殊的虚拟用户ID。当代码中使用 matchbox().get_user_llm() (不带 user_id 参数) 或 matchbox().get_user_llm(user_id="-1") 时,管理器会进入系统模式。
LLM_AUTO_KEY 规则回退到系统后备 Key。当前实现中,系统后备 Key 来自 DEFAULT_PLATFORM_CONFIGS,由 matchbox_cfg.yaml 提供平台结构、matchbox_key.yaml 提供平台 API Key,并解析环境变量占位符。在 config.py 中有两个重要的全局开关:
USE_SYS_LLM_CONFIG = True (多用户固定平台模式)
matchbox_cfg.yaml 中定义的系统平台。llm_sys_platform_keys 表中,与用户ID关联。USE_SYS_LLM_CONFIG = False (多用户自定义平台模式)
AIManager 的 add_platform, add_model 等方法来创建自己的私有平台和模型。LLM_AUTO_KEY)系统在获取 API Key 时遵循 “用户私有 > 系统后备” 的原则:
LLM_AUTO_KEY。LLM_AUTO_KEY = True
True,管理器会自动回退并使用系统后备 Key(当前实现来自 DEFAULT_PLATFORM_CONFIGS,由 matchbox_cfg.yaml + matchbox_key.yaml 解析而来)作为后备 API Key。False。LLM_AUTO_KEY = False
ValueError,提示用户需要配置API Key。推荐设置:
LLM_AUTO_KEY = True,并通过 GUI 为系统默认平台配置 API Key(由管理员支付)。LLM_AUTO_KEY = False,并在前端或用户设置中要求用户填写他们的 API Key。当前版本已把所有调用按实际命中的密钥来源拆成两条计费/配额口径:
sys_paid:系统平台 + 站长托管 Keyself_paid:用户自己的 Key
这样做的目的是:
sys_paidsys_paid 用完,只要切到自己的 Key,仍然可以继续使用 self_paid配额配置存储在 user_quota_policies 表中,支持两条口径分别配置:
*_window_hours*_window_token_limit*_window_request_limit*_total_token_limit*_total_request_limit所有字段都允许为空;为空表示该项限制未启用。
运行时拦截逻辑如下:
matchbox().get_user_llm(...) / matchbox().get_spec_sys_llm(...) 先解析本次调用实际命中的 Key。sys_paid 或 self_paid。QuotaExceededError。main(主模型)、fast(快速模型)、reason(推理模型)三个槽位,并在注册时绑定默认平台/模型。POST /api/ai/user-selection/usage 或 AIManager.create_user_usage_slot(...) 可以新增任意 usage_key,并指定初始模型。GET /api/ai/user-selection?usage_key=fast 可查询指定用途;响应中还会包含 usage_selections 列表以展示所有用途的当前绑定。POST /api/ai/user-selection 支持传入 usage_key 字段来更新特定用途的模型。matchbox().get_user_llm(user_id, usage_key="reason") 会直接返回该用途绑定的模型实例;若参数省略,则默认为主模型。当前仓库按源码组件提供,不包含 pyproject.toml。请把 agen_matchbox 目录放入宿主项目的 Python 导入路径,并由宿主依赖文件统一声明版本。直接开发时可显式安装所需依赖:
pip install langchain-core langchain-openai sqlalchemy tiktoken cryptography pyyaml requests python-dotenv
# 按需:PostgreSQL / GUI / Alembic
pip install "psycopg[binary]" "flet>=0.28.3,<0.29.0" alembic
推荐方式:在 Windows 下直接双击 matchbox_cfg_gui.pyw 启动,或使用命令行操作数据库,无需手动编辑 YAML。
python matchbox_cfg_gui.pyw
说明:
matchbox_cfg.yaml仅在首次启动时将预置平台写入数据库(增量同步,不覆盖已有配置)。 运行时平台/模型选择以数据库为准;系统后备 Key 解析仍会使用DEFAULT_PLATFORM_CONFIGS(来自matchbox_key.yaml/ 环境变量占位符)。 若仓库分发的密钥文件中包含其他环境加密的ENC:密钥,而当前站点无法解密,系统会跳过导入这些无效托管 Key,但仍然会正常同步平台与模型结构,随后由站长在本地 GUI 中填写自己的 Key。
GUI 操作步骤:
模型类型不再通过 is_embedding 或一组不断扩张的分类能力位保存。数据库、YAML 和 API 只使用两个事实字段:
input_modalities:当前支持 text、image。output_modalities:当前支持 text、image、embedding;embedding 与其他输出互斥。常见组合如下:
| 模型用途 | input_modalities | output_modalities |
|---|---|---|
| 文本模型 | [text] | [text] |
| 视觉文本模型 | [text, image] | [text] |
| 文生图 | [text] | [image] |
| 图生图 / 图片编辑 | [text, image] | [image] |
| 文本与图片统一输出 | [text, image] | [text, image] |
| 向量模型 | [text] | [embedding] |
Web 管理页和 CustomTkinter GUI 只显示“视觉 / 生图 / 向量”三个复选框,文本输入默认隐含。勾选向量会自动取消其他选项。模型列表使用紧凑标签:T 表示文本输出,I 表示图片输出,V 表示接收图片输入,E 表示向量输出;Web 端通过 Tooltip 解释标签。
models:
可编辑生图模型:
model_name: provider-image-model
input_modalities: [text, image]
output_modalities: [image]
image_generation_adapter: openai_images
extra_body:
quality: high
image_generation_adapter 是网关协议选择的唯一真相源。它由用户明确选择,不根据 base_url 域名、模型名或 extra_body 猜测,也不会写入或转发到上游请求。extra_body 仍专门承载供应商参数;适配层只过滤网关自己的内部控制键,其他参数会按对应协议尽力透传。生图模型未显式选择时使用 openai_images 默认值,不读取任何旧版嵌套 adapter。
| 值 | 上游协议 | 配置中的 model_name | 参考图传递方式 |
|---|---|---|---|
openai_images | /images/generations、/images/edits | GPT Image 或兼容生图模型 | multipart image[] |
openai_responses_image | /responses 的 image_generation 工具 | 支持该工具的主线文本模型 | 单次请求内的 input_image data URL,不经 Files API 持久上传 |
openai_chat_image | /chat/completions 兼容网关 | 网关暴露的生图模型名 | 多模态消息中的 data URL;兼容 Markdown/data URI 图片结果 |
gemini_generate_content | Gemini models/*:generateContent | Gemini / Nano Banana 模型 | inline_data |
gemini_interactions | Gemini Interactions | Gemini / Nano Banana 模型 | input 图片 part |
xai_images | xAI /images/generations、/images/edits | Grok Image 模型 | JSON data URL,编辑最多 3 张参考图 |
默认值仍为 openai_images,因为它最接近当前图片 API 的公共最小集合。openai_responses_image 不是 GPT Image 直连协议:它会让 model_name 对应的主线模型调用生图工具,因此还会产生主线模型 token 成本。网关不会为了传参考图自动调用供应商 Files API;宿主项目图片只在本地持久保存,调用时才按所选协议随请求发送。
直接编辑 matchbox_cfg.yaml 文件,下次启动时新增的平台会被增量同步到数据库。
api_key: 可使用占位符(如 {OPENAI_API_KEY}),系统启动时会自动从环境变量解析。
也可留空,后续通过 GUI 在数据库中填写加密 Key。在运行你的主应用之前,请确保在系统中设置了你在 matchbox_key.yaml 中引用的环境变量。
例如,如果你的配置是 api_key: '{GEMINIX_API_KEY}',你需要:
Windows:
$Env:GEMINIX_API_KEY="your_real_api_key"
(为了永久生效,请在系统属性中设置)
Linux/macOS:
export GEMINIX_API_KEY="your_real_api_key"
(为了永久生效,请添加到 .bashrc 或 .zshrc)
提示:GUI 工具的“保存 API Key”会将 Key 加密写入数据库(不是直接写入 YAML)。如果你希望将当前数据库配置回写到本地文件,请使用工具栏的“导出DB到YAML”,它会同时生成 matchbox_cfg.yaml(结构)和 matchbox_key.yaml(密钥)。
大模型管理器现已重构为组件化结构,通过 Mixin 模式集成管理、构建和统计能力。
推荐写法是:导入 initialize_matchbox、warmup_matchbox_runtime 和 matchbox,并在应用启动阶段显式执行两阶段初始化(建议放到 lifespan / startup 钩子中)。
from agen_matchbox import initialize_matchbox, warmup_matchbox_runtime, matchbox
# 建议在应用启动时执行(进程级,两阶段)
# 阶段一:轻启动——数据库引擎、表结构、默认配置同步(不加载 langchain_openai)
initialize_matchbox(ensure_defaults=True)
# 阶段二:异步预热——后台线程加载 ChatUniversal / LLMClient 等重运行时模块
# blocking=False(默认)立即返回,不阻塞应用启动
warmup_matchbox_runtime(blocking=False)
# --- 场景1: 获取指定用户的LLM ---
# 管理器会自动处理该用户的模型选择、API Key等所有配置
try:
user_llm = matchbox().get_user_llm(user_id="user_123")
fast_llm = matchbox().get_user_llm(user_id="user_123", usage_key="fast")
# response = user_llm.invoke("你好")
# for chunk in user_llm.stream("你好"):
# print(chunk.content, end="")
except ValueError as e:
# 可能是API Key未配置等问题
print(f"获取LLM失败: {e}")
# --- 场景2: 在后端服务或无用户场景下使用 ---
# 使用特殊的 SYSTEM_USER_ID,密钥来自 matchbox_key.yaml 中配置的加密 Key
try:
system_llm = matchbox().get_user_llm() # user_id=None 默认为系统用户
# response = system_llm.invoke("写一个Python的Hello World")
except ValueError as e:
print(f"获取系统LLM失败: {e}")
# --- 场景3: 轻量入口,直接指定系统模型(适用于本地测试/调试脚本)---
# 显示名称必须与配置中完全一致,调用期间不可修改
try:
qwen_llm = matchbox().get_spec_sys_llm(
platform_name="阿里云百炼",
model_display_name="通义flash"
)
# response = qwen_llm.invoke("介绍一下通义千问")
except ValueError as e:
print(f"获取指定LLM失败: {e}")
系统平台配置支持两种数据源,各有不同的使用场景:
| 数据源 | 存储位置 | 生效方式 | 适用场景 |
|---|---|---|---|
| 数据库 (推荐) | llm_config.db | 修改即时生效 | 生产环境、Web 前端管理、动态修改 |
| YAML | matchbox_cfg.yaml(结构)matchbox_key.yaml(密钥) | 需重启服务 | 初始化部署、配置分享、版本控制 |
首次启动 (First Initialization)
增量同步 (Incremental Sync)
强制重置 (Force Reset)
GUI 配置工具 (matchbox_cfg_gui.pyw) 直接操作数据库,修改即时生效,无需重启服务。
matchbox_cfg.yaml + matchbox_key.yaml 为准重置数据库中的系统平台;YAML 中不存在的平台会被软禁用,用户 API Key 会保留。适合恢复标准状态。matchbox_cfg.yaml(结构)和 matchbox_key.yaml(密钥),用于版本控制或分发。管理员可通过 REST API 直接管理数据库中的系统平台:
GET /api/ai/admin/sys-platforms # 获取所有系统平台
POST /api/ai/admin/sys-platform # 添加系统平台
PUT /api/ai/admin/sys-platform # 更新系统平台
DELETE /api/ai/admin/sys-platform # 软禁用系统平台
POST /api/ai/admin/sys-platform/api-key # 更新平台 API Key
POST /api/ai/admin/reload-from-yaml # 从配置文件强制重置数据库
数据库是运行时权威源
API Key 安全性
matchbox_key.yaml 或 .env 文件提交到公共代码仓库(如 GitHub)。.gitignore:请确保项目根目录下的 .gitignore 文件中包含 *.env,以防止意外泄露。.gitignore 忽略了敏感配置,为什么还需要加密解密这种多此一举的操作呢?”
实际上,这一设计的主要目的是在发生意外泄露的情况下,提升被破解的成本。目前互联网上 99.9% 的 API 密钥泄露,都是由恶意脚本使用正则表达式自动扫描明文搜出来的。
如果发生了泄露,且主密钥(LLM_KEY)和加密后的密钥文件同时暴露,真人或有针对性的 AI 确实能够破解它。但这已经极大地增加了攻击者的成本,能够完美规避绝大多数普通泄露被自动化机器人直接扫出明文的情况。数据库文件
llm_config.db。宿主可在初始化前调用 set_default_mgr_home(path) 设置默认目录,或通过优先级更高的 AGENT_MATCHBOX_HOME 显式指定运行目录。该 SQLite 文件包含所有用户数据和同步后的系统平台数据,请妥善保管。AGENT_MATCHBOX_DATABASE_URL=postgresql+psycopg://user:password@host:5432/dbname。该变量属于 Agent Matchbox 组件本身,方便在不同项目中复用。模型探测失败?
base_url:确保URL正确,并且末尾是否需要 /v1。base_url。extra_body 的使用
extra_body 提供了一个强大的机制来传递模型提供商的专有参数。ChatOpenAI 的 extra_body 或 model_kwargs 中。matchbox().get_user_llm() 返回 LLMClient:
invoke/stream 等调用)。.usage 子对象访问(如 client.usage.get_usage_last_24h())。from agen_matchbox import matchbox
# 获取客户端(默认可直接当作 LLM 用)
client = matchbox().get_user_llm(user_id="user_123", agent_name="agent_muse")
# 正常使用,用量会自动记录到数据库
result = client.invoke(messages)
# 流式输出也会在结束后自动记录
for chunk in client.stream(messages):
print(chunk.content, end="")
# 如果流式中断(客户端断开/取消),
# 系统会按“已输出的 token”估算 completion_tokens 并立刻入库(success=0)
# 如需查询用量,使用 .usage 子对象
usage_24h = client.usage.get_usage_last_24h()
print(usage_24h)
# 查询过去 24 小时消耗站长额度的用量
sys_paid_24h = client.usage.get_sys_paid_usage_last_24h()
# 查询过去 24 小时消耗用户自有 Key 的用量
self_paid_24h = client.usage.get_self_paid_usage_last_24h()
通过 client.usage 查询当前模型在当前用户维度下的用量:
# client = matchbox().get_user_llm(user_id="user_123")
# 获取过去 24 小时的用量
usage_24h = client.usage.get_usage_last_24h()
print(f"过去24小时: {usage_24h['total_tokens']} tokens, {usage_24h['requests']} 次请求")
# 获取过去 7 天的用量
usage_week = client.usage.get_usage_last_week()
# 获取过去 30 天的用量
usage_month = client.usage.get_usage_last_month()
# 获取所有时间的总用量
usage_total = client.usage.get_usage_total()
# 获取所有时间消耗站长额度的总用量
sys_paid_total = client.usage.get_sys_paid_usage_total()
# 获取所有时间消耗用户自有 Key 的总用量
self_paid_total = client.usage.get_self_paid_usage_total()
# 获取指定时间范围的用量
from datetime import datetime
usage = client.usage.get_usage_by_range(
start_time=datetime(2026, 1, 1),
end_time=datetime(2026, 1, 31)
)
返回的字典格式:
{
"total_tokens": 12345, # 总 Token 数
"prompt_tokens": 8000, # 输入 Token 数
"completion_tokens": 4345, # 输出 Token 数
"requests": 50, # 请求次数
"errors": 2, # 失败次数
}
matchbox() 返回的管理器提供了更丰富的用量查询接口:
from datetime import timedelta
from agen_matchbox import matchbox
mgr = matchbox()
# 获取用户过去 24 小时的总用量
usage = mgr.get_user_usage_last_24h(user_id="user_123")
# 获取用户过去 24 小时消耗站长额度的用量
usage = mgr.get_user_sys_paid_usage_last_24h(user_id="user_123")
# 获取用户过去 24 小时消耗自有 Key 的用量
usage = mgr.get_user_self_paid_usage_last_24h(user_id="user_123")
# 获取用户过去 7 天的总用量
usage = mgr.get_user_usage_last_week(user_id="user_123")
# 获取用户所有时间消耗站长额度的总用量
usage = mgr.get_user_sys_paid_usage_total(user_id="user_123")
# 获取用户所有时间消耗自有 Key 的总用量
usage = mgr.get_user_self_paid_usage_total(user_id="user_123")
# 按口径查询(sys_paid / self_paid / total)
usage = mgr.get_user_usage_by_scope(
user_id="user_123",
quota_scope="sys_paid",
)
# 获取用户的所有模型使用统计(按模型分组)
stats = mgr.get_user_usage_stats(
user_id="user_123",
since=timedelta(days=7) # 可选,限制时间范围
)
# 返回: [{"model_name": "gpt-4", "tokens": 5000, ...}, ...]
# 按 Agent 分组查看用量
by_agent = mgr.get_usage_by_agent(
user_id="user_123",
since=timedelta(hours=24)
)
# 返回: [{"agent_name": "agent_muse", "tokens": 1234, "requests": 10}, ...]
# 获取时间线数据(用于图表)
timeline = mgr.get_usage_timeline(
user_id="user_123",
granularity="hour", # 或 "day"
since=timedelta(hours=24)
)
# 返回: [{"time": "2026-01-01 10:00", "tokens": 500, "requests": 5}, ...]
# 清理旧日志(建议定期执行)
deleted = mgr.purge_old_usage_logs(older_than=timedelta(days=90))
print(f"已清理 {deleted} 条旧日志")
用量数据存储在 usage_log_entries 表中,每次 LLM 调用会创建一条记录,包含:
user_id 和 model_idquota_scope(sys_paid 或 self_paid)prompt_tokens, completion_tokens, total_tokenssuccess (1=成功, 0=失败)agent_name (调用的 Agent 名称)created_at (时间戳,用于时间范围查询)注意: 旧的
ModelUsageStats表已废弃,不再写入数据。如需查询历史汇总,请使用新的时序日志表进行聚合查询。
除用量查询外,管理器还提供了用户配额策略的读写与状态汇总能力:
from agen_matchbox import matchbox
# 获取当前用户的配额策略
policy = matchbox().get_user_quota_policy(user_id="user_123")
# 保存/更新配额策略
policy = matchbox().save_user_quota_policy(
user_id="user_123",
sys_paid_window_hours=24,
sys_paid_window_token_limit=100000,
sys_paid_window_request_limit=200,
sys_paid_total_token_limit=None,
sys_paid_total_request_limit=None,
)
# 查询配额策略 + 当前使用状态 + 剩余额度摘要
status = matchbox().get_user_quota_status(user_id="user_123")
其中:
sys_paid_*:限制站长承担费用的调用self_paid_*:限制用户自己承担费用的调用None,则表示该项配额未启用在 GUI 中点击“测试模型”后,内部调用 test_platform_chat(..., return_json=True),并在日志区打印响应 JSON(过长会截断)。
reasoning_content(或兼容字段),可在日志 JSON 中直接看到。usage、token_usage 或 billing 相关字段,也会原样出现在日志 JSON 中。为了保证跨平台兼容性和统计的一致性,管理器采用“优先真实 usage,缺失时本地估算”的混合策略:
优先使用 API 返回的 usage:若响应包含标准字段(如 prompt_tokens / completion_tokens,或 input_tokens / output_tokens),优先使用真实值。
缺失时降级到本地估算:若平台未返回 usage(常见于部分流式或非标准实现),使用 estimate_tokens 对输入与输出文本估算。
推理内容参与估算:流式回调会累积 reasoning_content(含部分第三方平台扩展),在无真实 usage 时计入 completion 估算,尽量减少低估。
按次与成功状态记录:每次调用都会落库,包含 success=1/0 与 token 字段;流式中断会记录已产出的估算结果并标记失败。
说明:当前内置统计聚焦 token/request/error 维度,不直接输出“金额”。如需金额计费,请按各平台单价在业务层做二次换算。
虽然本组件目前深度集成了 LangChain,但其核心逻辑(数据库管理、安全加密、用量统计)设计得非常独立。如果你需要将 matchbox 迁移到其他主流 Agent 框架,可以参考以下步骤:
AutoGen v0.4+ (python-v0.7+) 引入了 model_client 模式,不再强制依赖 llm_config 字典。
迁移核心:在 LLMBuilderMixin 中增加一个返回 model_client 的方法。
示例代码:
from autogen_ext.models.openai import OpenAIChatCompletionClient
def get_autogen_client(self, user_id, usage_key="main"):
# 1. 调用 resolved 获取底层的 base_url 和 api_key
resolved = self._resolve_user_choice(...)
# 2. 返回 AutoGen 兼容的客户端
return OpenAIChatCompletionClient(
model=resolved["model"].model_name,
api_key=resolved["api_key"],
base_url=resolved["base_url"]
)
CrewAI 仍然高度兼容 LangChain 对象,但它也提供了原生 LLM 类来直接处理 OpenAI 格式接口。
快速接入:get_user_llm() 返回的 LLMClient 已代理 LangChain 常用方法,可直接传入 Agent(llm=client) 或使用 client.invoke()/client.stream()。
原生接入:如果你想彻底去掉 LangChain,可以利用 CrewAI 的 LLM 类:
from crewai import LLM
# 从 matchbox 获取配置并实例化
crew_llm = LLM(
model=f"openai/{model_name}", # CrewAI 习惯使用 provider/model 格式
base_url=base_url,
api_key=api_key
)
如果你想做一个完全不依赖 LangChain 的通用后端,推荐使用 LiteLLM 作为中间件:
litellm 替换 langchain-openai。tracked_model.py 中的 LLMClient/UsageTrackingCallback,使其改为直接包装 litellm.completion 方法(保留 .usage 查询能力)。manager.py 和 usage_services.py,它们负责的数据库和统计逻辑是 100% 通用的。通过这种“两层架构”(管理层 + 适配层),你可以非常轻松地将 matchbox 接入任何新的 AI 生态。
火柴 Agent 网关按本目录内 LICENSE 以 Apache License 2.0 单独授权,可作为独立组件复用。
49 commits
Python
100.0%