Skip to main content

怎么让大模型同时给30个意图节点打分

开篇引言

上一篇把意图树讲清楚了——三级结构(DOMAIN → CATEGORY → TOPIC),三种意图类型(KB / MCP / SYSTEM),IntentNode 的字段含义,数据库存扁平行、Redis 缓存整棵树。意图树在内存中加载好了,leafNodes 列表已经准备就绪。

但准备就绪不等于能直接用。意图树只是一个静态的数据结构,要让它发挥作用,得把叶子节点的信息喂给 LLM,让 LLM 判断用户的问题跟哪个节点最匹配。

带着几个具体的疑问往下看:

  • 叶子节点那么多,怎么把每个节点的信息塞进 Prompt?
  • Prompt 里怎么告诉 LLM 该按什么规则打分?
  • LLM 返回的结果是什么格式?万一格式不对怎么办?
  • Temperature 和 TopP 设多少才合适?

说明:本篇 Prompt 模板中的示例(分类节点、输出示例等)统一用电商智能体场景举例,和 Ragent 代码中的 intent-classifier.st 会有细节差异,但核心规则和结构完全一致。

用一个直观的场景来感受整个过程。假设电商客服的意图树有 11 个叶子节点,用户问了一句:

跨境包裹清关一般要多久?

系统要做的事情是:把 11 个叶子节点全部序列化成文本,拼进 Prompt 模板,一次 LLM 调用搞定所有节点的打分。LLM 返回一个 JSON 数组,里面是每个匹配节点的 ID 和分数。分数最高的那个——清关流程,score=0.92——就是路由目标。

这篇文章要把从叶子节点序列化到 JSON 解析的完整链路走一遍。

叶子节点序列化:LLM 看到的是什么

1. buildPrompt() 的序列化逻辑

上一篇提到,只有叶子节点参与打分。DefaultIntentClassifierbuildPrompt() 方法负责把这些叶子节点变成 LLM 能读懂的文本。

private String buildPrompt(List<IntentNode> leafNodes) {
StringBuilder sb = new StringBuilder();

for (IntentNode node : leafNodes) {
sb.append("- id=").append(node.getId()).append("\n");
sb.append(" path=").append(node.getFullPath()).append("\n");
sb.append(" description=").append(node.getDescription()).append("\n");

// 添加节点类型标识(V3 Enterprise 支持 MCP)
if (node.isMCP()) {
sb.append(" type=MCP\n");
if (node.getMcpToolId() != null) {
sb.append(" toolId=").append(node.getMcpToolId()).append("\n");
}
} else if (node.isSystem()) {
sb.append(" type=SYSTEM\n");
} else {
sb.append(" type=KB\n");
}

if (node.getExamples() != null && !node.getExamples().isEmpty()) {
sb.append(" examples=");
sb.append(String.join(" / ", node.getExamples()));
sb.append("\n");
}
sb.append("\n");
}

return promptTemplateLoader.render(
INTENT_CLASSIFIER_PROMPT_PATH,
Map.of("intent_list", sb.toString())
);
}

逐行拆一下,每个叶子节点被序列化成五个字段:

  • id=xxx:节点的唯一标识。LLM 返回打分结果时,需要原样回传这个 ID,代码端靠它把分数关联回 IntentNode 对象。
  • path=xxx:全路径,比如物流与配送 > 跨境物流 > 清关流程。帮助 LLM 理解这个节点在树中的层级关系和所属领域。如果只给 description,LLM 不一定能准确判断它属于哪个业务板块。
  • description=xxx:语义说明,是 LLM 匹配的核心依据。描述越清晰,LLM 越容易判断用户问题跟这个节点的相关度。
  • type=KB/MCP/SYSTEM:节点类型标识。KB 是知识库检索,MCP 是工具调用,SYSTEM 是系统交互。类型不同,匹配逻辑可能不同——MCP 节点往往需要匹配具体的动作语义(查询、操作),SYSTEM 节点匹配的是社交性问候。
  • examples=xxx:示例问题,多个示例用 / 分隔。相当于给 LLM 做 Few-shot——告诉它这类节点通常匹配什么样的问题。

