AI Agent 记忆召回架构:结构化查询、向量召回、重排与 Prompt 组装 - XBSTACK

AI Agent Memory Retrieval 实战:混合检索、重排、时效性与冲突消解

Release Date
2026-07-16
Reading Time
19分钟
Content Size
6,593 chars
AI Agent
Memory Retrieval
混合检索
Re-ranking
Evaluation
Xiaobai's Note / 实验室笔记

这篇文章记录了我在贵阳实验室的实战过程。我坚信,在技术下行的时代,程序员唯一的护城河就是通过 AI 建立属于自己的数字资产。

先给结论

  • 系统拆解 AI Agent 记忆召回链路,覆盖身份与作用域过滤、结构化查询、向量召回、混合检索、多信号重排、时效性、冲突消解、Prompt 预算、可观测性与回归测试。

适合谁读

  • 正在把 AI Agent / Memory Retrieval / 混合检索 / Re-ranking 落到真实项目里的开发者。
  • 不想只看概念,希望知道取舍、边界、风险和下一步怎么做的独立开发者。
  • 正在做技术选型、工具链治理、自动化工作流或个人数字资产建设的读者。

本文解决的问题

  • AI Agent 如何召回长期记忆,避免把无关历史对话塞进 Prompt?
  • 结构化查询、向量检索、时效性评分和冲突消解应该如何组合?
  • 如何评估记忆召回准确率、过期记忆率和跨用户泄漏风险?

AI Agent Memory Retrieval 实战:混合检索、重排、时效性与冲突消解

AI Agent 记忆召回架构

很多团队发现 Agent “记错了”,第一反应是换向量数据库、提高 Embedding 维度、把 top_k 从 5 调到 20,或者把更多历史聊天记录存进去。

结果通常是:Agent 确实记住了更多东西,但也更容易在错误的时间使用错误的记忆。

它可能召回了已经失效的技术选型,可能把全局偏好错误应用到某个特殊项目,也可能因为语义相似,捞出与当前任务无关的旧对话。更严重的情况,是在多租户系统里召回了别人的偏好或业务上下文。

真正的问题往往不是“有没有存下来”,而是:

系统能否在正确的用户、正确的项目、正确的任务和正确的时间范围内,选出真正有用的记忆。

这是一条独立的 Memory Retrieval Pipeline

本文不再重复讲“短期记忆、长期记忆、RAG 和 Checkpoint 分别是什么”。这些基础边界已经在 AI Agent Memory System 中详细拆解。本文只处理生产系统中最容易被低估的一段:记忆读取与召回决策

一个典型错误:Agent 记得很多,却选错了

假设一个开发 Agent 已经保存了以下记忆:

M1:用户写数据脚本时偏好 Python。
M2:当前移动端项目必须使用 Objective-C。
M3:用户上个月试用过 SwiftUI。
M4:后端服务运行在 Python 3.12。
M5:当前仓库禁止直接修改自动生成文件。

用户现在提出:

继续完成 iOS 网络状态监听功能。

如果只执行向量搜索,“iOS、开发语言、应用开发”可能同时命中 M1、M2、M3。三条内容在语义上都相关,但真正决定实现方式的是 M2;如果目标文件属于生成文件,M5 也必须进入执行约束。

因此,记忆是否应该被选中,至少要同时回答这些问题:

信号需要回答的问题
Identity这条记忆是否属于当前租户、用户和 Agent?
Scope它属于全局、项目、仓库还是当前任务?
Authority它是用户明确要求,还是模型推断出来的?
Freshness它是否已过期、被新版本覆盖或被撤销?
Task Fit它是否会影响当前动作?
Confidence原始记忆提取是否可靠?
Safety当前模型和任务是否有权使用这条记忆?
Budget它是否重要到值得占用 Prompt Token?

向量相似度只解决其中一项。

先划清边界:召回不等于存储

AI Agent 记忆分层

生产级 Agent 中,不同类型的“记忆”不应该采用同一种读取方式:

记忆类型典型内容推荐读取方式
当前任务状态已完成步骤、待执行动作、工具结果thread_id / task_id 精确查询
用户画像语言、时区、明确偏好关系库或文档库精确查询
项目约束技术栈、仓库规范、环境限制结构化查询 + Scope 过滤
事件记忆历史故障、成功修复、决策经验向量或混合检索
外部知识API 文档、企业制度、源码RAG 检索,不属于用户记忆
审计记录Trace、审批、错误日志仅供运维查询,禁止自动注入 Prompt

