理解 VisibleBase
架构总览
一次请求如何从产品 client 进入 Base,再被路由到真实 provider。
理解架构时,最重要的不是记住模块名,而是先确认这个系统服务的是多个 client,而不是单个项目里的单次调用。
在这个前提下,再记住下面这条调用链:
- 产品侧拿到
product_id + user_token - 用
UserClient调某个 service - Base 校验 token、读取模型目录、合并 query fallback
- Base 通过
match()决定真实 handler - handler 再调用真实 provider 或内部 AI Gateway
整体结构
产品 client浏览器、插件、App、桌面端或产品后端代用户发起调用。
UserClientVisibleBase Runtime校验
user_token,读取 products/models,执行 service、hooks 和 HTTP 路由。业务系统登录、订阅、余额、订单和业务数据继续放在你自己的后端,不被 Base 接管。
上游调用层OpenAI、Claude、Gemini、私有模型,或你自己的 AI Gateway / 中转服务。
一次请求在 Base 里会发生什么
1. 先确认产品上下文
HTTP 请求进入 Base 后,Runtime 会先读取:
Authorization: Bearer <user_token>- 请求体里的
product_id - 当前调用的
service
如果 token 无效、过期或 product_id 不匹配,Base 会直接返回 401 或 403,而不是把这类问题伪装成普通 500。
2. 再拼出最终 query
service 的 handler 不直接吃“原始 client 输入”,而是吃最终的 ctx.query。
这个 ctx.query 来自两部分:
- client 显式传入的 query
service.default()返回的 fallback query
default() 不定义模型注册逻辑,它只是补齐默认 query。典型写法是:
base.text().default({
model: "gpt-5.4",
temperature: 0.2,
});3. 根据 ctx.query.model 解析模型
当 fallback 合并完成后,Runtime 才会从 ctx.query.model 解析真实模型。
模型来自数据库里的全局目录,而不是运行时代码注册:
- client 传什么 model
default()是否补了 model- 数据库里是否存在这个 active model
这三件事共同决定本次调用最终用哪个模型。
4. 通过 match() 选择 handler
service 里的不同 provider 或不同调用风格,交给 match() 分流:
base.text()
.default({ model: "gpt-5.4" })
.match((ctx) => ctx.model.provider === "openai", openaiHandler)
.match((ctx) => ctx.model.provider === "anthropic", anthropicHandler);也就是说,产品侧只需要知道自己在调用 text() 和哪个 model;真正怎么接 OpenAI、Claude 或内部网关,是 Base 内部决定的。
5. hooks 收口业务扩展
当 handler 命中后,Base 会按顺序执行:
beforehooks- service handler
afterhooksonErrorhooks(失败时)
这让 usage、限额、扣费、日志和风控逻辑可以统一挂在 Base 上,而不是散在每个产品 repo 里。
Base 和 AI Gateway 的区别
AI Gateway 面向的是“上游调用统一入口”。
VisibleBase 面向的是“产品如何稳定使用 AI”。
所以它更关心这些东西:
product_iduser_token- service 边界
- hooks 和 usage
- 多产品复用
如果你已经有 AI Gateway,VisibleBase 也可以把它当成自己的上游调用层来用。