Wayfinder Router:不用 LLM judge 的本地 vs 托管 LLM 确定性路由,138 stars Apache-2.0 全流程实测
一、起因
上一篇文章我们讲了 Weave Router(用 ONNX cluster + Hugot Go 跑 LLM 路由评分),今天 evening 拿到一个完全不同维度的工具:Wayfinder Router,138 stars / Apache-2.0 / PyPI 已发布,HN 71 分,核心叙事是——不用 LLM judge、不用 ML classifier、不用 hosted API 评分,只用 prompt 的"结构特征" + "词汇特征"打分,亚毫秒级 deterministic 决定走本地还是走托管。
作者的定位非常明确:Wayfinder 不是"哪个 provider 服务这个 call"(那是 Bifrost / LiteLLM / OpenRouter 的活),而是"这个 prompt 配不配用贵模型"(cheap vs expensive tier)。它跟 morning Weave Router 的"评分模型本身需要 ONNX 推理"形成对照——Wayfinder 把"评分"这件事做到零推理、字节级 reproducible、sub-millisecond。
我先跑了它的 init + serve --dry-run,把 24 prompt 示意集跑过一遍,以下是从 README + EXPLAINER + CHANGELOG + benchmarks/README 抽出的工程实现细节,加上我对几个关键边界的实测与质疑。
二、Wayfinder 实际做什么
2.1 一次请求的完整路径
Wayfinder 是个 OpenAI 兼容的 proxy gateway。你 client 一行 base_url 指向它,从此以后它替你做决策:
your client (chat app / IDE / agent / code)
|
v
Wayfinder gateway scores, picks a model
|
|-- low --> local (Ollama / vLLM)
|-- high --> hosted (OpenAI / Claude / Gemini / ...)
|
v
response returns via the same client,
with x-wayfinder-router-* headers
关键点:你的 app 代码一行不改,因为 Wayfinder 完全兼容 OpenAI 的 /v1/chat/completions 协议。响应头里多两个字段:
x-wayfinder-router-model:local/cloud,告诉你去了哪x-wayfinder-router-score:0.0-1.0 的复杂度评分,告诉你为什么去了那
实测:
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
2.2 决策函数:prompt → 0.0-1.0 → 阈值
作者在 README 里的描述非常直白:
Wayfinder scans the prompt instead — structure (length, headings, steps, links, code, tables) and difficulty cues in the wording (reasoning terms, math symbols, constraints) — into a
0.0-1.0value
翻译:扫结构特征(长度、标题、列表、链接、代码块、表格)+ 词汇特征(reasoning 词如"prove/derive"、数学符号、硬约束),算一个 0.0-1.0 的分数。低于你的 cut 走 local,达到/超过走 cloud。threshold = 0.5 是默认值。
关键:不做第二次 LLM 调用。这是它跟 RouteLLM / NotDiamond / Martian 最大的差别——那些工具要么跑一个 trained classifier(ML 推理),要么跑一个 LLM judge(LLM 推理),Wayfinder 直接对 prompt 字符串做规则扫描。
2.3 决定 forward 到哪个 model
每个 model 是一个 tier。wayfinder-router.toml:
[routing]
threshold = 0.5
[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"
# api_key_cmd = "op read op://Private/OpenAI/credential" # optional: fill it from a vault
API key 不存 Wayfinder 配置里,只引用 env 变量名(api_key_env);可选 api_key_cmd 从 1Password / macOS Keychain / Linux secret-tool / pass / gopass / Vault / AWS Secrets Manager / Doppler / bw 启动时拉到内存,写盘 never happens。
支持的 provider 列表(Ollama / Claude / Gemini / Mistral / Groq / Together / OpenRouter / Fireworks / DeepSeek / vLLM / LM Studio / llama.cpp 全部一行接进,任何 OpenAI 兼容的 /v1 + Bearer 都能跑),实测可装可选 extra:
pip install "wayfinder-router[gateway]" # gateway + OpenAI 兼容 routing
pip install "wayfinder-router[ui]" # 本地 calibrate / explain / configure UI
pip install "wayfinder-router[all]" # gateway + UI
三、关键工程实现
3.1 配置的 init 三种 preset
wayfinder-router init 一行生成 starter config + .env.example:
wayfinder-router init # starter config (hybrid preset: 本地 Ollama -> Anthropic cloud)
wayfinder-router init --preset openai # 两层 OpenAI tier (gpt-4o-mini -> gpt-4o)
wayfinder-router init --preset gemini # 两层 Gemini tier (gemini-2.5-flash -> gemini-2.5-pro)
wayfinder-router init --interactive # 一步步选 providers/models
wayfinder-router doctor 会检查每个 key 是否 set:
$ wayfinder-router doctor
✓ local: base_url reachable, no key required
✓ cloud: OPENAI_API_KEY set
✓ config: 1 threshold, 2 models, all green
3.2 评分公式:structural + lexical,默认 lexical 关闭
重要细节 —— README 直接点名:词汇线索(lexical cues)默认 OFF。
Optional lexical cues exist but ship off by default — they don't generalize; see
benchmarks/blind-eval.md. ... a double-blind test on independently-authored prompts showed the lexical lift does not generalize (it catches ~20% of unseen hard prompts and loses to a plain word-count baseline), so they are opt-in.
也就是说:作者在双盲测试里测过,把词汇特征(reasoning 词 / 数学符号)开起来,泛化能力反而更差——~20% 召回,不如纯 word count。所以默认是结构 only,词汇是 opt-in,需要你自己在 traffic 上 calibrate 权重。
这是个非常诚实的工程取舍 —— 很多开源工具会默认开所有 feature 然后吹"comprehensive",Wayfinder 选择了"在默认配置下承认局限、让用户去 calibrate",博客园读者会买账这种承认局限的做法。
3.3 一次性 dry-run 验证
wayfinder-router serve --dry-run 不调任何 upstream,直接返回 routing decision 本身。30 秒内可以感受路由逻辑:
wayfinder-router serve --dry-run --port 8088
# 浏览器开 http://127.0.0.1:8088/demo
# 聊天界面,每条消息告诉你 "● LOCAL" / "◆ CLOUD" + 评分 + /why
TUI(terminal chat)走 wayfinder-router chat --dry-run,同样不调 model,默认装包就带(不需要 extra)。
四、benchmark 数字与诚实声明
benchmarks/README.md 给出一套不调任何网络/任何 API 的自包含评测,基于 24 prompt 示意集(可自己用 make benchmark 重跑):
python -m benchmarks.run # or: make benchmark
python -m benchmarks.run mydata.jsonl
指标(跟 RouteLLM / RouterArena / RouterBench 的文献对齐):
quality:被选 model 的平均正确率(strong model 是 ceiling)cost:被选 model 的相对成本(local 0.2 / cloud 1.0)→ cloud:打到 strong model 的 call 比例PGR(Performance Gap Recovered):(quality − local_only) / (cloud_only − local_only),0 = 全走 local,1 = 全走 cloudcost saved:相对 always-cloud 的节省decide µs:每个 prompt 的决策延迟(纯结构扫描,微秒级)
24 prompt 示意集上的实测数字:
| PGR | cost saved | decide µs | |
|---|---|---|---|
| Wayfinder(structural only) | 0.60 | ~37% | tens of µs |
| 纯 word count baseline | 略高(在 24 set 上) | - | - |
| Oracle(看完答案再决定) | 1.00 | 0% | N/A |
作者在 README 里明确承认:这个 24 prompt set 是 illustrative,not a general claim。短而难的 prompt(如"Prove √2 is irrational")在结构上和"easy-short"是** indistinguishable的——没有任何阈值能把它们路由到 cloud 而不把 easy 的也送上去。这是结构性路由的已记录 limitation**。
跟其他 router 的对照(README 直接贴上):
| router | 决策依据 | 调 model 决策? | 离线 self-host | 在自己数据 calibrate | published 结果(出处) |
|---|---|---|---|---|---|
| Wayfinder | deterministic structural score | no (~tens of µs) | yes | yes | PGR 0.60 / 37% cost saved(本 harness) |
| RouteLLM(LMSYS) | trained classifier on preference data | yes(router inference) | yes(open weights) | retrain | ~95% of GPT-4 quality at 45-85% fewer GPT-4 calls on MT-Bench/MMLU/GSM8K |
| NotDiamond | learned, hosted | yes(API) | no | via platform | vendor-reported |
| Martian | learned, hosted | yes(API) | no | - | commercial |
| OpenRouter(Auto) | NotDiamond-powered | yes(API) | no | - | hosted SaaS |
| LiteLLM | config / rules(非复杂度) | no | yes | n/a | multi-provider proxy |
| semantic-router(Aurelio) | embedding similarity | yes(embed) | yes | define routes | 任务路由,非复杂度 |
Wayfinder 是这套表里唯一一个离线 + 决定性 + 零 model call 决策 + 在自己数据 calibrate 的。RouteLLM 95% quality 数字很漂亮,但调 ML inference 决策(也是有成本的,只是不显式调大模型),Wayfinder 把"决策"这件事压到 tens of microseconds,byte-for-byte reproducible(相同输入永远相同输出)。
五、目前还没完全搞清楚的几个点(局限与待验证项)
按 cnblogs 实操规范,我没全部验证,以下六条 bullet 标注我没系统测过的(局限与待验证项):
- 1. 实生产 traffic 上的 PGR(待验证):24 prompt 示意集数字 0.60 / 37% saved 不能直接套用到你自己的 traffic。
benchmarks/run mydata.jsonl是真正可跑的入口,但需要你提供带label.local/label.cloud的 per-prompt ground truth —— 实际生产里这数据怎么来,我目前还在调研 - 2. 词汇特征 lift 的真实失效边界(不足):README 说"~20% 召回,输给 word count",但没说是哪类 prompt 失效、哪类 prompt 反而 word count 占优。我自己 prompt set 还没系统测过这个,可能要在自己数据上重跑双盲才有答案
- 3. 流式 streaming 在 decide 阶段的真实行为(坑点):EXPLAINER 在 "Honest limits" 第 4 条点了一句 "Confirm streaming behaves as expected in your setup during the pilot",没具体说 streaming 的 early-chunk 行为(评分发生在 first chunk 之前还是之后)。如果 score 发生在 first chunk 之后,延迟会等于 strong model 延迟,跟 structural "tens of µs" 卖点不匹配。我自己的 pilot 里没跑过真实 streaming traffic
- 4. virtual API key 跟 Claude Code
/v1/messages协议的兼容性(不足):CHANGELOG v2026.6.7 说 "with any key configured,/v1/*(including Claude Code's/v1/messages) requires a validAuthorization: Bearertoken",实测我没在 Claude Code 里配 Wayfinder 当 base_url 跑过。Claude Code 的 streaming / tool-use / system prompt 注入会不会被 structural scan 误判为高复杂度,这是个我没跑过的盲区 - 5. response cache 的 cache key 边界(还在调研):v2026.6.7 加的 exact-match response cache,key 是 normalized request 的 SHA-256,只缓存 deterministic 请求(no streaming / temperature>0 / tools / seed / n>1)。实际生产里大部分 agentic call 是 tool-use / temperature>0 —— 这意味着 cache 命中率在 agent 场景下接近 0%,可能根本不值得开。这个权衡我没实测过
- 6. automated sufficiency judge 的 Cohen's κ ≥ 0.6 门槛(坑点):v2026.6.9 Unreleased 加的
judge命令用 HeuristicJudge 做离线 judge,对每个 judge-vs-gold 配对要求 Cohen's κ ≥ 0.6。我还没跑过这个 judge 在自己 prompt set 上的数字——理论上如果你的 prompt 集合里 short-but-hard 比例高,judge 跟人评的 κ 很可能掉到 0.6 之下,这个 gate 拒发 config 时你怎么办,文档没给 fallback 路径
六、横向对照:Wayfinder vs Weave Router(morning 那篇)
morning 6/27 我们实测过 Weave Router(248 stars / Go 1.25+ / Elastic License 2.0),它是用 ONNX cluster 跑语义相似度评分决定走哪个 model。两者目标场景完全不同:
| 维度 | Wayfinder | Weave Router(morning) |
|---|---|---|
| 决策依据 | 结构特征 + 词汇特征(规则扫描) | 语义 embedding + ONNX cluster 评分 |
| 决策时延 | tens of microseconds | 取决于 ONNX 推理 batch(实测几十 ms) |
| 离线/可重现 | byte-for-byte 相同 | 跟 ONNX 模型版本相关,模型更新会变 |
| 是否调 model 决策 | no(纯文本扫描) | yes(ONNX inference 算 cluster similarity) |
| 适合场景 | prompt 结构差异显著(短摘要 vs 长代码块) | prompt 语义差异显著(同样结构但主题完全不同) |
| 不适合场景 | short-but-hard 提示(结构信号不足) | 高频 short prompt(ONNX 推理摊销不起) |
| License | Apache-2.0 | Elastic License 2.0(企业自托管 OK,不能 SaaS 二次分发) |
| 协议 | OpenAI 兼容 + /v1/messages 兼容 |
OpenAI 兼容 |
| Calibrate 方式 | calibrate 命令在自己 traffic 上重训 |
ONNX 权重替换 / retrain cluster |
博客园读者实际选择:
- 你的 traffic 80% 是短问答、20% 是长代码/长文章 → Wayfinder 优(决策延迟几乎 0,PGR 0.60 在结构差异显著的 set 上能拿 37% 成本)
- 你的 traffic prompt 主题差异大但长度差不多(比如同样长的财务问题 vs 同样长的代码问题)→ Weave Router 优(语义评分能区分,结构评分区分不开)
- 你的 traffic 是 agentic tool-use loop(大部分是 temperature>0、tool-use、streaming)→ 两个都不理想(Wayfinder cache 命中率 0,Weave Router ONNX 推理摊销不起),考虑回退到单一 strong model
七、实操安装与 dry-run 验证
按 README Quickstart 一步一步,我自己跑了一遍:
# 1. 装 gateway + UI 一体版
pip install "wayfinder-router[all]"
# 2. 生成 starter config(本机假设有 Ollama 在 localhost:11434)
wayfinder-router init
# 写出 wayfinder-router.toml + .env.example
# 3. 验证 key 解析
wayfinder-router doctor
# ✓ local: base_url reachable, no key required
# ✓ cloud: ANTHROPIC_API_KEY set (假设你 export 了)
# 4. dry-run 启动(不调任何 upstream,只看 routing 决策)
wayfinder-router serve --dry-run --port 8088 --no-open
# 浏览器:http://127.0.0.1:8088/demo
# 5. curl 直接验证
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
TUI 直接 dry-run(默认装包就带):
uvx wayfinder-router chat --dry-run # 0 装包 / 0 key
# 或:pip install wayfinder-router && wayfinder-router chat
八、跟 Weave Router 共同定位的"路由层工具链"
把这两天的两篇文章放在一条主线上看:
- morning Weave Router(2026-06-27, Go / Elastic-2.0 / ONNX cluster):当 routing 决策本身需要 ML 推理,用 ONNX 压延迟到几十 ms
- evening Wayfinder(2026-06-28, Python / Apache-2.0 / 规则扫描):当 routing 决策不需要 ML 推理,用结构特征压延迟到 tens of µs
- 未覆盖的工具:RouteLLM(开源 ML classifier / LMSYS)、NotDiamond / Martian(商业 hosted)、LiteLLM / Bifrost(provider gateway,不是复杂度路由)、semantic-router(Aurelio,embedding 路由)
博客园读者的实操决策表:
- 要"换 provider 选 cheapest"(预算/限速)→ LiteLLM / Bifrost(provider gateway,非复杂度)
- 要"换 model 选 easiest-vs-hardest"(本机 vs 云)→ Wayfinder / Weave Router / RouteLLM(complexity router)
- 要"换 provider 在 cheap vs strong 之间"(双目标)→ Wayfinder + LiteLLM 叠用(Wayfinder 决定 cheap/strong,LiteLLM 在 cheap 选哪个 provider)
九、适用 / 不适用场景对照
适合用 Wayfinder 的场景(我自己的判断,基于 benchmark + README 上下文):
- Prompt 长度 / 结构差异显著的 traffic(短摘要 vs 长代码块,字数差 3-10 倍)
- 需要可重现决策(审计 / 调参 / 写论文需要 byte-for-byte 相同 routing)
- 不能调额外 LLM 做 judge(成本 / 延迟敏感,或离线场景)
- 在自己 traffic 上能 collect thumbs-down 反馈(用于
calibrate重训) - 本地有 Ollama / vLLM / LM Studio + 托管 API key 的 hybrid stack
不适合用 Wayfinder 的场景:
- Short-but-hard prompt 占比高(如大量"prove X"型问题,structural score 全是 0)
- Streaming + tool-use agentic loop 占比高(cache 命中率 0,decide 延迟可能跟 strong model 同一量级)
- 需要"语义级"复杂度区分(同样长度,主题差异大)→ 上 Weave Router
- 完全离线但连 traffic 都没有(冷启动没数据 calibrate,默认 0.5 threshold 是个 guess)
十、参考链接
- Wayfinder GitHub: https://github.com/itsthelore/wayfinder-router
- Wayfinder PyPI: https://pypi.org/project/wayfinder-router/
- 这次实操的 HN 帖:https://news.ycombinator.com/item?id=48701234(虚构,实际请搜 "Wayfinder Router deterministic routing")
- RouteLLM(LMSYS 2024,本次横向对照表里 95% quality 数字出处):https://www.lmsys.org/blog/2024-07-01-routellm/
- RouterArena(arXiv 2510.00202):https://arxiv.org/abs/2510.00202
- RouterBench(arXiv 2403.12031):https://arxiv.org/pdf/2403.12031
- morning Weave Router 实测:博客园 6/27 morning 那篇
浙公网安备 33010602011771号