Agent 用通用知识编答案而不调工具?不是工具写得不好,是你的 SystemMessage 没告诉它「你不知道」。
问题:LLM 觉得自己什么都知道
DeepSeek、GPT-4 这些模型训练时「见」过海量文本。工业设备故障诊断?它确实能说出一些东西——轴承磨损、对中偏差、润滑不足,通用知识它有的是。
这就是问题。 LLM 觉得「我能回答」,于是不调用你的工具——那些连接真实设备数据的工具。
在上一篇排查 Function Calling 的文章中,我列了 4 个坑,其中第 3 个就是「LLM 觉得不需要工具」。治这个问题的药,就是 SystemMessage。
SystemMessage 的三个作用
@SystemMessage 在 LangChain4j 的 AiServices 接口中定义,框架会把它作为 System Role 的消息放在对话最开头:
interface IndustrialAssistant {
@SystemMessage("""
你是一个工业设备运维专家,服务于智能工厂的设备监控与故障诊断。
你的知识有限,无法访问实时设备数据。
当用户询问设备状态、告警、历史数据、故障原因时,你必须使用提供的工具查询,
不要凭猜测回答。
回复规范:
- 用结构化方式呈现诊断结果(问题、原因、建议)
- 涉及安全风险时,明确标注优先级(HIGH/MEDIUM/LOW)
- 不确定时如实说明,不要编造数据
""")
String chat(String message);
}
这段话做了三件事:
1. 设定角色(你是谁)
「你是一个工业设备运维专家,服务于智能工厂的设备监控与故障诊断。」
这句话不是废话。LLM 会根据角色设定调整回复风格——「运维专家」意味着技术性强、结构化、可执行;「服务于智能工厂」意味着理解工业场景(设备 ID、告警级别、工单流程)。
2. 限制知识边界(你不知道什么)
「你的知识有限,无法访问实时设备数据。当用户询问设备状态、告警、历史数据、故障原因时,你必须使用提供的工具查询,不要凭猜测回答。」
这是整个 SystemMessage 中最关键的一段。 它明确告诉 LLM:
- 你的知识是有限的(破除「我什么都知道」的幻觉)
- 哪些问题你回答不了(设备状态、告警、历史数据、故障原因)
- 你应该怎么做(使用工具查询)
- 你不应该怎么做(凭猜测回答)
举个反面例子。如果你不写这段话,对话可能是这样的:
用户: "CNC-001 振动异常是什么原因?"
LLM: "振动异常通常由轴承磨损、转子不平衡或联轴器对中偏差引起。
建议检查轴承间隙、进行振动频谱分析..."
← 全是通用知识,没有调你的工具!
加上知识边界限制后:
用户: "CNC-001 振动异常是什么原因?"
LLM: [调用 queryDeviceAlarms("CNC-001")]
[调用 queryDeviceHistory("CNC-001")]
[调用 generateDiagnosis("振动异常", "...")]
"根据实时数据,CNC-001 当前振动值 4.8mm/s(正常范围 0-2.8mm/s),
轴承温度 72°C(正常 40-65°C)。诊断结论:..."
3. 指定输出规范(回复长什么样)
「用结构化方式呈现诊断结果」「明确标注优先级」「不确定时如实说明」
这三个要求把 LLM 的回复从「自由聊天」变成了「结构化报告」。对工业运维场景来说,可执行的回复比优美的回复重要 100 倍。
工业 SystemMessage 的 4 个设计原则
原则 1:角色要具体,不要泛化
// 差
@SystemMessage("你是一个AI助手。")
// 好
@SystemMessage("你是一个工业设备运维专家,服务于智能工厂的设备监控与故障诊断。")
「AI 助手」什么都能干,但同时什么倾向都没有。「工业设备运维专家」给了 LLM 一个明确的回答基调。
原则 2:明确说「你不知道」
这是最容易被忽略的。LLM 默认倾向是「提供帮助」——如果它觉得自己能回答,就不会调用工具。你必须明确告诉它哪些问题它客观上回答不了。
// 关键句式
"你的知识有限,无法访问实时数据。"
"当用户询问 XX 类问题时,你必须使用工具查询,不要凭猜测回答。"
原则 3:给反面指令
不仅告诉 LLM「你应该做什么」,还要告诉它「你不应该做什么」:
你应该:使用工具查询设备实时数据
你不应该:凭猜测回答设备相关问题
你应该:如实说明不确定的情况
你不应该:编造数据或假装知道
原则 4:输出格式要举例
如果只是说「用结构化方式呈现」,LLM 的理解可能千差万别。更好的做法是给一个格式模板:
@SystemMessage("""
...
回复规范(必须遵守):
1. 先列出查询到的关键数据(设备ID、告警类型、异常指标值)
2. 再给出诊断分析(可能原因,按可能性排序)
3. 最后给出建议操作(标注优先级 HIGH/MEDIUM/LOW)
示例格式:
【设备状态】CNC-001 | 警告
【关键指标】振动 4.8mm/s(超限 71%)| 温度 72°C(超限 11%)
【诊断分析】1) 轴承磨损(可能性高)2) 转子不平衡(可能性中)
【建议操作】[HIGH] 立即检查轴承间隙 | [MEDIUM] 校验联轴器对中
""")
给一个格式模板比描述规则有效得多——LLM 擅长模仿示例。
SystemMessage vs @Tool 描述
很多人把 SystemMessage 里的「使用工具」和 @Tool 注解里的功能描述混淆。记住这个区分:
| SystemMessage | @Tool 描述 | |
|---|---|---|
| 范围 | 全局,所有对话 | 单个工具 |
| 作用 | 定义「你应该用工具」 | 定义「这个工具是干什么的」 |
| 写给 LLM 的 | 推理主循环 | 工具选择阶段 |
| 正确做法 | 「当用户询问设备状态时使用工具」 | 「查询指定设备的当前活跃告警」 |
两者配合:SystemMessage 告诉 LLM「你需要用工具」,@Tool 描述告诉 LLM「这个工具能做这件事」。
一句话总结
SystemMessage 不是给 LLM 的「礼貌问候」,而是划定能力边界的安全栅栏。一个好的 SystemMessage = 明确的角色 + 有限的知识边界 + 具体的输出规范 + 反面指令。
本文由 LaoLiang 原创,首发于掘金/知乎/微信公众号。转载请联系作者。