| 无需调用模型来决定路由 | 确定性且完全离线 |
| 基于自身数据进行校准 | 自带密钥,自主托管 |
Wayfinder 会分析提示词的结构(长度、标题、列表、代码)和措辞(证明、数学、硬约束),然后告诉你应该将其发送到本地小模型还是云端大模型。它在微秒内做出决策,离线运行,从不调用其他模型来做出判断:无需 API 密钥、无需网络、无需调用模型来决策。你会得到一个分数和一个推荐建议,至于如何使用,完全由你决定。
简单的提示词留在本地,困难的提示词交给昂贵模型,这样你就不再为“总结一下”和“修个错别字”这类任务支付顶级价格了。
对比说明
大多数路由系统通过调用模型来决策:训练好的分类器、大语言模型评判或托管 API。这恰恰在旨在节省成本的步骤上增加了延迟、成本和随机性。而 Wayfinder 则通过读取结构和措辞来决策,因此判断结果免费且每次一致。
| 路由系统 | 决策方式 | 是否调用模型? | 自主托管 | 可校准 |
|---|---|---|---|---|
| Wayfinder | 确定性结构评分 | 否 | 是 | 是 |
| RouteLLM | 训练好的分类器(偏好数据) | 是 | 是 | 需重新训练 |
| NotDiamond / Martian | 基于学习,托管服务 | 是 | 否 | 通过平台 |
| OpenRouter (Auto) | 托管自动路由 | 是 | 否 | — |
| Bifrost / LiteLLM | 供应商网关(非复杂度路由) | 否 | 是 | 不适用 |
最后两行(OpenRouter、Bifrost、LiteLLM)中的网关回答的是另一个问题:根据价格、可用性和故障转移,哪个供应商来服务于一个调用。而 Wayfinder 回答的是提示词应该属于哪个层级:根据难度,离线决定是便宜还是昂贵。这两者可以组合使用。先运行 Wayfinder 做出“便宜还是昂贵”的判断,再通过底层的网关到达供应商。
Wayfinder 追求的不是顶尖的准确率数字。它提供的是一个你可以离线运行、无需调用模型、并能根据自身流量进行调优的路由决策。默认情况下,它只对提示词结构进行评分。它也能读取词汇线索(如证明、数学、约束条件),但这些功能默认是关闭的:一项针对独立编写的提示词进行的双盲测试显示,词汇提升并不具备泛化能力(它只能捕捉约 20% 的未见过的困难提示词,并且输给了一个简单的词数基线),因此这些功能是选用的。只有当你针对自身流量的词汇进行了校准后,才建议提高它们的权重。一个纯粹基于语义困难的提示词(例如一段微妙的代码片段,或者一个看似无害的问“第 100 个质数是什么?”)并没有结构上的信号,此时语义路由器会表现更好。在盲测中真正靠得住的部分才是你应该依赖的:一个确定性的、亚毫秒级的、无需调用模型的离线路由决策。基准测试(make benchmark)会显示它在哪些场景胜出、哪些场景失利,对标的是真实的基线和一个完美的“预言机”。你可以用它指向 RouterBench 或 RouterArena 来获取分级评分。
刚接触这个项目,或者在权衡利弊?常见问题解答给出了直白的回答——包括它在哪些场景表现不佳(在 RouterBench 的短而难的条目上表现不比随机选择好),以及为什么你仍然应该运行它。
试试演示(无需密钥)
两种方式让你亲眼看到路由决策——无需 API 密钥、无需模型、不上网。
在终端里——一个以决策驱动的聊天,运行在 Wayfinder 调色板中。终端聊天已包含在默认安装中,所以你无需额外添加任何东西——或者通过 uvx 无需安装直接运行:
uvx wayfinder-router chat --dry-run # zero install, zero keys # or: pip install wayfinder-router && wayfinder-router chat
每一次对话轮次都会显示路由去向(● 本地 / ◆ 云端)、结构评分及原因(/why),以及与始终使用云端相比的累积节省。/init 无需离开聊天即可设置模型,/route · /local · /cloud 强制指定一个轮次,对话会跨会话持久保存(/threads)。
在浏览器中——网页版聊天界面,带有实时调节的阈值滑块:
pip install "wayfinder-router[gateway]" wayfinder-router webchat --dry-run # opens http://127.0.0.1:8088/demo
webchat 是基于 serve(网关及其 /demo 页面;参数包括 --no-open、--port、--host 0.0.0.0、--dry-run)的轻量级启动器;serve 是无头命令。两个界面都会显示每条消息的路由去向(本地还是云端)、复杂度评分及其原因(功能分解),以及与始终使用云端相比节省的成本。无需配置时,两者仅作决策展示(web 端通过 --dry-run 参数,终端则显示预览),因此你可以零配置地进行试探。要获取真实回复,请运行 wayfinder-router init 来搭建 [gateway.models] 框架(然后运行 wayfinder-router doctor 确认你的密钥解析成功)——详见快速入门指南。
支持任何兼容 OpenAI 的 API
Wayfinder 将每次调用转发至 OpenAI 风格的 /chat/completions 端点——因此只要你的提供商支持该协议(绝大多数都支持),它就能直接工作。一个层级由 base_url、模型名称以及请求时从环境变量读取的密钥组成;无需 SDK,也不需要针对每个提供商的专用代码。你可以将免费的本地模型与托管的云端模型配对,或者运行两个云端层级。
… 此外还支持 Groq、Together、OpenRouter、Fireworks、DeepSeek 以及本地服务器(vLLM、LM Studio、llama.cpp)——以及任何接受 Bearer 密钥的 OpenAI 兼容端点。
快速入门
将 Wayfinder 置于你的模型前端。你的应用继续使用 OpenAI API 进行通信;你只需更改一个 base_url。
-
搭建配置——运行 init 会写入一个启动版的 wayfinder-router.toml 文件(默认配置为本地无密钥 Ollama 到 Anthropic 云端)以及一个 .env.example 文件,然后检查你的密钥:
pip install "wayfinder-router[gateway]" wayfinder-router init # starter config (hybrid preset) wayfinder-router init --preset openai # two OpenAI tiers (gpt-4o-mini → gpt-4o) wayfinder-router init --preset gemini # two Gemini tiers (gemini-2.5-flash → gemini-2.5-pro) wayfinder-router init --interactive # pick providers/models step by step
或者手动在 wayfinder-router.toml 中描述你的两个模型:
[routing] threshold = 0.5 # below -> local, at/above -> cloud [gateway.models.local] base_url = "http://localhost:11434/v1" model = "llama3.2" [gateway.models.cloud] base_url = "https://api.openai.com/v1" model = "gpt-4o" api_key_env = "OPENAI_API_KEY" # read from this env var, never stored # api_key_cmd = "op read op://Private/OpenAI/credential" # optional: fill it from a vault
Wayfinder 从不存储密钥:模型通过 api_key_env 指定环境变量名称,密钥在请求时从你的环境中读取。无需任何“安装”步骤——只需导出该变量即可。不想在 shell 中直接粘贴原始密钥?可添加可选的 api_key_cmd 参数,Wayfinder 会在启动时通过你的密钥库填充该变量——例如 op read …(1Password)、security …(macOS 钥匙串)、secret-tool …(Linux)、pass/gopass、vault kv get …、aws secretsmanager get-secret-value …、bw、doppler、gcloud secrets …,或任何能输出密钥的命令。密钥仅保存在内存中,仍然不会写入磁盘。wayfinder-router doctor 可检测你已安装的上述工具,并给出精确的命令行建议。
-
设置好你的密钥,然后运行网关。`doctor` 会在启动前重新检查配置以及每个模型的密钥是否有效(✓ 已设置 / ✗ 未设置)。
export ANTHROPIC_API_KEY=sk-... # or OPENAI_API_KEY, per your config wayfinder-router doctor # ✓/✗ per model — is each key set? wayfinder-router serve --port 8088
-
将现有的客户端指向该网关即可,无需改动代码。
client = openai.OpenAI(base_url="http://localhost:8088/v1", api_key="unused") client.chat.completions.create(model="auto", messages=[{"role": "user", "content": "..."}])
简单的提示词走本地,困难的提示词走云端,每次响应都会携带 `x-wayfinder-router-model` 和 `x-wayfinder-router-score`,方便你查看请求路由到了哪里。想强制某个请求使用某一层级?设置 `model="local"` 或 `"cloud"`(或 `prefer-local` / `prefer-hosted`),通过 `X-Wayfinder-Threshold` 标头为单次调用调整分界,也可以用 `/local` 或 `/cloud` 开始聊天消息(详见“控制单次请求”)。
验证运行是否正常:
curl -s localhost:8088/healthz # {"status":"ok","models":["cloud","local"]} curl -s -D - -o /dev/null http://localhost:8088/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"auto","messages":[{"role":"user","content":"hi"}]}' \ | grep -i x-wayfinder-router # x-wayfinder-router-model: local # x-wayfinder-router-score: 0.00
还没有后端?`wayfinder-router serve --dry-run` 会返回路由决策结果,而不是调用上游,这样你无需接入真实模型,只需 30 秒就能感受路由效果。
安装
| 命令 | 获取内容 |
|---|---|
| `pip install wayfinder-router` | 评分器、CLI、Python API 以及终端聊天(`chat`);评分器/库导入保持轻量依赖 |
| `pip install "wayfinder-router[gateway]"` | 额外添加与 OpenAI 兼容的路由网关,这是常见的服务场景 |
| `pip install "wayfinder-router[ui]"` | 额外添加本地校准/解释/配置界面 |
| `pip install "wayfinder-router[all]"` | 在默认安装基础上额外包含网关和界面 |
工作原理
Wayfinder 位于你已使用的任何 OpenAI 兼容客户端之后。你只需将客户端的 `base_url` 指向该网关一次,此后它就不可见。同一个客户端无论请求路由到本地还是托管,都能正常处理。
your client (chat app, IDE, agent, or code)
|
v
Wayfinder gateway scores, picks a model
|
|-- low --> local (Ollama, vLLM)
|-- high --> hosted (OpenAI, any /v1)
|
v
response returns via the same client,
with x-wayfinder-router-* headers
由此衍生出以下几点:
- 你面前的界面由你决定。可以是聊天 GUI(Open WebUI、LibreChat),带自定义端点的 IDE 助手(Cursor、Continue),智能体框架,或者你自己基于 OpenAI SDK 编写的代码。今天想要一个聊天窗口?把 Open WebUI 放在前面,指向网关即可。
- 本地和托管都是后端,而非应用。本地模型只是一个服务器(Ollama、LM Studio、vLLM、llama.cpp),使用 OpenAI 的 `/v1` 协议;托管模型也是同样的形态。用户永远不需要切换界面,通常也不知道是哪个模型回答了问题。
- 分数是计算得出的,而非第二意见。让模型判断一个提示词的难度会很慢、不确定性高,并且为了决定是否调用模型而先调用一次模型本身也会产生成本。Wayfinder 则直接扫描提示词——根据其结构(长度、标题、步骤、链接、代码、表格)以及措辞中的难度线索(推理术语、数学符号、约束条件)——生成一个 0.0 到 1.0 之间的值,并与你的阈值进行比较。相同的提示词、相同的阈值、相同的答案。这是一个难度代理指标,而非最终裁决,因此阈值由你自己调优。
密钥在请求时从环境变量中读取,不会写入配置文件或评分路径中。
通过命令行界面对提示词进行评分
echo "Summarise this paragraph in one sentence." | wayfinder-router route -
Recommended Model: local
Complexity Score: 0.00 (mode: tiered)
Tiers:
>= 0.00 local <-
>= 0.50 cloud
Contributing Features:
Word Count: 6
...
添加 `--json` 参数用于机器消费者(智能体读取该信息并将其路由到自己的模型):
{
"schema_version": "3",
"score": 0.66,
"recommendation": "cloud",
"mode": "tiered",
"features": { "word_count": 545, "heading_count": 12, "reasoning_term_count": 3, "...": 0 },
"tiers": [{ "min_score": 0.0, "model": "local" }, { "min_score": 0.5, "model": "cloud" }]
}配置路由
Wayfinder 会读取自己的 `wayfinder-router.toml` 配置文件,该文件通过从运行目录向上寻找的方式定位。共有三种模式,按优先级顺序排列(分类器 > 层级 > 阈值);标量分数权重适用于其中任何一种模式。
二进制模式(默认)是一个单一分界点:
[routing] threshold = 0.6 weights = { word_count = 4.0, list_item_count = 2.5 }
`--threshold N` 可在单次运行中覆盖阈值;`WAYFINDER_ROUTER_THRESHOLD` 环境变量可覆盖该值。
若要启用词汇线索,提高它们的权重并在拐点处切分——这是在实际前沿流量上相对于纯结构默认值所取得的唯一保持性改进(skill −0.038 → +0.057,在 RouterBench 上节省 61% 成本)。详见 `docs/lexical-routing.md` 和可直接编辑的 `examples/wayfinder-router.lexical.toml`;请根据自身流量重新校准阈值(仅用约 20 条提示词进行引导只是冒烟测试——参见 `benchmarks/calibration-eval.md`)。
分层路由将分数区间按顺序分配给任意数量的模型:
[[routing.tiers]] min_score = 0.0 model = "llama-3b" [[routing.tiers]] min_score = 0.3 model = "llama-70b" [[routing.tiers]] min_score = 0.6 model = "claude-cloud"
分类器是一个拟合的多项逻辑回归模型,基于每个模型线性分数的 argmax 进行决策。通常通过 `calibrate` 生成,而非手动编写。
每个 `[gateway.models.<name>]` 块将路由名称映射到上游 `base_url`、模型以及可选的 `api_key_env`(环境变量名称,绝不是密钥本身)。网关是唯一接触密钥或网络的组件;评分器、配置和校准器保持纯离线状态。
基于你的数据进行校准
cut 是一个代理值,因此需要根据您自身的流量进行调整。wayfinder-router calibrate 命令会读取带标签的 JSONL 数据集({"text": ..., "label": ...}),并输出一个配置片段。该命令离线运行,不会调用任何模型;标签就是您的真实标注数据。
wayfinder-router calibrate data.jsonl --mode threshold # sweep the binary cut wayfinder-router calibrate data.jsonl --mode tiers # ordinal multi-model wayfinder-router calibrate data.jsonl --mode classifier --out wayfinder-router.toml
该配置片段可直接放入 wayfinder-router.toml 文件中;准确率和选定的断点值会打印到标准错误输出上。分类器通过确定性的 L2 正则化牛顿法/IRLS 算法拟合,采用纯 Python 实现,只需数次迭代即可收敛。
若想从成本角度而非单纯准确率角度来选择 cut,可使用成本感知目标函数。--objective knee 可自动选择成本感知拐点(它最大化质量恢复 × 成本节省——无需猜测目标,并且不会像纯准确率那样在标签偏差场景下陷入“始终路由到昂贵模型”的困境);--objective cost-quality --target-savings X 则可指定一个具体的节省下限。添加 --weights 参数可对自定义特征权重(例如词汇主动切换)进行评分并输出,从而生成一份完整、可直接部署的配置(参见 docs/lexical-routing.md):
wayfinder-router calibrate data.jsonl --mode threshold --objective knee \ --costs local=0.2,cloud=1.0 \ --weights reasoning_term_count=5,math_symbol_count=3,constraint_term_count=1.5
成本仅作为元数据存在——它会影响校准后的 cut 值,并会暴露在 /metrics 端点上,但永远不会进入每次请求的决策过程,后者始终保持确定性和零成本。
控制单次请求
部署配置设定了默认边界,但客户端可以在单个请求上通过普通 OpenAI 传输方式覆盖该决策。覆盖仅改变请求的发送目的地;提示词评分依然会执行,且不会增加任何模型调用。
- model 字段是一条路由指令。auto(或任何常规模型 ID)表示让 Wayfinder 决定;配置好的端点名称(local、cloud)会将请求固定到该端点;prefer-local / prefer-hosted 则分别将请求固定到路由器的低端/高端(prefer-cloud 仍可作为 prefer-hosted 的别名使用)。
- X-Wayfinder-Threshold 头部可针对该请求重新划定 cut 值,取值为 0.0-1.0 之间的数字,会复用您已有的权重(仅适用于二元路由器)。
- 消息内指令(需主动启用:`[gateway] slash_directives = true`)让普通聊天框也能控制路由——以 `/local`、`/cloud`、`/prefer-hosted` 或 `/auto` 开头输入消息,即可锁定该轮对话的路由(指令在模型看到之前已被剥离)。只有已知指令才会生效;其他以 `/` 开头的内容将作为普通文本处理(WF-ADR-0036)。
# Pin one call to cloud regardless of score: client.chat.completions.create(model="cloud", messages=[...]) # Or move the cut for one call (keep model="auto"): client.chat.completions.create( model="auto", messages=[...], extra_headers={"X-Wayfinder-Threshold": "0.8"} )
每次响应都会在 `-model` 和 `-score` 请求头旁边添加 `x-wayfinder-router-mode`(取值:scored / pinned / threshold-override),这样你就能看到是哪个通道决定了路由。
从聊天 UI 驱动路由(无需分支)
由于模型字段本身就是路由指令,任何兼容 OpenAI 的聊天 UI 无需修改代码即可驱动路由:应用原有的模型下拉菜单会自动变成按对话设置的路由选择器(auto / prefer-local / prefer-hosted / 固定端点)。网关会在 `GET /v1/models` 中列出这些选项,因此 UI 可以自动发现它们。
- LibreChat——将 `examples/librechat.yaml` 和 `examples/docker-compose.override.yml` 复制到你的代码目录中,运行 `docker compose up`,然后选择 "Wayfinder" 端点。
- Open WebUI——添加一个指向该网关的 OpenAI 连接;它会自动发现路由选项。
具体配置请参见 `examples/` 目录下的两个示例。标准聊天 UI 无法实现的是实时按对话调整阈值的滑块;这正是 wayfinder-chat 分支所增加的功能,而这条无分支路径则是先用来验证该功能。
查看请求的去向
Wayfinder 的控制功能分布在您已经运行的各个工具中,因此很容易忽略它正在工作。以下四个界面可以展示或控制路由:
| 界面 | 显示内容 | 位置 |
|---|---|---|
| 模型下拉菜单 | 路由选择器(auto / prefer-local / prefer-hosted / 固定端点) | 您的客户端,来自 `GET /v1/models` |
| 响应头 | 每个请求的去向及原因(`-model` / `-score` / `-mode` / `-request-id`) | 每次响应 |
| 调试体字段 | 响应体内的决策信息,需主动启用 | 请求头 `X-Wayfinder-Debug: true` |
| 仪表盘 | 最近的决策、按模型统计的次数、分数——仅限元数据,绝不包含提示词文本 | `GET /router`(JSON 格式位于 `/router/recent`) |
该仪表盘与旁路的 wayfinder-router UI 控制台是分开的,后者用于调优,不面向生产流量。
从反馈中学习
不要猜测分界点,而是从你自己对本地输出与托管输出的判断中学习。循环是:收集判断、校准、自动路由。
通过 A/B 引导来启动。对于每个示例提示词,wayfinder-router onboard 运行两个分支,并询问哪一个足够好;答案就是一个标签:
wayfinder-router onboard prompts.jsonl --arms local,cloud --calibrate > wayfinder-router.toml比较结果输出到 stderr;--calibrate 将生成的配置打印到 stdout。每次判断都会将一条 {"text", "label"} 行追加到反馈日志中,该日志本身就是校准数据集,因此日志直接转化为配置。
要跳过手动分级,让 wayfinder-router 自动判断标签。它运行两个层级,并向自动判断器询问“较便宜的层级是否足够好?”——同样的充分性问题,无需人工介入:
wayfinder-router judge prompts.jsonl --arms local,cloud --gold gold.jsonl > wayfinder-router.toml内置判断器是一个确定性文本比较器,当无法判断时会弃权而不是猜测。因为一个错误标签会在静默中降低实时路由的质量,判断器只有在通过信任门限时才会输出配置——与您人工标记的 --gold 集的一致性(Cohen's κ ≥ 0.6)、在折外数据集上相比多数基线的提升,以及两个分支都有代表。如果门限检查失败,它会打印混淆矩阵并拒绝(标签仍会被记录)。传递 --save-comparisons out.jsonl 以同时保留原始响应(默认关闭——它存储大量数据)。
一旦你开始自动路由,通过记录哪个模型实际上足够好来保持诚实:
curl localhost:8088/v1/feedback -d '{"text": "...", "label": "cloud"}'然后按计划重新拟合——通过 cron、k8s CronJob 或点击 UI。重新校准只重写 [routing] 部分,保留您的 [gateway] 端点,运行中的网关会热重载结果而无需重启:
wayfinder-router recalibrate # log -> calibrate -> write config wayfinder-router recalibrate --min-labels 50 # no-op until you have enough signal
判断过程会运行模型,因此它位于网关层(使用您的密钥);评分核心保持不变,日志不携带任何秘密。
部署与集成
CLI、引导和 UI 用于操作员和启动阶段。在生产环境中,提示词通过网关(透明模式)或库(进程内模式)流动,因此路由发生在提示词所在的位置。
以服务、sidecar 或独立模式运行网关:
docker build -t wayfinder-router . && docker run -p 8088:8088 -v "$PWD/data:/data" wayfinder-router # or: docker compose up gateway (see docker-compose.example.yml)
将你现有的客户端指向它,无需修改应用程序。任何兼容 OpenAI API 的客户端都可以接受 `base_url`,包括 Agent 框架(LangChain、LlamaIndex)、支持自定义端点的 IDE 助手(Cursor、Continue),以及像 LiteLLM 这样的网关:
client = openai.OpenAI(base_url="http://localhost:8088/v1", api_key="unused")
请参阅集成方案,获取可直接复制粘贴的配置:聊天界面(Open WebUI、LibreChat、Jan)、编辑器(Continue、Cline、Zed、JetBrains)、Agent 框架(LangChain、LlamaIndex、CrewAI、AutoGen、OpenAI Agents SDK、Vercel AI SDK)以及 CLI(aider、Copilot CLI)——另外还有标准的 `OPENAI_BASE_URL` / `OPENAI_API_KEY` 对。
Claude Code 使用的是 Anthropic 的 Messages API 而非 OpenAI 的 API,因此网关暴露了一个 POST `/v1/messages` 适配器(WF-DESIGN-0011),负责 Anthropic ⇄ OpenAI 的双向转换——包括流式和工具调用。将其指向网关根地址,Claude Code 就能像其他客户端一样通过 Wayfinder 路由:
export ANTHROPIC_BASE_URL="http://localhost:8088" # client appends /v1/messages export ANTHROPIC_API_KEY="unused" # the gateway uses each upstream's own key claude
从用户所在的任何位置收集反馈。你的应用、IDE 或聊天界面会显示一个点赞或点踩按钮,并提交判断结果;下次重新校准时会从中学习:
fetch("http://localhost:8088/v1/feedback", { method: "POST", body: JSON.stringify({ text: prompt, label: wasGoodEnough ? "local" : "cloud" }), });
网关以异步方式转发并支持流式:当请求中包含 `stream: true` 时,返回结果为 Server-Sent-Events,因此聊天客户端可以在 token 到达时实时呈现。上游超时或连接失败时,会返回一个符合 OpenAI 格式的错误响应,而非裸的 500 状态码;每个响应都带有请求 ID 用于追踪,路由决策和重载失败信息都会被记录。可供调节的参数如下:
| 设置项 | 作用 |
|---|---|
| `WAYFINDER_ROUTER_TIMEOUT` / `serve --timeout` | 上游超时时间(秒,默认 60) |
| `WAYFINDER_ROUTER_FEEDBACK_TOKEN` | 设置后,`/v1/feedback` 需要 `Authorization: Bearer <token>` |
| `serve --dry-run` | 返回路由决策,但不调用任何上游 |
| `GET /healthz` | 当配置的 `api_key_env` 未设置时,报告降级状态并列出缺失的键 |
| `GET /router` | 只读面板,显示最近的决策记录,设置 `X-Wayfinder-Debug: true` 可在响应正文中输出一条决策 |
| `GET /v1/savings?period=today|7d|30d|all` | 实际成本与始终使用前沿模型的成本对比,以及两者之间的节省额,按路由展示(WF-DESIGN-0007) |
| `WAYFINDER_ROUTER_SAVINGS_FILE` | 节省账本的持久化路径(默认:`<config-dir>/wayfinder-savings.json`) |
| `[gateway] retries / breaker_threshold / breaker_cooldown` | 可靠性:针对传输层/429/5xx 错误进行有限重试,以及每个目标独立的断路器机制(WF-ADR-0031) |
| [网关] 故障转移 = 同层级|降级|升级 | 资源耗尽时,默认停留在当前层级,或回退到更便宜的层级(绝不增加成本),或升级到更贵的层级(需主动选择);通过每次请求的 X-Wayfinder-Failover 头控制 |
| [gateway.models.<名称>] fallbacks = [...] / context_window(备选模型列表 / 上下文窗口) | 同层级下失败时尝试的备选端点;跳过窗口不足以容纳提示词的目标。响应中包含 x-wayfinder-router-served-by 头 |
| [gateway.budget] limit(限额)/ window = day|month|all(周期 = 日|月|全部)/ on_breach = degrade|block(超限行为 = 降级|拦截) | 消费上限:一旦达到限额成本,默认降级到最便宜的层级(绝不增加成本),或使用 HTTP 402 进行拦截。通过 x-wayfinder-router-budget 头暴露;需要配置真实的 cost_per_1k 价格(WF-ADR-0032) |
| [gateway.cache] enabled(启用)/ ttl(缓存时间)/ max_entries(最大条目数)/ max_bytes(最大字节数) | 精确匹配响应缓存:对完全相同的确定性请求重播已存储的回答——即时且免费重复。默认关闭;仅内存缓存;增大 max_bytes(默认 64 MiB)可缓存更多。命中缓存免费,并通过 x-wayfinder-router-cache: hit|miss 头暴露;禁用缓存时会清空所有缓存(WF-ADR-0033) |
| [gateway.rate_limit] rpm(每分钟请求数)/ tpm(每分钟上游 token 数)/ window(时间窗口) | 在固定时间窗口(默认 60 秒)内限制每分钟请求数和/或每分钟上游 token 数;超限时返回 429 状态码及 Retry-After 头。这是全局网关的最外层防护栏(在评分前检查)。成功响应携带 X-RateLimit-Limit/-Remaining/-Reset 头,以便客户端自我节流;通过 x-wayfinder-router-rate-limit 和 wayfinder_router_rate_limited_total 指标暴露(WF-ADR-0034) |
| [gateway.keys.<ID>] hash(哈希)/ tags(标签)/ models(模型,可嵌套 budget / rate_limit) | 虚拟 API 密钥:一旦设置任何密钥,/v1/* 端点要求有效的 Authorization: Bearer 令牌(否则返回 401)。使用 wayfinder-router keys new 命令生成;仅存储 SHA-256 哈希值。每次花费和节省的金额都会按密钥归因(通过 /v1/savings 的 by_key 参数和 wayfinder_router_key_requests_total 指标);密钥可携带自己的预算/速率限制(以最严格的为准)以及模型白名单(将模型限制到最近允许的层级)(WF-ADR-0035) |
解释与调优
要了解某个提示词为何被路由到特定位置,可以请求逐特征分解:每个特征的值、其归一化水平、其权重以及其在评分中所占的份额。
wayfinder-router route prompt.md --explain
对于交互式调优,有一个本地 Web UI:
- 解释 —— 粘贴一条提示词;查看分数、等级阶梯和贡献柱状图,然后拖动阈值滑块,实时观察路由变化。
- 校准 —— 粘贴带标签的数据集,运行一种模式,查看准确率、扫描曲线以及生成配置片段。
- 配置 —— 编辑 wayfinder-router.toml,支持实时验证并保存。
- 上手 —— 在浏览器中对本地模型和托管模型进行 A/B 测试,为每个模型打分,并根据日志校准(需要 [gateway] 来处理模型调用)。
pip install "wayfinder-router[ui]" wayfinder-router ui --port 8099 # then open http://localhost:8099
该 UI 是对相同纯函数的薄封装;它从不调用模型,也不在其中暴露任何机密。
Python API
from wayfinder_router import score_complexity, RoutingConfig, explain_score result = score_complexity(prompt_text, config=RoutingConfig.binary(threshold=0.7)) print(result.recommendation, result.score, result.features) for fc in explain_score(result.features, RoutingConfig().weights): print(fc.name, fc.contribution)
起源
Wayfinder 最初是一个更大需求工具内部的路由实验,后来被拆分出来,因为路由是运行时关注点,而非知识关注点:提示词路由器不应该让你安装一个你并不需要的引擎。最终成果是一个小巧、专注的工具,其评分核心保持零依赖——你可以仅使用标准库导入 wayfinder_router 并对提示词进行评分(WF-ADR-0001, WF-ADR-0029)。
仓库布局
wayfinder-router/
wayfinder_router/ the package: scorer, tiers + classifier, config loader/writer,
offline calibration (Newton/IRLS), explain, the feedback log and
onboarding harness, recalibration, CLI, and the optional gateway
and local UI (the impure layers, behind their extras)
tests/ scorer, config, calibration, explain, feedback, onboard,
recalibrate, CLI, gateway, and UI coverage
decisions/ design notes behind the tool's own choices
docs/ the FAQ and the lexical-routing guide
Dockerfile, docker-compose.example.yml deploy the gateway as a service
测试
pip install -e .[dev] # or: pip install pytest make test