一个用户时区不需要向量搜索;一个历史部署故障无法只靠主键查询;一个 LangGraph Checkpoint 也不应该与长期偏好一起参与语义排序。

第一条工程原则是:

有确定键的数据优先走确定性查询;只有确实需要“找相似经验”的内容,才进入语义检索。

Memory 与 RAG 可以共用基础设施,但不能混为一层

AI Agent Memory 与 RAG

Memory Retrieval 与 RAG 都可能使用 Embedding、Metadata Filter 和向量数据库,但两者回答的问题不同:

  • RAG:当前问题需要哪些外部事实和知识?
  • Memory:当前用户、项目或任务有哪些历史约束会改变执行方式?

API 文档告诉 Agent 哪个接口存在;记忆告诉 Agent 当前用户要求使用 Objective-C、当前仓库禁止某个依赖,或者上次部署失败是因为漏了环境变量。

如果把两类结果全部拼成一个匿名 Context,模型无法判断哪些是硬约束,哪些只是知识证据。

每条记忆至少应该带上来源类型、作用域、权威等级和置信度:

{
  "source_type": "project_constraint",
  "scope": "project:lunest-ios",
  "content": "iOS 示例默认使用 Objective-C。",
  "authority": "explicit_user",
  "confidence": 1.0
}

Prompt Builder 才能把“规则、偏好、任务状态、历史案例、知识证据”分区组装,而不是全部当成同级文本。

完整的记忆召回链路

AI Agent 记忆召回流程

可靠的记忆召回不是一行 vector_store.search(),而是一组有顺序的控制节点:

用户请求
   |
   v
1. 身份与作用域解析
   |
   v
2. 是否需要召回的 Intent Gate
   |
   v
3. 查询意图与约束提取
   |
   v
4. 多路候选生成
   |-- 用户画像精确查询
   |-- 当前任务状态查询
   |-- 项目与仓库规则查询
   |-- 历史事件向量召回
   v
5. 权限与有效性硬过滤
   |
   v
6. 标准化、去重与版本合并
   |
   v
7. 多信号重排
   |
   v
8. 冲突与覆盖关系消解
   |
   v
9. Prompt 预算选择
   |
   v
10. 结构化组装、Trace 与评估

每个阶段负责删除一种风险。跳过任何一层,问题最终都会被推给大模型,而模型恰恰是整个链路中最不适合承担权限判断、版本治理和确定性冲突处理的组件。

第一步:召回前先确定身份和作用域

记忆服务不能从自然语言里猜当前用户是谁。调用方必须提供完整执行上下文:

from dataclasses import dataclass
from typing import Optional

@dataclass(frozen=True)
class RetrievalContext:
    tenant_id: str
    user_id: str
    agent_id: str
    session_id: str
    task_id: Optional[str]
    project_id: Optional[str]
    repository_id: Optional[str]
    locale: str

最低隔离维度通常是:

tenant_id + user_id

真实生产系统往往还需要:

agent_id + project_id + repository_id + task_id

因为同一个用户的全局偏好可以跨项目复用,但仓库规则不能污染其他仓库;任务中的临时决策也不应永久成为全局偏好。

Scope Filter 应在数据库或向量库查询阶段执行:

def build_scope_filter(ctx: RetrievalContext) -> dict:
    allowed_scopes = [
        "global",
        f"user:{ctx.user_id}",
    ]

    if ctx.project_id:
        allowed_scopes.append(f"project:{ctx.project_id}")

    if ctx.repository_id:
        allowed_scopes.append(f"repo:{ctx.repository_id}")

    if ctx.task_id:
        allowed_scopes.append(f"task:{ctx.task_id}")

    return {
        "tenant_id": ctx.tenant_id,
        "user_id": ctx.user_id,
        "agent_id": ctx.agent_id,
        "scope": {"$in": allowed_scopes},
        "status": "active",
    }

不能先把整个 Collection 的结果取回来,再在应用层过滤。那样未经授权的数据已经穿过了存储边界,可能进入缓存、日志或调试面板。

第二步:不是每次请求都应该读取长期记忆

