
把CPU、内存、网络流量同时丢给TimechoAI,做交叉分析
上一次我们聊了如何把一条时间序列数据交给 TimechoAI,造了一份 CPU 假数据,让它帮我们找出突刺点。
可回到真实场景想想,情况远比那复杂。只看一个 CPU 指标,很多时候根本看不出名堂。
所以今天这篇,我们要往前再走一步。我们要把多条不同的时序数据,同时喂给这个大模型。我们要让它帮我们做交叉分析。也就是看看这几个指标之间,到底有没有什么勾连关系。
一、 为什么单指标分析在实际中经常翻车
1.1 老板的真实需求不是找异常点
不少做技术的都有个习惯:一报警就赶紧查指标,看到 CPU 报警就盯着它的折线图,确认高了,就把截图发到群里。
其实老板看到这个截图,心里是没底的。他想要知道的,不是“出了什么问题”,而是“为什么会出这个问题,以及怎么解决”。这两个东西差别很大。
你光告诉他CPU高了,这仅仅只是一个现象。现象背后的原因,往往藏在别的地方。这就是单指标分析经常翻车的原因。你分析得很准,确实找出了最高点在哪,但是对解决问题毫无帮助。
1.2 举个例子:CPU高不一定是因为CPU的毛病
举个最常见的例子:一台服务器 CPU 使用率突然从 20% 飙到 90%,还持续了十分钟。
如果只看 CPU 这一条线,猜得出原因吗?猜不出,因为导致 CPU 高的原因太多了。
可能的情况是什么呢?可能是内存泄漏了。内存泄漏了,这是一个问题。那为什么会引起CPU高呢?因为程序没有内存可用了,它就会疯狂地触发垃圾回收机制,也就是GC。这个垃圾回收动作本身是非常吃CPU的。所以表现出来就是CPU很高。
还有一种可能。可能是网络流量突然被打满了。网络打满了之后,大量的请求堆积在队列里。CPU得不断地去切换上下文,去处理这些排队等待的线程。这样一来,CPU也会被拉高。
你看,同样都是CPU高,但是根因完全不一样。一个是内存的问题,一个是网络的问题。如果你不把内存和网络的拉过来一起看,你根本就无从下手。
二、 多条数据怎么在代码里组织
2.1 还是老办法,自己造几份假数据
要测交叉分析,手里得有三份数据:CPU 使用率、内存使用率,以及网络流入流量。
我们沿用上次的造数据思路,但这次让三份数据产生联动,人为制造一个“内存泄漏导致 CPU 高”的场景。
打开你的代码编辑器,我们把生成数据的函数稍微改复杂一点。
import random
def generate_multi_metrics_data():
cpu_data = []
mem_data = []
net_data = []
base_time = "2023-10-24 10:00:"
for i in range(100):
time_str = base_time + str(i * 60).zfill(2)
cpu_val = random.randint(20, 30)
mem_val = random.randint(40, 50)
net_val = random.randint(100, 200)
if i >= 50:
mem_val = 50 + (i - 50) * 2 + random.randint(-5, 5)
if mem_val > 95:
mem_val = 95
if i >= 60:
cpu_val = random.randint(75, 95)
if i >= 60:
net_val = random.randint(20, 50)
cpu_data.append(f"{time_str}, CPU使用率, {cpu_val}%")
mem_data.append(f"{time_str}, 内存使用率, {mem_val}%")
net_data.append(f"{time_str}, 网络流入流量, {net_val}MB")
return cpu_data, mem_data, net_data
这段代码里我用了三个列表存数据,关键在于中间 if i >= 50 那段逻辑。
我先让内存从第50个点开始涨。它不是一下子暴涨,是一点点爬。这很符合内存泄漏的真实表现。然后我加了个判断,当内存涨到一定高度,也就是到了第60个点的时候,我让CPU跟着飙上去。同时我让网络流量掉下来。
这就复现了一条非常经典的故障链路。等会儿我们要看大模型能否把它梳理出来。
2.2 数据的文本格式该怎么排版
数据造好后,下一个问题是:三组内容怎么拼成一个字符串交给模型?
你不能把它们全混在一个列表里。比如第一行是CPU,第二行是内存,第三行又回到CPU。这样排布太乱了,大模型看瞎了也找不出规律。
通常来说,我们要做分组。用一些明显的分隔符,或者空行,把不同指标的数据隔开。
我们可以写一个专门的函数来干这个排版的事。
def format_multi_data(cpu_list, mem_list, net_list):
cpu_str = "\n".join(cpu_list)
mem_str = "\n".join(mem_list)
net_str = "\n".join(net_list)
final_text = f"""
===第1组指标数据===
{cpu_str}
===第2组指标数据===
{mem_str}
===第3组指标数据===
{net_str}
"""
return final_text
注意这里我用了 ===第X组指标数据=== 这种醒目的标题,并在每组之间留空行。
为什么要这么干呢?其实说白了,就是在降低大模型的阅读负担。你把结构理得越清楚,它就越不容易把内存的数据错看成CPU的数据。这个排版的工作,本来应该是前端或者数据工程师做的,现在在我们这个测试代码里,只能自己手动拼。
三、 提示词该怎么写才能引导它做交叉对比
3.1 别只说“帮我分析一下”,要给具体的对比方向
数据格式化好之后,就到了最关键的一步:写 Prompt。我们要明确告诉模型,要看时间线上的先后顺序。
很多新手到这一步就写:“下面是三组数据,帮我分析一下。”这样写,模型大概率分别总结三组各自的平均值和异常点,不会主动找联系——因为它没接到“找联系”的指令。
我们得把要求写得更具体一点。我们要明确告诉它,我们要看时间线上的先后顺序。
def build_cross_prompt(formatted_data_text):
prompt = f"""
你现在是一个资深的运维专家。下面给你提供了同一台服务器在同一时间段内的三组监控数据。
格式是:时间, 指标名称, 数值。
第1组是CPU使用率。第2组是内存使用率。第3组是网络流入流量。
请你仔细对比这三组数据在时间线上的变化趋势。不要孤立地看每一个指标。
你的任务是:
1. 找出所有发生明显异常波动的具体时间点。
2. 重点分析这些异常点之间是否存在先后发生的关系。比如是不是A指标先变,然后B指标跟着变。
3. 根据这种先后顺序,推测一下最有可能的故障根因是什么。
数据如下:
{formatted_data_text}
"""
return prompt
看这段提示词:我先给它设定“资深运维专家”的角色,用来约束回答口吻。
然后我明确告诉它这三组数据分别是什么。接着我列了三个任务。特别是第二个任务,我强调了“先后发生的关系”。这就是引导它去做交叉分析的核心指令。没有这句话,它可能就只做横向对比,不做纵向的时间轴追踪了。
3.2 在示例页面先测一下提示词
在写代码跑之前,我强烈建议先到网页里试一次:
你可以手动复制几十行我们造的数据,加上这段提示词,扔到那个网页聊天框里。看看它怎么回。
为什么要多这一步呢?因为调接口是要消耗时间和额度的。如果你提示词写得不对,你用代码跑一遍要等十几秒。你在网页上跑,几秒钟就能看到结果。发现不对,马上改提示词,改到满意了,再搬到代码里去。
这其实是一个很实用的工作流。别一上来就死磕代码。先把业务逻辑和提示词在轻量级的环境里验证通了,再写成自动化脚本。
四、 改造代码把三组数据发出去
4.1 把之前的单变量函数改成多变量
前面的准备都就绪了,现在把主流程串起来。改动很小,只是把调一个生成函数改成调三个再拼起来。
import requests
def ask_timecho_ai(question, api_key):
url = "
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}
payload = {
"model": "timecho-model",
"messages": [
{
"role": "user",
"content": question
}
]
}
try:
response = requests.post(url, headers=headers, json=payload, timeout=60)
response.raise_for_status()
result_dict = response.json()
answer = result_dict['choices'][0]['message']['content']
return answer
except Exception as e:
return f"发生错误了:{e}"
if __name__ == "__main__":
my_key = "sk-你的真实KEY粘贴在这里"
print("正在生成三组联动假数据...")
cpu_list, mem_list, net_list = generate_multi_metrics_data()
print("正在排版数据格式...")
formatted_text = format_multi_data(cpu_list, mem_list, net_list)
print("正在构造交叉分析提示词...")
my_prompt = build_cross_prompt(formatted_text)
print("正在请求TimechoAI接口,数据量大,请耐心等待...\n")
final_answer = ask_timecho_ai(my_prompt, my_key)
print("=== 多指标交叉分析结果 ===")
print(final_answer)
你对比一下上一篇的代码,核心的请求函数 ask_timecho_ai 一点都没变。变的全都是外面的数据准备逻辑。
另外注意,我把 timeout 从 30 秒改到了 60 秒,因为这次发过去的数据量约是上一次的三倍。
4.2 跑一下看看它能不能找出“内存泄漏”这个假设
现在可以去跑这段代码了。倒杯水等半分钟,结果就出来了。
如果一切顺利,你看到的结果应该会非常惊艳。它大概会给你返回类似这样的内容:
“经过对比三组数据在时间轴上的变化,我发现了一个明显的先后异常链条:
- 首先,在第50分钟左右(10:50:00),第2组指标(内存使用率)开始出现异常,呈现出持续上升的趋势,偏离了原来40%-50%的正常区间。
- 接着,到了第60分钟左右(11:00:00),随着内存压力的增大,第1组指标(CPU使用率)突然从20%-30%飙升至80%以上。
- 与此同时,在第60分钟后,第3组指标(网络流入流量)出现了断崖式下跌,从100-200MB跌到了20-50MB。
根因推测:
这是非常典型的内存泄漏导致的问题。内存先发生泄漏并持续增长。当内存水位达到一定阈值时,触发了应用程序频繁的Full GC(垃圾回收)。GC过程会占用大量的CPU资源,导致CPU使用率飙升。由于CPU被GC任务占满,无法及时处理正常的网络I/O请求,导致网络流入流量大幅下降。”
你看,它把我们埋在代码里的逻辑完完整整地挖了出来,不仅找出三个异常点,更理出了“内存先变 → CPU 后变 → 网络跟着变”的时间顺序。
如果你把这段分析报告发给老板,老板一看就懂了。他会马上让开发去查是不是有内存泄漏的代码。这就是多指标交叉分析的价值。它帮你把散落的线索串成了一条完整的证据链。
五、 如果它分析跑偏了怎么办
5.1 大模型“瞎编”关联性的情况
当然,大模型并非万能。有时你给它三组其实毫无关联的数据,它为了交差也会硬编出一套因果。
比如内存其实只是正常波动,CPU也只是碰巧高了一下。它可能也会给你分析出一堆因果关系。这种情况在业内叫作“幻觉”。它不知道,但是它装作知道。
那怎么避免这种情况呢?这就需要我们在提示词里加上限制条件。
5.2 调整Prompt的技巧:加入否定指令
你不能光让它找,你得告诉它,如果找不到,就直说。我们把刚才的 build_cross_prompt 函数稍微改几句话。
def build_cross_prompt_safe(formatted_data_text):
prompt = f"""
你是一个严谨的运维专家。下面是同一台服务器的三组监控数据。
请对比这三组数据在时间线上的变化。
注意:只有当某个指标的异常明显发生在另一个指标异常之前(比如相差两三个时间点以上),并且逻辑上存在合理的因果关系时,你才可以将它们关联起来分析。
如果三个指标的异常是同时发生的,或者没有明显的时间先后顺序,请不要强行建立因果关联,只需分别列出它们各自的异常情况即可。
严禁在没有时间先后证据的情况下猜测根因。
数据如下:
{formatted_data_text}
"""
return prompt
这次我加了不少“只有当…才…”“请不要强行…”“严禁…”这类限定词,等于给它上了紧箍咒。
你告诉它了,必须要有时间差,必须要逻辑合理。如果没有,就老老实实分开报。这么一改,它乱编的概率就会大大降低。
写提示词其实就跟带新员工一样。你指令越模糊,他干得越走样。你得把边界划得清清楚楚,哪些能干,哪些不能干,它才能干好。
六、 再聊聊Token消耗和成本的问题
6.1 数据量翻倍,Token也翻倍
三组数据丢进去效果不错,但有个绕不开的现实问题:成本。
我们前面提到过Token的概念。你发过去的字数越多,消耗的输入Token就越多。你要求它做复杂的交叉分析,它生成的回答也会变长,消耗的输出Token也就越多。
三组数据,每组100条。这就好几千个Token了。如果你要分析100台机器,每台机器三组数据,那你跑一次脚本消耗的Token量是非常惊人的。
所以,你在去 这个后台看账单的时候,一定要心里有数。不要写个死循环无限次地去调。一定要在代码里做好异常拦截,避免发了无效的请求还在傻傻等。
6.2 压缩数据文本的小技巧
既然 Token 要花钱,能不能在不影响效果的前提下,把发过去的文本压短些?当然可以。
看现在的数据格式:“2023-10-24 10:00:00, CPU使用率, 25%”
这里面其实有很多废话。比如“2023-10-24”这个日期,如果我们的数据都是在同一天的,那这个日期部分就完全多余了。大模型又不需要知道今天是几月几号,它只需要知道时间点的先后顺序。
我们可以把格式简化成这样:“00:00:00, CPU, 25”。你看,省了多少个字符。
如果同在 10 点到 12 点这样的小区间里,你甚至可以把小时省掉,只留分钟和秒:“00分00秒, CPU, 25”。
你别小看省下来的这几个字。乘上几百条数据,再乘上三组指标,省下来的Token可能就有上千个了。长年累月下来,这也是不少钱。
我们在代码里改一下拼接的逻辑就行了。
for i in range(100):
time_label = f"第{i}分钟"
cpu_data.append(f"{time_label}, CPU, {cpu_val}%")
这种极简格式,大模型依然能看懂。它依然能根据“第50分钟”、“第60分钟”这种标签来判断先后顺序。但是你传输的成本就降下来了。这也是在实际业务里写代码必须要考虑的优化点。
七、 总结与下期预告
7.1 今天学到了什么核心逻辑
今天这篇信息量不小。我们解决了一个核心痛点:怎么让大模型做关联分析。
我们知道了单指标分析往往找不到根因。我们学会了在代码里造有联动关系的假数据。我们掌握了多组数据的排版技巧,要用明显的分隔符把它们隔开。
更重要的是,我们学习了怎么通过写提示词里的具体要求,去引导大模型关注“时间轴上的先后顺序”。我们还学了怎么加限制条件,防止它乱编因果关系。
最后我们也聊到了成本控制的问题。知道了可以通过简化时间戳的格式来压缩Token。
这些其实都是能直接落地的干货。把这套多指标交叉分析跑通,你就能拿去处理真实的运维报警。
7.2 下一期我们玩点更高级的
不过现在还是在造假数据玩。真实环境里,数据都躺在数据库里,比如 IoTDB、MySQL。
总不能每次分析,你都先写个脚本把数据导出来成txt,然后再读进Python里去调接口吧。那样太繁琐了。
所以下一篇,我们打算引入真实的数据库查询,在代码里直连数据库把 SQL 结果喂给大模型。
这个的话,就会涉及到一些数据库驱动安装的东西了。如果你对这一块感兴趣,那就继续跟着往下看吧。我们下期见。

评论0