Agent 如何看懂时序数据?时间序列查询的 Tool 设计模式

34 阅读3分钟

时序数据是 Agent 见过最「不友好」的数据——一行行数字,LLM 看了也懵。Tool 的设计决定了 Agent 是「看懂数据」还是「被数据淹没」。


问题:LLM 不擅长读原始数据

把 100 行原始时序数据扔给 LLM:

[
  {"ts": 1718400000, "temp": 72.5, "vibration": 4.8, "pressure": 0.9, ...},
  {"ts": 1718400001, "temp": 72.6, "vibration": 4.9, "pressure": 0.9, ...},
  ...  // 98 more rows
]

两件事会发生:

  1. Token 爆炸——100 行 ≈ 2000+ token,一次查询就吃掉大部分上下文
  2. LLM 迷失——一堆数字里看不出趋势、找不到异常

Agent 需要的是摘要,不是原始数据。 Tool 的职责是把原始数据「翻译」成 LLM 能理解的结构化摘要。


设计模式 1:预聚合 + 采样

不要给 LLM 全量数据,给统计摘要。

// 差:返回 100+ 条原始数据点
public List<Map<String, Object>> queryRaw(String deviceId, int minutes);

// 好:返回一个聚合统计 + 最新数据点
@Tool("查询指定设备的历史遥测数据..." +
      "返回最近1小时的统计摘要和最新数据点。")
public String queryDeviceHistory(String deviceId) {
    Map<String, Object> stats = tdengine.queryStats(deviceId, 60);  // avg/max/min
    Map<String, Object> latest = tdengine.queryLatest(deviceId);     // latest point
    return formatJson(stats, latest);
}

聚合后从 2000 token 降到 200 token,LLM 反而能更准确地判断。


设计模式 2:正常范围标注

仅仅给数字不够——LLM 不知道什么是「正常」、什么是「异常」。

// 差:只有数字
{"temp": 72.5, "vibration": 4.8}

// 好:标注正常范围 + 是否超限
{
  "temp": {"value": 72.5, "unit": "°C", "normalRange": "40-65", "status": "ABOVE"},
  "vibration": {"value": 4.8, "unit": "mm/s", "normalRange": "0-2.8", "status": "CRITICAL"}
}

正常范围是 Tool 的领域知识,应该写在 Tool 代码里,而不是指望 LLM 自己去判断。


设计模式 3:多粒度接口

不同的用户意图需要不同粒度的数据:

用户问法需要的数据粒度Tool 方法
「CNC-001 现在状态怎么样?」最新一个数据点queryLatest
「过去一小时的趋势怎么样?」分钟级聚合queryHistory(60min)
「这周的平均振动值?」小时级聚合queryWeekly
「振动是从什么时候开始升高的?」原始数据 + 趋势queryTrend
@Tool("查询指定设备最近5分钟的实时遥测趋势。")
public String queryRealtimeData(String deviceId) {
    // 返回最近 5 分钟原始数据,最多 10 条
}

@Tool("查询指定设备过去1小时的统计摘要和最新值。")
public String queryDeviceHistory(String deviceId) {
    // 返回 avg/max + latest
}

两个 Tool 方法,一个给「看趋势」,一个给「看摘要」。LLM 根据用户意图自动选择。


设计模式 4:异常数据自动标记

不需要 LLM 逐行扫描数据找异常——让 Tool 先做一轮预处理。

// Tool 内部预检
if (vibration > 2.8) {
    summary.put("vibrationStatus", "CRITICAL");
    summary.put("vibrationExceedPercent", (vibration - 2.8) / 2.8 * 100);
}

Tool 输出里直接带「振动超限 71%」——LLM 拿到的已经是结论性质的信息,只需要做最终判断。


完整示例

改造后的 DeviceDataTool 输出:

{
  "deviceId": "CNC-001",
  "latest": {
    "temp": 72.5, "vibration": 4.8, "pressure": 0.9, "rpm": 1500, "current": 28.5
  },
  "statsLast60Min": {
    "avg_temp": 68.3, "max_temp": 72.5,
    "avg_vib": 3.2,  "max_vib": 4.8,
    "avg_pressure": 0.85
  }
}

LLM 拿到这份数据后能轻松判断:

  • 当前温度 72.5°C,高于一小时平均 68.3°C → 温度在上升
  • 当前振动 4.8mm/s,一小时最大 4.8 → 振动在尖峰
  • 结合知识库 → 「轴承磨损导致温度升高 → 振动超标 → 建议立即检查」

一句话总结

不要让 LLM 做 Tool 该做的数据处理。时序 Tool 的核心职责是预聚合、标注正常范围、标记异常——把原始数据变成 LLM 可直接推理的「诊断素材」。好的 Tool 不是一个数据库查询接口,而是一个领域数据分析师。

代码仓库:github.com/LaoLiang-agent/industrial-agent-long


本文由 LaoLiang 原创,首发于掘金/知乎/微信公众号。转载请联系作者。