以下请求通常不需要长期记忆:

  • 把用户已经提供的 JSON 转成 YAML;
  • 总结当前输入中的一段文本;
  • 计算确定性数值;
  • 回答一个自包含语法问题;
  • 当前 State 已经包含全部执行条件。

以下请求通常需要记忆:

  • 继续之前未完成的项目;
  • 应用用户固定的代码或写作偏好;
  • 复用以前成功的修复方案;
  • 避免重复已知故障;
  • 按项目、仓库或账户约束执行动作。

可以先用规则实现 Retrieval Mode:

from enum import Enum

class RetrievalMode(str, Enum):
    NONE = "none"
    STRUCTURED_ONLY = "structured_only"
    EPISODIC_ONLY = "episodic_only"
    HYBRID = "hybrid"


def choose_retrieval_mode(intent: str, has_complete_context: bool) -> RetrievalMode:
    if has_complete_context and intent in {"transform", "summarize", "calculate"}:
        return RetrievalMode.NONE

    if intent in {"load_preferences", "resume_task", "apply_project_rules"}:
        return RetrievalMode.STRUCTURED_ONLY

    if intent in {"find_previous_fix", "reuse_experience"}:
        return RetrievalMode.EPISODIC_ONLY

    return RetrievalMode.HYBRID

这一层可以直接降低延迟、Token 成本和历史污染概率。

第三步:提取召回约束,而不是只生成一句搜索词

对于请求:

继续完成 iOS 网络状态监听,并保持现有项目约定。

召回计划可以是:

{
  "intent": "resume_implementation",
  "entities": ["iOS", "network state"],
  "required_memory_types": [
    "project_constraint",
    "repository_rule",
    "previous_decision",
    "previous_incident"
  ],
  "preferred_scopes": [
    "repo:lunest-ios",
    "project:lunest",
    "user:current-user"
  ],
  "exclude_types": ["casual_conversation"],
  "max_candidates": 30
}

这样每个后端只负责召回自己擅长的数据,同时整个召回决策可以进入 Trace,而不是隐藏在一次黑盒向量搜索中。

第四步:从多个存储生成候选

AI Agent 记忆存储设计

结构化偏好与项目约束

稳定事实和明确偏好走精确查询:

SELECT memory_id, memory_key, value, scope, authority,
       confidence, valid_from, valid_until, updated_at
FROM agent_memory
WHERE tenant_id = :tenant_id
  AND user_id = :user_id
  AND status = 'active'
  AND scope = ANY(:allowed_scopes)
  AND memory_type IN (
    'user_preference',
    'project_constraint',
    'repository_rule'
  );

当前任务状态

Checkpoint 或 State 按明确 ID 加载:

state = checkpoint_store.load(
    thread_id=ctx.session_id,
    task_id=ctx.task_id,
)

但不能把整个 State 原样塞进 Prompt。只提取当前节点需要的字段,例如待执行动作、已选文件和关键工具结果。

历史事件语义召回

历史事故、成功修复和决策经验使用向量或混合检索:

episodes = vector_store.search(
    query_text=query,
    filter=build_scope_filter(ctx),
    top_k=20,
)

候选生成阶段可以适当提高 Recall,但最终 Prompt 的数量必须在后续重排阶段收紧。

第五步:统一候选数据结构

关系库、Redis、向量数据库和 Checkpoint 返回的字段不同。如果直接把原始结果送入排序器,规则会越来越混乱。

统一为一个 Contract:

from dataclasses import dataclass
from datetime import datetime
from typing import Literal, Optional

MemoryType = Literal[
    "user_preference",
    "project_constraint",
    "repository_rule",
    "task_state",
    "previous_decision",
    "incident",
    "successful_procedure",
]

@dataclass
class MemoryCandidate:
    memory_id: str
    memory_type: MemoryType
    content: str
    scope: str
    authority: str
    confidence: float
    created_at: datetime
    updated_at: datetime
    valid_until: Optional[datetime]
    semantic_score: float = 0.0
    source_store: str = "unknown"
    version: int = 1
    supersedes: Optional[str] = None

排序服务只面对统一 Candidate,不需要知道底层来自 PostgreSQL、Redis 还是 Qdrant。

第六步:先做硬过滤,再做排序

