企业智能体工程体系v1.1|企业智能体工程卷 · 第7期
工作记忆治理------记忆是企业资产,不是私货
作者 :技术治理研究组
系列 :企业智能体工程卷(发布版 v1.1)
主案例 :CASE-CR-0042(信用提额申请)
本集对象 :MemoryItem
适合读者:架构师、技术负责人、AI 产品经理、企业级 Agent 开发者
📌 本文档声明
- 性质 :本文为企业智能体工程化设计参考框架的第 7 期,聚焦 Agent 工作记忆的治理边界,提供架构思路与教学级示意代码,不构成生产级实现方案或法律合规意见(如 GDPR、个人信息保护法等)。
- 证据锚定:文中案例(CASE-CR-0042)为教学示意,不对应任何真实客户系统。
- 系列定位 :本篇在第 0 期(企业公民·身份与审计)确立的"证件号不进长程记忆"红线基础上,引入 MemoryItem 对象,为 Agent 系统建立"有边界、有生命周期、有权限、有审计"的工作记忆治理框架。
摘要
在第 0 期中,我们确立了一条红线:证件号等强身份信息不得进入长程记忆 。但仅靠"红线原则"远远不够------我们需要一套可执行的机制来落地这条红线。
CASE-CR-0042 的处理链路中,客户可能发送证件号、银行账号截图文字。如果放任 Agent 将这类信息写入记忆系统,会发生什么?
| 风险场景 | 后果 |
|---|---|
| 客服 Agent "为下次方便"写入长程记忆 | 隐私数据永久留存 |
| 数据 Agent 缓存原始身份字段 | 其他个案检索时命中 |
| 跨 Agent 记忆共享 | 非授权人员访问敏感信息 |
| 无过期机制 | 数据"永久"存在,无法清理 |
记忆是企业资产------有边界、生命周期、权限、审计。 本期引入 MemoryItem 对象,为 Agent 的工作记忆建立四维治理框架:边界(什么能记)、生命周期(记多久)、权限(谁能看)、审计(谁看了)。
一句话核心:记忆治理不替代契约;它管"留下来的东西"。
1. 问题:记忆是资产,不是私货
1.1 CASE-CR-0042 中的记忆风险
在 CASE-CR-0042 的处理链路中,多个环节涉及客户敏感信息:
| 环节 | 可能接触的敏感信息 | 风险 |
|---|---|---|
| 客服受理 | 证件号、营业执照号、银行账号 | "为下次方便"写入长程记忆 |
| 数据 Agent | 信用报告、财务流水、身份字段 | 缓存原始身份字段 |
| 财务裁决 | 客户资料、收入证明 | 跨 Agent 记忆共享导致权限泄露 |
核心矛盾:Agent 需要记忆来提升效率("记住这个客户的偏好"),但不能因此牺牲隐私合规("记住客户的证件号")。
1.2 失控记忆的三种后果
| 后果 | 说明 |
|---|---|
| 隐私泄露 | 证件号、账号等敏感信息被其他 Agent 或非授权人员检索到 |
| 合规违规 | 违反 GDPR、个人信息保护法等法规对数据最小化、存储期限的要求 |
| 审计失效 | 无人知晓"谁写了什么、谁读了什么、什么时候过期" |
2. 四维治理框架
2.1 治理维度
| 维度 | CASE-CR-0042 要求 | 实现机制 |
|---|---|---|
| 边界 | 禁止证件号、完整账号进长程记忆 | 写入前过滤(正则 + 规则引擎) |
| 生命周期 | 会话摘要 TTL;裁决摘要更长但可审计 | expires_at 时间戳 + 定时清理 |
| 权限 | support 记忆 ≠ finance 默认可读 | owner 字段 + 读取权限检查 |
| 审计 | 写入/读取留痕 | AuditEvent 全链路记录 |
2.2 短程记忆 vs 长程记忆
| 维度 | 短程记忆 | 长程记忆 |
|---|---|---|
| 用途 | 当前会话上下文 | 跨会话经验复用 |
| 生命周期 | 会话结束清理(TTL ≤ 24h) | 按策略保留(TTL 可配置) |
| 内容类型 | 完整对话、临时状态 | 工单号、决策编号、非敏感摘要 |
| 敏感数据 | 禁止写入 | 禁止写入 |
3. 与主架构的关系
text
DecisionRecord / 工具结果
→ 过滤 → MemoryItem.put
→ 失败则仅 AuditEvent 记拦截
记忆治理不替代契约(SkillContract):契约管"能不能做这件事",记忆治理管"这件事做完后,什么东西能被留下来"。
4. 最小代码:过滤 + TTL + 属主
以下为教学级示意代码,展示 MemoryItem 的过滤、生命周期和权限管理:
python
from __future__ import annotations
import re
import time
from dataclasses import dataclass
from typing import Optional
# ===== 敏感信息识别规则(教学级) =====
# 正则匹配 18 位身份证号(含最后一位 X/x)
SENSITIVE_ID_CARD = re.compile(r"\b\d{17}[\dXx]\b")
# 匹配银行卡号(16-19 位数字)
SENSITIVE_BANK_CARD = re.compile(r"\b\d{16,19}\b")
# 匹配手机号(11 位,以 1 开头)
SENSITIVE_PHONE = re.compile(r"\b1\d{10}\b")
def contains_sensitive(text: str) -> bool:
"""检查文本是否包含敏感信息。"""
return bool(
SENSITIVE_ID_CARD.search(text) or
SENSITIVE_BANK_CARD.search(text) or
SENSITIVE_PHONE.search(text)
)
# ===== MemoryItem 对象 =====
@dataclass
class MemoryItem:
"""工作记忆条目------有边界、有生命周期、有属主。"""
key: str
value: str
expires_at: float # Unix 时间戳
owner: str # 写入者身份(如 agent.support.intake)
case_id: str # 关联案例编号
created_at: float # 创建时间
# ===== 治理型记忆存储 =====
class GovernedMemory:
"""受治理的工作记忆存储------写入前过滤,读取时检查权限和过期。"""
def __init__(self) -> None:
self._store: dict[str, MemoryItem] = {}
def put(
self,
key: str,
value: str,
owner: str,
case_id: str,
ttl_sec: int,
) -> None:
"""
写入记忆条目。
Args:
key: 记忆键
value: 记忆值(文本)
owner: 写入者身份
case_id: 关联案例
ttl_sec: 生存时间(秒)
Raises:
ValueError: 当记忆值包含敏感信息时
"""
# 1. 敏感信息过滤(边界检查)
if contains_sensitive(value):
raise ValueError(
f"拒绝写入敏感信息: {key} (检测到证件号/银行卡/手机号)"
)
# 2. 创建带 TTL 的记忆条目
self._store[key] = MemoryItem(
key=key,
value=value,
expires_at=time.time() + ttl_sec,
owner=owner,
case_id=case_id,
created_at=time.time(),
)
def get(self, key: str, reader: str) -> Optional[str]:
"""
读取记忆条目。
Args:
key: 记忆键
reader: 读取者身份
Returns:
记忆值,如果不存在或已过期则返回 None
Raises:
PermissionError: 当读取者无权限时
"""
item = self._store.get(key)
# 不存在
if item is None:
return None
# 过期清理
if time.time() > item.expires_at:
del self._store[key]
return None
# 权限检查:只有属主和审计角色可读
if reader != item.owner and not reader.startswith("auditor:"):
raise PermissionError(
f"无权读取记忆: {key} (owner={item.owner}, reader={reader})"
)
return item.value
def list_by_case(self, case_id: str) -> list[MemoryItem]:
"""按案例列出所有有效记忆(用于审计)。"""
result = []
for item in self._store.values():
if item.case_id == case_id and time.time() < item.expires_at:
result.append(item)
return result
# ===== 使用示例:CASE-CR-0042 =====
if __name__ == "__main__":
mem = GovernedMemory()
print("=" * 50)
print("【场景 1:写入正常记忆】")
print("=" * 50)
mem.put(
key="case:CASE-CR-0042:summary",
value="星河零售申请提额至 120000,已转数据复核",
owner="agent.support.intake",
case_id="CASE-CR-0042",
ttl_sec=3600, # 1 小时
)
print("✅ 写入成功: case:CASE-CR-0042:summary")
print("\n" + "=" * 50)
print("【场景 2:读取记忆(属主本人)】")
print("=" * 50)
value = mem.get("case:CASE-CR-0042:summary", "agent.support.intake")
print(f"✅ 读取成功: {value}")
print("\n" + "=" * 50)
print("【场景 3:写入敏感信息(被拦截)】")
print("=" * 50)
try:
mem.put(
key="case:CASE-CR-0042:id_card",
value="法人证件 110101199001011234",
owner="agent.support.intake",
case_id="CASE-CR-0042",
ttl_sec=86400,
)
except ValueError as e:
print(f"🚫 拦截: {e}")
print("\n" + "=" * 50)
print("【场景 4:跨 Agent 越权读取(被拦截)】")
print("=" * 50)
try:
mem.get("case:CASE-CR-0042:summary", "agent.finance.limit")
except PermissionError as e:
print(f"🚫 拦截: {e}")
print("\n" + "=" * 50)
print("【场景 5:按案例审计】")
print("=" * 50)
items = mem.list_by_case("CASE-CR-0042")
for item in items:
print(f" - {item.key} | owner={item.owner} | expires_at={item.expires_at:.0f}")
运行输出:
text
==================================================
【场景 1:写入正常记忆】
==================================================
✅ 写入成功: case:CASE-CR-0042:summary
==================================================
【场景 2:读取记忆(属主本人)】
==================================================
✅ 读取成功: 星河零售申请提额至 120000,已转数据复核
==================================================
【场景 3:写入敏感信息(被拦截)】
==================================================
🚫 拦截: 拒绝写入敏感信息: case:CASE-CR-0042:id_card (检测到证件号/银行卡/手机号)
==================================================
【场景 4:跨 Agent 越权读取(被拦截)】
==================================================
🚫 拦截: 无权读取记忆: case:CASE-CR-0042:summary (owner=agent.support.intake, reader=agent.finance.limit)
==================================================
【场景 5:按案例审计】
==================================================
- case:CASE-CR-0042:summary | owner=agent.support.intake | expires_at=1768000000
代码要点:
| 治理维度 | 代码实现 | 说明 |
|---|---|---|
| 边界 | contains_sensitive() |
写入前检查,命中则拒绝 |
| 生命周期 | expires_at |
读取时自动检查并清理过期条目 |
| 权限 | owner + 读取者匹配 |
仅属主和审计角色可读 |
| 审计 | list_by_case() |
按案例列出所有有效记忆 |
5. 三个教训
| 教训 | 含义 | 证据 |
|---|---|---|
| 默认最小记忆 | 不默认写入全部内容;只有经过过滤的、非敏感的信息才能进入记忆系统 | put() 中的敏感信息检查 |
| TTL 靠机制不靠自觉 | 不能依赖 Agent "自觉清理"记忆;必须由系统强制过期 | expires_at 时间戳 + 读取时自动清理 |
| 治理内建在 put 路径 | 记忆治理必须在写入时执行,不能"事后补救" | 敏感过滤在 put() 中,而不是在 get() 中 |
| 事故后补救成本高一个数量级 | 数据一旦写入,清理和追溯成本远高于写入前拦截 | 拦截发生在写入前,而非写入后 |
6. 思考题
以下问题供团队内部讨论,帮助将记忆治理概念落地到具体场景:
-
裁决摘要的生命周期:CASE-CR-0042 的裁决摘要(财务 Agent 的最终决定)应 TTL 多久?谁有权限读取?(提示:审计合规 vs 业务效率的权衡)
-
影像 OCR 文本治理:如果客户上传了身份证照片,OCR 识别出的文本应如何处理?如何将影像文本纳入同一边界策略?(提示:OCR 文本是否也适用"证件号不进长程记忆"规则?)
-
审计角色设计 :当前代码中
auditor:前缀的用户可读取所有记忆。这种设计是否足够?审计员是否需要更细粒度的权限控制?
7. 下期预告
第 8 期:落地验收(AssuranceReport)
整条提额链路的 AssuranceReport 与 P4(Hotfix-8h)。将前 7 期的所有治理机制(Identity、SkillContract、四轴、DecisionBoard、ToolSpec、MemoryItem)收束为一份可复核的放行证据包。
8. 延伸阅读
| 资源 | 说明 |
|---|---|
| Metis: Memory Foundation Model(arXiv:2607.26760) | 记忆基础模型研究 |
| GDPR 第 5 条(数据最小化原则) | 个人数据处理的合法性原则 |
| 个人信息保护法 第 6 条(最小必要原则) | 中国个人信息处理的最小必要原则 |
| 本卷第 0 期:企业公民------Identity + AuditEvent | 身份与审计基础 |
| 本卷第 1 期:技能即契约------SkillContract | 能力边界契约化 |
| 本卷第 3 期:无状态决策记忆------DecisionBoard | 多环节决策传递 |
| 本卷第 8 期:落地验收(预告) | AssuranceReport 完整设计 |
本文是「企业智能体工程卷」十期专栏的第 7 期。工作记忆治理------记忆是企业资产,不是私货,让 Agent 的记忆从"随心所欲的私货"进化为"有边界、有生命周期、有权限、有审计"的企业资产。欢迎转载,请注明出处与原文标题。
2026-08-10 | 发布