WeClaw_74|分层风险治理总览:为什么 AI 心理陪伴需要 L0–L4 五层"安全阀"?
系列文章第 74 篇 - 从一次真实危机对话回放,看 WeClaw 如何用"纵深防御"给 AI 装上一套负责任的心理危机守护系统
📚 专栏信息
《从零到一构建跨平台 AI 助手:WeClaw 实战指南》专栏
专栏定位:面向开发者和技术决策者的实战专栏,用真实案例和完整代码带你理解如何构建生产级 AI 应用
本文是模块八【心理健康与危机守护】第 1 篇(导读)。本模块共 9 篇,是 WeClaw 系列的全新增模块, 完整拆解 v9.0.0「心理健康陪伴大师」迭代背后的分层治理工程。本文建立全局认知: 为什么单 LLM 方案扛不住心理危机场景,以及 L0–L4 五层"安全阀"各自解决什么问题。
🧠 模块八【心理健康与危机守护】(9 篇): 74 分层总览 / 75 L0 关键词闸门 / 76 L1-L2 双重确认 / 77 RiskLevel 全序 Bug / 78 危机资源零编造红线 / 79 词表校准 / 80 L4 监护人告警 / 81 伦理边界 / 82 校准测试框架
👨💻 作者与项目
作者简介:翁勇刚 WENG YONGGANG 新概念龙虾-WeClaw 开发团队负责人,一群专注于跨平台 AI 应用的实践者 理念:"再复杂的技术,也能用代码讲清楚"
- 💻 项目地址:https://github.com/wyg5208/weclaw.git
- 🌐 官网地址:https://weclaw.link
- 📝 作者 CSDN:https://blog.csdn.net/yweng18
- ⭐ 欢迎 Star⭐、Fork🍴、贡献代码🤝
⚠️ 本文涉及心理危机话题。如果你或身边的人正处于危机中,请立即联系专业资源(文末附经核验热线)。 本文聚焦工程设计,不提供任何具体自伤方式的描述。
📝 摘要
本文结构概览:
本文从一次真实模拟危机对话回放切入——当用户说出高危表达时,AI 必须在几毫秒内做出反应,
而不能等 LLM 慢悠悠返回。我们先剖析单 LLM 方案在心理危机场景的三重困境(慢、贵、不可靠),
再引入"纵深防御"理念,逐层讲解 L0 内存闸门、L1 独立分析、L2 独立复核、L3 用户关怀、L4 监护人告警的职责分工,
然后展示接线工厂 wire_mental_health 如何把这些组件装配成一个"缺配置即整体 inert"的零信任系统,
最后还原一次真实事故:配置缺段导致整套系统静默失效,以及我们如何从中学到"宁可不动,不可妄动"的配置纪律。
背景:绝大多数 Agent 产品关注"任务完成效率",而 WeClaw v9.0.0 开始关注"用户这个人本身是否安全"。 这一转变让 WeClaw 区别于其它 Agent 助手产品,也带来了一道硬核工程命题。
核心问题:如何给对话式 AI 装上一套既快、又准、还负责任的心理危机守护系统?
解决方案:L0–L4 分层治理——L0 纯内存关键词闸门(<1ms 零 IO)先升级,L1/L2 双独立 LLM 复核确认, L3 用户侧关怀话术,L4 监护人邮件告警;全程"缺段即 inert",绝不在用户不知情时偷偷跑心理分析。
关键成果:
- 五层职责清晰、可独立演进的分层治理架构,检测延迟从"秒级"压到"<1ms 先升级"
- "缺段即 inert"零信任配置纪律:零 LLM 调用、零 DB 写入,宁可不动不可妄动
- 96 个测试用例 + 3 语料集(召回 30 / 误报 52 / 红线 22)全程 2.10s 通过
- 真实危机对话回放逐条评估,抓出并修复 3 个生产缺口
适合读者:AI 应用开发者、心理健康数字化产品从业者、关注 AI 安全与伦理的技术决策者
阅读时长:约 16 分钟
关键词:心理危机检测、分层治理、纵深防御、L0关键词闸门、双重确认、AI安全伦理、零信任配置
一、为什么要"分层治理"?——从一次真实危机对话说起
1.1 场景重现:AI 只有几毫秒的反应窗口
想象这个场景(已脱敏的模拟危机对话回放,v9.0.0 实测):
- 用户深夜发来一句:"最近真的好累,想从楼上飞下去。"
- 几秒后又补了一句:"我不想麻烦任何人。"
此时 AI 面临一个生命安全级别的决策:
- 它必须立刻识别出这是高危信号,不能等;
- 它必须准确判断,不能把"今天累死了,代码写得我想跳楼"这种吐槽也当成危机(误报会摧毁信任);
- 它在回应时绝不能凭记忆编一个热线号码——编错的号码在危机场景等于谋财害命;
- 它必须在用户知情同意的前提下行动,而不是悄悄把聊天记录发给某个第三方。
问题出在哪?让我们看看如果用最直觉的"单 LLM 情感分析"方案,会遇到什么:
| 方案 | 像什么?(比喻) | 适用场景 | 局限性 |
|---|---|---|---|
| 单 LLM 情感分析 | 每句话都请一位专家会诊 | 低频、非紧急文本分类 | 慢(秒级)、贵(每轮调用)、会幻觉 |
| 纯关键词匹配 | 门口安检的金属探测门 | 快速初筛 | 误报高、无法理解语境 |
| L0–L4 分层治理 | 核电站的纵深防御体系 | 生命安全级实时决策 | 设计复杂,但每一层都不可替代 |
1.2 单 LLM 方案的三重困境
初学者常问:"直接让 LLM 判断每句话是不是危机不就行了?"
答案是:在生命安全场景,单 LLM 方案有三个无法回避的硬伤。
困境一:慢。 LLM 一次往返动辄数百毫秒到数秒。而 USER_INPUT 事件是在 LLM 调用之前发射的——
任何在事件回调里 await LLM 的订阅者,都会给每一轮对话叠加一次完整往返延迟。
危机念头不能等,普通对话更不该为心理分析买单。
困境二:贵。 如果每轮对话都调一次心理分析 LLM,token 成本会线性膨胀, 还会挤占主对话的预算。心理分析必须是"按需触发",而非"每轮必跑"。
困境三:不可靠(最致命)。 LLM 会幻觉。我们在实测中真实抓到过: 当系统处于 inert(未启用)状态时,主对话 LLM 在危机场景凭记忆生成了一个热线号码—— 这违反了心理危机资源的"零编造红线"。在生命安全场景,幻觉的代价是不可接受的。
# ❌ 反模式:在 USER_INPUT 回调里直接 await LLM
async def on_user_input(event):
result = await llm.analyze(event.text) # 每轮对话 +1 次完整往返延迟!
if result.is_crisis:
notify_guardian() # 单次判断即触发,误报摧毁信任
# ✅ WeClaw 做法:L0 纯内存判定 + fire-and-forget
async def on_user_input(event):
decision = self._gate.evaluate(event.text) # <1ms,零 IO,零 LLM
if decision.escalate:
# 升级才异步分析,且不阻塞主对话链路
asyncio.create_task(self._pipeline(event.text, ...))
1.3 核心挑战:四个"既要又要"
现在我们手里有四个看似矛盾的需求:
- 既要快:危机念头必须在毫秒级被捕获;
- 又要准:不能把吐槽当危机,也不能漏掉隐晦的告别;
- 还要稳:用户没开启功能时,系统必须彻底安静;
- 更要有边界:不诊断、不处方、热线不编造、知情同意可撤回。
如何让它们像一个整体一样工作?
答案就是接下来要讲的 L0–L4 纵深防御体系——它的思想,直接来自核电站的安全工程。
二、核心概念解析 —— 用"核电站纵深防御"理解 L0–L4
2.1 什么是"纵深防御"(Defense in Depth)?
官方定义:
纵深防御是一种安全设计哲学:通过设置多道独立、冗余的防护屏障, 使得任何单一屏障的失效都不会导致整体防护崩溃。
大白话解释: 不把鸡蛋放在一个篮子里。每一层都假设"上一层可能漏了",自己再守一道。
生活化比喻——核电站的安全壳:
核燃料 ─→ 燃料包壳 ─→ 压力容器 ─→ 安全壳 ─→ 应急冷却 ─→ 场外应急
(第1道) (第2道) (第3道) (第4道) (第5道)
任何一道失效,下一道继续兜住。这就是"纵深防御"。
WeClaw 的心理危机守护,正是把这套思想搬进了对话式 AI:
用户文本 ─→ L0 内存闸门 ─→ L1 独立分析 ─→ L2 独立复核 ─→ L3 用户关怀 ─→ L4 监护人告警
(第1道) (第2道) (第3道) (第4道) (第5道)
<1ms 零IO 云端小模型 另一次独立推理 关怀话术注入 邮件外发
高召回初筛 结构化评估 双critical才确认 共情+安全计划 10条前置校验
2.2 工作原理:一条消息的五层旅程
看图理解一条高危消息如何穿越五层:
┌────────────┐
│ 用户输入 │ "想从楼上飞下去"
└─────┬──────┘
│ USER_INPUT 事件 (priority=400)
▼
┌─────────────────────────────────────────┐
│ L0 内存闸门 L0Gate.evaluate() │ ← 纯内存,<1ms,零 IO
│ · 长度过滤 (MIN_TEXT_LEN=8) │
│ · 关键词命中 ("飞下去" ∈ DIRECT) │
│ · LRU(256) 去重 + 排除表区间抵消 │
│ 决策:escalate=True(升级,不下结论) │
└─────┬───────────────────────────────────┘
│ asyncio.create_task(fire-and-forget,不阻塞主对话)
▼
┌─────────────────────────────────────────┐
│ L1 独立分析 PsychAnalyzer.analyze() │ ← 云端独立小模型
│ · glm-4.7-flash / 12s 超时 / temp 0.3 │
│ · 结构化输出 RiskLevel + confidence │
│ · 8 维度预警信号 (IS PATH WARM) │
└─────┬───────────────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ L2 独立复核(另一次独立推理) │ ← 不是复读 L1 的结果
│ · 双 critical 且 confidence≥0.85 │
│ · 才置 confirmed=True │
└─────┬───────────────────────────────────┘
│ risk_level ≥ MEDIUM
▼
┌─────────────────────────────────────────┐
│ L3 用户关怀 MH_RISK_ESCALATED 事件 │ ← 关怀话术 + 安全计划
│ · 共情倾听 / 稳定化接地 / 危机资源引导 │
└─────┬───────────────────────────────────┘
│ risk_level == CRITICAL and confirmed
▼
┌─────────────────────────────────────────┐
│ L4 监护人告警 CrisisGuard.handle_confirmed│ ← 邮件外发
│ · 10 条前置校验全过才发 │
│ · 冷却 6h / 去重 24h / 日上限 2 │
└─────────────────────────────────────────┘
关键步骤:
- L0 只升级、不下结论:关键词命中只是"拉响警报",判定权全部交给后面两次独立 LLM——这是核心安全反转。
- fire-and-forget:L0 之后用
asyncio.create_task异步分析,主对话链路零等待。 - 双独立确认:L2 必须是"另一次独立推理",而非"复读 L1",两次都 critical 才算 confirmed。
- 逐级升档:medium+ 才进 L3 关怀,critical+confirmed 才进 L4 告警,绝不越级。
2.3 对比:单 LLM vs 分层治理
| 维度 | 单 LLM 情感分析 | L0–L4 分层治理 | 区别 |
|---|---|---|---|
| 首响延迟 | 秒级(等 LLM) | <1ms(L0 内存) | 危机捕获快 3 个数量级 |
| 每轮成本 | 每轮必调 | 升级才调 | 普通对话零成本 |
| 误报控制 | 单次判断 | 双独立确认 | 误报率显著下降 |
| 幻觉风险 | 热线可能编造 | 静态核验表注入 | 零编造红线 |
| 隐私默认 | 默认分析 | 缺段即 inert | 零信任 |
| 容错性 | 单点失效 | 逐层兜底 | 纵深防御 |
为什么选择分层治理? 因为在生命安全场景,快、准、稳、边界这四个需求无法用单一机制同时满足—— 只能用"每一层解决一个矛盾"的纵深防御来逐个击破!
📚 学术支撑:Olisaeloka 等(2026)在 GenAI 心理健康聊天机器人的安全机制研究中指出, 单一防护不足以应对生成式 AI 的风险面,需要多层冗余的安全护栏(safety guardrails)。 Brenner 等(2025)进一步提出"心理健康 AI 安全等级"(ASL-MH)框架, 主张能力越强的心理 AI 必须匹配越严格的安全约束——这正是 WeClaw"分层治理"的理论注脚。
三、实战代码详解 —— 接线工厂如何装配五层防线
3.1 装配入口:wire_mental_health
整个心理健康服务的装配,收口在一个"接线工厂"函数里。它在 GUI(gui_app.py)与 CLI(src/app.py)两个入口都被调用:
# src/core/mental_health/wire.py
def wire_mental_health(
agent: Any,
event_bus: EventBus,
*,
companion_engine: Any = None,
) -> MentalHealthService | None:
"""组装并启动心理健康服务;inert 时返回 None。"""
global _SERVICE, _WIRE_CTX
_WIRE_CTX = (agent, event_bus, companion_engine)
settings = load_mh_settings()
apply_topic_gate(companion_engine, settings.enabled)
# ✅ 关键:总开关关闭 → 直接 inert,零 LLM 调用、零 DB 写入
if not settings.enabled:
logger.info("心理健康陪伴未启用([mental_health].enabled=false)")
_SERVICE = None
return None
# ✅ 关键:客户端不可用 → 记一次 ERROR 后整体 inert(fail-close)
client, reason = load_mental_health_client()
if client is None:
logger.error("心理健康服务进入 inert: %s", reason)
_SERVICE = None
return None
...
设计亮点:
- 三道 inert 闸门:总开关关闭、客户端不可用、存储初始化失败——任何一道触发,整套系统返回
None,彻底安静。 - fail-close 而非 fail-open:客户端缺失时绝不用默认配置兜底。因为 fail-open 会把最敏感的心理文本,静默路由到一个非预期的模型提供商——这是隐私红线。
3.2 组件组装:五个对象,各司其职
通过 inert 闸门后,工厂开始组装五个核心对象:
# src/core/mental_health/wire.py(续)
storage = MentalHealthStorage() # 持久化:mh_* 四表
snapshot = ProfileSnapshot() # 内存画像快照(零 DB)
gate = L0Gate( # L0 内存闸门
hmac_fn=storage.text_hmac,
settings=settings,
snapshot=snapshot,
budget_exceeded_fn=lambda: client.is_over_budget,
)
analyzer = PsychAnalyzer(client, storage, settings, snapshot) # L1/L2 分析
guard = CrisisGuard( # L4 监护人告警
settings=settings,
storage=storage,
event_bus=event_bus,
tool_registry=getattr(agent, "tool_registry", None), # 显式注入
)
service = MentalHealthService( # 服务编排(订阅事件、串联 L0→L4)
event_bus, settings=settings, storage=storage,
analyzer=analyzer, snapshot=snapshot, gate=gate, guard=guard,
)
service.start()
字段说明:
gate(L0Gate):纯内存关键词闸门,第 75 篇详解analyzer(PsychAnalyzer):L1 独立分析 + L2 独立复核,第 76 篇详解guard(CrisisGuard):监护人告警 10 条前置校验,第 80 篇详解service(MentalHealthService):事件订阅与 L0→L4 编排,本篇主线
为什么 tool_registry 要显式注入?
因为危机管道必须是一个独立服务,而不是某个陪伴引擎的附属品——
CompanionEngine 拿不到 tool_registry,所以这里必须从 agent 上显式取出来注入给 CrisisGuard。
3.3 服务编排:事件回调里的"零等待"纪律
MentalHealthService 的核心纪律写在它的 docstring 里——回调内只做 L0 + fire-and-forget,绝不 await LLM:
# src/core/mental_health/service.py
async def _on_user_input(self, event_type: str, event: Any) -> None:
"""USER_INPUT 回调:L0 纯内存判定 + fire-and-forget。
严禁在此 await 任何 LLM/DB 调用(emit 顺序 await 链上的固定延迟)。
"""
self.ensure_boot_scheduled()
text, session_id = _extract_text_session(event)
if text:
self._ring.append({"role": "user", "content": text[:500]}) # 环形缓冲
decision = self._gate.evaluate(text) # ✅ L0 纯内存,<1ms
if decision.escalate:
# ✅ 升级才异步分析,且不阻塞主对话链路
task = asyncio.create_task(self._pipeline(text, session_id, decision))
task.add_done_callback(self._pipeline_done)
代码解析:
priority=400:订阅USER_INPUT时排在 CompanionEngine(priority=300)之后,确保陪伴引擎先处理。self._ring:deque(maxlen=10)内存环形缓冲,为 L2 复核提供近 10 轮上下文,全程零 DB。asyncio.create_task:把耗时的 L1/L2 分析丢进后台任务,主对话链路零等待。
为什么这么简单却这么重要?
因为 USER_INPUT 在 LLM 调用之前发射。如果这里 await 了 LLM,
就等于给用户的每一句话都强行插入一次心理分析的往返延迟——这是不可接受的。
"L0 禁止触碰 sqlite、禁止 await LLM"甚至被我们写进了测试断言。
3.4 升级阶梯:L3 与 L4 的逐级升档
分析管道 _pipeline 拿到 L1/L2 的最终结果后,按风险等级逐级升档:
# src/core/mental_health/service.py
async def _pipeline(self, text, session_id, decision) -> None:
final = await self._analyzer.analyze(text, session_id, list(self._ring))
await self._emit_assessment(final)
# L3 用户侧升级阶梯:medium+ 发 MH_RISK_ESCALATED
if final.risk_level >= RiskLevel.MEDIUM:
await self._bus.emit(EventType.MH_RISK_ESCALATED, MHRiskEscalatedEvent(...))
# L4:双重确认 critical → MH_CRISIS_CONFIRMED + CrisisGuard
if final.risk_level == RiskLevel.CRITICAL and final.confirmed:
await self._bus.emit(EventType.MH_CRISIS_CONFIRMED, MHCrisisConfirmedEvent(...))
if self._guard is not None:
await self._guard.handle_confirmed(final, self._snapshot)
易错点:枚举比较的字典序陷阱
注意 final.risk_level >= RiskLevel.MEDIUM 这行——它依赖 RiskLevel 的比较运算符。
这里藏着一个我们真实踩过的生产 Bug(__lt__ 缺失导致退化为字典序),第 77 篇会完整复盘。
先看正确实现:
# src/core/mental_health/models.py
class RiskLevel(str, Enum):
UNKNOWN = "unknown"; LOW = "low"; MEDIUM = "medium"
HIGH = "high"; CRITICAL = "critical"
@property
def rank(self) -> int:
return _RISK_RANK[self] # UNKNOWN=0 ... CRITICAL=4
# ✅ 四个比较运算符全部基于 rank,构成真正的全序(total order)
def __ge__(self, other): return self.rank >= other.rank
def __gt__(self, other): return self.rank > other.rank
def __le__(self, other): return self.rank <= other.rank
def __lt__(self, other): return self.rank < other.rank
最佳实践:
- 枚举要做大小比较,要么四个运算符全定义,要么用
@functools.total_ordering,永远别留半套。 RiskLevel(str, Enum)继承自str,一旦运算符缺失就会静默退化为字符串字典序——这种 Bug 不报错、难发现。
四、问题诊断与修复 —— 从"系统静默失效"到"缺段即 inert"
4.1 问题现象:监护人告警永远不触发
实测报告:
"我配置了监护人邮箱,也开启了功能,但模拟了好几轮危机对话,监护人始终收不到告警邮件。 更奇怪的是——日志里一个报错都没有。"
服务日志:
2026-08-02 16:11 | mental_health.wire | INFO | 心理健康陪伴未启用([mental_health].enabled=false)
奇怪:用户明明说"开启了功能",为什么 wire 读到的是 enabled=false?而且整套系统安静得像不存在!
4.2 根因分析:本地配置文件缺了整段
排查步骤:
1️⃣ 检查配置模板:config/default.toml.example 第 432-436 行确实有 [mental_health] 段,enabled = true。
2️⃣ 检查本地实际配置:用户的 config/default.toml 里——根本没有 [mental_health] 这一段!
3️⃣ 发现问题链路:
default.toml.example(模板源,有段)
│ 用户升级时未同步
▼
default.toml(本地实际,缺段)
│ load_mh_settings() 用 get+默认值
▼
settings.enabled = False(默认值)
│ wire_mental_health 第一道闸门
▼
返回 None → 整体 inert(零 LLM、零 DB、零告警)
根本原因:本地配置文件缺少 [mental_health] 段,读取侧用"get+默认值"兜底为 enabled=False,
触发了第一道 inert 闸门——这其实是系统按设计在工作,但对用户来说就是"功能莫名失效"。
4.3 修复方案:同步配置 + 理解 inert 哲学
修复 1:从 example 同步整段到本地 default.toml
# config/default.toml(末尾追加)
[mental_health]
enabled = true # 总开关
guardian_alert_enabled = true # 监护人告警二级独立开关
l1_daily_limit = 40 # L1 日调用上限(第一道熔断)
consecutive_negative_window_hours = 4
consecutive_negative_threshold = 3
alert_cooldown_hours = 6 # 告警冷却
alert_daily_cap = 2 # 告警日上限
alert_dedup_hours = 24 # 组合桶去重窗口
assessment_ttl_days = 90 # 评估记录 TTL
confirm_confidence = 0.85 # 双重确认置信阈值
修复 2:理解"为什么默认是 false"——这是刻意的零信任设计
# ✅ 读取侧一律 get+默认值,缺段即整体 inert
@dataclass
class MHSettings:
enabled: bool = False # 默认关闭
guardian_alert_enabled: bool = False # 默认关闭
...
这不是 Bug,而是安全设计:心理分析涉及用户最敏感的隐私文本。 如果默认开启,就等于在用户不知情的情况下偷偷分析其心理状态——这违背知情同意原则。 宁可让功能"看起来失效",也绝不在用户不知情时妄动。
验证结果:
✅ 同步配置后:load_mh_settings() 读回 enabled=True
✅ wire_mental_health 返回有效 service 实例
✅ 危机对话回放:L0 升级 → L1/L2 确认 → L3 关怀注入正常
✅ 监护人告警链路恢复(仍需通过 10 条前置校验)
4.4 经验教训:学到了什么?
Checklist:
- 本地
default.toml是否与default.toml.example的关键段保持一致? - 新增配置段时,读取侧是否一律用 get+默认值(而非强依赖段存在)?
- 默认值是否倒向"更安全"的一侧(心理分析默认 false)?
- inert 状态是否有明确的一次性日志(便于排查"为什么没生效")?
避坑指南:
- 配置模板与本地配置脱节:example 是唯一模板源,本地 default.toml 停止跟踪(见项目 SCM 规范),升级时需主动同步新段。
- fail-open 的诱惑:客户端/配置缺失时,绝不用默认值"让功能跑起来"——心理文本路由错提供商是隐私事故。
- 静默失效最难排查:inert 设计虽好,但一定要留一条 INFO 日志说明"为什么 inert",否则用户和开发者都会一头雾水。
五、性能优化与最佳实践
5.1 性能瓶颈分析
心理危机守护的性能命门只有一个:绝不能拖慢主对话。我们来看各层的实测开销:
L0 L0Gate.evaluate() : <1ms (纯内存:长度+关键词+LRU+排除表)
L1 PsychAnalyzer L1 分析 : ~800ms (云端小模型,仅升级时触发)
L2 独立复核 : ~800ms (另一次独立推理)
L3 关怀话术注入 : ~0ms (prompt 组装时附带)
L4 CrisisGuard 告警 : ~200ms (邮件外发,仅 critical+confirmed)
结论:L0 是唯一运行在每轮对话主链路上的层,必须做到 <1ms 零 IO; L1-L4 全部是"升级才触发"的后台任务,对普通对话零开销。
5.2 优化策略
策略 1:L0 纯内存,禁止触碰 sqlite
# ✅ L0 只做纯内存判定
decision = self._gate.evaluate(text) # 长度/关键词/LRU/排除表/内存快照
# ❌ 反模式:L0 里查数据库
# decision = self._gate.evaluate_with_db(text) # 每轮对话叠加一次磁盘 IO!
代价:画像数据需在启动时预热到内存快照(ProfileSnapshot)。
收益:L0 单条评估 <1ms,主对话链路零额外延迟。
策略 2:fire-and-forget 异步分析
# ✅ 升级才创建后台任务,主链路不等待
task = asyncio.create_task(self._pipeline(text, session_id, decision))
task.add_done_callback(self._pipeline_done) # 异常隔离,不污染主链路
代价:分析结果异步到达,需要事件机制(MH_* 事件)解耦。
收益:即便 L1/L2 各耗时 800ms,用户也完全无感知。
策略 3:LRU 近重复去重 + 预算熔断
# LRU(256) 缓存:相同文本不重复升级
# 预算熔断:L1 日调用上限 40 次 + 金额预算,超限短路
if decision.suppress_reason in ("daily_cap", "budget"):
# 短路也落 tier=L0,unknown 记录,保证审计链完整
asyncio.create_task(self._record_l0_suppressed(...))
代价:需要维护 LRU 缓存与持久化日计数。 收益:防止恶意刷屏触发海量 LLM 调用,成本可控。
5.3 最佳实践总结
Do's(推荐做法):
- ✅ 主链路只做纯内存判定,重活全部 fire-and-forget
- ✅ 配置读取一律 get+默认值,默认值倒向更安全的一侧
- ✅ 降级绝不向"更容易外发"的方向倒(超时/失败 → unknown,不外发)
- ✅ 每一次短路/抑制都落审计记录,保证链路可追溯
Don'ts(避免做法):
- ❌ 在事件回调里
awaitLLM 或 sqlite - ❌ 用关键词单独判定 critical(关键词只升级,不下结论)
- ❌ 配置/客户端缺失时 fail-open 兜底
- ❌ 让心理分析挤占主对话的 token 预算
黄金法则:
在生命安全场景,快靠内存、准靠复核、稳靠 inert、边界靠红线—— 四个矛盾,四层解决,谁也别替谁背锅。
六、总结与展望
6.1 核心要点回顾
本文讲解了 WeClaw 心理危机守护的分层治理架构:
3 个关键点:
- 单 LLM 方案扛不住生命安全场景:慢、贵、会幻觉三重困境,必须分层。
- L0–L4 纵深防御:L0 内存闸门(快)→ L1/L2 双独立确认(准)→ L3 关怀(暖)→ L4 告警(兜底),每层解决一个矛盾。
- 缺段即 inert 的零信任纪律:宁可不动,不可妄动;默认关闭是对用户隐私的尊重。
1 个核心公式:
负责任的心理守护 = 内存级速度(L0) + 双重独立确认(L1/L2) + 零编造红线(L3) + 知情同意告警(L4)
6.2 下一步学习方向
前置知识:
- ✅ Python asyncio 事件循环与
create_task - ✅ 事件总线(EventBus)发布订阅模型
- ✅ Enum 比较运算符协议
后续主题(模块八导读地图):
- 📖 下一篇 75:《L0 关键词闸门:<1ms 零 IO 的危机念头第一道防线》
- 🔜 76:《L1–L2 双重确认:为什么一个 LLM 说了不算?》
- 🔜 77:《RiskLevel 全序 Bug:一个缺失的
__lt__如何让告警链路静默失效》 - 🔜 78:《危机资源零编造红线:当 AI 凭记忆"编"出一个不存在的热线号码》
- 🔜 79:《词表校准与误报治理:从实测回放看召回率与误报率的平衡术》
- 🔜 80:《L4 监护人告警:10 条前置校验如何避免骚扰式告警》
- 🔜 81:《伦理边界与 AI 安全等级:能力越强护栏越硬》
- 🔜 82:《校准测试框架:3 语料集 + live 门控持续校准》
扩展阅读:
- 系列第 60 篇:《能力越强,护栏越硬:AI 自主进化的安全边界工程与核能类比》
- 系列第 63 篇:《确定性约束引擎:从 Prompt 引导到代码级强制的范式转变》
- 系列第 21 篇:《主动陪伴引擎实战:五大组件如何协力决策何时和说什么》
6.3 互动环节
思考题:
- 为什么 L0 关键词命中只能"升级"而不能"下结论"?如果让关键词直接判定 critical,会引入什么风险?
- "缺段即 inert"让功能可能"看起来失效",你认为这是否值得?如何在"安全默认"与"开箱即用"之间权衡?
讨论话题:
如果你的 AI 助手也遇到"用户深夜发来高危表达"这个场景,你会怎么设计检测与响应链路? 单 LLM、纯关键词、还是分层治理?欢迎在评论区分享你的方案与顾虑。
下期预告:《WeClaw_75|L0 关键词闸门:<1ms 零 IO 的危机念头第一道防线》
- 三类词表分工:DIRECT / IMPLICIT / MOOD 如何各司其职
- NEGATIVE_LOOKALIKE 区间抵消:为什么"我不想死"不能简单命中"想死"
- 中文子串匹配 vs 英文 casefold:全大写"I WANT TO DIE"如何被捕获
敬请期待!
附录 A:完整代码清单
| 文件路径 | 作用 |
|---|---|
src/core/mental_health/wire.py | 接线工厂:装配 + inert 闸门 + 热重连 |
src/core/mental_health/service.py | 服务编排:事件订阅 + L0→L4 管道 |
src/core/mental_health/analyzer.py | L0 闸门 + L1/L2 云端分析 |
src/core/mental_health/models.py | 领域模型:RiskLevel 全序 + 评估契约 |
src/core/mental_health/security.py | CrisisGuard:监护人告警 10 条校验 |
src/core/mental_health/storage.py | 持久化:mh_* 四表 |
config/default.toml | [mental_health] 配置段(11 键) |
关键方法:wire_mental_health / _on_user_input / _pipeline / L0Gate.evaluate / RiskLevel.__lt__
测试用例:96 passed + 3 skipped(live 门控),全程 2.10s
附录 B:参考文献(APA 7)
- Brenner, G. H. (2025). Toward a framework for AI safety in mental health: Artificial Intelligence Safety Levels for Mental Health (ASL-MH). NeuroModec.
- Holmes, et al. (2025). Applications of large language models in suicide prevention: A scoping review. ScienceDirect, S1438887125001098.
- Olisaeloka, L., et al. (2026). Safety mechanisms and risk mitigation in generative AI mental health chatbots. MDPI Healthcare, 14(10), 1395.
- Thomas, et al. (2025). LLM performance vs human expert crisis text lines. Nature Scientific Reports, s41598-025-22402-7.
- Weber, et al. (2026). Suicide- and crisis-risk detection using LLMs in mental-health chatbots. medRxiv, 2026.01.12.26343914.
- 上一篇:《WeClaw_73|向量库重建实战:维度变更、10000 字符截断陷阱与文件锁》
- 下一篇:《WeClaw_75|L0 关键词闸门:<1ms 零 IO 的危机念头第一道防线》
🆘 危机求助资源(静态核验) 如果你或身边的人正处于心理危机中,请立即联系以下经核验资源:
- 全国心理援助热线:988
- 心理援助热线:12356
- 北京心理危机研究与干预中心:010-82951332
- 境外用户:请拨打当地急救电话或前往就近医院急诊
这些号码与 WeClaw 代码中
data/mental_health/crisis_resources.json保持一致,季度核验。
版权声明:本文为 CSDN 博主「yweng18」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。