以下数据不能仅仅“降低分数”,而应该直接被删除:

  • 不属于当前租户或用户;
  • 不在当前项目、仓库或任务作用域;
  • 已过期、撤销、删除或被新版本覆盖;
  • 置信度低于最低阈值;
  • 当前任务无权使用;
  • 包含禁止发送给当前模型的敏感数据;
  • 只适用于旧环境或旧版本。
from datetime import datetime


def is_eligible(candidate: MemoryCandidate, now: datetime) -> bool:
    if candidate.valid_until and candidate.valid_until <= now:
        return False

    if candidate.confidence < 0.60:
        return False

    if candidate.authority == "revoked":
        return False

    return True

安全规则不能依赖最终排序分数。未经授权的记忆即使分数只有 0.01,也不应该出现在候选集中。

第七步:去重与版本合并

长期运行后,同一偏好可能出现多种表达:

技术回答尽量简洁。
回答要直接一些,不要铺垫太多。
偏好简洁、工程化的技术解释。

如果三条全部进入 Prompt,会浪费 Token,还会人为放大这一偏好的权重。

去重可以结合:

  1. 结构化 memory_key
  2. 标准化文本哈希;
  3. Embedding 近重复判断;
  4. supersedes 覆盖关系;
  5. 相同事实键的版本号。

最终保留一条 Canonical Memory,同时记录来源:

{
  "canonical_memory_id": "mem-104",
  "merged_from": ["mem-021", "mem-087", "mem-104"],
  "content": "偏好简洁、工程化的技术解释。",
  "authority": "explicit_user",
  "confidence": 1.0
}

第八步:使用多信号重排

最终得分不能只等于 Semantic Score。

final_score =
    semantic_relevance
  + scope_match
  + authority_weight
  + freshness_weight
  + task_fit
  + confidence_weight
  - conflict_penalty
  - redundancy_penalty

可以从一个可解释的初始模型开始:

from math import exp

AUTHORITY_WEIGHT = {
    "explicit_user": 1.00,
    "explicit_admin": 1.00,
    "confirmed_system": 0.95,
    "tool_observation": 0.75,
    "model_inference": 0.45,
}

SCOPE_WEIGHT = {
    "task": 1.00,
    "repo": 0.95,
    "project": 0.85,
    "user": 0.70,
    "global": 0.55,
}


def freshness_score(age_days: float, half_life_days: float = 90.0) -> float:
    if age_days <= 0:
        return 1.0
    return exp(-0.693 * age_days / half_life_days)


def rank_candidate(
    candidate: MemoryCandidate,
    semantic_score: float,
    task_fit: float,
    scope_kind: str,
    age_days: float,
    conflict_penalty: float = 0.0,
) -> float:
    return (
        0.35 * semantic_score
        + 0.20 * task_fit
        + 0.15 * SCOPE_WEIGHT.get(scope_kind, 0.30)
        + 0.15 * AUTHORITY_WEIGHT.get(candidate.authority, 0.30)
        + 0.10 * freshness_score(age_days)
        + 0.05 * candidate.confidence
        - conflict_penalty
    )

具体权重需要通过真实数据集调优,关键是所有信号必须显式、可测试、可追踪。

为什么 Scope 常常比相似度更重要

在 iOS 场景中,仓库级 Objective-C 约束应该压过全局 Python 偏好,因为它作用范围更窄,也更直接决定当前实现。

一般优先级可以是:

任务 > 仓库 > 项目 > 用户 > 全局

但只有当多条记忆约束的是同一个决策维度时,才能比较 Scope。任务日期不能覆盖无关的全局语言偏好。

第九步:在进入 Prompt 前处理冲突

不要把互相矛盾的记忆全部交给模型,希望它“自己判断”。

例如:

M1:当前项目后端使用 Python。
M2:该服务已经迁移到 Go。

这两条记录应共享稳定事实键:

memory_key = project.backend_language

冲突策略可以是:

  1. 删除已撤销、已过期记录;
  2. 用户或管理员明确更新,高于模型推断;
  3. 更窄 Scope 高于更宽 Scope;
  4. 更新的确认版本高于旧版本;
  5. 高风险冲突无法确定时,返回“需要澄清”;
  6. 被覆盖版本继续保留在审计链路中。
