给 Agent 一个「人设」:SystemMessage 在工业场景的设计技巧

46 阅读6分钟

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 原创,首发于掘金/知乎/微信公众号。转载请联系作者。