picoclaw/docs/guides/routing-guide.zh.md
lxowalle 77b0c43392
refactor: support explicit provider field in model list entries (#2609)
* refactor: support explicit model list providers

* fix(web): preserve explicit model providers

* fix(web): preserve legacy provider prefixes on model updates

fix(models): normalize explicit provider-prefixed ids

fix(api): preserve legacy model updates across providers

fix(agent): preserve config identity for explicit provider refs

* fix ci
2026-04-22 11:28:47 +08:00

333 lines
7.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 路由使用指南
> 返回 [README](../project/README.zh.md)
PicoClaw 里用户能直接感知到的“路由”主要有两部分:
- **agent 路由**:决定哪一个 agent 处理一条消息
- **模型路由**:决定这一轮是走主模型,还是走轻量模型
这份文档面向真实部署中的配置使用场景。
## 快速开始
### 把一个 Telegram 群路由给 support agent
```json
{
"agents": {
"list": [
{ "id": "main", "default": true },
{ "id": "support" }
],
"dispatch": {
"rules": [
{
"name": "telegram support group",
"agent": "support",
"when": {
"channel": "telegram",
"chat": "group:-1001234567890"
}
}
]
}
}
}
```
### 只处理某个 Slack workspace 里的 @提及
```json
{
"agents": {
"list": [
{ "id": "main", "default": true },
{ "id": "support" }
],
"dispatch": {
"rules": [
{
"name": "slack mentions",
"agent": "support",
"when": {
"channel": "slack",
"space": "workspace:t001",
"mentioned": true
}
}
]
}
}
}
```
### 给简单请求启用轻量模型
```json
{
"model_list": [
{
"model_name": "gpt-main",
"provider": "openai",
"model": "gpt-5.4",
"api_keys": ["sk-main"]
},
{
"model_name": "flash-light",
"provider": "gemini",
"model": "gemini-2.0-flash-exp",
"api_keys": ["sk-light"]
}
],
"agents": {
"defaults": {
"model_name": "gpt-main",
"routing": {
"enabled": true,
"light_model": "flash-light",
"threshold": 0.35
}
}
}
}
```
## Agent 路由
Agent 路由通过下面这个配置项定义:
```text
agents.dispatch.rules
```
规则从上到下依次检查。
**第一条匹配的规则直接生效**
如果没有规则命中PicoClaw 会回退到默认 agent。
## 支持的匹配字段
| 字段 | 含义 | 示例 |
| --- | --- | --- |
| `channel` | Channel 名称 | `telegram``slack``discord` |
| `account` | 归一化后的 account ID | `default``bot2` |
| `space` | workspace、guild 等上层容器 | `workspace:t001``guild:123456` |
| `chat` | 私聊、群或频道 | `direct:user123``group:-100123``channel:c123` |
| `topic` | 线程或话题 | `topic:42` |
| `sender` | 归一化后的发送者身份 | `12345``john` |
| `mentioned` | 是否显式 @ 了 bot | `true` |
注意,配置里要写的是运行时归一化后的值,不是原始 webhook / SDK payload。
## 规则顺序
把更具体的规则放前面,把更宽泛的规则放后面。
正确顺序:
1. 某个群里的 VIP 用户
2. 这个群的全部消息
3. 某个 channel 的更宽泛兜底
错误顺序:
1. 这个群的全部消息
2. 同一个群里的 VIP 用户
在错误顺序下宽泛规则会先命中VIP 规则永远不会生效。
## 和 Session 的关系
路由和 Session 是相关但不同的两件事:
- 路由决定由哪个 agent 处理
- Session 决定这些消息是否共享同一段记忆
如果你想让某条命中的路由使用不同的会话策略,可以用 `session_dimensions` 覆盖全局 `session.dimensions`
示例:
```json
{
"agents": {
"list": [
{ "id": "main", "default": true },
{ "id": "support" },
{ "id": "sales" }
],
"dispatch": {
"rules": [
{
"name": "vip in support group",
"agent": "sales",
"when": {
"channel": "telegram",
"chat": "group:-1001234567890",
"sender": "12345"
},
"session_dimensions": ["chat", "sender"]
},
{
"name": "support group",
"agent": "support",
"when": {
"channel": "telegram",
"chat": "group:-1001234567890"
},
"session_dimensions": ["chat"]
}
]
}
},
"session": {
"dimensions": ["chat"]
}
}
```
在这个配置里:
- VIP 用户会被路由到 `sales`
- 其他群成员会进入 `support`
- VIP 路由还会额外按 `chat + sender` 做每用户隔离
## Identity Links
当你用 `sender` 做匹配时,`session.identity_links` 也会影响路由结果。
适合这种场景:同一个真实用户可能出现为多个原始 sender ID。
示例:
```json
{
"session": {
"identity_links": {
"john": ["slack:u123", "legacy-user-42"]
}
},
"agents": {
"dispatch": {
"rules": [
{
"name": "john goes to sales",
"agent": "sales",
"when": {
"sender": "john"
}
}
]
}
}
}
```
## 模型路由
模型路由配置在:
```text
agents.defaults.routing
```
当前支持字段:
| 字段 | 含义 |
| --- | --- |
| `enabled` | 开启或关闭模型路由 |
| `light_model` | `model_list` 中用于简单请求的 `model_name` |
| `threshold` | `[0, 1]` 范围内的复杂度阈值 |
关键行为:
- `light_model` 必须存在于 `model_list`
- PicoClaw 会在启动时解析轻量模型;如果模型无效,路由会被禁用
- 同一轮 turn 只会使用同一档模型,不会中途切档
## 什么会影响复杂度分数
当前模型路由会看一些结构化信号,例如:
- 消息长度
- fenced code block
- 同一 session 最近是否频繁调用工具
- 会话深度
- 是否带有媒体或附件
因此,看起来“很简单”的消息,在以下情况下仍可能走主模型:
- 带代码
- 带图片或音频
- prompt 很长
- 当前是一个工具调用很多的工作流
## 阈值怎么选
推荐起点:
```json
{
"agents": {
"defaults": {
"routing": {
"enabled": true,
"light_model": "flash-light",
"threshold": 0.35
}
}
}
}
```
通用规律:
- 阈值越低,越容易回到主模型
- 阈值越高,越积极地使用轻量模型
实用建议:
- `0.25`:更保守,更少轻量模型 turn
- `0.35`:默认推荐起点
- `0.50+`:只有当你的轻量模型已经能覆盖大多数聊天任务时再考虑
## 常见问题
### 某条规则没有命中
优先检查:
- 规则顺序
- 值的形状是否写成了归一化格式,例如 `group:-100123` 而不是裸 `-100123`
- 当前 channel 是否真的提供了 `space``topic``mentioned`
### 消息被错误的 agent 处理了
最常见原因还是顺序。
记住:第一条匹配的规则直接生效。
### 轻量模型从来没有被用到
检查:
- `agents.defaults.routing.enabled` 是否为 `true`
- `light_model` 是否存在于 `model_list`
- 轻量模型能否成功初始化
- 阈值是不是设得太低
### 明明是短消息,还是走了主模型
这通常是因为当前 turn 同时满足了其他“复杂”信号,例如:
- 带代码块
- 带媒体或附件
- 最近的 session 历史里工具调用很多
### 路由没问题,但上下文还是共享得太多
去调整 `session.dimensions` 或某条 route 上的 `session_dimensions`
路由只决定“谁来处理”session 才决定“记忆怎么共享”。
## 相关文档
- [Session 使用指南](session-guide.zh.md)
- [配置指南](configuration.zh.md)
- [Provider 与模型配置](providers.zh.md)