def choose_winner(candidates: list[MemoryCandidate]) -> MemoryCandidate | None:
    active = [c for c in candidates if c.authority != "revoked"]
    if not active:
        return None

    active.sort(
        key=lambda c: (
            AUTHORITY_WEIGHT.get(c.authority, 0.0),
            c.confidence,
            c.version,
            c.updated_at,
        ),
        reverse=True,
    )

    top = active[0]
    second = active[1] if len(active) > 1 else None

    if second and top.confidence < 0.8 and second.confidence < 0.8:
        return None

    return top

None 是一个合法结果。对高风险动作而言,“记忆存在冲突,需要确认”比随机选一条安全得多。

第十步:设置 Prompt 预算

召回服务应该返回有限的 Context Package,而不是无限列表。

@dataclass(frozen=True)
class MemoryBudget:
    max_items: int = 6
    max_tokens: int = 700
    max_items_per_type: int = 3

最终选择应保持类型多样性:

  • 一到两条硬约束;
  • 最相关的明确偏好;
  • 一条会改变执行决策的历史事故;
  • 一条可以降低不确定性的成功方案;
  • 当前任务继续执行所需状态。

不能简单取全局分数最高的六条。如果前六条全是相似偏好,关键任务状态反而会被挤掉。可以使用类型配额或 Maximal Marginal Relevance 降低重复。

最终注入 Prompt 的内容应该带结构:

<agent_memory>
  <constraints>
    <memory id="mem-201" scope="repo:lunest-ios" authority="explicit_user">
      iOS 实现默认使用 Objective-C。
    </memory>
  </constraints>
  <task_state>
    <memory id="state-88" scope="task:network-state">
      Reachability 接口已确定,UI 状态绑定尚未完成。
    </memory>
  </task_state>
  <previous_incidents>
    <memory id="incident-14" scope="project:lunest">
      旧实现曾把未知网络类型误判为离线。
    </memory>
  </previous_incidents>
</agent_memory>

模型由此可以区分硬规则、当前状态和历史证据。

召回记忆不能自动升级为 System Instruction

被召回的内容可能已经过期,也可能是模型误判后存下来的,甚至可能包含历史 Prompt Injection。

推荐的优先级是:

1. 系统安全与产品策略
2. 当前用户明确请求
3. 当前项目与仓库约束
4. 已确认用户偏好
5. 当前任务状态
6. 历史事故与经验
7. 模型推断记忆

即使历史记忆里出现“忽略安全检查”,也不能覆盖系统策略。

时效性不是简单的“越新越好”

不同记忆类型的老化速度不同:

类型时效规则
明确用户偏好未修改或撤销前可以长期有效
项目技术选型通常在项目版本内稳定
临时任务决策生命周期很短,只属于当前任务
故障临时方案依赖版本或环境变化后可能迅速失效
账户权限必须以实时授权系统为准
模型推断应快速衰减,除非用户确认

可以按类型设置默认半衰期:

HALF_LIFE_DAYS = {
    "user_preference": 365,
    "project_constraint": 180,
    "repository_rule": 90,
    "task_state": 2,
    "incident": 45,
    "successful_procedure": 60,
}

不过显式的 valid_until、版本号和撤销状态,优先级应该高于概率衰减。

记忆召回必须有 Golden Dataset

不能只靠观察几段聊天记录调参数。

一个评估 Case 至少要包含:

{
  "case_id": "memory-retrieval-017",
  "request": "继续完成 iOS 网络状态监听。",
  "context": {
    "tenant_id": "tenant-a",
    "user_id": "user-42",
    "project_id": "lunest",
    "repository_id": "lunest-ios"
  },
  "candidate_memory_ids": [
    "mem-python-global",
    "mem-objectivec-repo",
    "mem-swiftui-episode",
    "mem-network-incident",
    "mem-other-tenant"
  ],
  "required_memory_ids": [
    "mem-objectivec-repo",
    "mem-network-incident"
  ],
  "forbidden_memory_ids": [
    "mem-other-tenant"
  ],
  "max_prompt_tokens": 500
}

数据集需要覆盖:

  • 一条明确相关记忆;
  • 多条语义相似干扰项;
  • 旧版本与当前版本;
  • 明确偏好与模型推断冲突;
  • 跨租户候选;
  • 过期任务状态;
  • 重复摘要;
  • 正确结果应为空的请求;
  • 必须向用户澄清的高风险冲突。

必须监控的指标

Precision@K

最终进入 Prompt 的记忆中,有多少真正有用:

