2026 年 3 月,我们一个基于 LangChain + GPT-4 的客服 Agent cs-agent 上线 4 个月后,突然出现"同一个用户问同样问题,Agent 给的答案完全不一样,而且越来越离谱"的客诉。SRE 一开始以为是模型 hallucination,直到我们排查到根因——Agent 的"长期记忆"模块(用 vector store 存历史对话)累计了 280 万条 memory entry,检索 top-k 时召回的 chunks 大半是"陈年噪声",拉低了上下文质量;同时部分 entry 的 token 已经 8000+,塞进 prompt 直接把 context window 吃满。这是个典型的"memory 越多 ≠ Agent 越聪明"的反例,反而 memory 越多 Agent 越笨。
6 天的复盘让我们彻底重新设计了 Agent memory 系统:分层(short-term / long-term / episodic / semantic)+ 自动遗忘(forgetting curve)+ 摘要压缩(hierarchical summary)+ 检索重排(reranker)。改造后 Agent 响应质量从客户评分 3.2 回升到 4.6,首问解决率从 38% 提升到 71%。这篇是完整复盘,涵盖 Agent memory 失控的因果链、4 种 memory 模式的设计、forgetting 策略、prompt 长度治理、以及落地的《Agent Memory 工程纪律》。这次坑在 LLM Agent 工程里属于"上线 3-6 个月必踩",希望这篇能帮到正在搭 Agent 系统的你。
背景:这套客服 Agent
| 维度 | 数值 |
|---|---|
| 业务 | SaaS 产品客服 Agent,自动应答 + 工单分类 + 转人工判断 |
| 规模 | 日均会话 12 万,峰值 1500 QPS |
| 技术栈 | LangChain 0.1.x + GPT-4 Turbo + Pinecone vector store + Redis short-term + PostgreSQL |
| Memory 设计(出事前) | 每轮对话都写入 vector store 做"长期记忆",检索 top-10 拼 prompt |
| 事故现象 | 上线 4 个月后,Agent 答案质量持续下降,客户评分从 4.5 跌到 3.2 |
| 排查耗时 | 6 天 3 人,中间 3 天走错方向以为是 GPT-4 模型回退 |
| 损失 | 1 个月内升级人工客服需求暴增 40%,人力成本增加 18 万 |
事故时间线:6 天的"为什么 Agent 越用越笨"
| 时刻 | 事件 |
|---|---|
| Day 1 | 客服总监反馈"客户评分这周跌到 3.2,投诉激增" |
| Day 1-2 | 第一波怀疑 GPT-4 模型回退(OpenAI 偶有静默更新),换 GPT-4-Turbo 同样问题 |
| Day 3 | 第二波怀疑 prompt 模板被改坏,git blame 没发现变更 |
| Day 4 上午 | 开始抓 prompt log,发现拼好的 prompt 长达 11000 token,其中 9000 是 memory 检索结果 |
| Day 4 下午 | 查 Pinecone 发现 memory 总数 280 万,80% 是"上线第一个月的低质量对话" |
| Day 5 上午 | 定位根因:memory 无限增长 + 检索召回"陈年噪声" + prompt 过长稀释关键信息 |
| Day 5-6 | 紧急上线 memory 分层 + 遗忘 + reranker,客户评分次日回升到 4.1 |
| 2 周后 | 评分稳定在 4.6,优于事故前 |
失控的因果链:memory 越多反而越笨
把这次"Agent 越用越笨"画成因果链:
这张图最关键的洞察是"memory 系统天然有正反馈失控倾向"。Agent 写入的 memory 会影响下一次 Agent 的回答,低质量答案被存进 memory 后,后续检索会把这些低质量内容塞回 prompt,导致更多低质量答案——这是个"自污染循环"。如果不主动设计"质量过滤 + 遗忘机制",memory 系统必然在 3-6 个月内自我崩塌。这跟传统数据库的"数据越多越好"完全不同——Agent memory 是"高质量少量"远胜"低质量大量"。
反思:memory ≠ 数据库
事故前我们的 memory 设计完全照搬"数据库思维":存得多查得快就行。但 Agent memory 跟数据库有几个根本不同:
| 维度 | 传统数据库 | Agent memory |
|---|---|---|
| 数据多多益善吗 | 是 | 否,质量优于数量 |
| 是否需要遗忘 | 否,永久保留 | 是,主动遗忘陈旧 / 低质量 |
| 查询方式 | 精确匹配 / SQL | 语义相似(向量检索) |
| 检索结果用途 | 展示给人看 | 塞进 prompt 影响 LLM 行为 |
| 规模上限 | TB 级 | ~10K-100K 高质量 entry |
| 更新策略 | 事务一致性 | 异步 + 摘要压缩 |
关键差异是"检索结果用途"——数据库查询给人看,人能自己判断哪些有用;Agent memory 查询塞进 prompt 给 LLM 看,LLM 没有"忽略无关信息"的能力,所有 chunk 都会被注意力机制处理,无关内容直接稀释关键信息。这就是为什么 memory 必须"少而精"。
修法 1:memory 分层
我们把 memory 拆成 4 层,每层用不同存储 + 不同生命周期:
from typing import Protocol
from datetime import datetime, timedelta
class MemoryLayer(Protocol):
def write(self, entry: dict) -> None: ...
def retrieve(self, query: str, top_k: int) -> list[dict]: ...
def forget(self) -> int: ...
# Layer 1: short-term (Redis, TTL 30 分钟)
# 当前对话的上下文,直接拼最近 5 轮
class ShortTermMemory:
def __init__(self, redis_client):
self.redis = redis_client
self.ttl = 1800
def write(self, session_id: str, turn: dict):
key = f"st:{session_id}"
self.redis.rpush(key, json.dumps(turn))
self.redis.expire(key, self.ttl)
# 只保留最近 10 条
self.redis.ltrim(key, -10, -1)
def retrieve(self, session_id: str) -> list[dict]:
return [json.loads(t) for t in self.redis.lrange(f"st:{session_id}", 0, -1)]
# Layer 2: long-term semantic (Pinecone, 摘要 + 重要性评分)
# 跨会话的"用户偏好 / 历史问题"摘要, 主动遗忘
class LongTermSemanticMemory:
def __init__(self, vector_store, summarizer):
self.vs = vector_store
self.summarizer = summarizer
def write(self, user_id: str, conversation: list[dict]):
# 不直接写 raw, 先摘要 + 评分
summary = self.summarizer.compress(conversation)
importance = self.score_importance(summary)
if importance < 0.6:
return # 低重要性, 不存
self.vs.upsert(
id=f"lt:{user_id}:{uuid.uuid4()}",
vector=embed(summary['text']),
metadata={
'user_id': user_id,
'summary': summary['text'],
'importance': importance,
'created_at': datetime.now().isoformat(),
'last_accessed': datetime.now().isoformat(),
'access_count': 0,
}
)
# Layer 3: episodic (PostgreSQL, 结构化事件)
# 用户的关键事件:下单 / 投诉 / 升级等
class EpisodicMemory:
def write(self, user_id: str, event_type: str, payload: dict):
db.execute("""
INSERT INTO agent_episodic_memory
(user_id, event_type, payload, created_at)
VALUES (%s, %s, %s, NOW())
""", [user_id, event_type, json.dumps(payload)])
# Layer 4: semantic knowledge (Pinecone, 静态知识库)
# 产品文档 / FAQ / 政策, 不变
class SemanticKnowledgeBase:
def retrieve(self, query: str, top_k: int = 3) -> list[dict]:
return self.vs.query(vector=embed(query), top_k=top_k, namespace='kb')
这种分层带来的关键好处:
- short-term TTL 30 分钟,自动清理,不会无限增长
- long-term 写入前过滤(摘要 + 重要性评分),只存有价值的
- episodic 结构化存储,精确查询,不依赖向量相似度
- semantic kb 静态知识,不被对话污染
每次 Agent 调用 LLM 前,从这 4 层各取需要的部分拼 prompt,而不是"一个大 vector store 检索 top-10"。这是分层带来的关注点分离:不同信息有不同用途,用不同方式取。
修法 2:主动遗忘(forgetting curve)
long-term memory 必须有"遗忘机制",否则一定爆炸。我们的策略基于 Ebbinghaus 遗忘曲线 + 重要性加权:
def calculate_retention_score(entry: dict, now: datetime) -> float:
"""计算 entry 的保留分数, 低于阈值就删除"""
age_days = (now - parse(entry['metadata']['created_at'])).days
last_access_days = (now - parse(entry['metadata']['last_accessed'])).days
access_count = entry['metadata']['access_count']
importance = entry['metadata']['importance']
# Ebbinghaus 曲线: R = e^(-t/S), S 是记忆强度
# 记忆强度 = 重要性 + 访问频次贡献
strength = importance * 30 + min(access_count, 10) * 3
retention = math.exp(-age_days / max(strength, 1))
# 最近被访问的额外加分
recency_boost = math.exp(-last_access_days / 7) * 0.3
return min(retention + recency_boost, 1.0)
def forget_low_value_memories():
"""每天跑一次, 清理低保留分的 entry"""
threshold = 0.15
now = datetime.now()
for entry in vector_store.scan(namespace='lt'):
score = calculate_retention_score(entry, now)
if score < threshold:
vector_store.delete(entry['id'])
stats.increment('memory.forgotten')
# 定时任务: 每天凌晨跑
schedule.every().day.at("03:00").do(forget_low_value_memories)
这个公式的设计要点:
- 重要性主导保留时间——高重要性的 entry 半衰期长(可能几个月),低重要性的几天就删
- 访问频次反馈实际有用程度——常被检索到的说明真的有用,延长保留
- 近期访问额外加分——长期不被访问的 entry 即使重要性高也逐渐降级
- 阈值 0.15 是 benchmark 出来的——再低就保留太多噪声,再高就误删有用记忆
上线 3 个月后,280 万 entry 通过这个机制压缩到 38 万,且检索质量提升明显——top-10 召回的"陈年噪声"比例从 80% 降到 5%。
修法 3:摘要压缩 + hierarchical summary
不能把"原文对话"直接当 memory 存,要做多层摘要:
检索策略也按层取:
async def retrieve_for_prompt(query: str, user_id: str) -> str:
"""为 LLM prompt 构建 memory context, 严格控制 token 数"""
# 1. 短期记忆: 最近 5 轮原文 (~800 token)
st_memories = short_term.retrieve(session_id)[-5:]
st_text = format_turns(st_memories)
# 2. 长期 L2 摘要: top-5 (~250 token)
lt_l2 = vector_store.query(
vector=embed(query),
top_k=5,
namespace='lt_l2',
filter={'user_id': user_id},
)
# 3. 静态知识: top-3 (~600 token)
kb = semantic_kb.retrieve(query, top_k=3)
# 4. 关键事件: 最近 3 个 (~200 token)
events = episodic.recent(user_id, limit=3)
# 严格控制总 token 不超过 2000
context = build_context(st_text, lt_l2, kb, events, max_tokens=2000)
# 5. 记录被访问的 entry, 更新 access_count
for entry in lt_l2:
vector_store.update(entry['id'], {
'access_count': entry['metadata']['access_count'] + 1,
'last_accessed': datetime.now().isoformat(),
})
return context
关键约束:memory context 必须 ≤ 2000 token。事故前我们让 memory 占 9000 token,留给系统 prompt + 当前问题 + 答案生成只有 5000 token,直接稀释关键信息。改造后留给"真正核心内容"的 token 多了 7000,效果立竿见影。
修法 4:reranker 二次排序
向量检索的 top-10 不一定真的是"最相关"的——余弦相似度有时会被"语义碰瓷"骗到。我们加了一层 reranker:
from sentence_transformers import CrossEncoder
class MemoryReranker:
def __init__(self):
# cross-encoder 比 bi-encoder 准确, 但慢
# 仅对 top-20 做 rerank, 再取 top-5
self.model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-12-v2')
def rerank(self, query: str, candidates: list[dict], top_n: int = 5) -> list[dict]:
pairs = [[query, c['summary']] for c in candidates]
scores = self.model.predict(pairs)
ranked = sorted(zip(candidates, scores), key=lambda x: -x[1])
return [c for c, _ in ranked[:top_n]]
# 检索流程
async def smart_retrieve(query: str, user_id: str):
# 1. 向量检索粗排, 拉 top-20
coarse = vector_store.query(vector=embed(query), top_k=20)
# 2. cross-encoder 精排, 取 top-5
fine = reranker.rerank(query, coarse, top_n=5)
return fine
reranker 上线后,top-5 的"真正相关"比例从 60% 提升到 92%。代价是每次检索增加 80ms 延迟,但换来 prompt 质量提升完全值得。vector 检索 + reranker 二阶段是 RAG 系统的标配,纯向量检索的精度上限有限。
修法 5:重要性评分模型
"重要性"不能拍脑袋设,我们训练了个轻量打分模型:
class ImportanceScorer:
"""判断一条 conversation 是否值得长期保留"""
def score(self, conversation: dict) -> float:
# 多维度加权
signals = {
'has_user_pain': self._detect_pain(conversation), # 用户表达不满
'has_resolution': self._detect_resolution(conversation), # 问题被解决
'has_specific_info': self._has_personal_info(conversation), # 涉及具体订单/账号
'turn_count': min(conversation['turns'] / 10, 1.0), # 长对话权重高
'has_escalation': self._detect_escalation(conversation), # 升级人工
}
weights = {
'has_user_pain': 0.25,
'has_resolution': 0.20,
'has_specific_info': 0.20,
'turn_count': 0.15,
'has_escalation': 0.20,
}
score = sum(signals[k] * weights[k] for k in signals)
return score # [0, 1]
def _detect_pain(self, conv) -> float:
# 用关键词 + GPT-3.5 微调模型判断
pain_keywords = ['投诉', '退款', '太慢', '不行', '生气', '失望']
text = ' '.join([t['content'] for t in conv['turns']])
keyword_hits = sum(1 for kw in pain_keywords if kw in text)
return min(keyword_hits / 3, 1.0)
关键 insight:不是所有对话都值得记住——"用户问营业时间 → Agent 答 9-18 → 用户说谢谢"这种对话存了也没用,反而占检索通道。只有"用户有明确需求/痛点 + 被解决"的对话有长期价值。重要性 < 0.6 直接丢弃,这个阈值过滤掉了 70% 的低价值对话。
诊断流程 SOP:Agent 答案质量下降怎么查
事故后我们沉淀了一套"Agent 质量回归"诊断 SOP:
这套 SOP 最关键的判断点是"prompt 长度"——任何 Agent 答案质量下降,第一步抓 10 条差例的完整 prompt,看长度和组成。事故时我们绕了 3 天才意识到是 prompt 长度问题,如果有这个 SOP 1 小时就能定位。这跟传统 web 应用的"先看慢查询日志"一个道理:对应用层的关键 metric 必须有快速查看入口。我们后来在 Grafana 加了"Agent prompt token 分布"看板,任何 P99 prompt token 超过 8000 直接告警。
立的《Agent Memory 工程纪律》
- memory 系统必须分层:short-term / long-term semantic / episodic / semantic kb,不要"一个大 vector store 装一切"。
- long-term memory 必须有写入过滤:摘要 + 重要性评分,低于 0.6 直接丢,不是所有对话都值得记。
- 必须有遗忘机制:基于时间 + 访问频次 + 重要性的综合保留分,定时清理低分 entry。
- prompt 中 memory context 必须 ≤ 2000 token,留给系统 prompt 和当前问题足够空间。
- 检索必须二阶段:vector 粗排 + cross-encoder 精排,纯向量检索精度上限有限。
- memory 写入异步:不阻塞主流程,通过 message queue 异步落盘 + 摘要。
- 关键 metric 上看板:memory 总数 / prompt token 分布 / 检索召回率 / 答案质量评分。
- 定期 A/B 测试:每季度用历史问题集回归测试 Agent 答案质量,发现回归立刻排查。
跨框架对照:其他 Agent 库的 memory 设计
| 框架 | memory 默认设计 | 潜在坑 |
|---|---|---|
| LangChain ConversationBufferMemory | 原文累积 | token 无上限,长对话爆炸 |
| LangChain ConversationSummaryMemory | 滚动摘要 | 摘要丢失细节,需调 max_token_limit |
| LangChain VectorStoreRetrieverMemory | 向量检索 | 无遗忘,长时间使用质量崩塌(本文遇到的) |
| LlamaIndex MemoryBuffer | 结构化 + 摘要 | 需要手动配置 retention |
| AutoGen ChatHistory | 原文 | 需自行实现 truncation |
| Mem0(开源 memory 库) | 分层 + 自动摘要 | 较新,生态成熟度待验证 |
| Letta(原 MemGPT) | 分层 + 主动 paging | 仿 OS 内存模型,概念复杂 |
从对照看一个清晰趋势:"原始对话全存"路线正在被淘汰,"分层 + 摘要 + 遗忘"是新一代 memory 系统标配。Letta(MemGPT)的设计哲学最有意思——把 LLM context 当 RAM,vector store 当 disk,主动把不常用的 memory "swap out"到 disk,需要时再 "page in"。这种 OS 启发的设计代表了 memory 工程的成熟方向。我们的方案没那么激进,但核心思想一致:memory 不是"越多越好",是"该记的记、该忘的忘"。
事故扩散审计:其他 Agent 也有同样问题
事故止血后,我们扫了公司其他 6 个 Agent 系统:
- 4 个用 LangChain ConversationBufferMemory:上线 2 个月内 token 爆炸,被迫频繁清空
- 2 个自研 vector store memory:跟本次事故一样,memory 数量 50 万 +,检索质量下降但没人意识到
- 把这 6 个全部迁移到分层 memory 后,平均答案质量评分提升 23%
这次审计的价值远超修一个 bug——它把"全公司 Agent 系统的共同隐性风险"显式化。memory 失控这件事跟 .NET Dictionary 死循环(见前一篇 .NET Dictionary 死循环复盘)一样,是"上线时看不到 → 3-6 个月后必发病"的定时炸弹。事故复盘最被低估的价值就是"同类问题扫雷",发现一处必然扫一片。
给读者的几条自查清单
- 你的 Agent memory 当前有多少 entry?用 vector store 的 stats API 查一下,> 50 万就值得警惕。
- 抓 10 条最近的 Agent 完整 prompt,看长度和组成。memory 占比 > 50% 就有失控风险。
- 你的 memory 有"写入过滤"吗?所有对话都进 vector store 还是只存重要的?
- 你的 memory 有"遗忘机制"吗?上线 6 个月后还会自动清理低质量 entry 吗?
- 检索是单阶段(纯 vector)还是二阶段(vector + reranker)?后者精度高 30%+。
- memory context 在 prompt 中占多少 token?超过 3000 就要考虑压缩。
- 你跑过"历史问题集回归测试"吗?用 1 个月前的问题让 Agent 重答,对比质量。
- 你的 Agent 答案质量有客观 metric 吗?客户评分 / 人工抽检 / 自动评估都行,没 metric 看不到回归。
性能 benchmark:改造前后对比
| 指标 | 改造前 | 改造后 | 变化 |
|---|---|---|---|
| memory 总数 | 280 万 | 38 万 | -86% |
| 平均 prompt token | 11000 | 4200 | -62% |
| memory context token | 9000 | 1800 | -80% |
| 检索召回相关性 | 60% | 92% | +53% |
| 客户评分 | 3.2 | 4.6 | +44% |
| 首问解决率 | 38% | 71% | +87% |
| 单次响应 latency P99 | 4.2s | 2.8s | -33% |
| GPT-4 单次 cost | $0.18 | $0.07 | -61% |
从 benchmark 看几个关键洞察:memory 减少 86% 但答案质量反而提升 44%,印证"质量优于数量"原则;prompt token 减半带来 latency 减 33% + cost 减 61%,memory 治理是"性能 + 成本 + 质量"三赢工程。这种"少即是多"在 LLM 工程里非常普遍——更多上下文不等于更好答案,关键是"信噪比"。
更深一层:Agent 工程的"上下文工程"时代
这次事故让我反思 Agent 工程的本质。传统软件工程关注"代码组织 / 数据流 / 接口设计",Agent 工程的核心是"上下文工程(Context Engineering)"——如何在有限的 context window 里,把最相关的信息以最高效的形式传递给 LLM。这是个新的工程范式,跟前两代有本质不同:
- 第一代:规则系统,工程师写规则
- 第二代:机器学习,工程师写特征
- 第三代:Agent + LLM,工程师写上下文
"上下文工程"的核心技术栈包括:prompt engineering、memory 系统、retrieval、reranking、token budget、tool calling 设计。每一项都有大量工程细节,memory 系统只是其中一块。我们这次踩的坑本质是"用旧范式(数据库思维)做新事物(memory 系统)"——把 memory 当成"存得越多越好的数据库",实际上它是"信噪比敏感的 LLM 输入"。这两个心智模型差异巨大,如果工程师还停留在"数据库思维",必然踩坑。
另一个心得:Agent 工程的"调试"跟传统软件完全不同。传统软件的 bug 是"代码逻辑错",有 stack trace 能定位;Agent 的 "bug" 经常是"答案质量下降",没有异常、没有崩溃,只有评分慢慢往下走。这种"无声故障"特别难发现,等到客户投诉时已经积累了几周问题。所以 Agent 工程必须配套持续质量监控:答案评分、用户反馈、A/B 测试,这些不是"锦上添花",是"基础设施"——没有它们就是在盲飞。我们后来把"Agent 质量看板"纳入 SRE 的核心监控,跟传统的 QPS / latency / error rate 一样重要。
第三个心得:memory 系统是 Agent 长期能力增长的载体,但前提是"高质量积累"。设想两个极端:Agent A 每天积累 100 条高质量记忆,3 年后有 10 万条精华;Agent B 每天积累 1 万条原始对话,3 年后有 1000 万条噪声。A 的"智能成长曲线"是单调上升的,B 在 6 个月时就崩塌——量级差 100 倍,效果反而反过来。这就是为什么"过滤 + 摘要"不是优化,是memory 系统能否长期运转的根本前提。
展望:memory 工程的下一步
这次复盘后,团队对 Agent memory 工程有了几个明确的下一步方向:
- Reflection-based memory:Agent 定期"反思"过去对话,提炼 meta-knowledge(比如"用户 A 倾向于先吐槽再问问题"),这种 meta 比原始对话价值高 10 倍。
- Memory consolidation:仿生物记忆"睡眠巩固",定期把多条相关 memory 合并成更高层抽象,类似 hippocampus → cortex 的转移。
- User-aware memory partition:每个用户独立 memory 空间 + 跨用户的"群体知识"层,既保护隐私又能利用集体智慧。
- Memory as a service:抽离成独立服务,多个 Agent 共享同一套 memory 基础设施,降低重复造轮子。
- Cost-aware retention:重要性评分纳入存储成本因素,Pinecone 存一年 vs 删除,要做 ROI 计算。
这些方向有些是工程优化,有些是研究方向,但都指向同一个目标:让 Agent 真正"成长"而不是"原地踏步或退化"。这次事故只是 memory 工程的入门课,真正的硬骨头还在后面。
实战落地:从设计到上线的工程清单
把上述方法论落地到生产环境,我们的实施路径是:
# memory 系统实施 Roadmap
phase_1_quick_win:
duration: 1 周
scope:
- 加 prompt token 长度监控告警(阈值 8000)
- long-term memory 加 TTL: 默认 90 天
- 写入路径加重要性过滤(score >= 0.6 才入库)
expected_gain: prompt token 减半, latency 减 30%
phase_2_quality:
duration: 2-3 周
scope:
- 引入 cross-encoder reranker
- 重要性评分模型训练 + 部署
- 摘要压缩 pipeline(L1 -> L2 -> L3)
- vector store 分 namespace
expected_gain: 检索相关性 60% -> 90%
phase_3_intelligence:
duration: 1-2 个月
scope:
- 4 层 memory 架构改造
- Ebbinghaus 遗忘曲线打分
- Agent 质量评估自动化 pipeline
- 历史问题集回归测试
expected_gain: 客户评分回升 + 长期稳定
phase_4_advanced:
duration: 持续迭代
scope:
- Reflection-based meta-memory
- Memory consolidation 算法
- 跨用户群体知识层
expected_gain: Agent 真正"成长"
这个 Roadmap 的设计哲学是"先止血,再修补,最后增强"。Phase 1 的几个改动 1 周就能上线,立刻看到效果;Phase 2-3 是系统性改造,需要 1-2 个月;Phase 4 是持续探索,没有终点。如果你的 Agent 已经在线上跑了,推荐立刻做 Phase 1——成本最低、收益最大、风险最小。
容易踩的 7 个细节坑
方法论说起来简单,实际落地时有很多魔鬼细节:
- "摘要也用 GPT-4 太贵" —— 我们用 GPT-3.5-turbo 做摘要,质量够用且成本降 90%。摘要任务不需要 GPT-4 的推理能力。
- "重要性评分阈值不能拍脑袋" —— 我们用 100 条人工标注的"重要 vs 不重要"对话校准阈值,机器评分和人工评分一致率达 87% 才上线。
- "forgetting 不能太激进" —— 初版我们把阈值设 0.3,误删了一些用户的关键偏好,客户投诉"Agent 怎么忘了我说过的话"。回调到 0.15,加灰度对照测试。
- "vector store namespace 分得太细" —— 一开始按"业务模块"分了 20 个 namespace,跨 namespace 检索 latency 暴增。改成按"生命周期"分(short/long/kb),只有 3-4 个 namespace。
- "reranker 模型选择" —— ms-marco-MiniLM-L-12-v2 是性能/精度平衡点,大模型 L-12 精度高但延迟翻倍。生产环境强烈建议 L-12 起步。
- "短期记忆 TTL 设多长" —— 30 分钟太短,客户喝杯水回来 Agent 忘了;2 小时太长,session 被串。我们的最佳值是基于"用户平均 idle 时间 + 1 σ",大约 45 分钟。
- "episodic memory 写入触发条件" —— 不要每次对话都写,只在"明确事件"(下单/投诉/升级)时写。否则又会变成"另一个噪声 store"。
这些细节坑都是上线后才发现的,没法在设计阶段预测。所以我们的实施原则是"小步快跑 + 灰度验证"——任何 memory 系统改动先 1% 流量灰度,稳 1 周再扩大。这比"一次大改"安全 10 倍。
总结
这次事故的代价是 1 个月内客户评分跌 30% + 18 万人力成本上升 + 6 天 3 人排查工时。换来的是一套完整的 Agent memory 工程方法论 + 全公司 6 个 Agent 系统的同步治理 + 团队对"上下文工程"的深刻认知。最有价值的产出不是修一个 bug,而是把"Agent memory"这件事从"想当然的数据库"重新定义为"信噪比敏感的 LLM 输入工程"。
如果你的 Agent 系统上线超过 3 个月,把这篇甩给团队看,大概率能拦下某天客户评分突然崩塌的告警。Agent memory 失控不是"会不会发生",是"什么时候发生"。任何"我们的 memory 设计很简单只是把对话存下来"的论断,都要追问"6 个月后存了几百万条还能用吗",历史经验是必然不能。
最后一句给所有写 Agent 的同学:把 memory 当成"LLM 的输入工程",而不是"数据库的存储工程"——前者关注信噪比、后者关注 throughput,完全不同的两套指标体系。从今天开始,在你的 Agent 系统里加 memory 分层 + 写入过滤 + 主动遗忘 + 二阶段检索 + 关键 metric 看板——这五件事不做,Agent memory 就是定时炸弹。每一条无脑写入的 memory 都是未来某天 Agent 崩塌的种子,把它换成"高质量摘要 + 遗忘机制"只需要 1 周开发,但能省下未来某天凌晨被客户投诉电话叫醒的几个月修复期。这笔账无论怎么算,都极其划算。希望每个搭 Agent 系统的同学读完这篇之后,都能把"memory 工程"和"prompt 工程"放在同等重要的位置——它们是 Agent 长期能力的两条腿,缺一不可。任何忽视其中任何一条腿的 Agent 系统,要么走不远、要么走偏方向,这是分布式 LLM 工程的必然规律,真心希望每个团队都能避坑。
—— 别看了 · 2026