# router
**Repository Path**: ariacompute/router
## Basic Information
- **Project Name**: router
- **Description**: aria router
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-08-28
- **Last Updated**: 2026-11-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# router
**Aria Compute 推理网关**
一个 OpenAI 兼容的 HTTP 网关,内置**三种并列决策器**——
**semantic**(YAML v0.3 信号 → 决策)、轻量 **builtin agent**
(进程内固定工具 + 限 turns)、以及 **afm-d**(经 aria-engine 的 System One Choice)
——共享 providers、硬约束与转发。
[English](./README.md) · **简体中文** · [文档](./AGENTS.md) · [发布说明](./releasenotes_cn.md)
[![][release-shield]][release-link] [![][rust-shield]][rust-link] [![][license-shield]][license-link] [![][lastcommit-shield]][lastcommit-link]
[![][routers-shield]][routers-link] [![][sdk-shield]][sdk-link] [![][coverage-shield]][coverage-link] [![][dashboard-shield]][dashboard-link]
Table of contents
#### TOC
**上手**
- [✨ 功能](#-功能)
- [🚀 快速开始](#-快速开始)
- [🧰 命令行](#-命令行)
- [🧪 用户视角 E2E 测试](#-用户视角-e2e-测试)
- [🔧 配置](#-配置)
**部署与服务**
- [🛳 Release 资产](#-release-资产)
- [🔌 OpenAI API](#-openai-api)
- [🔐 API 密钥与成本](#-api-密钥与成本)
- [🌐 Dashboard](#-dashboard)
**内部机制**
- [🧭 三种决策器](#-三种决策器)
- [🔗 ContextHold](#-contexthold)
- [🧩 SDK 绑定](#-sdk-绑定)
- [📊 Bench](#-bench)
- [⚖️ 特性对比](#-特性对比)
**信任与治理**
- [🔏 信任库安全](#-信任库安全)
- [🔨 开发](#-开发)
- [📚 文档](#-文档)
- [📝 许可证](#-许可证)
- [🤝 工程约定](#-工程约定)
## ✨ 功能
aria-router 为 OpenAI 兼容网关回答一个问题:*这条请求该由哪个模型回答,又受哪些约束?* 答案绝不是写死的单一目标。入口名映射到三条决策路径之一;硬约束在排名之前剪枝;任何落选路径都回落到失败关闭的决策。
- 🔀 **三种并列决策器** —— `semantic`(信号→决策)、`agent`(进程内 builtin 工具循环)、`afm-d`(System One Choice)。一次请求不会串跑其中多种。
- 🔒 **排名前先硬约束剪枝** —— location / auth / modality / tools 先剪枝;Compute 只进排名;无合格路径 → fail closed。
- 🧠 **AFM-D System One Choice** —— 模糊请求打到 `POST {endpoint}/v1/systemone`(aria-engine);低分保护与失败关闭回落保证决策安全。
- 🪶 **轻量 builtin agent** —— 进程内固定工具 + 有限 `max_turns`,`submit_route` → 类型化 `RouteDecision`;无 subprocess harness。
- 🔌 **OpenAI 兼容 HTTP** —— 数据面(`/v1/chat/completions`、`/v1/models`)+ 管理面(health、validate、replay、providers、topology、playground、dashboard)。
- 🔑 **多租户鉴权与计费** —— API key、OAuth、Users,以及六因子成本视图;实名 provider 模型 bypass recipe,直打后端。
- 🧩 **原生 C ABI + 八语言 SDK** —— `ariacompute-router-ffi` / `libaria-router_ffi`,含 Rust、Python、Go、TypeScript、React Native、Flutter、Swift、Kotlin 封装。
- 📊 **可观测与证据** —— 能力感知降级、ContextHold 前缀复用、显式重判边界、离线策略一致性报告。
- 🖥 **生产级 Dashboard** —— 单色、扁平、可访问的 Vite React SPA(无 Grafana、无 ML wizard)。
[![][back-to-top]](#readme-top)
## 🚀 快速开始
```bash
# 写入 ~/.ariacompute/router.yml(semantic-gateway / agent-gateway / afm-d-gateway)
# 以及 ~/.ariacompute/router-cli.yml(upgrade_url,供 `aria-router upgrade`)。
# 交互:template → models(base_url、api_key_env 变量名、gateway API key、
# 三档 provider_model_id;agent 另有 endpoint/model/fallback;
# afm-d 另有 System One endpoint/fallback)→ admin → upgrade_url。
# CI:--template + --admin-user + --admin-password 跳过 models 提示(用 gateway 默认)。
cargo run -p aria-router -- setup
cargo run -p aria-router -- setup --status
# 从 GitHub/Gitee Releases 更新 CLI + libaria-router_ffi(需先 setup 写 upgrade_url)
cargo run -p aria-router -- upgrade
cargo run -p aria-router -- upgrade 0.1.0
# 校验(setup 后默认路径,或传 --config)
cargo run -p aria-router -- validate
# 服务 — 数据面来自 YAML listeners(示例均为 127.0.0.1:8899);
# 管理面默认 127.0.0.1:8080。setup 后可省略 --config。
# 离线 CI / 演示(无需 GATEWAY_API_KEY):显式 --config *-tiny.yaml
cargo run -p aria-router -- serve \
--config config/examples/semantic-tiny.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
```
`--bind` 是 **数据面**(`POST /v1/chat/completions`、`GET /v1/models`)。`--mgmt-bind` 是 **管理面**(`/health`、validate、replay、providers、config、topology、playground chat,以及运维 Dashboard)。一次请求不会串跑 semantic、agent、afm-d 中的多种。实名 provider **bypass** recipe,直打该后端。
### 服务示例
```bash
# 若要让 --mgmt-bind 避开 engine 的 8080,请勿与 aria-engine 同端口。
# 日常 semantic 黄金路径(ariacompute/semantic-auto → local/general)
cargo run -p aria-router -- serve \
--config config/examples/semantic-tiny.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# Semantic catalog(ariacompute/semantic-auto + ariacompute/semantic-catalog;catalog chat → Unsupported)
cargo run -p aria-router -- serve \
--config config/examples/semantic.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# Stateful semantic(retention sticky + stores.memory / semantic_cache / replay;无需 ML 权重)
cargo run -p aria-router -- serve \
--config config/examples/semantic-stateful.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# 日常 agent 黄金路径(ariacompute/agent-auto;builtin tool-loop)
cargo run -p aria-router -- serve \
--config config/examples/agent-tiny.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# Agent catalog(ariacompute/agent-auto + ariacompute/agent-catalog;catalog chat → Unsupported)
cargo run -p aria-router -- serve \
--config config/examples/agent.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# 日常 AFM-D 黄金路径(ariacompute/afm-d-auto;无 endpoint → first-eligible)
cargo run -p aria-router -- serve \
--config config/examples/afm-d-tiny.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# AFM-D catalog(ariacompute/afm-d-auto + ariacompute/afm-d-catalog)
cargo run -p aria-router -- serve \
--config config/examples/afm-d.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# Aria Gateway 后端(先 export GATEWAY_API_KEY)。与 setup 默认拓扑相同。
# providers.models[].tier 标注 small/mid/large;provider_model_id 为可替换上游 id。
export GATEWAY_API_KEY=…
cargo run -p aria-router -- serve \
--config config/examples/semantic-gateway.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
export GATEWAY_API_KEY=…
cargo run -p aria-router -- serve \
--config config/examples/agent-gateway.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# AFM-D gateway(另需 DECISION_MODEL_URL 上的 aria-engine,默认 :8010 encoder)
export GATEWAY_API_KEY=…
cargo run -p aria-router -- serve \
--config config/examples/afm-d-gateway.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
```
[![][back-to-top]](#readme-top)
## 🧰 命令行
`aria-router` 二进制有四条子命令:
| 命令 | 主要参数 | 用途 |
|------|----------|------|
| `setup` | `--template` · `--admin-user` · `--admin-password` · `--base-url` · `--api-key-env` · `--api-key` · `--model-small` · `--model-mid` · `--model-large` · `--agent-endpoint` · `--agent-model` · `--agent-fallback` · `--afm-d-endpoint` · `--afm-d-fallback` · `--upgrade-url` | 写 `~/.ariacompute/router.yml` + `router-cli.yml` |
| `validate` | `--config` | 校验 YAML 策略(setup 后默认路径) |
| `serve` | `--config` · `--bind` · `--mgmt-bind` · `--no-dashboard` | 启动数据面 + 管理面 |
| `upgrade [version]` | — | 从 GitHub/Gitee Releases 拉取 CLI + `libaria-router_ffi`(需 `upgrade_url`) |
| `--version` | — | 打印版本 |
`setup` 为交互式:template → models(`base_url`、`api_key_env` 变量名、gateway API key、三档 `provider_model_id`;agent 另有 `endpoint`/`model`/`fallback`;afm-d 另有 System One `endpoint`/`fallback`)→ admin → `upgrade_url`。API key 写入 `router.yml` 的 `backend_refs.api_key`;serve 默认加载该文件(无明文 key 时回退 `$GATEWAY_API_KEY` / `--api-key-env`)。逻辑名 `ariacompute/ariamodel-{small,mid,large}` 固定;只换上游 provider_model_id。CI:`--template` + `--admin-user` + `--admin-password` 跳过 models 提示(用 gateway 默认)。
[![][back-to-top]](#readme-top)
## 🧪 用户视角 E2E 测试
[`scripts/run-user-e2e.sh`](scripts/run-user-e2e.sh) 是一套从**用户视角**验证 router 的黑盒测试,并把每条用例映射到 `task.md` 的任务编号。**默认**只探测一个**已运行**的实例(数据面 + 管理面),不会对该实例执行编译、安装、启动、停止或升级。两个可选模式提供隔离的额外覆盖:`--with-fixtures` 用临时 YAML fixture 启动短生命周期的**本地**实例(仅使用现成二进制,独立 `ARIA_COMPUTE_HOME` 与回环端口,退出时停止),`--with-local` 执行静态 / bench / UI 审计等离线命令。两个模式都不会触碰默认实例。
```bash
# 默认数据面 :8899 / 管理面 :8090
./scripts/run-user-e2e.sh
# 显式 URL + 管理员凭据(走登录/密钥旅程)
./scripts/run-user-e2e.sh \
--data-url http://127.0.0.1:8899 --mgmt-url http://127.0.0.1:8090 \
--admin-user admin --admin-password '…'
# 对生产实例安全:跳过所有写请求
./scripts/run-user-e2e.sh --read-only
# 任务编号覆盖(含隔离 fixture 场景)
./scripts/run-user-e2e.sh --list
./scripts/run-user-e2e.sh --self-test
./scripts/run-user-e2e.sh --with-local --with-fixtures
./scripts/run-user-e2e.sh --task-focus T90,T91,T92 --with-fixtures
```
要获得最广覆盖,用单个实例同时启用三种决策器,并打开所有可选模式。保留默认云端上游(`https://tokenhub.tencentmaas.com`)时必须设置 `GATEWAY_API_KEY`(YAML 中的 `api_key_env` 指向它);使用无需鉴权的本地 stub 时可省略。`GATEWAY_BASE_URL` 可覆盖三个云端上游,`LOCAL_BASE_URL` / `LOCAL_MODEL_ID` 配置 `local/general` bypass 模型(默认 `127.0.0.1:8011`):
```bash
# 终端 1 —— 自行启动实例(套件本身不会编译/启动/停止它)
# 默认云端上游必需;使用无需鉴权的本地 stub 时可省略。
export GATEWAY_API_KEY=…
# 可选:把云端 provider 与 bypass 模型都指向本地 OpenAI 兼容 stub。
# export GATEWAY_BASE_URL=http://127.0.0.1:8011
# export LOCAL_BASE_URL=http://127.0.0.1:8011
cargo run -p aria-router -- serve \
--config config/examples/triad-gateway.yaml \
--bind 127.0.0.1:8899 --mgmt-bind 127.0.0.1:8090
# 终端 2 —— 全量覆盖运行
./scripts/run-user-e2e.sh \
--data-url http://127.0.0.1:8899 --mgmt-url http://127.0.0.1:8090 \
--admin-user admin --admin-password '…' \
--with-local --with-fixtures --allow-mutating \
--out-dir out/full
```
[![][back-to-top]](#readme-top)
## 🔧 配置
路由策略为 YAML v0.3(`--config`)。密钥用 `${VAR}` / `${VAR:-default}` 展开。`entrypoint.router` 必须等于 `recipe.router`。Semantic recipe 禁止 `agent:` / `afm-d:`;agent recipe 禁止 `signals` / `decisions` / `afm-d:`;afm-d recipe 禁止 `routing` / `agent:`。未知顶层键 → `validate` 失败。未实现的 YAML 能力返回 `Unsupported`(禁止静默空实现)。**无**顶层 `extensions`,不做 pi / deepseek-harness 子进程。
| 块 | 含义 |
|----|------|
| `listeners` | 数据面绑定(`address` + `port`;`--bind` 默认值) |
| `providers` | `defaults.default_model` + 具名模型 / `backend_refs` |
| `entrypoints` | 虚拟模型名 → `router: semantic\|agent\|afm-d` + `recipe` |
| `recipes` | Semantic 的 `routing.*`、agent 的 `agent.*`、或 afm-d 的 `afm-d.*`(System One endpoint / 可选 `track` / timeout / fallback) |
| `global` | 鉴权 / keys / users 路径(可选) |
### 示例策略
| 文件 | 用途 |
|------|------|
| [`semantic-tiny.yaml`](config/examples/semantic-tiny.yaml) | 日常 semantic 黄金路径 — `ariacompute/semantic-auto`;keyword 启发式 → `local/general`;可选 retention;无 ML 权重即可 `validate` + `serve` |
| [`semantic.yaml`](config/examples/semantic.yaml) | Semantic catalog — 黄金 `ariacompute/semantic-auto` + `ariacompute/semantic-catalog`(learned signal + 未实现 algorithm → chat `Unsupported`,除非 `--features ml`);演示优先 tiny |
| [`semantic-gateway.yaml`](config/examples/semantic-gateway.yaml) | Semantic + Aria Gateway — 三档 `ariamodel-{small,mid,large}`(`tier` + 可换 `provider_model_id`);**setup `--template semantic` 默认**;需 `GATEWAY_API_KEY` |
| [`semantic-stateful.yaml`](config/examples/semantic-stateful.yaml) | Retention sticky + `stores.memory` / `semantic_cache` / replay |
| [`agent-tiny.yaml`](config/examples/agent-tiny.yaml) | 日常 agent 黄金路径 — `ariacompute/agent-auto`;进程内 builtin tool-loop(无 `endpoint` → first-eligible);演示 / CI |
| [`agent.yaml`](config/examples/agent.yaml) | Agent catalog — 对称 `semantic.yaml`:黄金 `ariacompute/agent-auto` + `ariacompute/agent-catalog`(故意 `Unsupported`);演示优先 tiny / gateway |
| [`agent-gateway.yaml`](config/examples/agent-gateway.yaml) | Agent + Aria Gateway — 同上三档(`tier` + 可换上游);**setup `--template agent` 默认**;需 `GATEWAY_API_KEY` |
| [`afm-d-tiny.yaml`](config/examples/afm-d-tiny.yaml) | 日常 AFM-D 黄金路径 — `ariacompute/afm-d-auto`;无 `endpoint` → first-eligible;演示 / CI |
| [`afm-d.yaml`](config/examples/afm-d.yaml) | AFM-D catalog — 对称 `agent.yaml`:黄金 `ariacompute/afm-d-auto` + `ariacompute/afm-d-catalog`;演示优先 tiny / gateway |
| [`afm-d-gateway.yaml`](config/examples/afm-d-gateway.yaml) | AFM-D + Aria Gateway — System One Choice 三档;**setup `--template afm-d`**;需 `GATEWAY_API_KEY` + `DECISION_MODEL_URL` 上的 aria-engine |
校验任一示例:
```bash
cargo run -p aria-router -- validate --config config/examples/semantic-tiny.yaml
cargo run -p aria-router -- validate --config config/examples/semantic.yaml
cargo run -p aria-router -- validate --config config/examples/semantic-gateway.yaml
cargo run -p aria-router -- validate --config config/examples/semantic-stateful.yaml
cargo run -p aria-router -- validate --config config/examples/agent-tiny.yaml
cargo run -p aria-router -- validate --config config/examples/agent.yaml
cargo run -p aria-router -- validate --config config/examples/agent-gateway.yaml
cargo run -p aria-router -- validate --config config/examples/afm-d-tiny.yaml
cargo run -p aria-router -- validate --config config/examples/afm-d.yaml
cargo run -p aria-router -- validate --config config/examples/afm-d-gateway.yaml
```
[![][back-to-top]](#readme-top)
## 🛳 Release 资产
每次 GitHub Release,[`.github/workflows/release.yml`](.github/workflows/release.yml) 上传与 `aria-router` CLI 并列的平台包:
| 资产 | 内容 |
|------|------|
| `aria-router__linux_x86_64.tar.gz` | `aria-router` |
| `aria-router__linux_arm64.tar.gz` | `aria-router` |
| `aria-router__macos.tar.gz` | `aria-router` |
| `aria-router__windows_x86_64.zip` | `aria-router.exe` |
| `libaria-router_ffi__.tar.gz` | `libaria-router_ffi.so` / `.dylib` / `aria-router_ffi.dll` |
```bash
# 示例:Linux x86_64
tar -xzf aria-router_0.1.0_linux_x86_64.tar.gz
chmod +x aria-router
./aria-router --version
# 或:aria-router upgrade(setup 后;替换 CLI 并把 FFI 装到 ~/.ariacompute/lib/)
```
创建 **GitHub Release** — 语言包发布为 **fail-pass**,不阻塞 CLI / FFI 资产。版本号 = tag 去掉前缀 `v`。
[![][back-to-top]](#readme-top)
## 🔌 OpenAI API
假设数据面 `http://127.0.0.1:8899`、管理面 `http://127.0.0.1:8090`:
```bash
# 列出入口名 + provider 名
curl -s http://127.0.0.1:8899/v1/models | jq .
# Semantic 入口(semantic-tiny.yaml 的 keyword recipe)
curl -s http://127.0.0.1:8899/v1/chat/completions \
-H 'content-type: application/json' \
-d '{
"model": "ariacompute/semantic-auto",
"messages":[{"role":"user","content":"please explain rust"}],
"max_tokens": 32
}' | jq .
# Agent 入口(agent-tiny.yaml)
curl -s http://127.0.0.1:8899/v1/chat/completions \
-H 'content-type: application/json' \
-d '{
"model": "ariacompute/agent-auto",
"messages":[{"role":"user","content":"Hello"}],
"max_tokens": 32
}' | jq .
# AFM-D 入口(afm-d-tiny.yaml;无 endpoint → first-eligible)
curl -s http://127.0.0.1:8899/v1/chat/completions \
-H 'content-type: application/json' \
-d '{
"model": "ariacompute/afm-d-auto",
"messages":[{"role":"user","content":"Hello"}],
"max_tokens": 32
}' | jq .
# Chat(SSE)
curl -sN http://127.0.0.1:8899/v1/chat/completions \
-H 'content-type: application/json' \
-d '{
"model": "ariacompute/semantic-auto",
"messages":[{"role":"user","content":"please explain rust"}],
"stream": true
}'
# 实名 bypass
curl -s http://127.0.0.1:8899/v1/chat/completions \
-H 'content-type: application/json' \
-d '{
"model": "local/general",
"messages":[{"role":"user","content":"Hello"}]
}' | jq .
# 路由响应头(layer = semantic | agent | afm-d | bypass)
curl -sD - -o /dev/null http://127.0.0.1:8899/v1/chat/completions \
-H 'content-type: application/json' \
-d '{"model":"ariacompute/semantic-auto","messages":[{"role":"user","content":"please explain rust"}]}' \
| grep -i x-aria-router
# 管理面
curl -s http://127.0.0.1:8090/health | jq .
curl -s -X POST http://127.0.0.1:8090/v1/router/validate | jq .
curl -s 'http://127.0.0.1:8090/v1/router/replay?n=20' | jq .
curl -s http://127.0.0.1:8090/v1/router/overview | jq .
curl -s http://127.0.0.1:8090/v1/router/providers | jq .
curl -s http://127.0.0.1:8090/v1/router/topology | jq .
curl -s http://127.0.0.1:8090/v1/router/config | jq .
```
响应头:`x-aria-router-layer`、`x-aria-router-decision`、`x-aria-router-model`,以及 `x-aria-router-redecision`(显式跨请求边界:`new-user-message` / `context-compaction` / `tool-continuation` / `same-turn` / `unknown`)。若设置了 retention emit,还会有:`x-aria-router-retention-keep-current-model`、`x-aria-router-retention-ttl-turns`、`x-aria-router-retention-drop`、`x-aria-router-retention-prefer-prefix`。
[![][back-to-top]](#readme-top)
## 🔐 API 密钥与成本
1. Dashboard → **API 密钥** → 生成(`sk-aria_…` 只显示一次),或 `POST /v1/router/keys`。
2. setup 默认写入 `global.require_api_key: true`(chat 与 `PUT /v1/router/providers` 须 Bearer)。可在 YAML 改为 `false` 开放数据面。
3. 客户端与 `aria-engine` 使用 Bearer `sk-aria_…` 或 `sk-bf-…`(`router_api_key` / `--router-api-key`)。
4. **Cost** 页 / `GET /v1/router/cost` 展示六因子、`by_local_user` / `by_serve_user` / `by_key`(YAML `pricing`)。
5. OAuth:Dashboard → Account 配置 `sk-bf-…`(关联 / 粘贴 / 展示)。
```bash
curl -s http://127.0.0.1:8899/v1/chat/completions \
-H 'content-type: application/json' \
-H 'Authorization: Bearer sk-aria_…' \
-d '{"model":"local/general","messages":[{"role":"user","content":"Hi"}],"max_tokens":16}'
```
硬约束(location / auth / modality / tools)在排名 **之前** 剪枝。Compute 只进排名。无合格路径 → fail closed。
[![][back-to-top]](#readme-top)
## 🌐 Dashboard
管理面在存在 `dashboard/dist` 时托管 Vite React SPA,地址为 `http://{mgmt}/`。完成 `aria-router setup` 后先 **Login**(本地用户名/密码):
| 段落标题(与 CLI 同名) | 页面 |
|------------------------|------|
| **Local (router Dashboard)** | Login / Register · API 密钥(`sk-aria_…`)· Users(admin) |
| **OAuth (Aria Compute)** | Account — 已关联用户、`sk-bf-…` 展示/粘贴/OAuth |
| **Routing** | Config · Topology · Providers · Replay · **Playground**(多轮聊天、SSE、路由 Header;不做 MCP/Claw)· Cost · Cost by Competence · **Model Performance** · **Trust Tiers** |
Cost 分 **Local users** 与 **OAuth users**。构建:
```bash
npm --prefix dashboard ci
npm --prefix dashboard run build
cargo run -p aria-router -- serve \
--config config/examples/semantic-tiny.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# 打开 http://127.0.0.1:8090/
```
`--no-dashboard` 只提供 JSON API(仍可用 curl 管理 keys/cost)。无 Grafana、ML wizard;默认绑 `127.0.0.1`。
**设计契约与 UI 回归审计**:Dashboard 为单色 / 扁平 / 直角 / AA / ≥12px,动效仅 transform/opacity,使用本地字体栈(无 web-font CDN)。设计契约见 [`PRODUCT.md`](PRODUCT.md)。UI 护栏测试:`npm --prefix dashboard run test`。
### 本地渲染检查(重跑)
`scripts/audit-ui.sh` 以五种模式运行 impeccable 风格审计:`--static`(CSS + `index.html` 主规则)、`--dom`(15 路由 × 深浅双主题 jsdom 渲染)、`--url`(实时页面 + Chrome + impeccable 二进制)、`--browser`(同 `--url`,但缺浏览器即硬失败)、`--list-rules`(打印规则 id + 页面矩阵)。无法运行的模式记为 `skipped`,绝不伪造成功。
```bash
npm --prefix dashboard ci
npm --prefix dashboard run build
npm --prefix dashboard run test
./scripts/audit-ui.sh --list-rules
./scripts/audit-ui.sh --static
./scripts/audit-ui.sh --dom
# 在有 Chrome 与 impeccable 二进制的机器上:
IMPECCABLE_BROWSER=/usr/bin/google-chrome ./scripts/audit-ui.sh --url
```
[![][back-to-top]](#readme-top)
## 🧭 三种决策器
每个入口名精确映射到一种决策器;一次请求不会串跑多种。硬约束(location / auth / modality / tools)在排名 **之前** 剪枝,任何通过排名的合格路径都会产出类型化的 `RouteDecision`。无合格路径时,网关失败关闭。
| 决策器 | Recipe 块 | 行为 |
|--------|-----------|------|
| `semantic` | `routing.*` | 信号 → 投影 → 布尔决策 → algorithm / looper。keyword 启发式无需 ML 权重;learned signal + 未实现 algorithm 除非 `--features ml` 否则返回 `Unsupported`。 |
| `agent` | `agent.*` | 进程内 `BuiltinAgent` 工具循环,固定工具 + 有限 `max_turns`;`submit_route` 产出类型化 `RouteDecision`。无 `endpoint` → first-eligible 后端。 |
| `afm-d` | `afm-d.*` | 合格候选 → System One Choice → `POST {endpoint}/v1/systemone` → 类型化 `RouteDecision`。实名模型 bypass recipe,直打该后端。低分保护 + 失败关闭回落保证决策安全。 |
数据面暴露 `x-aria-router-layer`(`semantic` | `agent` | `afm-d` | `bypass`)、`x-aria-router-decision`、`x-aria-router-model` 与 `x-aria-router-redecision`,使每个重判边界都可观测。完整决策契约见 [AGENTS.md](./AGENTS.md)。
[![][back-to-top]](#readme-top)
## 🔗 ContextHold
同一用户轮次内的 tool / assistant 往返保持已选模型;只在自然边界(用户新消息或上下文压缩)再重新决策。混用便宜模型与主力模型时,KV Cache / 前缀复用仍然划算。
- semantic / agent / afm-d 配方上,**`prefix_reuse` 缺省为 `true`**(显式 `false` 关闭)。同一**显式 session**、同一**用户消息条数**内,复用冻结的 `RouteDecision`。
- **用户新消息**(turn 计数上升)→ 全新决策。
- **压缩**(消息列表变短,或 system 提示词 / `tools` 指纹变化)→ 清除该 session 的 DecisionCache 与 sticky,再重新路由。
- **Sticky TTL**(`stores.memory`)**仅在用户消息条数上升时**递减——不会在每一次工具调用的 HTTP 请求上扣减。同一模型再次 pin **不会**刷新 TTL;换模型才重新记满 TTL。
- **afm-d / agent sticky**:当 `global.stores.memory.enabled` 与 `prefix_reuse` 均开启时,路由成功后 pin 所选模型;后续用户轮上若仍通过硬约束则短路(跳过 System One / builtin agent),reason 为 `sticky:…`。semantic 仍由 decision 的 `emits.retention.keep_current_model` 在 algorithm 选型之后驱动 sticky。
- **仅显式 session**:ContextHold 只认 `x-aria-session` 或 body `session`。自动生成的 `req_*` id 仅用于成本记账——不冻结决策、不写 sticky。
请发送显式 session,并带上**累积的对话历史**(或至少让用户消息条数递增),以驱动 turn 键与 TTL。只发最后一条消息的客户端会一直保持 `turn_of == 1`,sticky TTL 不会前进。未提供显式 session 时,路由粘性等价于「每次请求都重新决策」。
### 行为一览
```text
带显式 session 的请求
│
▼
已压缩? ──是──► 清除 DecisionCache + sticky ──► 重新路由
│ 否
▼
若用户轮计数上升则 tick sticky TTL
│
▼
同 (session, turn) 命中缓存? ──是──► 复用冻结模型
│ 否
▼
afm-d/agent sticky 仍 eligible? ──是──► 短路
│ 否
▼
跑 semantic / agent / afm-d → pin(若适用)
```
### 配置示意
```yaml
recipes:
- name: mom
router: semantic
routing:
strategy: priority
prefix_reuse: true # 省略时默认 true
# …
- name: agent-default
router: agent
agent:
prefix_reuse: true
# …
- name: afm-d-default
router: afm-d
afm-d:
prefix_reuse: true
# …
global:
stores:
memory:
enabled: true
max_sessions: 4096
default_ttl_turns: 2
```
semantic 跨轮 sticky 仍需要 decision emit,例如:
```yaml
emits:
- kind: retention
retention:
ttl_turns: 2
keep_current_model: true
```
参见 [`semantic-stateful.yaml`](config/examples/semantic-stateful.yaml) 与各 `*-gateway.yaml` 示例(已写 `prefix_reuse: true`)。
[![][back-to-top]](#readme-top)
## 🧩 SDK 绑定
C ABI(`ariacompute-router-ffi` / `libaria-router_ffi`)加 `bindings/` 薄封装。**不要**与 `libaria_ffi` / `ariacompute-engine` 混用。
| Binding | 路径 | 包 |
|---------|------|-----|
| Rust | `bindings/rust` | crates.io `ariacompute-router`(原生;不 dlopen) |
| Python | `bindings/python` | PyPI `ariacompute-router`(`aria_router`) |
| Go | `bindings/go` | `github.com/ariacompute/router/bindings/go`(cgo;build tag `aria_router_ffi`) |
| TypeScript | `bindings/typescript` | npm `@ariacompute/router-ts` |
| React Native | `bindings/react-native` | npm `@ariacompute/router-rn` |
| Flutter | `bindings/flutter` | pub.dev `aria_router` |
| Swift | `bindings/swift` | CocoaPods / SPM `AriaComputeRouter` |
| Kotlin | `bindings/kotlin` | Maven `com.ariacompute:router` |
C 头文件:[`ffi/include/aria_router.h`](ffi/include/aria_router.h) — `aria_router_init`(进程内加载 YAML;`NULL`/空串 → `~/.ariacompute/router.yml`)、`aria_router_connect`(HTTP 连已运行的 `serve`)、`aria_router_setup`(仅内存 `base_url` / `token`;`NULL` 保持不变、空串清除)、`aria_router_complete` / `_stream`、`aria_router_models`、`aria_router_last_route`、`aria_router_destroy`、`aria_router_last_error`。
`aria_router_complete_stream` 当前是**整块单次回调**(一次携带完整响应体),不是逐 SSE chunk。`aria_router_last_route` 返回 `RouteDecision` JSON 及 `headers` 扩展对象(白名单 `x-aria-router-*`,含 `x-aria-router-redecision`;不暴露 `authorization` / cookie)。`connect` 句柄的 `models` 代理 `GET /v1/models`,`last_route` 暴露最近一次 `complete` 捕获的响应头;失败显式报错,不返回空结果。
动态库顺序:`ARIA_ROUTER_FFI_LIB` → 包内捆绑路径 → `~/.ariacompute/lib/`。实例 `setup` 仅内存 `base_url` / `token`,**禁止**写 `router.yml`。无路径的 `init` 使用 `aria-router setup` 写入的默认 `router.yml`(gateway 模板 serve 前需 `export GATEWAY_API_KEY`)。离线 binding CI 使用 [`bindings/testdata/fast-response.yaml`](bindings/testdata/fast-response.yaml)(固定回复,无需 upstream)。`init` 进程内跑 semantic 与轻量 builtin agent(固定工具 + `max_turns`);无 subprocess harness。Rust 原生绑定不 dlopen,Go 走 cgo `-L… -laria_router_ffi`,二者均不使用该查找顺序。
```bash
cargo test -p ariacompute-router-ffi -p ariacompute-router
./scripts/run-binding-tests.sh # 宿主矩阵(Rust / Python / Go / TS / RN / Flutter / Kotlin;Swift 需工具链可用时手工 swift test)
```
C ABI 变更必须同步 [`bindings/testdata/cases.json`](bindings/testdata/cases.json) 与宿主测。
### 示例
进程内 `init` + `complete` **不需要** Dashboard API key。`connect` 到开启 `require_api_key` 的已运行 `serve` 仍需 `setup(token=…)`。实例 `setup` 仅写内存。
八语言 SDK 统一暴露 `setup` / `setup_status`(或 `setupStatus` / `SetupStatus`)/ `setup_clear`、`complete`、`complete_stream`(或 `completeStream` / `CompleteStream`)、`models`、`last_route`(或 `lastRoute` / `LastRoute`)、`close` + `destroy`。`complete_stream` 交付一次完整响应体,`last_route()["headers"]` 携带白名单决策响应头。例如:
```python
r.setup(token="sk-...")
print(r.setup_status())
chunks = []
out = r.complete_stream([{"role": "user", "content": "hi"}], callback=chunks.append)
print(out, chunks[0][:80])
print(r.last_route()["headers"])
```
**Python**(需 `ARIA_ROUTER_FFI_LIB` 或捆绑/缓存的 `libaria-router_ffi`):
```bash
pip install ariacompute-router
```
```python
from aria_router import Router
# aria-router setup 后:加载 ~/.ariacompute/router.yml
r = Router().init()
print(r.models())
print(r.complete(
[{"role": "user", "content": "hi"}],
{"model": "ariacompute/semantic-auto"},
))
print(r.last_route())
r.close()
# 或连接已运行的 serve(开启 require_api_key 时需 token):
r = Router().connect("http://127.0.0.1:8899")
r.setup(base_url="http://127.0.0.1:8899", token="")
```
**Rust**(`ariacompute-router` — 原生 API,不 dlopen `libaria-router_ffi`):
```bash
cargo add ariacompute-router
```
```rust
use ariacompute_router::{Router, SetupUpdates};
use serde_json::json;
fn main() -> Result<(), Box> {
let mut r = Router::new();
// 空路径 → ~/.ariacompute/router.yml(需先 aria-router setup)
r.init("")?;
println!("{}", r.models()?);
println!(
"{}",
r.complete(
json!([{"role": "user", "content": "hi"}]),
json!({"model": "ariacompute/semantic-auto"}),
)?
);
println!("{}", r.last_route());
// 或连接已运行的 serve(开启 require_api_key 时需 token):
r.connect("http://127.0.0.1:8899");
r.setup(&SetupUpdates {
base_url: Some("http://127.0.0.1:8899".into()),
token: Some("".into()),
})?;
Ok(())
}
```
**Go**(cgo + `libaria-router_ffi`;build tag `aria_router_ffi`):
```bash
go get github.com/ariacompute/router/bindings/go
```
```go
package main
import (
"fmt"
"log"
aria "github.com/ariacompute/router/bindings/go"
)
func main() {
r := aria.NewRouter()
// 空路径 → ~/.ariacompute/router.yml(需先 aria-router setup)
if err := r.Init(""); err != nil {
log.Fatal(err)
}
defer r.Close()
models, err := r.Models()
if err != nil {
log.Fatal(err)
}
fmt.Println(models)
out, err := r.Complete(
[]map[string]string{{"role": "user", "content": "hi"}},
map[string]string{"model": "ariacompute/semantic-auto"},
)
if err != nil {
log.Fatal(err)
}
fmt.Println(out)
route, err := r.LastRoute()
if err != nil {
log.Fatal(err)
}
fmt.Println(route)
// 或连接已运行的 serve(开启 require_api_key 时需 token):
_ = r.Connect("http://127.0.0.1:8899")
r.Setup("http://127.0.0.1:8899", "")
}
```
**TypeScript**(`@ariacompute/router-ts`):
```bash
npm install @ariacompute/router-ts
```
```ts
import { Router } from "@ariacompute/router-ts";
// aria-router setup 后:加载 ~/.ariacompute/router.yml
const r = new Router().init();
console.log(r.models());
console.log(
r.complete([{ role: "user", content: "hi" }], {
model: "ariacompute/semantic-auto",
}),
);
console.log(r.lastRoute());
r.close();
// 或连接已运行的 serve(开启 require_api_key 时需 token):
const c = new Router().connect("http://127.0.0.1:8899");
c.setup({ base_url: "http://127.0.0.1:8899", token: "" });
```
**React Native**(`@ariacompute/router-rn`):
```bash
npm install @ariacompute/router-rn
```
```js
const { Router } = require("@ariacompute/router-rn");
const r = new Router().init();
console.log(r.models());
console.log(
r.complete([{ role: "user", content: "hi" }], {
model: "ariacompute/semantic-auto",
}),
);
console.log(r.lastRoute());
r.close();
// 或连接已运行的 serve(开启 require_api_key 时需 token):
const c = new Router().connect("http://127.0.0.1:8899");
c.setup({ base_url: "http://127.0.0.1:8899", token: "" });
```
**Flutter**(`ariacompute_router`):
```bash
flutter pub add ariacompute_router
```
```dart
import 'package:ariacompute_router/ariacompute_router.dart';
void main() {
// aria-router setup 后:加载 ~/.ariacompute/router.yml
final r = AriaRouter()..init();
print(r.models());
print(r.complete(
[
{'role': 'user', 'content': 'hi'}
],
{'model': 'ariacompute/semantic-auto'},
));
print(r.lastRoute());
r.close();
// 或连接已运行的 serve(开启 require_api_key 时需 token):
final c = AriaRouter()..connect('http://127.0.0.1:8899');
c.setup(baseUrl: 'http://127.0.0.1:8899', token: '');
}
```
**Swift**(dlopen `libaria-router_ffi`;SPM / CocoaPods `AriaComputeRouter`):
```bash
pod 'AriaComputeRouter'
```
```swift
import AriaComputeRouter
import Foundation
do {
// load(nil) → ~/.ariacompute/router.yml(Swift 关键字;对应 aria_router_init)
let r = try Router().load(nil)
print(try r.models())
print(try r.complete(
messages: [["role": "user", "content": "hi"]],
options: ["model": "ariacompute/semantic-auto"]
))
print(try r.lastRoute())
r.close()
// 或连接已运行的 serve(开启 require_api_key 时需 token):
let c = try Router().connect("http://127.0.0.1:8899")
c.setup(baseUrl: "http://127.0.0.1:8899", token: "")
} catch {
fputs("\(error)\n", stderr)
}
```
**Kotlin**(JNA + `libaria-router_ffi`;Maven `com.ariacompute:router`):
```kotlin
implementation("com.ariacompute:router:+")
```
```kotlin
import com.ariacompute.router.Router
fun main() {
// aria-router setup 后:加载 ~/.ariacompute/router.yml
Router().init().use { r ->
println(r.models())
println(
r.complete(
listOf(mapOf("role" to "user", "content" to "hi")),
mapOf("model" to "ariacompute/semantic-auto"),
),
)
println(r.lastRoute())
}
// 或连接已运行的 serve(开启 require_api_key 时需 token):
Router().connect("http://127.0.0.1:8899").use { c ->
c.setup(baseUrl = "http://127.0.0.1:8899", token = "")
}
}
```
[![][back-to-top]](#readme-top)
## 📊 Bench
[`bench/`](bench/) 下 Python ≥3.10 **report-only** 评测(标准库;对齐 `engine/bench`)。**不**拉起 `aria-router`、[vLLM Semantic Router](https://github.com/vllm-project/semantic-router) 或 provider——需自行 serve 后把 `--router` / `--pool` 指到 OpenAI 兼容 URL。报告恒含 `ci_fail: false`。
| 子命令 | 用途 |
|--------|------|
| `routing` | ADR-040 矩阵 + always / oracle / domain / knn / **多 live router** ladder |
| `routing-accuracy` | **仅 Router API** 的 label 准确率(选路 vs `expected_model`;无需 `--pool`) |
| `build-routing-eval` | 生成 `routing_gateway_eval.json`(≥200 模板;可选并入 MMLU/DRACO) |
| `research` | DRACO 形 JSONL + rubric,对比 always_X 与各 router |
| `compare` | MCQ **accuracy + E2E latency + tokens**(对齐 VSR 公开 bench 叙事) |
| `policy-consistency` | 离线重算 4 种确定性策略(cheapest / strongest / expected-label / by-config-expected),输入为已保存的决策记录——无需 live router;report-only,恒含 `ci_fail: false` |
| `list-corpus` | 打印内置 tiny 语料 |
| `download-draco` / `download-mmlu` | 可选 HF 拉取(失败 skip) |
`--router` 可重复 `NAME=URL`(裸 URL → `aria_router`)。配合 `--entrypoint` / `--pick-header` / `--pick-map`。
并排对标建议端口:**aria-router `:8899`**、**vLLM SR `:8890`**、共享 backend `:8000` / `:9001+`。
routing / research 的质量模式(`--quality`):`label`、`overlap`、`judge`(需 `--judge-url`)。
两条常用 track(同一套 `routing` / `compare` CLI,测的对象不同):
| Track | 实际打谁 | 主要 flags | 测什么 |
|-------|----------|------------|--------|
| 上游仅 pool | **直接** chat TokenHub(与 Track B 同一三档) | `--pool` + `--model-id` + `--api-key` + `--prices`;**无** `--router` | `glm-5.3-flash` / `glm-5.3` / `deepseek-v4-pro` 后端质量 / 成本阶梯(不启本地 router) |
| 多 router ladder | 经 **live router** chat,再(可选)共享 pool | `--router` + `--entrypoint` / `--pick-header`,外加 `--pool` 做 always/oracle 基线 | 选路质量(aria-router vs vLLM SR 等)在 ADR-040 / MCQ 上的表现 |
可选中间步:本地 serve `semantic-gateway` / `agent-gateway` / `afm-d-gateway`,在仅 pool track 上追加 `--router aria_router=…`(及 `--pick-map`),使选路经 router 再进同一上游 pool。
下方片段:**A** = 上游仅 pool;**B** = semantic-auto vs vLLM SR;**C** = agent-auto vs vLLM SR;**D** = afm-d-auto vs vLLM SR;**E** = routing-accuracy(三决策器共用 eval 语料,仅 API)。
```bash
python -m unittest discover -s bench/tests -t .
# --- Track A:上游仅 pool(可不启本地 router)---
# 与 Track B semantic routing 同一 TokenHub 三档;corpus expected_model = 上游名。
export GATEWAY_BASE=https://tokenhub.tencentmaas.com
export GATEWAY_API_KEY=your-api-key
# ADR-040 ladder:直接 chat pool(always_* / oracle / domain / knn;无 --router)。
# --quality label 按 corpus expected_model 打分;写出 out/gateway_routing.{json,md}。
python -m bench routing \
--pool small=$GATEWAY_BASE --pool mid=$GATEWAY_BASE --pool large=$GATEWAY_BASE \
--model-id small=glm-5.3-flash \
--model-id mid=glm-5.3 \
--model-id large=deepseek-v4-pro \
--api-key small=$GATEWAY_API_KEY --api-key mid=$GATEWAY_API_KEY --api-key large=$GATEWAY_API_KEY \
--prices bench/prices/ariamodel.json \
--quality label \
--corpus bench/corpus/routing_gateway.json \
--report ./out/gateway_routing.json
# MCQ accuracy + E2E latency + tokens:同一 pool 上跑 mmlu_tiny(仅 always_*)。
python -m bench compare \
--pool small=$GATEWAY_BASE --pool mid=$GATEWAY_BASE --pool large=$GATEWAY_BASE \
--model-id small=glm-5.3-flash \
--model-id mid=glm-5.3 \
--model-id large=deepseek-v4-pro \
--api-key small=$GATEWAY_API_KEY --api-key mid=$GATEWAY_API_KEY --api-key large=$GATEWAY_API_KEY \
--prices bench/prices/ariamodel.json \
--corpus bench/corpus/mmlu_tiny.jsonl \
--report ./out/gateway_compare.json
```
```bash
# --- Track B: semantic ladder (aria-router vs vLLM Semantic Router) ---
# 需本地 aria-router(:8899)、vLLM SR(:8890)以及 pool 后端(:9001+ / :8000)。
# 自行启动 router(见 bench/vllm-sr/README.md)。下方 aria-router 配置三选一
#(semantic XOR agent XOR afm-d,同一 --bind)。--router = live 选路质量;--pool = always/oracle 基线。
export GATEWAY_BASE=https://tokenhub.tencentmaas.com
export GATEWAY_API_KEY=your-api-key
# aria-router 数据面 :8899 — semantic keyword 路由 → Gateway ariamodel-{small,mid,large}。
# Bench 使用 --entrypoint ariacompute/semantic-auto。
aria-router serve \
--config config/examples/semantic-gateway.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# 上一段的替代:builtin agent 路由(同端口,勿同时跑)。
# bench 改用 --entrypoint ariacompute/agent-auto(替代 semantic-auto)。
# aria-router serve \
# --config config/examples/agent-gateway.yaml \
# --bind 127.0.0.1:8899 \
# --mgmt-bind 127.0.0.1:8090
# vLLM Semantic Router 数据面 :8890 — **keyword 基线**(冻结;不对齐 aria advanced)。
# 建议先 validate:vllm-sr validate --config bench/vllm-sr/config-gateway.yaml
# Bench 使用 --entrypoint vllm_sr=auto。
vllm-sr validate --config bench/vllm-sr/config-gateway.yaml
vllm-sr serve --config bench/vllm-sr/config-gateway.yaml
# vllm-sr status
# vllm-sr stop
# Live-router ADR-040 ladder(非对称 §6.6):aria = advanced mom;vllm_sr = keyword baseline。
# 优先 hard corpus + --prices,用成本与陷阱事实题拉开差距。
python -m bench routing \
--router aria_router=http://127.0.0.1:8899 \
--router vllm_sr=http://127.0.0.1:8890 \
--entrypoint aria_router=ariacompute/semantic-auto \
--entrypoint vllm_sr=auto \
--pick-header aria_router=x-aria-router-model \
--pick-header vllm_sr=x-vsr-selected-model \
--pick-map ariacompute/ariamodel-small=glm-5.3-flash \
--pick-map ariacompute/ariamodel-mid=glm-5.3 \
--pick-map ariacompute/ariamodel-large=deepseek-v4-pro \
--pool small=https://tokenhub.tencentmaas.com \
--pool mid=https://tokenhub.tencentmaas.com \
--pool large=https://tokenhub.tencentmaas.com \
--model-id small=glm-5.3-flash \
--model-id mid=glm-5.3 \
--model-id large=deepseek-v4-pro \
--api-key small=$GATEWAY_API_KEY --api-key mid=$GATEWAY_API_KEY --api-key large=$GATEWAY_API_KEY \
--prices bench/prices/ariamodel.json \
--quality label \
--corpus bench/corpus/routing_gateway_hard.json \
--timeout 300 \
--report ./out/vs_vsr_routing.json
# MCQ compare:经各 live router(accuracy / latency / tokens / cost)对比 Gateway 三档 always_*。
# 公平延迟:先重启 aria 以清空进程内 response-cache(否则 p50≈ms 多为缓存命中)。
# 重建/重启后,共享 reqwest Client(§6.7)在 compare 多题间复用到 TokenHub 的 TLS。
# (在 aria serve 终端)Ctrl-C,再带 GATEWAY_API_KEY 重新执行同一条 `aria-router serve …`。
python -m bench compare \
--router aria_router=http://127.0.0.1:8899 \
--router vllm_sr=http://127.0.0.1:8890 \
--entrypoint aria_router=ariacompute/semantic-auto \
--entrypoint vllm_sr=auto \
--pick-header aria_router=x-aria-router-model \
--pick-header vllm_sr=x-vsr-selected-model \
--pick-map ariacompute/ariamodel-small=glm-5.3-flash \
--pick-map ariacompute/ariamodel-mid=glm-5.3 \
--pick-map ariacompute/ariamodel-large=deepseek-v4-pro \
--pool small=https://tokenhub.tencentmaas.com \
--pool mid=https://tokenhub.tencentmaas.com \
--pool large=https://tokenhub.tencentmaas.com \
--model-id small=glm-5.3-flash \
--model-id mid=glm-5.3 \
--model-id large=deepseek-v4-pro \
--api-key small=$GATEWAY_API_KEY --api-key mid=$GATEWAY_API_KEY --api-key large=$GATEWAY_API_KEY \
--prices bench/prices/ariamodel.json \
--corpus bench/corpus/mmlu_tiny.jsonl \
--timeout 300 \
--report ./out/vs_vsr_compare.json
```
```bash
# --- Track C: agent ladder (aria-router vs vLLM Semantic Router) ---
export GATEWAY_BASE=https://tokenhub.tencentmaas.com
export GATEWAY_API_KEY=your-api-key
# 若 semantic-gateway 在跑,先停掉,再:
aria-router serve \
--config config/examples/agent-gateway.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# vllm-sr keyword 基线仍在 :8890(config-gateway.yaml)。
python -m bench routing \
--router aria_router=http://127.0.0.1:8899 \
--router vllm_sr=http://127.0.0.1:8890 \
--entrypoint aria_router=ariacompute/agent-auto \
--entrypoint vllm_sr=auto \
--pick-header aria_router=x-aria-router-model \
--pick-header vllm_sr=x-vsr-selected-model \
--pick-map ariacompute/ariamodel-small=glm-5.3-flash \
--pick-map ariacompute/ariamodel-mid=glm-5.3 \
--pick-map ariacompute/ariamodel-large=deepseek-v4-pro \
--pool small=https://tokenhub.tencentmaas.com \
--pool mid=https://tokenhub.tencentmaas.com \
--pool large=https://tokenhub.tencentmaas.com \
--model-id small=glm-5.3-flash \
--model-id mid=glm-5.3 \
--model-id large=deepseek-v4-pro \
--api-key small=$GATEWAY_API_KEY --api-key mid=$GATEWAY_API_KEY --api-key large=$GATEWAY_API_KEY \
--prices bench/prices/ariamodel.json \
--quality label \
--corpus bench/corpus/routing_gateway_hard.json \
--timeout 300 \
--report ./out/agent_vs_vsr_routing.json
python -m bench compare \
--router aria_router=http://127.0.0.1:8899 \
--router vllm_sr=http://127.0.0.1:8890 \
--entrypoint aria_router=ariacompute/agent-auto \
--entrypoint vllm_sr=auto \
--pick-header aria_router=x-aria-router-model \
--pick-header vllm_sr=x-vsr-selected-model \
--pick-map ariacompute/ariamodel-small=glm-5.3-flash \
--pick-map ariacompute/ariamodel-mid=glm-5.3 \
--pick-map ariacompute/ariamodel-large=deepseek-v4-pro \
--pool small=https://tokenhub.tencentmaas.com \
--pool mid=https://tokenhub.tencentmaas.com \
--pool large=https://tokenhub.tencentmaas.com \
--model-id small=glm-5.3-flash \
--model-id mid=glm-5.3 \
--model-id large=deepseek-v4-pro \
--api-key small=$GATEWAY_API_KEY --api-key mid=$GATEWAY_API_KEY --api-key large=$GATEWAY_API_KEY \
--prices bench/prices/ariamodel.json \
--corpus bench/corpus/mmlu_tiny.jsonl \
--timeout 300 \
--report ./out/agent_vs_vsr_compare.json
```
```bash
# --- Track D:AFM-D ladder(aria-router vs vLLM Semantic Router)---
# aria = System One Choice(afm-d-auto);vllm_sr = keyword 基线(auto)。
export GATEWAY_BASE=https://tokenhub.tencentmaas.com
export GATEWAY_API_KEY=your-api-key
# afm-d 所需 Encoder System One(afm-d-gateway 默认 track: encoder @ :8010):
# aria-engine serve --track encoder --bind 127.0.0.1:8010
# Decoder 轨用 :8011(`--track decoder`);若指向 Decoder 则设 afm-d.track: decoder。
export DECISION_MODEL_URL=http://127.0.0.1:8010
# 若 :8899 上已有 semantic/agent gateway,先停掉,再:
aria-router serve \
--config config/examples/afm-d-gateway.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# vllm-sr keyword 基线仍在 :8890(config-gateway.yaml)。
python -m bench routing \
--router aria_router=http://127.0.0.1:8899 \
--router vllm_sr=http://127.0.0.1:8890 \
--entrypoint aria_router=ariacompute/afm-d-auto \
--entrypoint vllm_sr=auto \
--pick-header aria_router=x-aria-router-model \
--pick-header vllm_sr=x-vsr-selected-model \
--pick-map ariacompute/ariamodel-small=glm-5.3-flash \
--pick-map ariacompute/ariamodel-mid=glm-5.3 \
--pick-map ariacompute/ariamodel-large=deepseek-v4-pro \
--pool small=https://tokenhub.tencentmaas.com \
--pool mid=https://tokenhub.tencentmaas.com \
--pool large=https://tokenhub.tencentmaas.com \
--model-id small=glm-5.3-flash \
--model-id mid=glm-5.3 \
--model-id large=deepseek-v4-pro \
--api-key small=$GATEWAY_API_KEY --api-key mid=$GATEWAY_API_KEY --api-key large=$GATEWAY_API_KEY \
--prices bench/prices/ariamodel.json \
--quality label \
--corpus bench/corpus/routing_gateway_hard.json \
--timeout 300 \
--report ./out/afmd_vs_vsr_routing.json
python -m bench compare \
--router aria_router=http://127.0.0.1:8899 \
--router vllm_sr=http://127.0.0.1:8890 \
--entrypoint aria_router=ariacompute/afm-d-auto \
--entrypoint vllm_sr=auto \
--pick-header aria_router=x-aria-router-model \
--pick-header vllm_sr=x-vsr-selected-model \
--pick-map ariacompute/ariamodel-small=glm-5.3-flash \
--pick-map ariacompute/ariamodel-mid=glm-5.3 \
--pick-map ariacompute/ariamodel-large=deepseek-v4-pro \
--pool small=https://tokenhub.tencentmaas.com \
--pool mid=https://tokenhub.tencentmaas.com \
--pool large=https://tokenhub.tencentmaas.com \
--model-id small=glm-5.3-flash \
--model-id mid=glm-5.3 \
--model-id large=deepseek-v4-pro \
--api-key small=$GATEWAY_API_KEY --api-key mid=$GATEWAY_API_KEY --api-key large=$GATEWAY_API_KEY \
--prices bench/prices/ariamodel.json \
--corpus bench/corpus/mmlu_tiny.jsonl \
--timeout 300 \
--report ./out/afmd_vs_vsr_compare.json
```
```bash
# --- Track E:routing-accuracy(仅 Router API;≥200 金标)---
# 度量选路是否命中 expected_model — 无 --pool / 无 TokenHub 矩阵。
# 用 triad-gateway 一次挂起三个 entrypoint(或脚本 --start-router)。
# AFM-D 需 System One:aria-engine serve --track encoder --bind 127.0.0.1:8010
# GATEWAY_API_KEY 必须在 *serve* 进程环境里(TokenHub)。不要把它当作客户端
# Authorization 发给 :8899(triad-gateway 的 require_api_key: false)。
export GATEWAY_API_KEY=your-api-key
export DECISION_MODEL_URL=http://127.0.0.1:8010
aria-router serve \
--config config/examples/triad-gateway.yaml \
--bind 127.0.0.1:8899 \
--mgmt-bind 127.0.0.1:8090
# 一键:semantic → agent → afm-d;写出 out/acc_{semantic,agent,afmd}.json + acc_summary.json
# (本 shell 不必设 GATEWAY_API_KEY;若开启 require_api_key 再用 ROUTER_API_KEY)
./scripts/run-routing-accuracy.sh
# 或:./scripts/run-routing-accuracy.sh --start-router
# 决策模型未起时跳过 AFM-D:./scripts/run-routing-accuracy.sh --skip-afmd
# 单决策器:
python -m bench routing-accuracy \
--router http://127.0.0.1:8899 \
--entrypoint ariacompute/semantic-auto \
--pick-header x-aria-router-model \
--pick-map ariacompute/ariamodel-small=glm-5.3-flash \
--pick-map ariacompute/ariamodel-mid=glm-5.3 \
--pick-map ariacompute/ariamodel-large=deepseek-v4-pro \
--corpus bench/corpus/routing_gateway_eval.json \
--report ./out/acc_semantic.json
# 重建 eval 语料(模板;可选本地合并 MMLU/DRACO — 勿提交全量 dump):
python -m bench build-routing-eval
```
详见 [`bench/corpus/README.md`](bench/corpus/README.md) 与 [`bench/vllm-sr/`](bench/vllm-sr/)(外部 `vllm-sr` 配置与 validate/serve)。
[![][back-to-top]](#readme-top)
## ⚖️ 特性对比
| 维度 | Router (aria-router) | HiRoute | vLLM Semantic Router |
|------|:---:|:---:|:---:|
| OpenAI 兼容 HTTP 网关(服务端) | ✅ | ❌ | ❌ |
| 三种并列决策器(semantic / agent / afm-d) | ✅ | ❌ | ❌ |
| 语义路由(信号 → 决策) | ✅ | ✅ | ✅ |
| 硬约束剪枝(location/auth/modality/tools,决策前) | ✅ | ❌ | ❌ |
| 能力感知降级(competence-aware 经济降档) | ✅ | ✅ | ❌ |
| 有界故障转移 / fail closed(不静默改行为) | ✅ | ✅ | ❌ |
| 执行证据留存(任务/模型/工具/输出/能力 结构化证据链) | ✅ | ✅ | ❌ |
| 长周期 Agent 编排 / 委托 | ✅ | ✅ | ❌ |
| 决策模型扩展 API(外置决策服务) | ✅ | ✅ | ❌ |
| 多租户 / 鉴权 / 计费(API key·OAuth·Users·六因子成本) | ✅ | ❌ | ❌ |
| 成本·能力可观测(按 tier 成本归因 + Dashboard) | ✅ | △ | ❌ |
| 显式重判边界可观测(`x-aria-router-redecision`) | ✅ | ❌ | ❌ |
| 离线策略一致性报告(用留存记录重算,无需 router) | ✅ | ❌ | ❌ |
| 上下文前缀复用(同 turn 冻结决策) | ✅ | ✅ | ❌ |
| 集成面(C ABI + 八语言 SDK / 部署形态) | ✅ | △ | △ |
| 生产级服务端定位(多租户·管控·可运维) | ✅ | ❌ | ❌ |
图例:✅ 完整支持 · △ 部分/受限 · ❌ 不支持。
**结论**:Router 在保持「硬约束 + fail closed + 三决策器隔离」的前提下,补齐了 HiRoute 的 competence-aware 软降级、结构化执行证据、可插拔决策扩展与长周期委托,并以生产级 OpenAI 兼容网关 + 多租户计费 + C ABI / 八语言 SDK 的组合,覆盖 vLLM SR 之外的服务端全栈需求。
[![][back-to-top]](#readme-top)
## 🔏 信任库安全
2026-10 的 eMudhra(emSign)中间 CA 私钥泄露事件在二进制层面兜底:`aria-router` / `ariacompute-router-ffi` 通过 `reqwest` + `rustls-tls` 进行 provider API 调用与 `bin` 的 `upgrade` GitHub/Gitee Release 下载,其信任库是编译进二进制的 **`webpki-roots`**,已在 `http/Cargo.toml` 钉 `>=1.0.10`(`Cargo.lock` 经 `cargo update -p webpki-roots --precise 1.0.10` 由 1.0.9 → 1.0.10)。1.0.10+ 移除受泄露的 emSign 根。二进制**不**走系统 bundle,故锁升级是决定性修复。
Dashboard 是由 Rust 二进制托管的静态 Vite React SPA,**无运行时信任库**,自身不做出向 TLS。构建期 `npm install` 走主机 `ca-certificates`,但那只是构建环境。脚本 `scripts/check-emudhra.sh` 离线扫描系统或镜像 CA bundle 中已知根/中间证书的 SHA-256 指纹(退出码 `0` 干净、`1` 命中、`2` 无法读取)。
```bash
scripts/check-emudhra.sh /etc/ssl/certs/ca-certificates.crt
```
[![][back-to-top]](#readme-top)
## 🔨 开发
| 目标 | 作用 |
|------|------|
| `cargo test` | 跑工作区测试 |
| `cargo clippy --workspace --all-targets -- -D warnings` | Lint,拒绝警告 |
| `./scripts/coverage.sh` | 100% 行覆盖门禁(llvm-cov) |
| `./scripts/run-binding-tests.sh` | 宿主矩阵(Rust / Python / Go / TS / RN setup) |
| `cargo build --release -p aria-router -p ariacompute-router-ffi` | 构建 CLI + FFI |
运维 Dashboard 另有护栏测试与 impeccable 风格审计(在仓库根目录执行):
```bash
npm --prefix dashboard ci
npm --prefix dashboard run build
npm --prefix dashboard run test # 样式 token + 15 路由 × 深浅双主题 jsdom 快照
./scripts/audit-ui.sh --static # CSS / index.html 静态规则
./scripts/audit-ui.sh --dom # 全部路由 × 双主题(jsdom)
./scripts/audit-ui.sh --url # Chrome 页面加载(无浏览器时 skip)
```
[![][back-to-top]](#readme-top)
## 📚 文档
详细的工程上下文位于仓库根目录:
| 文档 | 覆盖内容 |
|------|----------|
| [AGENTS.md](./AGENTS.md) | Agent 工程上下文入口与目录索引 |
| [requirements.md](./requirements.md) | 需求规格(功能边界 / 异常 / 验收,人审后编码) |
| [task.md](./task.md) | 实施清单 |
| [PRODUCT.md](./PRODUCT.md) | Dashboard 设计契约(单色 / 扁平 / 直角 / AA / ≥12px) |
| [releasenotes_cn.md](./releasenotes_cn.md) | 发布说明 |
[![][back-to-top]](#readme-top)
## 📝 许可证
MIT.
[![][back-to-top]](#readme-top)
## 🤝 工程约定
本仓库遵循 Harness Engineering:
- [`AGENTS.md`](./AGENTS.md):Agent 工程上下文入口与目录索引
- [`requirements.md`](./requirements.md):需求规格(功能边界 / 异常 / 验收,人审后编码)
- [`task.md`](./task.md):实施清单
[![][back-to-top]](#readme-top)
---
Copyright © 2026 [ariacompute][org].
This project is [MIT](./LICENSE) licensed.
[org]: https://github.com/ariacompute
[release-shield]: https://img.shields.io/github/v/release/ariacompute/router?style=flat-square&labelColor=black&color=555
[release-link]: https://github.com/ariacompute/router/releases
[rust-shield]: https://img.shields.io/github/rust/cargo/v/ariacompute-router?style=flat-square&labelColor=black&color=555
[rust-link]: ./Cargo.toml
[license-shield]: https://img.shields.io/github/license/ariacompute/router?style=flat-square&labelColor=black&color=555
[license-link]: ./LICENSE
[lastcommit-shield]: https://img.shields.io/github/last-commit/ariacompute/router?style=flat-square&labelColor=black&color=555
[lastcommit-link]: https://github.com/ariacompute/router/commits
[routers-shield]: https://img.shields.io/badge/routers-3_parallel-555?style=flat-square&labelColor=black
[routers-link]: #-三种决策器
[sdk-shield]: https://img.shields.io/badge/SDK-8_languages-555?style=flat-square&labelColor=black
[sdk-link]: #-sdk-绑定
[coverage-shield]: https://img.shields.io/badge/coverage-100%25-555?style=flat-square&labelColor=black
[coverage-link]: ./scripts/coverage.sh
[dashboard-shield]: https://img.shields.io/badge/dashboard-monochrome_SPA-555?style=flat-square&labelColor=black
[dashboard-link]: #-dashboard
[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square