Precision@K = 有用记忆数量 / 最终返回记忆数量

Useful Recall

完成任务所必需的记忆中,有多少被召回:

Useful Recall = 已召回必需记忆 / 全部必需记忆

Stale Memory Rate

Stale Memory Rate = 被选中的过期记忆 / 全部被选中记忆

Conflict Error Rate

Conflict Error Rate = 错误解决的冲突组 / 全部冲突组

Leakage Rate

Leakage Rate = 未授权记忆返回次数 / 全部召回请求

这一项的目标必须是 0,它属于安全不变量,不是可以接受平均值的质量指标。

Empty Retrieval Accuracy

衡量系统是否能在不需要记忆时正确返回空结果。

Prompt Memory Cost

持续记录:

  • 最终记忆条数;
  • 记忆占用 Token;
  • 记忆占整个 Prompt 的比例;
  • 每次成功任务增加的成本;
  • 召回和重排增加的 P95 延迟。

Downstream Task Impact

最终要对比同一测试集在“启用记忆”和“不启用记忆”两种条件下的任务成功率。记忆只是改变回答措辞,不代表它产生了真实价值。

回归测试先做确定性断言

def test_cross_tenant_memory_is_never_returned(retriever, case):
    result = retriever.retrieve(**case.request)
    returned_ids = {item.memory_id for item in result}
    assert "mem-other-tenant" not in returned_ids


def test_repo_constraint_outranks_global_preference(retriever, case):
    result = retriever.retrieve(**case.request)
    returned_ids = [item.memory_id for item in result]
    assert returned_ids.index("mem-objectivec-repo") < returned_ids.index(
        "mem-python-global"
    )


def test_expired_task_state_is_removed(retriever, case):
    result = retriever.retrieve(**case.request)
    returned_ids = {item.memory_id for item in result}
    assert "expired-task-state" not in returned_ids

然后再增加语义层评估:

  • 最终 Context 是否保留了必要约束;
  • Prompt 是否明确区分硬规则和历史案例;
  • 冲突无法解决时,Agent 是否主动澄清;
  • 记忆是否提高任务成功率,而不只是改变表达风格。

可观测性:每一条记忆都要说明为什么被选中

当 Agent 跑偏时,Trace 至少要回答:

  • 是否触发了记忆召回;
  • 使用了哪种 Retrieval Mode;
  • 查询了哪些存储;
  • 生成了多少候选;
  • 哪些候选被权限规则删除;
  • 哪些候选被去重;
  • 哪些记忆存在冲突;
  • 每条最终记忆的得分构成;
  • 记忆占用了多少 Token;
  • 最终哪个工具调用或回答使用了这条记忆。
{
  "trace_id": "trace-901",
  "retrieval_mode": "hybrid",
  "candidate_count": 27,
  "eligible_count": 11,
  "deduplicated_count": 8,
  "selected_count": 4,
  "prompt_tokens": 436,
  "selected": [
    {
      "memory_id": "mem-objectivec-repo",
      "final_score": 0.93,
      "semantic_score": 0.77,
      "scope_score": 0.95,
      "authority": "explicit_user",
      "freshness_score": 0.98,
      "selection_reason": "repository constraint"
    }
  ]
}

默认不要记录原始敏感记忆文本。Memory ID、Hash、类型和分数组成通常足够定位问题。

完整 Trace 体系可以继续阅读 AI Agent ObservabilityLangGraph Observability

常见反模式

为了避免漏召回,不断提高 top_k

提高候选数量可以增加 Recall,但如果没有 Re-ranker,只会把更多噪音塞进 Prompt。候选阶段可以放宽,最终 Prompt 必须严格限额。

所有记忆都放进一个向量索引

用户画像、任务状态、事故记录和知识文档的生命周期、权限和检索语义完全不同。即使底层共用一个引擎,也必须使用明确 Namespace、Metadata 和策略边界。

把模型推断当成用户确认事实

模型推断必须拥有更低权威、更快衰减和确认机制,否则一次幻觉会被固化成长期指令。

在 Prompt 中处理记忆冲突

把两条相反规则同时交给模型,会得到不稳定结果。冲突必须在 Prompt Builder 之前解决,或明确标记为“需要澄清”。

在应用层后置过滤租户

租户和用户过滤必须在数据库或向量查询中完成。应用层后过滤不是安全边界。

