把告警初筛交给模型,能省下的是第一遍读日志的时间,不能省的是对高风险结论的确认。要让这件事真正跑起来,通常先定三件事:模型能看到哪些字段、它只能输出什么结构、哪些结论必须回到人手上。Gemini 3.5 Flash Cyber 在这条链路里适合做第一段,把一条告警读成“像真、像误报、信息不够”三类,并给出原始日志里的依据;至于封禁、关单、联系业务这类动作,放在人工确认点之后。下面按可执行的顺序给字段映射、复核触发条件和记录方式,接口和平台按自己环境替换。
告警初筛可以先用模型跑,但边界要写在配置里:输入只给规则名、来源、资产等级和裁剪后的日志片段,输出只接受固定字段的 JSON,缺少依据或涉及核心资产的结论一律转人工。判断初筛是否可用,不看模型说了什么,而看十条样本里结构化结果能解析出几条、人工复核记录与模型结论的差异能不能逐条对上。做不到这两点,先把范围缩到低等级规则试。
在近七天告警里挑出可复现的样本集
样本集的作用是让每次评估有对照。换一批告警就换一个结论,说明不了模型行不行,只能说明样本在变。建议先从近七天里挑三到五条高频规则,固定下抽取口径后再扩。
抽取字段至少包含四项:事件时间、来源(数据源或设备类型)、规则名、原始日志行。下面的查询只是骨架,表名和字段名按本地 SIEM 或日志库替换,拿到结果先人工看一眼字段是否齐全。
-- 字段名按本机告警表替换,先限定规则范围再取量
SELECT
event_time,
source,
rule_name,
raw_log
FROM alerts
WHERE event_time >= now() - interval '7 days'
AND rule_name IN ('规则A', '规则B', '规则C') -- 先选 3~5 条高频规则
ORDER BY event_time DESC
LIMIT 200;
去重分两步走。第一步做机器去重:把原始日志里的行号、时间戳、PID、请求 ID、连续空白替换成占位符,再按“规则名 + 归一化日志”算重复,重复的只留最早一条并把重复次数记在附列上。第二步做人工标注:每条样本标注为真告警、误报、信息不足三类之一,标注时只看日志本身,不参考历史处置结论,否则模型评估会被旧结论带偏。
样本量需要写进文件头:抽取时间范围、抽取规则、原始条数、去重后条数、实际标注条数、标注人。样本文件按日期命名并保留原始日志列,后续第五节回看差异时要用同一份样本做对照。
把告警字段映射成模型输入
映射的目标是让模型看到判断所需的最少信息,同时让“看不到什么”有明确记录。字段缺失时统一写 unknown,不要让模型去猜。
| 告警字段 | 是否入模 | 处理方式 |
|---|---|---|
| event_time | 入模 | 截到分钟,附时区 |
| source(数据源/设备类型) | 入模 | 原样传入 |
| rule_name | 入模 | 原样传入,不改写成自然语言 |
| asset_level(资产等级) | 入模 | 取自资产表,缺失写 unknown |
| src_ip / dst_ip / user | 入模 | 用于“涉及对象”字段 |
| raw_log | 裁剪后入模 | 见下方裁剪策略 |
| 历史处置结论、处理人、工单号 | 不入模 | 避免模型顺着旧结论做判断 |
| 完整原始日志 | 不入模 | 只留在本地,供人工复核时查 |
超长日志的裁剪位置比裁剪长度更关键。建议保留头部若干行与尾部若干行,中间用一行占位标记截断,例如 [truncated 120 lines];命令行类日志优先保留触发命令前后那一段。不要从日志中间随机截,否则判定依据会被切断,模型给出的“依据”和原文对不上。裁剪阈值按本机日志长度分布定,先用几十条样本试出上限再写进配置。
提示骨架只给通用写法,具体调用方式按所在平台的接入方式替换。关键是把字段边界和输出要求写在同一段里:
你是安全告警初筛助手,只依据下列字段判断,缺失信息不要推测。
输入:
- event_time: {event_time}
- source: {source}
- rule_name: {rule_name}
- asset_level: {asset_level}
- log_excerpt: """{log_excerpt}"""
要求:
1) verdict 只能是 true_positive / false_positive / insufficient_info 之一
2) evidence 必须引用 log_excerpt 中的原文片段,最多 3 条,不要改写原文
3) entities 列出涉及的主机、账号、IP,没有就写 unknown
4) confidence 只能是 high / medium / low,并说明是字段缺失还是日志歧义
5) 无法判断时输出 insufficient_info,不要猜
只输出 JSON,不要输出其他文字。
让输出固定成可解析的结构
初筛结果要进入后续流程,就不能是一段散文。输出字段固定为四项:判定(verdict)、依据(evidence)、涉及对象(entities)、置信描述(confidence)。置信描述用 high / medium / low 三档并附一句原因,不用数字打分,避免看起来精确而实际无法校验。
解析失败要有兜底:原始输出不丢,落进待处理区并标记 parse_failed,同时标记失败原因;这类结果直接进人工队列,不能因为“没解析出来”就当作误报放过去。下面的骨架只做结构校验,具体存放位置按本地流程接。
import json
REQUIRED = ("verdict", "evidence", "entities", "confidence")
ALLOWED_VERDICT = {"true_positive", "false_positive", "insufficient_info"}
def parse_result(raw_text):
try:
data = json.loads(raw_text)
except json.JSONDecodeError:
return {"parse_ok": False, "reason": "json_error", "raw": raw_text}
missing = [k for k in REQUIRED if k not in data]
if missing:
return {"parse_ok": False, "reason": "missing:" + ",".join(missing), "raw": raw_text}
if data["verdict"] not in ALLOWED_VERDICT:
return {"parse_ok": False, "reason": "bad_verdict", "raw": raw_text}
data["parse_ok"] = True
return data
验证方式很直接:从第一节的样本集里取十条,逐条跑解析函数,人工数出 parse_ok 为真的条数,并看失败原因集中在哪类。常见失败是缺字段、模型用中文标点或代码块包住 JSON、把依据写成自由文本,这几种先回去改提示词,不要急着写复杂的容错解析器。
用人工确认点限制高风险结论
有些结论不能由模型单独给出,判断标准要写在流程里而不是靠人临场把握。建议至少覆盖以下触发条件,命中任意一条即转人工:
- 涉及对象等级高:核心资产、生产数据库、域控或统一身份系统相关的主机与账号。
- 规则类型属于高风险:权限提升、账号创建或授权变更、横向移动、对外数据外发、安全设备策略被修改。
- 输出缺少依据:evidence 为空,或引用的片段在裁剪日志里找不到对应原文。
- 判定与规则等级冲突:高等级规则被判为误报,或低等级规则被判为真告警但没有任何对象信息。
- 解析失败:兜底路径里的结果一律人工过目。
- 日志被截断且截断处可能藏有关键信息,模型又在置信描述里提到日志歧义。
复核记录建议固定填这些字段:告警 ID、模型判定、模型依据原文、人工结论、人工依据、是否与模型一致、不一致原因、复核人、复核时间。不一致原因用有限选项,例如“日志被裁剪”“规则本身误配”“资产等级标错”“模型漏看对象”,后续统计才好归类。
回看初筛与人工结论的差异
差异回看的目的不是给模型打分,而是判断下一轮该动哪一端。统计口径建议固定为交叉表:以人工结论为准,分别统计“模型判真告警且人工一致”“模型判误报但人工判真”“模型说信息不足”的数量,再按规则名和资产等级各拆一张,看偏差集中在哪类告警上。
每条初筛记录需要留存的字段包括:告警 ID、样本集批次、入模字段的完整快照(含裁剪后的日志和截断标记)、模型原始输出、解析结果、是否触发人工确认点、人工结论、复核时间。日志留存要注意原始日志和入模快照分开存,否则无法判断问题出在裁剪还是判断。
下一轮调整方向的判断可以先用这套粗规则:如果大量差异是“证据缺失、模型说信息不足”,优先改输入,补字段或调整裁剪位置;如果证据引用正确但结论与人相反,优先改约束,收紧提示词里的判断标准和人工触发条件;如果差异集中在某一条规则上,先查规则本身是否误配,别急着改模型侧。每次只改一处,改完仍用同一份样本集对照,才看得出是哪一处起了作用。