Files
magpie/site/public/docs/zh/intent.html
T

344 lines
34 KiB
HTML
Raw 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.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>意图路由 · magpie 文档</title>
<meta name="description" content="magpie 如何按用户想做什么来路由每一轮:何时询问意图判断模型、它具体看到什么、回答怎么用、失败时怎么办,以及详细答疑。">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#fafafa">
<script>
(() => {
let mode = "system";
try { mode = localStorage.getItem("theme") || "system"; } catch {}
const q = matchMedia("(prefers-color-scheme: dark)");
const apply = () => {
const t = mode === "system" ? (q.matches ? "dark" : "light") : mode;
document.documentElement.dataset.theme = t;
document.querySelector('meta[name="theme-color"]').content = t === "dark" ? "#0b0b0c" : "#fafafa";
document.querySelectorAll("button.theme").forEach((b) => { b.dataset.mode = mode; b.title = "Theme: " + mode; });
};
q.addEventListener("change", () => mode === "system" && apply());
window.setTheme = (m) => { mode = m; try { m === "system" ? localStorage.removeItem("theme") : localStorage.setItem("theme", m); } catch {} apply(); };
window.themeMode = () => mode;
apply();
})();
</script>
<link rel="icon" href="/favicon.png" type="image/png">
<link rel="apple-touch-icon" href="/img/icon-256.png">
<link rel="alternate" hreflang="en" href="https://usemagpie.ai/docs/intent">
<link rel="alternate" hreflang="zh" href="https://usemagpie.ai/docs/zh/intent">
<link rel="stylesheet" href="/docs.css">
<meta property="og:title" content="意图路由">
<meta property="og:description" content="按用户想做什么,把每一轮交给合适的模型。">
<meta property="og:image" content="https://usemagpie.ai/img/og.png">
<meta property="og:url" content="https://usemagpie.ai/docs/zh/intent">
<meta name="twitter:card" content="summary_large_image">
</head>
<body>
<nav><div class="wrap">
<a class="brand" href="/"><img class="bird" src="/img/magpie-small.svg" alt="">magpie <small>文档</small></a>
<div class="links">
<a href="/">首页</a>
<a href="/docs/zh/start">快速上手</a>
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
</div>
<a class="icon-btn lang" href="/docs/intent" hreflang="en" lang="en" title="English version">EN</a>
<a class="icon-btn" href="https://discord.gg/vGSnD3ZKQF" aria-label="magpie on Discord" title="Join us on Discord"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg></a>
<a class="icon-btn" href="https://github.com/yetone/magpie" aria-label="magpie on GitHub" title="Source on GitHub"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg></a>
<button class="icon-btn theme" type="button" aria-label="Theme" title="Theme: system"><svg class="i-system" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1.75" y="2.75" width="12.5" height="8.5" rx="1.5"/><path d="M5.5 14h5M8 11.25V14"/></svg><svg class="i-light" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="3"/><path d="M8 1v1.5M8 13.5V15M1 8h1.5M13.5 8H15M3 3l1 1M12 12l1 1M3 13l1-1M12 4l1-1"/></svg><svg class="i-dark" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"><path d="M13.5 9.5A5.5 5.5 0 0 1 6.5 2.5a5.5 5.5 0 1 0 7 7Z"/></svg></button>
<a class="btn small" href="/#get">下载</a>
</div></nav>
<main class="wrap doc">
<aside class="toc">
<p>意图路由</p>
<a href="#overview">概览</a>
<a href="#setup">设置</a>
<a href="#how">一轮是怎么路由的</a>
<a href="#prompt">意图判断模型看到什么</a>
<a href="#failures">判断不出来时</a>
<a href="#turn">这一轮的后续请求</a>
<a href="#trace">看懂路由记录</a>
<a href="#cost">延迟、费用、隐私</a>
<a href="#tips">怎么写意图</a>
<a href="#limits">数值一览</a>
<a href="#faq">答疑</a>
</aside>
<article>
<p class="eyebrow" id="overview">文档 · 路由组</p>
<h1>意图路由</h1>
<p class="lede">按用户想做什么来选模型:写测试交给强模型,随口一问交给又快又便宜的模型。你用几个词描述每一类请求,每一轮开始时,由你指定的一个小模型判断这条消息属于哪一类。</p>
<figure class="figure">
<img class="for-light" src="/img/intent-rules-zh-light.png" width="2060" height="1590" alt="路由组编辑页:三条规则,其中两条带意图(意图是“a quick question”、意图是“writing or fixing tests”),意图判断模型为 Groq 上的 llama-3.1-8b-instant">
<img class="for-dark" src="/img/intent-rules-zh-dark.png" width="2060" height="1590" alt="路由组编辑页:三条规则,其中两条带意图(意图是“a quick question”、意图是“writing or fixing tests”),意图判断模型为 Groq 上的 llama-3.1-8b-instant">
<figcaption>Coder 分组:长对话交给 Kimi,简单提问交给 DeepSeek Flash,写测试交给 DeepSeek Pro;由 Groq 上的 Llama 3.1 8B 判断意图。</figcaption>
</figure>
<p>意图是<a href="/docs/zh/start#rules">路由组规则</a>的条件之一,与长度、图片、推理、Agent 并列。本页按 v0.1.99 的实际实现,逐条说明 magpie 拿它做了什么,让你能预判每一次路由决定,并在「路由」页里核对。</p>
<div class="callout"><b>一段话说清楚。</b>magpie 每一轮只做一次决定:在你发消息的时候做,Agent 调用工具的过程中不再做。如果某条带意图的规则有可能第一个命中,magpie 就通过自己的网关向分组的<em>意图判断模型</em>发一个很短的问题:给它带编号的意图列表和你最新一条消息的文字,让它回答一个数字。magpie 用这个数字从上往下检查规则,第一条命中的规则把它的模型排到第一,组里其他模型排在后面做故障转移。之后这一轮一直留在那个模型上,直到你再次发言。意图判断模型出错、超时或答非所问时,所有意图都不命中,这一轮按没有这些规则的方式路由。整个过程没有向量嵌入、没有关键词表,也不需要训练,就是让你选的那个模型读一遍消息,选一个编号。</div>
<h2 id="setup"><a class="anchor" href="#setup">设置</a></h2>
<p>你需要一个至少有两个模型的<a href="/docs/zh/start#routing">路由组</a>,再加一个用来判断意图的模型。这个模型可以是 magpie 里的任意模型,在不在组里都行。</p>
<ol class="steps">
<li><b>打开分组</b>进入<span class="ui">路由</span>页,在「路由组」里找到分组,点<span class="ui">编辑</span>。</li>
<li><b>添加带意图的规则</b>点<span class="ui">添加规则</span>,在<span class="ui">先发给</span>里选模型,点<span class="ui">意图是</span>,写下这类消息想做什么,例如 <em>writing or fixing tests</em> 或「写测试或修测试」。同一条规则上的其他条件也必须同时满足。</li>
<li><b>选择意图判断模型</b>有规则带意图后,下方会出现<span class="ui">意图判断模型</span>。建议选小而快、不带推理的模型。有意图却没有判断模型时,分组无法保存。</li>
<li><b>保存,然后发条消息</b>从下一轮起生效,不用重启。<span class="ui">路由</span>页会显示判断模型说了什么、这一轮去了哪里。</li>
</ol>
<p>命令行里也可以设置:</p>
<div class="code">
<span class="lang">终端</span>
<button class="copy" data-copy="ex-cli">复制</button>
<pre id="ex-cli">magpie group rule add coder use=deepseek/deepseek-v4-flash intent=<span class="s">"a quick question"</span> classifier=groq/llama-3.1-8b-instant
magpie group rule add coder use=deepseek/deepseek-v4-pro intent=<span class="s">"writing or fixing tests"</span>
magpie group rule coder <span class="c"># 带编号的规则列表和意图判断模型</span>
magpie group rule classifier coder groq/llama-3.1-8b-instant <span class="c"># 更换意图判断模型</span>
magpie group rule mv coder 3 1 <span class="c"># 顺序很重要:从上往下</span>
magpie group rule rm coder 2</pre>
</div>
<p><code>intent=</code> 也可以写成 <code>asks=</code> 或 <code>about=</code>;<code>classifier=</code> 也可以写成 <code>classify=</code> 或 <code>by=</code>。在 <code>rule add</code> 上加 <code>classifier=</code> 会设置整个分组的意图判断模型,只有第一次需要。它保存在 <code>~/.config/magpie/providers.json</code> 该分组的 <code>"classifier"</code> 字段里,每条规则保存为 <code>{"use": …, "intent": …}</code>。</p>
<h2 id="how"><a class="anchor" href="#how">一轮是怎么路由的</a></h2>
<p>Agent 发往带规则的分组的每个请求,都按下面的顺序处理:</p>
<ol class="steps">
<li><b>是不是新的一轮?</b>magpie 看请求里最后一条用户消息:带文字或图片、不带工具结果,说明是你刚刚发言,新的一轮开始;带工具结果,说明是 Agent 在继续进行中的一轮(见<a href="#turn">这一轮的后续请求</a>)。轮次按会话分别计数。子 Agent 是单独的会话:它和主 Agent 共用一个 session,但第一句话不同。</li>
<li><b>哪些意图可能起作用?</b>magpie 从上往下看规则,先不管意图:一条规则的其他条件(tokens、图片、推理、Agent)对这个请求都成立,它就是候选,把它的意图收集起来。只有大小写不同的意图算同一个。遇到第一条<em>不带</em>意图的候选规则就停止,因为它会直接命中,排在它下面的规则都轮不到。一个意图也没收集到时,不会询问意图判断模型,也不会产生费用。</li>
<li><b>询问意图判断模型</b>把收集到的意图和你这条消息的文字发给它,完整提示词见<a href="#prompt">下文</a>。这个调用和 Agent 的请求一样,走 magpie 自己的网关,所以用的是该模型所属供应商的 Key 或账号,以及它们的故障转移。它有 8 秒时间作答。</li>
<li><b>读取回答</b>取回答里出现的第一个整数:<code>1</code> 到 <code>n</code> 表示对应的意图,<code>0</code> 表示都不是。其他情况(没有数字、数字超出范围)这一轮都不命中意图。同一条消息、同一组意图的回答会缓存 10 分钟。</li>
<li><b>匹配规则</b>带着这个回答从上往下检查规则。带意图的规则,只有判断模型说的正是它的意图(不区分大小写),并且其他条件也都满足时才命中。第一条命中的规则生效;一条都不命中时,分组按平常的方式路由这一轮。</li>
<li><b>给模型排序</b>命中规则指定的模型排第一,组里其他模型按平常的顺序排在后面做故障转移。即使上一轮是别的模型回答的,也是这样:新一轮开始时,规则优先于<span class="ui">会话保持</span>。如果规则指定的模型此刻没有可用的 Key 或账号(都在冷却、供应商已关闭),就按分组原本的顺序,路由记录里会写明原因。</li>
<li><b>记住这次决定</b>magpie 把这次决定和判断模型的回答记在这个会话名下(由分组、Agent 的 session 和会话的第一句话确定),这一轮后面的请求都照它走。</li>
</ol>
<h3>举个例子</h3>
<p>上图的 Coder 分组有三条规则:</p>
<div class="table-wrap"><table>
<tr><th>#</th><th>条件</th><th>先发给</th></tr>
<tr><td>1</td><td>≥ 128000 tokens</td><td>kimi/kimi-k2.5</td></tr>
<tr><td>2</td><td>意图是“a quick question”</td><td>deepseek/deepseek-v4-flash</td></tr>
<tr><td>3</td><td>意图是“writing or fixing tests”</td><td>deepseek/deepseek-v4-pro</td></tr>
</table></div>
<ul>
<li>2 万 tokens 的一轮:规则 1 不成立,两个意图都是候选,判断模型在“a quick question”和“writing or fixing tests”之间选。</li>
<li>15 万 tokens 的一轮:规则 1 不带意图且成立,收集到这里就停止。不询问判断模型,这一轮直接发给 Kimi。</li>
<li>判断模型回答 <code>0</code>(都不是):没有规则命中,按分组的路由方式走(这里是「按顺序」,deepseek-v4-pro 排第一)。</li>
</ul>
<h3>在界面里看</h3>
<p>一个真实请求:分组里有两条带意图的规则(“a quick question or explanation, with no code to write or change”和“writing or fixing tests”),从 Claude Code 发出一条消息,要它给 cache.go 里的 LRU 缓存加单元测试,覆盖淘汰和并发读写。<span class="ui">路由</span>页实时显示它的去向。</p>
<figure class="figure">
<img class="for-light" src="/img/intent-routing-hit-zh-light.png" width="2080" height="1116" loading="lazy" alt="路由页:Claude Code 经 magpie 到 aihubmix/glm-5.3 并由它回答;记录里写着命中规则 2(意图是“writing or fixing tests”),deepseek-flash 用时 964 毫秒作出判断">
<img class="for-dark" src="/img/intent-routing-hit-zh-dark.png" width="2080" height="1116" loading="lazy" alt="路由页:Claude Code 经 magpie 到 aihubmix/glm-5.3 并由它回答;记录里写着命中规则 2(意图是“writing or fixing tests”),deepseek-flash 用时 964 毫秒作出判断">
<figcaption>判断模型 deepseek-flash 用 964 毫秒判断为“writing or fixing tests”,于是规则 2 把 glm-5.3 排到分组原本的第一个模型前面,5.6 秒完成回答。</figcaption>
</figure>
<h2 id="prompt"><a class="anchor" href="#prompt">意图判断模型看到什么</a></h2>
<p>下面就是完整的请求,此外不发送任何内容。系统消息是固定的;用户消息按第 2 步收集的顺序(也就是规则顺序)列出你的意图:</p>
<div class="code">
<span class="lang">System</span>
<pre class="wrapped">You route a user's message to a coding assistant by what it asks for. Given numbered kinds of request and the user's message, answer with the number of the kind the message is, or 0 if it is none of them. Answer with the number only.</pre>
</div>
<div class="code">
<span class="lang">User</span>
<pre>Kinds:
1. a quick question
2. writing or fixing tests
The user's message:
&lt;message&gt;
Write unit tests for the parseTokens function and fix any that fail.
&lt;/message&gt;
The number of its kind (0 for none):</pre>
</div>
<ul>
<li><b>只发你最新的一条消息。</b>magpie 只发送最后一条用户消息里的文字部分:不含之前的轮次、系统提示词、工具定义或文件。</li>
<li><b>不发送图片。</b>只有图片的消息没有文字可判断,所有意图都不命中。想按图片路由,请用规则的<span class="ui">带图片</span>条件。</li>
<li><b>去掉 Agent 插入的提醒。</b>Claude Code 附加在消息里的 <code>&lt;system-reminder&gt;…&lt;/system-reminder&gt;</code> 会被去掉;Agent 放在同一条消息里的其他文字会保留。</li>
<li><b>长消息会截短。</b>超过 4000 个字符时,保留前 3000 个和后 1000 个,中间用 <code>…</code> 连接。请求想做什么,一般在开头和结尾都能看出来。</li>
<li><b>请求参数。</b>OpenAI 格式的 chat completion,不流式,<code>temperature 0</code>,<code>max_tokens 2048</code>,User-Agent 为 <code>magpie-router/1</code>。magpie 知道判断模型支持哪些推理档位时,会要最低的一档:支持 <code>none</code> 就用 <code>none</code>,否则用它最低的档位(比如 <code>low</code>);不知道档位时不传,由厂商默认。magpie 会像处理其他请求一样把它转换成判断模型所属供应商的协议,所以 Anthropic、Gemini 等模型也能用。</li>
</ul>
<h2 id="failures"><a class="anchor" href="#failures">判断不出来时</a></h2>
<p>意图判断模型只是辅助,不是关卡。它出什么问题,这一轮都照常有回答,只是带意图的规则不命中。</p>
<div class="table-wrap"><table class="loose">
<tr><th>情况</th><th>magpie 的处理</th></tr>
<tr><td>回答 <code>0</code></td><td>所有意图都不命中,由不带意图的规则和分组平常的顺序决定</td></tr>
<tr><td>8 秒内没有回答</td><td>算失败:意图都不命中,这一轮立即继续</td></tr>
<tr><td>报错(额度用完、5xx、Key 无效)</td><td>先在 8 秒内尝试它所属供应商自己的故障转移,仍然失败则意图都不命中</td></tr>
<tr><td>回答不是 0 到 n 之间的数字</td><td>这一轮意图都不命中。判断模型确实回答了,所以不冷却,下一轮照常询问</td></tr>
<tr><td>报错或 8 秒超时</td><td>这个判断模型会冷却 30 秒:这段时间里的轮次不再等它,路由记录里会写 “not asked again for now”。之后第一次答对就解除</td></tr>
<tr><td>消息里没有文字</td><td>不询问,意图都不命中</td></tr>
</table></div>
<p>每种情况都会连同原因写进这一轮的路由记录。所以带意图的规则可能让这一轮去了你没想到的模型,但不会让它失败。</p>
<h2 id="turn"><a class="anchor" href="#turn">这一轮的后续请求</a></h2>
<p>一轮往往包含几十个请求:Agent 调用工具,把结果发回来,再调用下一个工具……magpie 对它们的处理如下:</p>
<ul>
<li><b>保持。</b>这一轮里的请求都发给这一轮开始时选定的模型,不再询问意图判断模型。中途换模型会丢掉厂商为这段会话做的缓存(prompt caching),有些厂商还会拒绝别家模型写的推理内容。</li>
<li><b>故障转移后留在接手的模型上。</b>规则指定的模型失败、由组里另一个成员接手后,这一轮剩下的请求都交给接手的成员(<span class="ui">会话保持</span>为「自动」「整个会话」或「一轮之内」时)。等你下次发言,规则再重新决定。</li>
<li><b>超出上下文。</b>唯一的例外:这一轮进行中,会话涨到当前模型上下文的 95% 时,magpie 会沿用之前的判断结果重新检查一遍规则。如果有规则把它发往上下文<em>更大</em>的模型,就换过去;不会换到上下文更小的模型。</li>
<li><b>等待下一轮。</b>如果 magpie 没看到这一轮开始(magpie 是在某一轮进行中启动或重启的),或者当初决定这一轮的规则被删除、或改成了发往别的模型,那么不会有规则移动这一轮,等你下次发言时再重新决定。</li>
<li><b>子 Agent。</b>子 Agent 的轮次单独决定,依据它自己的消息。规则可以把子 Agent 发往别处,而主 Agent 留在原来的模型上。</li>
</ul>
<p>这些决定保存在内存里,保留到会话最后一个请求之后 24 小时。重启 magpie 会清空它们,下一条消息会重新决定。</p>
<h2 id="trace"><a class="anchor" href="#trace">看懂路由记录</a></h2>
<p><span class="ui">路由</span>页会显示每个分组最近的一个请求由谁回答;<span class="ui">最近一个请求是怎么路由的</span>下面写着原因。</p>
<figure class="figure">
<img class="for-light" src="/img/intent-trace-zh-light.png" width="2064" height="898" loading="lazy" alt="一次请求后的路由页:Claude Code 经 magpie 到 DeepSeek deepseek-v4-pro,记录里写着命中了规则 3 以及判断模型的回答">
<img class="for-dark" src="/img/intent-trace-zh-dark.png" width="2064" height="898" loading="lazy" alt="一次请求后的路由页:Claude Code 经 magpie 到 DeepSeek deepseek-v4-pro,记录里写着命中了规则 3 以及判断模型的回答">
<figcaption>一条要求写测试的消息:判断模型 323 毫秒后说是“writing or fixing tests”,于是规则 3 把 deepseek-v4-pro 排到第一。</figcaption>
</figure>
<p>与意图相关的几种记录:</p>
<div class="table-wrap"><table class="loose">
<tr><th>记录里写的</th><th>含义</th></tr>
<tr><td>问 … 第 1 轮属于 “A”, “B” 中的哪一个,它说是“B”(用时 323 毫秒)。</td><td>询问了判断模型,候选是这几个,它选了 B</td></tr>
<tr><td>… 它说都不是(…)。</td><td>它回答了 0</td></tr>
<tr><td>…(同一条消息之前已判断过)。</td><td>回答来自 10 分钟缓存,这次没有发出调用</td></tr>
<tr><td>… 本应判断 … 但没能给出答案(<em>原因</em>),所以带意图的规则都不命中。</td><td>判断失败;原因是报错、超时、它的原始回答,或者它还在失败后的冷却期</td></tr>
<tr><td>第 N 轮开始,命中规则 R(意图是“B”),所以 M 排第一 …</td><td>生效的规则,以及被排到第一的模型</td></tr>
<tr><td>命中规则 R,但 M 现在没有可用的账号或 Key,所以按分组原本的顺序。</td><td>规则指定的模型此刻没有可用的 Key 或账号</td></tr>
<tr><td>第 N 轮开始时命中规则 R(…),发给了 M;这一轮一直留在那里。</td><td>这一轮里的工具往返请求,沿用开始时的决定</td></tr>
<tr><td>这一轮在 magpie 看到之前就已开始,规则等下一轮再判断。</td><td>见上文「等待下一轮」</td></tr>
</table></div>
<p>意图判断模型自己的调用,在<span class="ui">用量</span>页里记在 Agent <em>magpie</em> 名下,带 token 数和费用,判断意图花了多少一看便知。</p>
<h2 id="cost"><a class="anchor" href="#cost">延迟、费用、隐私</a></h2>
<ul>
<li><b>延迟。</b>只有每一轮的第一个请求需要等判断模型,在快的供应商上通常是几百毫秒(截图里是 323 毫秒),最长 8 秒。这一轮后面的工具往返、没有意图需要判断的轮次、命中缓存的回答,都不增加延迟。</li>
<li><b>费用。</b>每轮一次很短的调用:输入大约是你的消息加上意图列表,输出一两个 token。上面的例子是输入 212、输出 1。用订阅里的模型做判断模型时,这些调用会占用该订阅的额度。</li>
<li><b>隐私。</b>你这条消息的文字(最多约 4000 个字符)会发给判断模型所属的供应商。想让它不离开本机,可以用 Ollama 或 LM Studio 里的本地模型来判断。magpie 只在内存里保存消息的哈希值和判断结果,保留 10 分钟;消息原文不存储,也不写日志。</li>
</ul>
<h2 id="tips"><a class="anchor" href="#tips">怎么写意图</a></h2>
<ul>
<li><b>描述请求本身,而不是模型。</b>写「写测试或修测试」,不要写「交给 Opus 的难题」。判断模型只看得到你写的这几个词和那条消息。</li>
<li><b>类别之间要分得开。</b>两个意图有重叠(「提问」和「关于代码的提问」)时,小模型会在两者之间随机摇摆。要么合并,要么把区别写清楚。</li>
<li><b>少而宽,胜过多而窄。</b>2 到 5 个意图对 8B 级别的模型很轻松。每个意图最多 200 个字符,但通常几个词就够了。</li>
<li><b>顺序依然重要。</b>只有第一条命中的规则生效,所以必须优先的规则(例如长上下文规则)要放在带意图的规则上面。它成立时,连判断模型都不用问。</li>
<li><b>“都不是”也是有效回答。</b>不属于任何意图的轮次按分组平常的顺序走,所以把你的默认模型放在这个顺序的第一位。</li>
<li><b>中文英文都行。</b>意图和消息可以用判断模型能理解的任何语言书写。固定的提示词是英文的,小模型处理中英混合没有问题。</li>
<li><b>怎么选判断模型。</b>选小而快、不带推理的模型:Groq 上的 Llama 3.1 8B、各家的 Flash-Lite / nano / mini 系列、Claude Haiku、DeepSeek 的非推理对话模型,或者本地模型。推理模型更慢;magpie 会要它最低的推理档位并留 2048 个 token,但慢的模型仍可能超过 8 秒。先观察几轮路由记录,判断不准时,先改意图的措辞,再考虑换模型。</li>
</ul>
<h2 id="limits"><a class="anchor" href="#limits">数值一览</a></h2>
<div class="table-wrap"><table class="loose">
<tr><th>项目</th><th>数值</th></tr>
<tr><td>判断超时</td><td>8 秒,包含其供应商的故障转移</td></tr>
<tr><td>失败后冷却</td><td>30 秒,按判断模型计</td></tr>
<tr><td>回答缓存</td><td>10 分钟,按判断模型 + 意图 + 消息;仅内存</td></tr>
<tr><td>轮次决定保留</td><td>会话最后一个请求之后 24 小时;仅内存</td></tr>
<tr><td>发送的消息长度</td><td>最多 4000 字符;更长时取前 3000 + 后 1000</td></tr>
<tr><td>判断请求参数</td><td>temperature 0,max_tokens 2048,模型最低的推理档位,不流式</td></tr>
<tr><td>意图长度</td><td>最多 200 字符;多余空白会合并,比较时不区分大小写</td></tr>
<tr><td>超出上下文</td><td>达到当前模型上下文的 95% 时,只换到上下文更大的模型</td></tr>
</table></div>
<h2 id="faq"><a class="anchor" href="#faq">答疑</a></h2>
<h3>这是向量嵌入 / 语义检索吗?</h3>
<p>不是。没有嵌入模型,没有相似度阈值,没有关键词表,也不需要训练。判断模型就是一个普通的对话模型:读你的意图和消息,回答一个编号。效果取决于这个模型和你的措辞,每一次的回答都能在路由记录里看到。</p>
<h3>它会看整段对话吗?</h3>
<p>不会,只看你刚发的那一条。所以像「另一个文件也照这样改」这样的追问,只按这几个字判断。要么把这一轮的消息写清楚一些,要么加一个适合追问的意图。</p>
<h3>我发一条消息,Agent 发了 40 个请求,判断模型被问了 40 次吗?</h3>
<p>不是,只问一次。另外 39 个请求带的是工具结果,属于同一轮,沿用这一轮的决定。路由记录(「这一轮一直留在那里」)和用量页(只有一次 <em>magpie</em> 调用)都能看到这一点。</p>
<h3>一轮进行到一半会换模型吗?</h3>
<p>只有两种情况:当前模型失败、由组里另一个成员接手;或者会话超出当前模型的上下文,有规则把它发往上下文更大的模型。一轮之中不会重新判断意图。</p>
<h3>判断模型挂了或者很慢怎么办?</h3>
<p>这一轮照常进行。最多等 8 秒后,意图都不命中,由分组的其他规则和顺序决定。接下来 30 秒内 magpie 不再询问这个判断模型,免得每一轮都要等满超时。路由记录里会写明原因。</p>
<h3>判断模型说的就是这条规则的意图,为什么没有发给规则指定的模型?</h3>
<p>看路由记录。常见原因有:</p>
<ul>
<li>上面有别的规则先命中了;</li>
<li>这条规则的其他条件不满足(tokens、图片、推理、Agent);</li>
<li>规则指定的模型此刻没有可用的 Key 或账号(「现在没有可用的账号或 Key」);</li>
<li>这是一轮里的工具往返请求,而这一轮开始时去的是别的模型;</li>
<li>magpie 没看到这一轮开始(「规则等下一轮再判断」)。</li>
</ul>
<h3>两条规则写了同一个意图会怎样?</h3>
<p>这个意图只给判断模型看一次。选中它时,这几条规则里其他条件也满足的第一条生效。可以借此把 Codex 发来的「写测试」交给一个模型,把 Claude Code 发来的交给另一个。</p>
<h3>改了规则,会影响正在进行的一轮吗?</h3>
<p>不会。保存后从下一轮开始生效。如果决定当前这一轮的规则被删除了,或者改成发往别的模型,这一轮会留在原处直到结束,路由记录里写着规则等下一轮再判断。</p>
<h3>判断模型可以是一个路由组吗?可以是组里的成员吗?</h3>
<p>不能是路由组:它必须是单个模型(<code>provider/model</code>),保存时 magpie 会校验。它可以是 magpie 里的任何模型,包括组里的成员、订阅里的模型或本地模型。</p>
<h3>可以用推理模型吗?</h3>
<p>可以,只要它能在 8 秒内回答。magpie 会要它最低的推理档位(有 <code>none</code> 用 <code>none</code>,否则用最低一档),并留 2048 个 token,思考不会挤掉答案。不过小而快的模型仍是更好的选择:更便宜,而且每一轮都要等它。</p>
<h3>重试同一条消息,会再问一次吗?</h3>
<p>10 分钟内不会:同一条消息、同一组意图、同一个判断模型,直接用缓存的回答,路由记录里写「同一条消息之前已判断过」。改了意图或者换了判断模型,就会重新询问。</p>
<h3>支持哪些 Agent?</h3>
<p>凡是通过 magpie 使用路由组的 Agent 都支持:Claude Code、Codex、OpenCode、Gemini CLI 等。轮次是从请求本身识别的(带文字、不带工具结果的消息),和 Agent 用哪种 API 无关。</p>
<h3>我的消息会被存下来吗?</h3>
<p>magpie 只在内存里保存它的哈希值(用于缓存)和判断结果。原文会像其他请求一样发给判断模型所属的供应商,适用该供应商的条款。判断调用的用量记录里只有 token 数,没有原文。</p>
<h3>准确率怎么样?</h3>
<p>取决于判断模型,也取决于你的意图之间区分得是否清楚。几个明显不同的类别,小模型能判断得很好;有重叠的类别,它有时会猜错。判断错或没判断出来,影响的只是由哪个模型回答,所以建议先写两三个宽泛的意图,看一天路由记录,再调整措辞。</p>
<h3>用真实模型测过吗?</h3>
<p>测过。我们把 20 条真实的编程消息(中英文都有:简单提问、写测试/修测试、普通编码任务,还有 “Fix the bug in parseTokens and add a test for it” 这种混合的)发给一个带两条意图规则的分组。DeepSeek V4 Flash、GLM-5.3 Flash、MiMo v2.6 Flash 作为判断模型都是 20/20 全对,判断耗时中位数分别是 0.75 秒、1.3 秒、1.9 秒。某家中转上的 Gemini 3.8 Flash 即使用 <code>low</code> 推理档位也要 11 秒,每一轮都超时,于是都按没有意图规则的方式路由,路由记录每次都写明了原因。这也说明:判断模型选得不好,损失的只是路由效果,不会让这一轮失败。</p>
<p style="margin-top:48px">有问题,或者有值得分享的配置?欢迎到 <a href="https://discord.gg/vGSnD3ZKQF">Discord</a> 交流。路由组的基础用法见<a href="/docs/zh/start#routing">快速上手</a>。</p>
</article>
</main>
<footer><div class="wrap">
<span>© 2026 magpie · MIT</span>
<div class="links">
<a href="/">首页</a>
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
</div>
</div></footer>
<script>
(() => {
const next = { system: "light", light: "dark", dark: "system" };
document.querySelectorAll("button.theme").forEach((b) => {
b.dataset.mode = themeMode();
b.title = "Theme: " + themeMode();
b.addEventListener("click", () => setTheme(next[themeMode()]));
});
document.querySelectorAll(".copy").forEach((b) => b.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(document.getElementById(b.dataset.copy).textContent);
b.textContent = "已复制"; setTimeout(() => (b.textContent = "复制"), 1400);
} catch {}
}));
// The table of contents follows the reading position.
const links = [...document.querySelectorAll(".toc a")];
const heads = links.map((a) => document.getElementById(a.hash.slice(1)));
const spy = () => {
let i = 0;
heads.forEach((h, j) => { if (h.getBoundingClientRect().top < 140) i = j; });
if (innerHeight + scrollY >= document.body.scrollHeight - 4) i = heads.length - 1;
links.forEach((a, j) => a.classList.toggle("on", j === i));
};
addEventListener("scroll", spy, { passive: true }); spy();
})();
</script>
</body>
</html>