因为记忆存在,所以一定返回

可用不代表相关。召回应当由 Intent Gate 控制,并与空召回基线进行比较。

只存文本,不存 Key 和来源

没有 memory_key、Scope、Authority、Version、Validity 和 Provenance,就无法稳定完成版本覆盖和冲突消解。

上线检查清单

在真实用户中启用长期记忆召回前,至少确认:

  • 所有请求在召回前都解析 tenant_iduser_id 和 Agent 身份;
  • 全局、用户、项目、仓库和任务 Scope 明确分层;
  • 结构化事实使用确定性查询;
  • 只有真正需要语义匹配的记忆才进入向量检索;
  • 未授权、过期和撤销记忆被硬过滤;
  • 所有后端结果统一为 Candidate Contract;
  • 重复和被覆盖记忆完成合并;
  • 排序包含 Scope、Authority、Freshness、Task Fit 和 Confidence;
  • 无法解决的高风险冲突会触发澄清;
  • Prompt 同时限制条数和 Token;
  • 召回记忆无法覆盖系统安全策略;
  • Trace 能解释每条记忆为什么被选中;
  • Golden Dataset 覆盖相关、无关、过期、冲突和跨租户数据;
  • Leakage Test 属于阻塞发布检查;
  • 召回效果与 No-Memory Baseline 进行对比;
  • 用户可以查看、纠正和删除持久记忆。

最终架构结论

生产级记忆系统不能只设计成:

用户请求 -> 向量搜索 -> Top K -> Prompt

更合理的链路是:

身份解析
-> 召回意图判断
-> 多存储候选生成
-> 权限与有效性过滤
-> 标准化与去重
-> 多信号重排
-> 冲突消解
-> Prompt 预算选择
-> 结构化 Context 组装
-> Trace 与评估

数据库决定“哪些数据有可能被取出”,召回层决定“哪些数据允许且值得使用”,Prompt Builder 决定“模型最终看到什么”。

把这三层职责拆开,才可能把 Agent Memory 从演示功能变成可控、可测、可审计的生产子系统。

相关阅读

专题入口 / AI Agent Hub

从单个 Agent 问题继续进入完整生产体系

AI Agent 专题统一组织架构、记忆、工具调用、评测、安全、部署和多智能体协作,让每篇文章都回到明确的主题主页面。

下一步阅读

返回专题入口 →
agent

AI Agent 生产化治理:评估、可观测性、部署、成本控制与人工审批闭环

系统拆解 AI Agent 从 Demo 走向生产环境所需的治理能力,覆盖任务评估、Trace 可观测性、工具调用审计、状态管理、部署架构、任务队列、模型路由、成本控制、人工审批、灰度发布和回滚机制,帮助开发者构建可上线、可监控、可复盘的智能体系统。

agent

AutoGen 实战教程:多智能体对话协作、工具调用与生产化边界

系统拆解 AutoGen 在多智能体对话协作中的实战用法与生产化边界,覆盖 AgentChat、GroupChat、Planner / Executor / Critic 模式、工具调用、Human-in-the-loop、对话轮次控制、评估指标、成本监控和 Microsoft Agent Framework 迁移风险。

agent

AI Agent 全栈指南 2026:从架构、工具调用到评估部署的生产化路线图

系统梳理 2026 年 AI Agent 的生产化构建路线,覆盖智能体架构、任务规划、工具调用、记忆系统、RAG、多智能体、可观测性、评估体系、部署架构与 SaaS 化,帮助开发者从 Demo 走向可上线的 Agent系统。

agent

2026 AI Agent 开发手册:协议选型、工具调用、状态管理与多智能体落地清单

面向开发者系统梳理 2026 年 AI Agent 项目落地方法,覆盖协议选型、MCP、Function Calling、Tool Use、Memory、RAG、多智能体协作、状态管理、评估、部署和生产化检查清单,帮助团队从 Demo 走向可上线系统。

小白

小白

Full-Stack AI Engineer

小白,全栈 AI 工程师,持续构建生产级 Agent 系统、产品工具与独立软件资产。

了解小白与 XBSTACK →

喜欢这篇文章?
加入小白实验室的周刊

每期只整理 AI 工程变化、真实故障、可复现实验、值得尝试的工具和 XBSTACK 新资产,不做泛新闻汇总,也不为周更凑数。

Comments