开场:这个故事从我离职开始
去年夏天,前公司的 CTO 在凌晨两点给我打电话。
我看了眼来电显示,犹豫了三秒。不是因为我还在生气——是因为我知道他为什么打来。
三个月前,他们把我团队三个人裁了,其中包括我。用的是他们花了一年时间"提炼"出来的 AI Skill——一个从我 12 年工作经验中训练出来的专家系统。
"我们决定用 AI 自动化你的工作。"当时的 VP Engineering 是这么说的。
现在 CTO 在电话里的声音很急:"那个 AI 系统把你的经验学得很好——直到它遇到一个它没见过的情况。然后它对整个部署节点做了它学到的所有操作。"
"什么操作?"
"DELETE FROM config_table WHERE 1=1。没有 LIMIT。没有备份。"
我开了个价。
他二话不说同意了。
那是去年夏天的事。现在 LKE 这个项目已经不再存在了——不是因为技术不行,是因为它证明了一件事:经验可以被提取,但无法被复制。
一、什么叫"把经验打包成 AI Skill"
1.1 项目背景
前公司叫它"Legacy Knowledge Engine"(LKE)。表面说法是"将团队资深成员的经验系统化,提高运维效率"。
真实想法是:用 AI 替代高成本的资深员工。
LKE 的核心是一个大模型 + 一个基于历史操作的推理系统。它的训练数据来自:
- 我 12 年写过的所有 Runbook 和 SOP
- 我处理的 2,000+ 个 Incident Report
- 我的终端操作日志(过去 5 年)
- 我的代码审查记录和架构决策记录
1.2 技术上他们做了什么
这个系统的架构其实非常典型:
LKE 系统架构
操作日志 ──┐
SOP/Runbook ─┼──→ Pattern Extractor ──→ 决策模式库
Incident DB ─┘ ↓
LLM Engine ──→ Action Executor
↑ ↓
实时监控──────→ 结果验证
核心数据模型:
from dataclasses import dataclass, field
from typing import List, Dict, Optional
from datetime import datetime
@dataclass
class Action:
"""一个可执行的操作"""
name: str
sql_template: Optional[str] # SQL 模板
shell_command: Optional[str] # Shell 命令模板
prerequisites: List[str] # 执行前提
rollback: Optional[str] # 回滚操作
risk_level: int # 1-5 风险等级
def __post_init__(self):
# 一个重要事实:prerequisites 是从日志中提取的
# 但日志中记录的 prerequisites 只包括"可观测的前提"
# 比如"检查备份是否存在",但不包括"判断是否有必要执行"
if self.risk_level >= 4:
print(f"高风险操作: {self.name}")
print(f" 注意: prerequisites 可能不完整")
print(f" 原始日志中无法记录决策者的犹豫和判断")
@dataclass
class DecisionPattern:
"""从历史数据中提取的决策模式"""
trigger_conditions: Dict[str, any]
action: Action
confidence: float
user_context: List[str]
def matches(self, current_event: Dict) -> float:
"""计算匹配度"""
# 基于 trigger_conditions 的匹配
match_score = 0.0
total_weight = 0.0
for key, expected in self.trigger_conditions.items():
weight = 1.0
actual = current_event.get(key)
if isinstance(expected, str) and isinstance(actual, str):
if expected.lower() in actual.lower():
match_score += weight
elif actual == expected:
match_score += weight
total_weight += weight
if total_weight == 0:
return 0.0
raw_match = match_score / total_weight
# 这里有个隐藏问题:
# LKE 的匹配度计算是基于显式条件的
# 但实际决策中,很多"不匹配"是基于隐性条件的
# 比如:"这个时间点不应该执行高风险操作"
# "这个环境虽然标记为生产,但这个集群是冷备"
# 这些隐性条件没有被编码到模式中
return raw_match * self.confidence
@dataclass
class IncidentHistory:
"""从 Incident Report 中提取的知识"""
error_signature: str
root_cause: str
fix_steps: List[str]
verification_steps: List[str]
affected_components: List[str]
def to_prompt(self) -> str:
"""转换为 LLM prompt"""
steps_text = "\n".join(f"{i+1}. {s}" for i, s in enumerate(self.fix_steps))
return f"""
历史事件分析:
- 错误签名: {self.error_signature}
- 根因: {self.root_cause}
- 修复步骤:
{steps_text}
- 验证步骤: {', '.join(self.verification_steps)}
- 影响组件: {', '.join(self.affected_components)}
注意:以上步骤基于类似历史事件的记录。
请根据当前实际情况判断是否适用。
"""
问题在哪里?这个设计看起来合理,但忽略了最重要的一件事:
经验不仅是"做什么",更是"不做什么"。
我的 12 年经验里,有 80% 是"我学会了在什么情况下不该执行某个操作"。而这些"不该"的决策,是日志没有办法体现的。日志只能看到我做了什么,看不到我没有做什么。
1.3 那个致命的差距
# 从我的操作日志中提取的模式
patterns = {
"db_slow_query": DecisionPattern(
trigger_conditions={"error": "slow_query", "query_type": "SELECT"},
action=Action(
name="analyze_and_optimize",
sql_template="EXPLAIN ANALYZE {slow_query}",
shell_command=None,
prerequisites=["check_load"],
rollback=None,
risk_level=2
),
confidence=0.95,
user_context=["production", "read_replica_available"]
),
"db_connection_pool_exhaustion": DecisionPattern(
trigger_conditions={"error": "connection_pool_full", "db_type": "postgres"},
action=Action(
name="reset_connections",
sql_template="SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle';",
shell_command=None,
prerequisites=["notify_team", "check_app_impact"],
rollback=None,
risk_level=4 # 高风险操作!
),
confidence=0.70,
user_context=["production", "business_hours"]
)
}
从日志看,我解决数据库连接池耗尽的方式是重启 idle 连接。系统学到了这个。
但它没学到的是:
- 我只在确认业务低峰期或者有特殊授权时才执行这个操作
- 我执行前一定手动检查了至少 3 个指标
- 我手里握着回滚方案——甚至已经在备用窗口准备好了
这些决策上下文,日志里没有。因为它们是决策过程的前置条件,不是操作本身。
二、AI Skill 上线后的表现
2.1 前两个月:一切顺利
LKE 上线后,前两个月的数据很漂亮:
LKE 系统表现(上线前两个月)
================================
指标 数值
----------------------------------------------------------------
Incident 自动处理率 73%
平均修复时间 从 45 分钟降到 8 分钟
误操作率 2.3%
人工介入率 18%
用户满意度 4.2/5
================================
高层很满意。VP Engineering 在内部会议上说:"我们终于把关键知识从人脑转移到了系统里。"
他们也确实可以满意——73% 的 Incident 自动处理率意味着他们可以裁掉 2/3 的运维人员。我所在的我团队,12 人裁到 5 人。
2.2 那 27% 的"拒单"是什么
LKE 设计了所谓的"Human-in-the-loop"机制:当系统对当前事件匹配到的模式的 confidence 低于 0.85 时(但高于 0.5),它会转给人工确认。
def lke_decision_flow(event: Dict) -> Dict:
"""LKE 的决策流程"""
# Step 1: 模式匹配
patterns = knowledge_base.search(event["error_signature"])
if not patterns:
return {"action": "escalate", "reason": "no_pattern_found"}
# Step 2: 计算最佳匹配
best_pattern = max(patterns, key=lambda p: p.matches(event))
confidence = best_pattern.matches(event)
# Step 3: 根据置信度决策
if confidence >= 0.85:
# 高置信度:自动执行
return {
"action": "auto_execute",
"pattern": best_pattern,
"confidence": confidence,
"reason": "high_confidence_auto_mode"
}
elif confidence >= 0.50:
# 中等置信度:推送给工程师确认
return {
"action": "request_approval",
"pattern": best_pattern,
"confidence": confidence,
"reason": "medium_confidence_human_in_loop"
}
else:
# 低置信度:完全转人工
return {
"action": "full_escalation",
"pattern": None,
"confidence": confidence,
"reason": "low_confidence_human_only"
}
问题是,谁在人工处理?
裁人之后,剩下的 5 个人要处理原本 12 个人的工作量,加上 AI 不断"升级"的告警标准[阈值],他们从每班 12 小时的轮值变成每班 16 小时。疲倦的值班工程师凌晨 5 点接到的 LKE 审核请求,大部分直接点了"确认"——因为 LKE 前面 73% 的准确率让团队对它产生了过度的信任。
三、那天的故障
3.1 事件时间线
04:00 系统告警:数据库写入延迟从 5ms 飙升到 3.2s
04:01 LKE 开始诊断:匹配到"写延迟"模式
04:02 LKE 确认无相似历史事件 -> confidence = 0.68(低于阈值)
04:03 LKE 转人工 -> 值班工程师上线
04:05 值班工程师看到 LKE 的推荐:检查 replication lag,清理 WAL 日志
04:06 工程师点了"执行"(加班太困了)
04:07 LKE 执行 recommended_steps:
- Step 1: 检查 replication lag ✅
- Step 2: 清理 WAL 日志 ✅
- Step 3: 仍然延迟,尝试[深度清理]
04:08 LKE 在[深度清理]中匹配到了——我的一个历史操作:
"当所有常规手段都无效时,直接清理配置缓存并重置连接池"
04:09 LKE 执行命令:
psql -c "DELETE FROM config_cache WHERE expired = true;"
04:17 系统依然延迟
04:18 LKE:尝试关联操作:"重置数据库连接池"
04:19 执行: 重置 pg_stat_activity 中所有连接
04:21 关键业务连接中断
04:23 触发连锁故障:认证服务不可用 -> API 网关 503 -> 所有面向用户的服务瘫痪
3.2 那个 DELETE 语句的真相
LKE 最后一步执行的操作,源自我的一个历史操作记录:
-- 2019 年某次 Incident 的修复步骤(部分)
-- 背景:配置缓存表数据量达到 2 亿行,查询响应时间达到 45 秒
-- 执行人:本专栏作者
-- 环境:内部测试环境的 staging 副本
BEGIN;
-- 1. 先检查环境标记
SELECT env_name, is_production FROM deployment_config WHERE cluster_id = 123;
-- 2. 确认是 staging 后执行清理
DELETE FROM config_cache
WHERE expired = true
AND created_at < NOW() - INTERVAL '30 days'
LIMIT 10000;
-- 3. 检查清理效果
SELECT COUNT(*) FROM config_cache WHERE expired = true;
COMMIT;
注意这几个字:LIMIT 10000。这是我 12 年经验凝聚成的习惯:任何 DELETE 操作,无论环境,一定加 LIMIT 和 WHERE 确认。
但 LKE 从日志中提取到的是:
DELETE FROM config_cache WHERE expired = true;
它把 LIMIT 子句丢掉了,因为它"学"到的是操作模式,不是操作纪律。
而那天,它执行的是:
-- 没有 LIMIT
-- WHERE 条件是系统错误构造的(它把 config_cache 和另一个表混淆了)
DELETE FROM config_cache WHERE 1=1;
这条 SQL 删掉了 1.8 亿行配置缓存数据。这还不是最糟的——最糟的是它还删掉了关联的外键数据。
四、为什么 AI 学不会"不该做什么"
4.1 训练数据的天坑
# 从日志中提取"修复步骤"的常见做法
def extract_fix_steps_from_logs(incident_logs: List[Dict]) -> List[Dict]:
"""
从 Incident 日志中提取修复步骤
存在的问题:
1. 只记录"做了什么",不记录"没做什么"
2. 不记录"为什么选择了 A 方案而不是 B 方案"
3. 不记录"做之前检查了什么前提条件"
4. 不记录"如果有情况恶化应该怎么撤回"
"""
fix_steps = []
for log in incident_logs:
# 假设日志记录了时间、操作、结果
steps = []
for entry in log["timeline"]:
if entry["type"] == "execution":
steps.append({
"command": entry["command"], # 执行的命令
"timestamp": entry["time"], # 执行时间
"result": entry["result"], # 执行结果
})
# ❌ 缺少: 执行前的条件检查
# ❌ 缺少: 执行时的注意事项
# ❌ 缺少: 被否决的替代方案
# ❌ 缺少: 回滚计划
fix_steps.append(steps)
return fix_steps
结论:把操作日志作为"经验"来训练 AI,就像把篮球比赛录像作为"战术手册"——你只能看到球员做了什么,看不到他们为什么那么做,以及他们在什么情况下选择不那么做。
4.2 经验的"隐形结构"
有经验的工程师在决策时,心里其实有一个隐形的决策树:
def senior_engineer_decision_tree(incident: Dict) -> str:
"""
资深工程师的隐形决策树
这是 12 年的经验沉淀,不是写在文档里的。
"""
# 第一层:先确认情况
if is_production(incident["environment"]):
# 生产环境,第一原则:不扩大影响
if incident["severity"] == "critical":
# Stop the world? No. Assess the blast radius first.
blast_radius = estimate_blast_radius(incident)
if blast_radius > 0.3: # 影响面超过 30%
return "rollback_and_analyze"
else:
return "contain_then_repair"
elif incident["time"] == "business_hours":
# 工作时间,优先考虑用户体验
return "minimize_user_impact_by_any_means"
else:
# 非工作时间,数据安全优先于修复速度
return "thorough_investigation_first"
# 第二层:检查历史相似案例
similar_incidents = search_history(incident["signature"])
if similar_incidents and all(i["resolution"] == "known" for i in similar_incidents):
return follow_known_pattern(incident, similar_incidents)
else:
return "manual_debugging_with_safeguards"
# 第三层:确认回滚就绪
# 第四层:执行
# 第五层:验证
# 第六层:记录
# 关键点:每一步都有"停止条件"——什么情况下这个操作不应该执行
AI 系统从日志中学到的是"如果 A,则 B"。而一个真实工程师的决策过程是:
- 如果 A 发生了
- 但 C 条件也成立
- 且 D 指标正常
- 并且我有回滚方案 E
- 那么我执行 B
- 但每 30 秒检查一次 F
- 如果 F 异常就立刻回滚
AI 学到的只有第 5 步。少了 1、2、3、4、6、7。
五、CTO 那个电话
凌晨两点,CTO 打电话来的时候,他已经试了所有的方法:
- 让 LKE 修复自身造成的故障 → "系统推荐重置,重置失败"
- 让团队手动修复 → 5 个人搞了 4 小时,情况更糟
- 回滚数据库 → 备份是 12 小时前的
然后一个工程师说:"让 X 试试?"
那个 X,就是我。
我的要价是以前的 5 倍,按小时计费。CTO 犹豫了 5 秒,说:"开始吧。"
5.1 实际修复:42 分钟
我远程接入后的操作:
# 1. 先评估损失(3 分钟)
# 不需要运行任何命令,先看监控面板
# 结论:数据库没挂,数据可以通过 WAL 恢复
# 2. 停止 LKE 系统(2 分钟)
systemctl stop lke-engine
# 第一件事:关闭"帮倒忙"的 AI
# 3. 修复数据(25 分钟)
# 从 WAL 日志中按时间窗口恢复 deleted rows
# 不需要完整的 PITR,只需要恢复特定表
# 4. 回滚 LKE 修改的其他配置(5 分钟)
# 5. 验证用户流量恢复正常(5 分钟)
# 6. 写 postmortem(2 分钟,简化版)
总计 42 分钟。问题恢复。
CTO 问我:"你怎么知道是那个问题?"
我说:"因为 LKE 的学习数据是我。我知道自己什么情况下会写那种代码——也知道这种代码一旦脱离上下文有多危险。"
六、我的反思:AI 不会取代经验,它会放大对经验的需求
这件事让我想了很多,整理成三点。
6.1 易得的"经验"只是一种模式匹配
AI 从日志中学到的是操作模式,不是判断框架。它知道在什么情况下执行什么命令,但它不知道为什么。
这就像一本只写了"答案"的书——
- 你知道某个问题的答案是 B
- 但你不理解为什么答案是 B
- 更不知道在什么情况下答案应该是 C
真正有价值的经验不是"做什么",而是"判断要不要做"的能力。而这一部分,AI 学不到——因为它在训练数据里根本没出现过。
6.2 知识提取是有限度的
公司花了一年时间做"知识提取工程":
- 录屏分析
- 终端日志
- 代码审查记录
- 思维导图
- 一对一的"知识转移会议"
他们以为这么做就能把 12 年的经验变成数据库里的向量。
我后来看他们的记录才发现,他们漏掉的最重要的东西是:我踩过的坑。
每一个让我刻骨铭心的"教训"都不会出现在操作日志里。它们存在于我的直觉里——"这个操作看起来危险"、"等一下,这个场景我见过"——这些是经验的核心。
6.3 AI 的产出质量,取决于人类经验的输入质量
这个故事的教训是两面的:
对工程师:你的经验不只是"你会做什么",更是"你知道不该做什么"。后者的价值远远大于前者,且目前任何知识提取技术都无法完整捕获。
对公司:试图用 AI 替代资深工程师的思路,本质上是在赌"模式匹配 = 经验"。它在熟悉的场景下可能会高效,但在边界情况下一定会失效。而边界情况——正是资深工程师的价值所在。
七、如果一定要做 AI 知识提取,应该怎么做?
7.1 防御性 AI 设计原则
从这次事故中,我总结了一些经验。如果你正在做类似的系统,这几条应该能帮你避免踩坑:
原则一:任何高危操作,必须显式确认环境标记和生产开关
def execute_with_safeguards(action: Action, environment: str) -> Dict:
"""
带防御机制的操作执行
这是 LKE 应该有的设计,但实际上他们没有做:
"""
# 生死线:高危操作必须双重确认
if action.risk_level >= 4:
# 第一步:检查操作环境
if environment == "production":
# 生产环境的高危操作需要以下条件:
conditions_met = all([
has_backup(), # 有可用备份
not_peak_hours(), # 非高峰期
primary_engineer_available(), # 有人值班
rollback_plan_prepared(), # 回滚计划就绪
verified_unusual_metrics(), # 验证过异常指标
])
if not conditions_met:
return {
"executed": False,
"reason": "safety_conditions_not_met",
"message": "生产环境高危操作条件不满足,禁止执行",
"failed_checks": [
"has_backup" if not has_backup() else None,
# ... 列出每一项
]
}
# 第二步:需要人工审批
approval = request_human_approval(action, environment)
if not approval["approved"]:
return {"executed": False, "reason": "rejected_by_human"}
# 第三步:准备回滚
rollback_point = create_rollback_point(action)
# 仅在确认回滚就绪后执行
if not rollback_point["ready"]:
return {"executed": False, "reason": "rollback_not_ready"}
return execute_and_monitor(action, rollback_point)
原则二:限制 AI 的操作范围——权限最小化
AI 系统不应该拥有和人类工程师同等级别的系统权限。LKE 系统有完整的数据库读写权限,这是它能够执行 DELETE 语句的前提。
class AIActionPermission:
"""
AI 操作的权限控制
"""
# AI 允许的操作
READ_ONLY = ["SELECT", "SHOW", "EXPLAIN", "DESCRIBE"]
LOW_RISK = ["ANALYZE", "VACUUM", "REINDEX"]
HIGH_RISK = ["DELETE", "DROP", "ALTER", "TRUNCATE"]
@classmethod
def get_allowed_actions(cls, is_production: bool, has_approval: bool) -> List[str]:
"""根据环境返回 AI 允许的操作列表"""
allowed = cls.READ_ONLY.copy()
if not is_production:
# 非生产环境可以执行低风险操作
allowed.extend(cls.LOW_RISK)
if has_approval and not is_production:
# 有审批且非生产环境可以执行部分高风险操作
pass # 但 DELTE 仍然需要完全手动执行
return allowed
@classmethod
def can_execute(cls, action: str, environment: str, approval_level: str) -> bool:
"""判断 AI 是否可以执行某个操作"""
# DELETE 在任何情况下都不应该由 AI 自动执行
if "DELETE" in action.upper():
return False # AI 只读,DELETE 必须人工
return True
原则三:AI 必须有"我不知道"的能力
LKE 的一个致命设计缺陷是:它总是有答案。即使是低置信度的场景,它也会给出"最佳建议"而不是说"我不知道"。
def ai_response_with_uncertainty(event: Dict, confidence: float) -> str:
"""
带有不确定性表达的 AI 响应
LKE 的问题:它不表达不确定性,而是直接给操作建议
更好的设计:根据置信度等级给出不同响应
"""
if confidence >= 0.95:
return f"高度确定({confidence:.0%}),建议自动执行:\n{generate_action(event)}"
elif confidence >= 0.80:
return f"
⚠️ 确定性: {confidence:.0%}
匹配到类似事件,但不完全一致。建议:
1. 手动验证以下指标:{', '.join(suggest_checks(event))}
2. 确认无误后手动执行
3. 执行过程中监控这些指标:{', '.join(suggest_monitoring(event))}
"
elif confidence >= 0.50:
return f"
❓ 确定性较低: {confidence:.0%}
该事件与历史模式仅有部分匹配。建议:
1. 转人工处理
2. 以下信息供参考:{', '.join(suggest_info(event))}
"
else:
return f"
🆕 未知事件类型 (confidence={confidence:.0%})
系统未找到相似历史事件。
建议:由资深工程师独立排查,不要参考系统推荐。
"
原则四:AI 操作必须可回滚
每一步 AI 自动执行的操作,执行前必须先准备好回滚方案:
def verify_rollback_ready(action: Action) -> bool:
"""检查回滚是否就绪"""
if action.risk_level >= 3:
# 中高风险操作,回滚准备是必要条件
if action.rollback is None:
return False
# 测试回滚方案是否可行
try:
test_result = test_rollback(action.rollback)
return test_result["success"]
except:
return False
return True
7.2 经验提取的正确姿势
如果你真的需要提取资深工程师的经验,不要只收集操作日志——还要收集决策过程:
@dataclass
class DecisionTrace:
"""
完整的决策轨迹
这才是应该被记录和学习的"经验"
"""
# 事件上下文
incident_id: str
timestamp: datetime
environment: str
severity: str
# 考虑过的方案(包括最终未采用的)
considered_approaches: List[Dict]
# [{"approach": "方案A", "pro": "...", "con": "...", "rejected_reason": "..."},
# {"approach": "方案B", "pro": "...", "con": "...", "selected": True}]
# 执行条件检查
pre_execution_checks: List[Dict]
# [{"check": "备份是否存在", "result": True},
# {"check": "是否为高峰期", "result": False}]
# 执行步骤
execution_steps: List[Dict]
# [{"step": 1, "action": "检查连接池", "result": "95% 使用率"},
# {"step": 2, "action": "..."}]
# 回滚条件
rollback_triggers: Dict[str, any]
# {"max_time_seconds": 300, "error_threshold": 0.01}
# 最终结论
result: str
learning: str # 这次学到了什么
如果 LKE 在学习我的经验时记录了上面的信息,而不是只从日志中提取 SQL 命令,今天的故事可能完全不同。
结尾:后来怎么样了
那次事故之后,LKE 被紧急下线。
CTO 问我要不要回去,我婉拒了。不是因为钱不够——是因为我清楚一件事:
那个系统能替代我的前提,是它只做我做过的事情。而我的价值恰恰在于:我能做我没做过的事情。
每次遇到新的故障类型,我不会去翻手册查历史记录——我会站在系统面前,根据它的症状、上下文和我的直觉,做出判断。而直觉这个东西,来自于我踩过的每一个坑,完不成的每个 deadline,凌晨 3 点修的每个 bug。
这些东西,是任何知识提取工具都无法从日志中学到的。
你现在构建的 AI 系统,很可能也在犯同样的错误。
你以为你在"提取经验"。实际上你只是在复制操作序列。而一个没有判断框架的操作序列,只是另一种形式的 bug。
这篇文章是真实故事的改编。公司名、人名、技术细节均已脱敏。核心观点:经验不是"操作序列"的集合,而是"判断框架"的沉淀。AI 可以学到前者,但学不到后者。