告别 500ms 延迟:两阶段意图识别让 Agent 既快又准
启发式规则优先、LLM 按需介入 —— 一种兼顾速度与准确性的意图识别架构
一、问题背景
Agent 系统面临一个经典难题:用户输入是闲聊、询问能力,还是真实操作请求?
识别不清会导致两类问题:
- 过度暴露:用户只是说了句"你好",系统却打开了所有工具权限,既浪费 LLM 调用,又容易产生误操作
- 遗漏意图:用户说"帮我读一下这个 PDF",系统没识别出文件操作意图,导致无法执行
常见解决方案有两种:
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 纯规则 | 关键词匹配 | 极速,< 1ms | 覆盖不全,难以处理歧义 |
| 纯 LLM | 大模型判断 | 准确,可处理复杂语义 | 慢,每次 200-500ms |
| 两阶段混合 | 规则快筛 + LLM 兜底 | 兼具两者优点 | 架构稍复杂 |
本文介绍一种两阶段混合意图识别的实现原理。
二、整体架构
┌─────────────────────┐
│ 用户输入 │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Stage 1: 启发式规则 │
│ 毫秒级检测 │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ 单意图 + 高置信度 │
│ (置信度 ≥ 0.85) │───Yes──▶ 直接返回结果
└──────────┬──────────┘
│ No
▼
┌─────────────────────┐
│ Stage 2: LLM 精细分类 │
│ 仅在模糊/多意图时触发 │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ 两阶段结果合并 │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ 工具暴露决策 │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ 按需暴露工具列表 │
└─────────────────────┘
核心思路:
- Stage 1(启发式):毫秒级初筛,简单意图直接命中
- Stage 2(LLM):仅在复杂/模糊场景介入,作为精细化校正
- 结果合并:按置信度动态选择,规则与 LLM 互补
三、意图类型体系
设计意图识别前,首先要定义清楚需要识别哪些意图类型:
class IntentType(Enum):
# 社交类
CASUAL_CHAT # 闲聊问候(你好、bye、谢谢)
CAPABILITY_QUESTION # 询问能力(能做什么?)
# 文件操作类
FILE_READ # 读取文件/目录
FILE_EDIT # 修改/编辑文件
FILE_CREATE # 创建新文件
# 执行类
COMMAND_EXEC # 命令行执行
WEB_SEARCH # 联网搜索
WEB_FETCH # 获取网页内容
# 委托类
SPAWN_SUBAGENT # 启动子 Agent
RUN_SKILL # 运行技能
# 调度类
CRON_SCHEDULE # 定时任务
MESSAGE_SEND # 发送消息
# 分析类
PROJECT_INSPECTION # 项目代码分析
UNKNOWN # 未知意图
分类时每条意图附带一个 confidence(置信度),取值 0.0 ~ 1.0,表示对该判断的确信程度。
四、Stage 1:启发式规则分类
4.1 设计思路
启发式规则的核心是用关键词组合快速排除明显场景,典型模式:
def _looks_like_casual_chat(text: str) -> bool:
short_smalltalk = {"你好", "hello", "hi", "thanks", "bye"}
lowered = text.strip().lower()
# 精确匹配
if lowered in short_smalltalk:
return True
# 短句 + 问候词(防止"帮我读一下"误判为闲聊)
if len(lowered) <= 12 and any(token in lowered for token in ("你好", "hi")):
return True
return False
4.2 关键词组设计
每种意图对应一组关键词,高效覆盖常见表达:
# 文件读取意图
LOOKS_LIKE_FILE_READ_KEYWORDS = (
"读取", "查看", "分析文件", "看下", "目录",
".py", ".md", ".json", "read ", "open ", "inspect",
)
# 命令执行意图
LOOKS_LIKE_EXEC_KEYWORDS = (
"执行", "运行", "命令", "终端", "shell",
"bash", "cmd", "python ", "exec", "run ",
)
# Web 搜索意图
LOOKS_LIKE_WEB_KEYWORDS = (
"搜索", "联网", "网页", "网站", "最新",
"web", "search", "fetch", "browse",
)
def _contains_any(text: str, keywords: tuple[str, ...]) -> bool:
return any(kw in text for kw in keywords)
4.3 防误触设计
一个难点:"能不能读取文件" 既像能力询问,又像文件操作。
解决方案:引入排除列表。
def _looks_like_capability_question(text: str) -> bool:
capability_markers = ("能不能", "能否", "can you", "do you support")
explicit_execution_markers = (
"帮我读", "请读取", ".pdf", ".md",
"这个文件", "read this", "open this",
)
has_question_tone = _contains_any(text, question_markers)
has_capability_tone = _contains_any(text, capability_markers)
if not has_capability_tone:
return False
# 有能力询问特征,但同时有执行意图 → 不算纯询问
if _contains_any(text, explicit_execution_markers):
return False
return has_question_tone or text.startswith(("能", "可以", "是否"))
4.4 分类决策
def heuristic_classify(text: str) -> IntentResult:
lowered = text.strip().lower()
detected_intents: list[IntentDecision] = []
# 闲聊/能力询问 → 无需工具
if _looks_like_casual_chat(lowered):
return IntentResult.casual_chat()
if _looks_like_capability_question(lowered):
return IntentResult.capability_question()
# 工具类意图检测
if _contains_any(lowered, LOOKS_LIKE_FILE_READ_KEYWORDS):
detected_intents.append(IntentDecision(IntentType.FILE_READ, confidence=0.85))
if _contains_any(lowered, LOOKS_LIKE_EXEC_KEYWORDS):
detected_intents.append(IntentDecision(IntentType.COMMAND_EXEC, confidence=0.90))
if _contains_any(lowered, LOOKS_LIKE_WEB_KEYWORDS):
detected_intents.append(IntentDecision(IntentType.WEB_SEARCH, confidence=0.85))
# ... 其他意图检测
# 单意图 + 高置信 → 直接返回,不走 LLM
if len(detected_intents) == 1 and detected_intents[0].confidence >= 0.85:
return IntentResult(
primary_intent=detected_intents[0],
should_enable_tools=True,
requires_llm=False, # 关键标志:无需 Stage 2
)
# 多意图或低置信 → 需要 LLM 介入
return IntentResult(
primary_intent=detected_intents[0],
secondary_intents=tuple(detected_intents[1:]),
should_enable_tools=True,
requires_llm=True,
)
五、Stage 2:LLM 精细分类
5.1 何时触发
以下情况会触发 LLM 分类:
- 多个意图同时命中(用户说"帮我读这个文件然后执行命令")
- 单意图但置信度 < 0.85(输入模糊)
- Stage 1 无法匹配的未知输入
5.2 提示词设计
LLM 的提示词需要简洁、输出格式固定:
LLM_SYSTEM_PROMPT = """你是一个意图识别专家。根据用户输入判断其意图。
意图类型:CASUAL_CHAT, CAPABILITY_QUESTION, FILE_READ, FILE_EDIT,
COMMAND_EXEC, WEB_SEARCH, SPAWN_SUBAGENT, CRON_SCHEDULE, ...
输出 JSON(不要输出其他内容):
{
"intent": "意图类型",
"confidence": 0.0-1.0,
"reasoning": "简短推理",
"recommended_tools": ["tool1"] // 可选
}
confidence 评分标准:
- 0.95+: 非常确定
- 0.80-0.94: 较确定
- 0.60-0.79: 中等,需要参考上下文
- <0.60: 不确定
"""
5.3 带超时的异步调用
import asyncio
async def llm_classify_async(text: str, llm, timeout: float = 10.0) -> IntentResult:
messages = [
SystemMessage(content=LLM_SYSTEM_PROMPT),
HumanMessage(content=f"用户输入:\n{text}")
]
try:
# 带超时保护,防止 LLM 响应慢导致阻塞
response = await asyncio.wait_for(llm.ainvoke(messages), timeout=timeout)
parsed = json.loads(response.content)
return IntentResult(
primary_intent=IntentDecision(
intent=IntentType[parsed["intent"]],
confidence=parsed["confidence"],
reasoning=parsed["reasoning"],
),
recommended_tools=tuple(
ToolRecommendation(tool, 1.0) for tool in parsed.get("recommended_tools", [])
),
metadata={"source": "llm"},
)
except asyncio.TimeoutError:
# LLM 超时 → 回退到启发式结果
return IntentResult.default_unknown()
六、两阶段结果合并
当两阶段都完成后,需要合并结果。核心策略:置信度高者胜出。
CONFIDENCE_THRESHOLD_LLM = 0.80
CONFIDENCE_THRESHOLD_HEURISTIC = 0.85
def merge_results(heuristic: IntentResult, llm: IntentResult) -> IntentResult:
# LLM 置信度高 → 信任 LLM
if llm.primary_intent.confidence >= CONFIDENCE_THRESHOLD_LLM:
return llm
# 启发式置信度高 → 信任启发式
if heuristic.primary_intent.confidence >= CONFIDENCE_THRESHOLD_HEURISTIC:
return heuristic
# 都不确定 → 以 LLM 为主,保留启发式的候选意图
return IntentResult(
primary_intent=llm.primary_intent,
secondary_intents=heuristic.secondary_intents,
should_enable_tools=llm.should_enable_tools or heuristic.should_enable_tools,
metadata={"source": "merged"},
)
合并的三种情况:
| 场景 | 处理方式 |
|---|---|
| LLM 高置信(≥ 0.80) | 直接采用 LLM 结果 |
| 启发式高置信(≥ 0.85) | 采用启发式结果 |
| 都不高 | LLM 为主 + 保留启发式候选意图 |
七、工具暴露控制
意图识别的最终目的是按需暴露工具,不是一次性全开。
def select_tools_for_intent(result: IntentResult) -> tuple[str, ...]:
# 闲聊/能力询问 → 不暴露任何工具
if not result.should_enable_tools:
return ()
if not result.recommended_tools:
return ALL_TOOLS # 没有推荐时保守返回全部
# 按 exposure_level 排序,选取最相关的 N 个
sorted_tools = sorted(result.recommended_tools, key=lambda t: t.exposure_level, reverse=True)
selected = [t.tool_name for t in sorted_tools if t.exposure_level >= 0.3]
return tuple(selected[:MAX_TOOLS_PER_INTENT])
意图与工具的典型映射:
| 意图 | 推荐工具 | exposure_level |
|---|---|---|
| FILE_READ | filesystem | 1.0 |
| FILE_EDIT | filesystem | 1.0 |
| COMMAND_EXEC | shell | 1.0 |
| WEB_SEARCH | web | 1.0 |
| SPAWN_SUBAGENT | spawn | 1.0 |
| PROJECT_INSPECTION | filesystem + shell | 0.8, 0.3 |
八、完整流程图
┌──────────────────┐
│ 用户输入 │
└────────┬─────────┘
│
▼
┌────────────────────────┐
│ 闲聊 / 能力询问? │
└────────┬────────────────┘
Yes │ No
▼ │ ▼
┌──────────────────┐ │ ┌────────────────────┐
│ should_enable_ │ │ │ 启发式多意图检测 │
│ tools = False │ │ └─────────┬──────────┘
│ 无需 LLM │ │ │
└────────┬────────┘ │ ▼
│ │ ┌────────────────────┐
│ │ │ 单意图 + 置信度 │
│ │ │ ≥ 0.85? │
│ │ └─────────┬──────────┘
│ │ Yes │ No
│ │ ▼ │ ▼
│ │ ┌──────┐ │ ┌────────────┐
│ │ │采用 │ │ │ 调用 LLM │
│ │ │启发式│ │ │ 精细分类 │
│ │ └──┬───┘ │ └─────┬──────┘
│ │ │ │ │
│ │ │ │ ▼
│ │ │ │ ┌────────────┐
│ │ │ │ │ LLM 超时? │
│ │ │ │ └─────┬──────┘
│ │ │ │ Yes │ No
│ │ │ │ ▼ │ ▼
│ │ │ │ ┌──┐ │ ┌────┐
│ │ │ │ │回│ │ │合并│
│ │ │ │ │退│ │ │结果│
│ │ │ │ └──┘ │ └─┬──┘
│ │ │ │ │ │
└──────────┴────┴────┴───────┴───┘
│ │
▼ ▼
┌─────────────────────────────┐
│ 按置信度选择工具 │
└─────────────┬───────────────┘
│
▼
┌─────────────────────────────┐
│ 暴露选中的工具列表 │
└─────────────────────────────┘
九、性能对比
| 方案 | 响应时间 | 准确性 | LLM 调用成本 |
|---|---|---|---|
| 纯 LLM | 200-500ms | 高 | 每次都调用 |
| 纯启发式 | < 1ms | 中 | 无 |
| 两阶段混合 | 1ms(命中高速路径) | 高 | 仅模糊场景调用 |
实测数据:约 70-80% 的用户输入可在 Stage 1 高速路径完成,LLM 调用减少显著。
十、核心设计思想总结
- 规则做快筛:简单明确的场景用规则快速处理,避免无意义的 LLM 调用
- LLM 做兜底:复杂模糊的场景交给 LLM,确保准确性
- 置信度驱动:所有决策点都有置信度阈值,避免非此即彼的硬判断
- 工具按需暴露:意图识别结果直接决定工具权限,实现最小权限原则
- 超时保护:LLM 调用设置 10 秒超时,防止外部服务延迟影响主流程
这套两阶段混合架构在生产级 Agent 系统中具有良好的参考价值,可根据具体业务场景裁剪意图类型和规则集合。