序列化完成后,所有叶子节点的文本拼成一个大字符串,填入 Prompt 模板的 {intent_list} 占位符。

2. 一个完整的分类列表长什么样

用电商客服场景来看,11 个叶子节点序列化后在 Prompt 中的样子:

- id=product-info
path=商品服务 > 商品信息
description=商品规格、参数、功能介绍、适用场景等产品信息
type=KB
examples=iPhone 16 Pro 的摄像头参数是什么? / 这款耳机支持降噪吗?

- id=after-sales-repair
path=商品服务 > 售后维修
description=商品维修政策、保修期限、维修流程、维修网点等信息
type=KB
examples=手机屏幕碎了怎么保修? / 保修期内维修要收费吗?

- id=return-exchange
path=商品服务 > 退换货政策
description=退货条件、换货流程、退款时效、七天无理由退货规则等
type=KB
examples=买了一周的东西还能退吗? / 退货运费谁出?

- id=logistics-domestic-delivery
path=物流与配送 > 国内物流 > 配送方式
description=国内快递公司选择、配送时效、同城配送、预约配送等说明
type=KB
examples=你们用什么快递发货? / 能选顺丰吗?

- id=logistics-domestic-fee
path=物流与配送 > 国内物流 > 运费规则
description=国内物流的运费计算规则、包邮条件、偏远地区加收等
type=KB
examples=满多少包邮? / 新疆发货运费多少?

- id=logistics-overseas-warehouse
path=物流与配送 > 跨境物流 > 海外仓
description=海外仓备货、海外仓发货时效、海外仓退货流程等说明
type=KB
examples=海外仓发货一般几天到? / 你们有美国仓吗?

- id=logistics-overseas-customs
path=物流与配送 > 跨境物流 > 清关流程
description=跨境物流的清关申报、关税计算、禁运品规则等相关说明
type=KB
examples=海淘包裹清关一般要多久?

- id=logistics-overseas-fee
path=物流与配送 > 跨境物流 > 运费计算
description=跨境物流运费计算方式、不同国家和地区的费率对比等
type=KB
examples=寄到日本运费怎么算? / 跨境运费比国内贵多少?

- id=order-query
path=订单管理 > 订单查询
description=用户订单状态、物流进度、支付记录等实时查询
type=MCP
toolId=order-query-tool
examples=帮我查一下订单 2024112801 的物流进度 / 我的订单到哪了?

- id=system-greeting
path=系统交互 > 欢迎与问候
description=用户打招呼、问好、感谢等社交性问答
type=SYSTEM
examples=你好 / 谢谢你

- id=system-about
path=系统交互 > 关于助手
description=介绍助手身份、能力范围、使用指南等
type=SYSTEM
examples=你是谁? / 你能做什么?

这就是 LLM 在 Prompt 的分类列表部分看到的全部内容。11 个叶子节点,每个四到六行,信息密度不低但结构清晰。

3. 序列化的设计权衡

几个设计选择值得说一下。

为什么用 key=value 格式而不是 JSON? 可读性好,Token 占用少。如果用 JSON,每个节点要写成 {"id": "logistics-overseas-customs", "path": "物流与配送 > 跨境物流 > 清关流程", ...}——引号、花括号、冒号、逗号全都是额外的 Token。11 个节点下来差距不大,但如果叶子节点有 30 个,省下来的 Token 就可观了。

为什么把 examples/ 拼成一行而不是每个示例一行? 控制长度。11 个节点如果每个有 3 个示例、每个示例独占一行,Prompt 会多出几十行。拼成一行,LLM 照样能理解,但 Prompt 紧凑很多。

为什么要带 type 标识? 帮助 LLM 理解不同节点的性质。MCP 节点是工具调用——用户的问题通常带有明确的操作意图(查一下、帮我看看);SYSTEM 节点是系统交互——匹配的是问候、感谢这类社交性问题;KB 节点是知识检索——匹配的是知识性问题。类型信息相当于给 LLM 一个额外的判断维度。

Prompt 模板拆解:每条规则在干什么

解锁付费内容,👉 戳