跳到正文
Cooing's Blog
返回

当 Agent 开始检索个人记忆:资源抽象、权限边界与工具迁移

TL;DR

一个带持续录音能力的个人助手,会保存会议、待办、日程、联系人,原始录音及其转写文本,长期记忆等,数据越积越多,用户的问题也会逐渐跨过这些业务边界:他可能先问会议结论,再追问任务是否完成,最后要求找出当时的一句原话,此时系统已经保存了这些事实,Agent需要跨过摘要、日历、任务、人物和转写等不同入口,去凭借搜索能力,拼出一份可靠答案的答案

我们把内部记忆读取整理为 list / search / read 三种抽象操作类型进行“渐进式披露”,并用资源声明描述每类数据的身份、字段和过滤规则。每次读取都要验证可信运行身份和有效权限;搜索先按可见字段召回候选,再由业务源记录确认当前状态;长文本通过带签名的游标分段读取,写入另有一套业务命令与确认/交互流程

统一资源层完成后,在记忆搜索治理层面,形成了较完整的访问契约,随后,工具改名和公开目录的变化进入了另一条更长的生产链路:Agent Runtime、公开 MCP1、Capability2、注册目录和既有客户端都需要按照同一版本工作。旧巡检用例、部署超时以及鉴权与 CICD 的失败,陆续暴露了接口迁移之外的工程依赖,MCP1 工具还经历了从四个业务工具切换到三个通用工具、短暂恢复四工具、再准备重新发布三工具的“业务与产品打架”问题

本文先解释资源访问层为什么这样设计,再沿着真实发布链路说明它如何遇到兼容性问题,以及怎样判断一项改造真正完成

flowchart TD
    U["用户提出跨资料问题"] --> A["Agent 判断要找什么证据"]
    A --> L["list:发现可用资源"]
    L --> S["search:查找候选与 ref"]
    S --> R["read:读取当前允许的内容"]
    R --> E["组织结论、状态与引用"]
    L -.-> AUTH["Run 身份与授权"]
    S -.-> AUTH
    R -.-> AUTH
    AUTH --> SRC["业务源记录"]
    SRC <--> IDX["PostgreSQL 搜索投影"]
    SRC --> OBJ["转写工件 / 对象存储"]
    E --> OUT["可核验的回答"]

一、旧 Memo 搜索的边界:找到了摘要,还要找到事实

先从一个具体问题看起。

上周和设计同学讨论移动端提醒的那场会,最后决定了什么?还有哪些待办没有做完?把当时讨论这件事的原话找出来

用户在描述同一段经历,但系统保存的是多种对象。会议结论通常出现在 Episode3 中;任务的当前完成状态保存在 Todo4 中;会议时间可能属于 Calendar5;发言原文则来自 Transcript6。这些对象会互相引用,也可能在不同时间生成或更新,因此,回答需要连续完成四件事:定位会议,确认相关人物与时间,读取任务的当前状态,再沿录音关联找到原话。最后还要区分摘要中的归纳和转写中的逐字证据

这里还存在数据更新时序的问题:Todo 已经从“待处理”改成“已完成”,但搜索索引仍保留旧文本,Agent 就可能引用过期状态。日程取消或录音工件被替换时,也会出现类似风险。只返回索引中的内容,会把“曾经匹配”误当成“当前仍然成立”,Memo 工具命中 ≠ 找到了可精读/有价值的对象,资源访问层需要把两个动作分开:搜索负责找出可能相关的对象,精读负责回到当前业务记录确认事实。搜索结果必须带有稳定引用7,让后续读取能准确找到同一个对象

在我接手前的 memo search,已经有实体对象搜索,以及日程、待办、录音和转写等业务工具。Memo8 负责从整理后的记忆文本中找候选,业务工具负责访问各自的对象并返回摘要。单独调用时,这些业务 tool 都能完成明确任务;随着用户问题跨越多种对象,Agent 需要自行拼接不同的参数、身份表达、时间过滤、结果格式和分页方式,还要识别不同结果之间的关联。旧 tool 返回的一条摘要可能同时提到一场会议、两名参与者和三个行动项。搜索命中摘要后,模型还要判断它对应哪些真实对象,再分别进入各自入口读取;摘要本身也无法确认底层对象的删除状态、日程状态、任务进度,以及当前 Agent 对具体字段的访问权限。随着资源种类增加,单纯修改 Prompt9 已经很难稳定处理这些差异,在 few shot 中添加样例也不够高效。

旧工具按业务能力增长,每个入口都有自己的参数和返回格式。Agent 可能先搜索记忆,再查日程,再列任务,最后调用转写接口。模型需要记住每个工具的名称、时间口径和错误处理方式;客户端和巡检脚本也会固化这些名字与响应字段,导致不可避免的耦合

flowchart LR
    subgraph OLD["旧式入口:按业务对象分别调用"]
        Q1["同一个用户问题"] --> M["Memo 搜索"]
        Q1 --> T["Todo 工具"]
        Q1 --> C["Calendar 工具"]
        Q1 --> A["音频 / Transcript 工具"]
        M --> JOIN["模型拼接对象身份、状态和证据"]
        T --> JOIN
        C --> JOIN
        A --> JOIN
    end
    subgraph NEW["统一读取:动作稳定,资源显式"]
        Q2["同一个用户问题"] --> L["list"]
        L --> S["search"]
        S --> R["read"]
        R --> ANSWER["结论与来源"]
    end

我希望模型掌握的动作尽量稳定:先发现资源,再检索候选,最后按引用读取,各个阶段可并行调用多个工具,至于资源存在哪张表、哪些字段可以返回、长文本如何分页,应由服务端明确处理,把资源读取的共性放进一层稳定服务,同时保留各类业务数据自己的生命周期,应当尽可能降低 Agent 调用难度;Anthropic 在 Writing effective tools for agents 中讨论了类似的工具设计原则。工具应有明确的任务边界,返回内容要方便模型继续使用,最终还要通过实际任务评测效果10

二、从DDD的视角看资源,记忆及其关系

本项目内包括但不限于以下对象, Memory Resource11 指 Agent 可读取的一类业务对象

episode3 保存一段经历或会议的整理结果,常见内容包括摘要、话题、参与者和关联工件。它适合回答“讨论了什么”,但精确引用仍应回到来源转写

transcript6 保存语音识别产生的文字,可能包含说话人、时间位置和长文本。它适合核对“当时具体说了什么”

calendar5 保存日程和相关时间,todo4 保存需要执行的任务。两者都可能出现在同一场会议之后,各自采用不同的时间语义:日程通常依据事件时间或提醒配置,待办可能依据截止日期,也可能没有截止日期。

contact12 保存人物及其关系信息,帮助 Agent 确认“设计同学”或某个别名对应谁、联系人合并、别名消歧和字段可见性都会影响搜索结果

voice_memo13 保存用户主动记录的语音笔记及其整理内容,以及负责兜底部分录入时无法明确从属的对象digest14 则汇总一段时间内的重要变化,便于回顾长期活动。这两类内容属于派生记忆,读取时需要留意来源和更新时间

intent15 保存 Agent 提出的待确认意图,例如准备创建某项任务。它记录的是拟执行或待确认的动作;只有完成必要确认并走过业务写入流程,系统才能把它视为已执行

user_profile16 保存长期偏好和个性化上下文,它可能包含比普通任务更敏感的信息,所以可读范围需要按 Agent 和受众进一步收窄

这些资源形成了一张业务数据关系图(Data Relationship,DR)17。例如,一次录音可能生成 Transcript,再被整理成 Episode;Episode 可能关联参与者、日程或后续 Todo;Digest 可以总结多个 Episode;Intent 则可能指向待确认的业务动作。这里需要区分 Episode 与 Transcript:前者回答“这段经历讲了什么”,后者回答“当时说了什么”。Agent 使用摘要组织答案时,仍需要能够检索来源转写,以便提供逐字证据。

flowchart LR
    REC["录音 / 音频工件"] --> ASR["Transcript:转写工件"]
    ASR --> EP["Episode:事件整理"]
    EP -.->|"可能关联"| CA["Calendar:日程"]
    EP -.->|"可能关联"| TD["Todo:待办"]
    EP -.->|"参与者"| CT["Contact:人物"]
    VM["Voice Memo:语音笔记"] -.-> DG["Digest:阶段摘要"]
    EP -.-> DG
    DG -.-> UP["User Profile:长期画像"]
    IN["Intent:待确认动作"] -->|"确认、执行业务写入后"| TD
    IN -.->|"也可能指向"| CA

图中的实线表示较直接的生产或状态转换关系,虚线表示可能存在的派生或业务关联。例如 Episode 和 Todo 都可能引用同一场会议。关联判断需要沿确定的资源引用7完成,标题相似只能提供线索,不能单独证明两个对象指向同一件事。

资源声明18进一步规定每类对象允许读取哪些字段、能使用哪些过滤条件,以及哪些调用方可以访问。源记录保留各自的存储方式、生命周期和写入规则。这样的边界使以后新增一种资源时,开发者能够沿固定清单实现读取能力,而不必改动其他资源的数据结构

2.1 list / search / read 的“渐进式披露”设计

list() 用来发现资源目录。Agent 不带参数时,服务端返回当前可见的资源类型及操作范围。资源如果没有任何可读字段,就不会出现在目录中,Agent 可以据此了解目前有哪些信息来源

list(resource, filters, cursor) 用来浏览一种资源的记录,例如,Agent 可以查看最近更新的 Todo,或按时间范围列出 Calendar 事件,filters由对应资源声明

search(query, resource?, top_k?) 用来寻找可能相关的记录。查询文本必填;指定资源时只检索该类对象,不指定时则在当前授权范围内跨资源搜索,结果带稳定 ref,并返回足够用于判断的标题、片段和时间等信息,随后由 Agent 判断哪些对象需要精读

read(ref, cursor?) 用来精读一个确定对象,短内容直接返回;长内容按结构化片段返回,并提供继续读取的游标,这样模型可以先用较小的响应发现证据,再按需读取更长正文

sequenceDiagram
    autonumber
    actor U as 用户
    participant A as Agent
    participant D as 资源目录
    participant S as 统一搜索
    participant R as 精读接口
    participant DB as 业务数据
    U->>A: 找上周的提醒方案和未完成任务
    A->>D: list()
    D-->>A: 当前可用资源与操作
    A->>S: search("移动端提醒")
    S->>DB: 按授权范围检索候选
    DB-->>S: 当前可见的候选与匹配分数
    S-->>A: 资源 ref、标题、片段
    A->>R: read(episode ref)
    R->>DB: 重新校验源对象和字段
    DB-->>R: 当前摘要与关联信息
    R-->>A: data 或 fragments + next_cursor
    A->>R: read(todo ref)
    R-->>A: 当前任务状态
    A-->>U: 结论、任务状态与证据来源

2.2 让资源声明可程序化创建与检查

每类资源都有读取 Schema19,其中规定字段类型、嵌套路径、允许的过滤条件,以及不同受众能读取哪些字段,Schema 也用于校验请求,避免客户端与服务端各自猜测参数含义

例如,一个会议对象可能允许读取 /title、/summary 和 /participants/*/display_name。内部分析状态、对象存储地址和原始转写引用可能只允许特定的内部 Agent 访问;字段路径也有层级约束。假如只允许返回参与者姓名,服务端需要沿 participants → 每个参与者 → display_name 这条路径构造结果

请求解析同样采用严格规则。未知键、重复键、显式 null、无效时间戳和资源不支持的过滤器都会得到明确错误/拒绝,更容易排查,也能避免客户端误以为过滤条件已经生效

三、从模型调用到源memo记录:一次读取经过哪些检查

从模型视角看,调用 search 只需要一个查询字符串,但服务端需要考虑的就多了

服务端接到请求后,还需要确定调用者身份、当前 Run、允许读取的字段和对象所属用户。内部 Agent 与外部 MCP 客户端复用同一套 runtime 与 tool ,但两条入口的信任依据有所区别

内部 Agent 通过 Agent Runtime20 发起调用。可信入口传递用户、Thread21、Run 和 Agent 的身份,再由授权层读取 Run 快照和最新 Config22。服务端计算有效权限后,才把请求交给 Resource Reader23,Resource Reader 选择相应的业务适配器,查源记录、应用过滤并生成返回内容

外部 MCP 客户端首先经过 OAuth24 和工具目录发现,Capability 根据公开的权限策略决定这一调用能否进入读取层,外部目录只发布 Episode、Calendar、Todo、Voice Memo 和 Contact 五类资源。内部 Digest、Intent、Transcript、User Profile 仍保留在更窄的授权范围内不暴露

sequenceDiagram
    autonumber
    actor U as 用户
    participant M as Agent / Tool Runtime
    participant E as 服务入口
    participant A as Run 授权服务
    participant C as Config 权限中心
    participant R as Resource Reader
    participant P as PostgreSQL
    participant O as 对象存储
    U->>M: 找会议并还原原话
    M->>E: search(query, resource?)
    Note over M,E: 模型提供查询参数,身份来自可信调用上下文
    E->>A: 校验 user / Run / Thread / Agent
    A->>P: 读取 Run 初始授权快照
    A->>C: 查询当前 Config[^config] 权限
    C-->>A: 当前资源、操作和字段许可
    A-->>E: 计算有效授权
    E->>R: search(可信身份, 有效授权, query)
    R->>P: 查询可见字段与有效源记录
    P-->>R: 各资源的候选和词法分数
    R->>R: 跨资源重排、去重与预算裁剪
    R-->>M: ref / 标题 / 片段 / 时间
    M->>E: read(确切 transcript ref)
    E->>A: 重新校验本次读取权限
    A-->>E: 当前有效授权
    E->>R: read(可信身份, ref)
    R->>P: 校验工件归属、存在性与字段
    opt 正文位于对象存储
        R->>O: 读取精确工件
        O-->>R: 当前转写内容
    end
    R-->>M: data 或 fragments + cursor
    M-->>U: 结论与可追溯原话

这条链路也解释了三个经常混用的概念。资源描述可以被读取的数据;工具描述模型可以执行的操作;公开目录描述外部客户端能发现并调用哪些工具。MCP 标准还定义了另一种名为 Resources 的协议对象,与本文的 Memory Resource 的产品内部业务抽象不同,目前公开的 list/search/read 则属于 MCP Tools 类型25

3.1 Run 授权快照:冻结起点,每次读取重新求交

Agent 的一次执行称为 Run26;Thread21 则代表持续的对话或任务上下文,同一个 Thread 可能经历多个 Run,服务端为每个可信启动的 Run 保存授权快照27,记录该次运行起初获准访问的资源、操作和字段。

每次 list / search / read 都要将这份初始授权与当前 Config22 权限,以及系统对该受众的固定上限求交:本次有效读取权限 = Run 启动时冻结的权限 ∩ 本次读取时的当前权限 ∩ 系统对该受众和资源的固定上限

新增权限只影响新生成的授权快照,旧 Run 仍受启动时的权限约束。管理员撤回权限后,下一次读取就会按收窄后的结果执行,Run 不存在、Thread 不匹配、执行已经结束,或授权源不可用时,服务端会拒绝读取,被撤销的资源也会从下一次 list() 目录中消失

stateDiagram-v2
    [*] --> 待启动
    待启动 --> 冻结中: 可信入口登记 Run
    冻结中 --> 可读取: 身份成立且授权快照写入成功
    冻结中 --> 拒绝: 身份错误 / Config 失败
    可读取 --> 本次校验: list / search / read
    本次校验 --> 可读取: 初始授权与当前授权有交集
    本次校验 --> 权限收窄: 当前权限撤回或字段减少
    权限收窄 --> 本次校验: 下一次请求重新计算
    本次校验 --> 拒绝: Run 失效 / 授权源不可用
    可读取 --> 已结束: Run 进入终态
    权限收窄 --> 已结束: Run 进入终态
    已结束 --> 拒绝: 后续读取

这项设计的边界也很明确:它约束后续工具读取,已经进入模型上下文的内容仍需由其他机制处理。若要在撤权或注销后进一步清理上下文、缓存和派生结果,还需要与运行终止、数据删除等链路协作;当前实现尚未覆盖这一部分。

3.2 字段权限必须先于搜索命中生效

对象能否读取,与对象的哪些字段可以参加搜索,是两个相关但不同的问题。

假设:Todo 的标题为“更新推送配置”,备注包含一段内部联系方式,Agent A 只获准读取 /title 和 /status,Agent B 额外拥有 /notes 权限。若数据库先在整条任务上匹配关键词,再把备注字段从返回内容中删掉,Agent A 仍可能通过“这条任务是否出现”推测备注中是否有该关键词

因此:服务端在检索前先计算字段白名单28,从当前源记录中提取获准检索的正文,再计算文本匹配。受限字段既不进入片段,也不参与候选资格判断;嵌套结构通过 JSON Pointer29 路径授权,并检查父路径,避免授权一个子字段时顺带暴露整组对象

flowchart TD
    Q["搜索请求:关键词"] --> A["运行授权与字段白名单"]
    A --> S["源记录归属、状态和时间校验"]
    S --> F["只拼接获准字段"]
    F --> MATCH{"可见文本是否命中?"}
    MATCH -->|是| C["进入有限候选集"]
    MATCH -->|否| X["不进入结果"]
    C --> R["跨资源排序与预算裁剪"]
    R --> OUT["安全标题、片段与 ref"]
    A -.-> SECRET["受限字段不参与检索"]

有一个额外的影响点:这会影响检索评测!Agent 只允许读取标题时,搜索备注里的关键词应当没有命中;这种结果属于权限规则正常生效。只有在目标字段本来就允许读取时,漏掉相关记录才属于召回问题,Memo eval 很神奇吧

四、搜索投影:索引负责候选,源记录负责事实

以上,我们通过授权解决了谁能看到哪些字段,接下来要解决数据变化后的正确性,Memo 内容散在多张业务表里,有的对象按时间查询,有的带状态机,还有一些正文放在对象存储中。如果每一种资源都单独维护索引,开发者需要反复处理更新、删除和回填;如果完全依赖索引副本回答问题,记录更新后又可能出现过期事实

因此,本项目采用维护一个 PostgreSQL 中的搜索投影30,它记录每个业务对象的搜索资格和相关检索内容,帮助系统快速找到候选;真实业务状态仍以源记录为准

4.1 写入与投影放在同一个数据库事务

当用户修改 Todo 时,服务端在同一 PostgreSQL 事务中更新业务记录和对应投影。两项操作一起提交,或一起回滚,原子化事务可以避免源记录已经更新、搜索索引却还保留旧资格的半提交脏状态注:这个设计也导致向量嵌入和HNSW更新,数据库写入等混在一个事务内过长,具体业务具体分析,自行取舍了,搞异步还要引入一系列补偿和幂等的措施,太麻烦遂放弃

flowchart LR
    subgraph WRITE["写路径:同一事务"]
        W["App / 同步器 / 业务命令"] --> TX["PostgreSQL transaction"]
        TX --> SRC["业务源记录"]
        TX --> IDX["搜索投影资格"]
        SRC --> COMMIT["共同提交或回滚"]
        IDX --> COMMIT
    end
    subgraph READ["读路径:重新校验源记录"]
        Q["list / search / read"] --> AUTH["运行与字段授权"]
        AUTH --> SQL["源表查询和当前过滤"]
        IDX -.候选资格.-> SQL
        SRC --> SQL
        SQL --> RES["当前合法候选或原文"]
    end
    subgraph REPAIR["恢复路径"]
        BF["受控回填 / 工件修复"] --> VALID["核对源记录与工件身份"]
        VALID --> IDX
    end

假设:用户已经把一个 Todo 标记为完成。投影即使仍存有旧的候选登记,读取层也会重新检查源表中的状态,避免用旧文本覆盖最新任务事实。删除操作同样需要检查对象归属和有效状态;read(ref) 遇到已删除对象,应返回明确的不存在结果

另一种失配出现在投影缺失时:源记录已经存在,但投影尚未建立。此时精确 read(ref) 仍可能成功,search 却无法发现该对象。排障时需要分别检查源记录写入和投影回填,因为两个方向的失配会呈现不同症状。

另外,PostgreSQL 能保证数据库内部的事务提交,S331 等对象存储不参与同一事务,如无法保证转写的元数据已经入库,原始文件暂时无法下载等特殊边界情况,还需另行处理

4.2 当前统一搜索以词法召回为主

统一读取路径主要使用 PostgreSQL 的两种检索能力。pg_trgm32 通过字符三元组计算文本相似度,处理部分拼写差异;全文检索33 使用 tsvector、tsquery 和 ts_rank 匹配词项并计算词法相关度

一个问题可能同时命中 Episode、Todo、Calendar 和 Contact,如果每类资源各返回十条后直接拼接,先执行的资源就会天然占据前排,统一重排器把候选转换为相同结构,结合词法相关度、状态、置信度、资源类型和更新时间调整顺序,然后按 ref 去重并限制最终返回数量

flowchart TD
    Q["query:上周提醒方案"] --> E["Episode 召回"]
    Q --> T["Todo 召回"]
    Q --> C["Calendar 召回"]
    Q --> V["其他已授权资源召回"]
    E --> N["统一候选:ref / 分数 / 状态 / 时间 / 类型"]
    T --> N
    C --> N
    V --> N
    N --> R["确定性重排"]
    R --> D["按 ref 去重"]
    D --> K["top_k 与响应字节预算"]
    K --> O["模型按 ref 精读"]

每种资源按自己的源表和过滤规则召回候选,再把结果转换成共同结构,交给跨资源重排器34,词法匹配是当前主要排序信号,对于未完成的 Todo,排序器增加状态权重;近期更新的内容也获得一定加权。资源类型、置信度与时间接近程度同样作为输入,搜索路径保留支持 Embedding35、BM2536 和混合检索策略;当前统一没有把语义分数传入排序器,以词法检索为主降本

影响实际用户体验和搜索效果的点主要在于:如“提醒补发”和“通知兜底”在业务上接近,字面却不相似,词法检索可能漏掉正确记录,引入向量检索前的 Eval 评测准备工作正在进行中37

五、长文本分页:让 Agent 能读完,又能说明读了多少

长录音转写可能有数万字,还可能包含说话人、时间锚点和嵌套结构。一次性返回整份内容会占用大量上下文38,也容易被工具框架或中间层截断冷知识 pi 设置为50KB

统一读取层把每个结构化响应限制在 65,536 个 UTF-8 字节以内,JSON 编码、字段信息和游标都计入预算。短对象直接返回 data;超过预算的对象返回 fragments 和 next_cursor。片段使用 JSON Pointer29 路径定位结构节点,长字符串再按 Unicode 字符位置切分。

sequenceDiagram
    autonumber
    participant A as Agent / 客户端
    participant R as Resource Reader
    participant P as 源记录 / 转写工件
    A->>R: read(transcript ref)
    R->>P: 校验身份并读取当前内容
    P-->>R: 超过单页预算的结构化正文
    R-->>A: fragments + complete=false + cursor_1
    A->>A: 按 path / offset 重组片段
    A->>R: read(同一 ref, cursor_1)
    R->>R: 重新计算本次权限
    R->>P: 重新读取当前源内容
    P-->>R: 本次读取到的内容
    R-->>A: 后续 fragments + cursor_2
    loop 仍有 next_cursor
        A->>R: read(同一 ref, 当前 cursor)
        R-->>A: 下一页与完成标记
    end
    A->>A: 只有 complete=true 才视为读完

next_cursor39 是不透明的续页令牌,服务端用 HMAC40 防止客户端修改它,并把它绑定到用户、调用方、受众、资源类型、操作、精确引用和过滤条件。精读游标还会关联字段集合指纹与片段位置,因此 Todo 游标无法用于读取 Contact,一个用户的游标也无法用于另一个用户的读取。

每一页都会重新检查权限。如果字段授权改变、源结构/内容在读取过程中发生变化,或游标位置已经超出正文范围,服务端返回明确具体的错误消息目前还没做跨页面的一致性快照41

六、内部统一资源层与公开 MCP 是不同层级的抽象

内部与外部的资源范围分别授权:内部统一九类,外部只开放其中五类。Memory Resource 是业务层的对象分类;MCP1 是客户端发现和调用工具的协议,规范中也定义了 Resources 能力。MCP Resources 属于协议层,与本文的业务资源分类处于不同层级42。内部 Agent 和外部 MCP 使用不同入口,最终都会进入同一套资源授权与读取逻辑。

flowchart TB
    subgraph DOMAIN["业务资源层"]
        EP["Episode"]
        CAL["Calendar"]
        TODO["Todo"]
        VM["Voice Memo"]
        CONTACT["Contact"]
        DIG["Digest"]
        INT["Intent"]
        TR["Transcript"]
        PROF["User Profile"]
    end
    subgraph CORE["内部统一读取层"]
        READER["Resource Reader"]
        AUTH["Run / 当前授权 / 字段策略"]
    end
    subgraph PUBLIC["公开能力层"]
        CAT["Capability Registry / Manifest"]
        TOOLS["MCP tools/list 与 tools/call"]
        OAUTH["OAuth 受众与授权"]
    end
    DOMAIN --> READER
    READER --> AUTH
    AUTH --> CAT
    CAT --> TOOLS
    OAUTH --> TOOLS
    EP -.对外开放.-> CAT
    CAL -.对外开放.-> CAT
    TODO -.对外开放.-> CAT
    VM -.对外开放.-> CAT
    CONTACT -.对外开放.-> CAT

内部 Agent 通过 Echo43 调用内部工具,Enso44 承担资源读取与业务数据访问;外部客户端经过 MCP Gateway45,再由 Capability2 和 Registry 检查公开目录与授权


flowchart LR
    USER["用户 / 外部 Agent"] --> ECHO["Echo:Agent 运行层"]
    ECHO --> INTERNAL["内部工具契约"]
    INTERNAL --> ENSO["Enso:资源读取与业务数据入口"]
    USER --> CLIENT["外部 MCP Client"]
    CLIENT --> GATEWAY["MCP Gateway"]
    GATEWAY --> CAP["Capability 目录与授权"]
    CAP --> ENSO
    ENSO --> PG["PostgreSQL 源记录 / 搜索投影"]
    ENSO --> S3["S3 等转写工件"]

现在回到开头的问题:“上周那场关于移动端提醒的会议,最后决定了什么?还有什么没做?请引用当时的一句原话。”

Agent 可以先通过 list() 判断本轮能访问哪些资源,然后 search("移动端提醒"),得到可能相关的 Episode、Todo 与 Calendar。它精读 Episode,获得会议的整理结论;接着根据返回的任务引用精读 Todo,检查源表中的当前状态。若获得 Transcript 访问许可,还可以沿确切工件引用逐页读取原话

这种顺序使不同来源的事实各归其位:Episode 回答当时讨论了什么,Todo 回答任务现在完成到哪一步,Transcript 提供可核对的发言证据。若本轮没有原文权限,Agent 应当只给出摘要并说明证据范围

flowchart LR
    U["结论 / 待办 / 原话"] --> S["跨资源 search"]
    S --> E["read Episode:会议结论"]
    S --> T["read Todo:当前状态"]
    E --> REF["获准的转写工件引用"]
    REF --> L["read Transcript + cursor"]
    E --> A["组织回答"]
    T --> A
    L --> A
    A --> O["标注结论、进度与证据来源"]

这条链路也是一组自然的 Memory Eval46 测试场景。评测除了检查最终回答,还要记录模型调用的工具、查询的资源、取得的 ref、续页完成情况,并记录模型在缺少权限时发起的受限读取尝试。

我们从 prod 数据中脱敏,为资源访问层准备一组固定的 Eval Case47:查询会议结论、核对未完成 Todo、定位原话,以及授权撤销后的再次读取。每个 Case 都要明确可见资源、期望命中的 ref、允许返回的字段和最终证据。测试时保留完整调用轨迹,检查 Agent 能否按顺序发现资源、找到候选并读取正确对象。

这组测试主要衡量底层读取的可靠性:list 准确描述可用资源,search 召回目标对象,read 返回源记录中的当前事实;同时检查分页完整性和权限变化后的拒绝行为。指标包括 Recall@K、证据命中率、字段越权次数、分页完成率和响应延迟。底层读取通过验证后,再比较统一工具、专项工具和混合工具的公开体验,最后评估从 MCP 迁移到 CLI 的可能性。

七、全链路改造的教训:一个接口变化,要检查所有直接间接使用它的地方

接口统一之后,工具经过多个系统到达用户:服务端维护工具到业务操作的映射,Capability2 描述可公开调用的能力,Capability Registry48 保存对外目录。Manifest49 描述工具名称、Schema 和能力预算。

客户端还要完成 OAuth 授权,才能调用已获准的能力。它通过 Tool Catalog50 读取 tools/list 的结果,再用工具名称和参数执行 tools/call。服务端会按调用方与受众边界51 限制可用能力;自动化巡检有自己的工具目录、mock数据和业务断言

flowchart TB
    SRC["权威资源声明与工具定义"] --> GEN["共享生成器"]
    GEN --> SCHEMA["工具输入 / 输出 Schema"]
    GEN --> MANIFEST["Registry Manifest"]
    GEN --> HANDLER["服务端工具映射"]
    GEN --> TEST["SDK 测试与文档基线"]
    SCHEMA --> HASH["内容哈希 / 不可变发布身份"]
    MANIFEST --> HASH
    HASH --> PUB["新的 Publication 记录"]
    PUB --> REC["Registry reconcile"]
    REC --> SESSION["客户端重建 MCP 会话"]
    SESSION --> CALL["真实 OAuth + tools/list + tools/call"]
    CALL --> VERIFY["检查结果、权限和续页"]

一旦这些部分更新时间不同,就会产生契约漂移52,进而引发意想不到的后果

老搜索工具绑定的一套 MCP ,包含四种专项工具53:搜索记忆、获取转写、列出录音和列出任务,它们分别对应用户熟悉的业务动作,客户端也已经围绕这些名称、参数和返回结构组织调用

统一资源访问层采用了另一种接口设计:用 list / search / read 表达通用读取动作,再通过资源类型和 ref 指定具体对象。迁移问题集中出现在公开接口:内部实现统一后,已经接入产品的客户端仍保留旧约定。部分旧客户端继续按四个专项工具发起请求,提示词、工具缓存和结果解析也依赖旧名称与结构。当前实现未依赖 notifications/tools/list_changed 自动刷新目录,因此客户端需要重新初始化并再次调用 tools/list。

产品侧反馈表明,已有用户仍依赖原来的搜索、转写、录音和任务操作。此时直接切换到三工具,会让一部分已有调用路径失效,例如,Registry 已发布三工具,服务端执行表仍然只认识旧四工具;或者服务端已经恢复三工具,客户端却继续使用缓存的四工具目录。OAuth 登录和 MCP 初始化可能成功,真正调用时才收到 tool_not_available、stale_session 或参数不匹配错误,甚至无法发现相关工具,导致 MCP 能力完全失效

我们内部进行了一次版本回滚,主要恢复外部接口的兼容性;内部统一读取能力仍可作为四个入口共用的底层实现。因此,内部采用几种通用操作,与产品向用户公开几种工具,是两个独立的设计决策,需要分别设计、测试并完成端到端联调。真实生产事故来的

sequenceDiagram
    autonumber
    actor U as 用户 / 外部 Agent
    participant C as MCP 客户端
    participant R as Registry / Capability
    participant G as 工具网关
    participant M as Memory Reader
    U->>C: 查昨天的待办
    C->>R: OAuth + initialize
    R-->>C: 会话已建立
    C->>R: tools/list
    R-->>C: 旧四工具目录
    Note over R,G: 服务端执行器已切到统一三工具
    C->>G: tools/call(旧任务列表工具)
    G-->>C: tool_not_available / stale_session
    C-->>U: 调用失败
    Note over C,M: 请求尚未进入 Resource Reader

这次经历说明,减少工具数量可以简化内部调用;产品接口升级还要管理外部客户端已经依赖的工具名称、参数和返回格式,并将它们视为正式的产品契约54。兼容策略、调用埋点和过渡迁移也应纳入升级计划,因为一次接口变化会同时影响客户端、服务端和发布目录。

下一次升级至少应兼容上一个客户端版本55,并提前定义版本识别、迁移窗口和旧接口下线时间。可以先发布新工具,让客户端逐步切换;再通过真实调用记录确认旧工具使用量已经下降;最后才移除旧入口。兼容逻辑需要有明确范围和结束条件,避免新旧接口长期并存、行为逐渐分叉、对于目标的不同客户端预期行为,还要做另行自动化回归测试

确实需要兼容旧版本时,应把兼容入口作为有边界的适配层:明确支持哪个版本、记录调用量、覆盖兼容测试,并设定移除条件。对于真正的业务错误,则应修复共同的数据结构、状态转换或调用契约,别让 fallback 成为你的第二条并行业务链路

从这次经历可以提炼出一条通用工程原则:需求设计和 Bug 修复都应从全链路排查开始。收到需求或缺陷后,先明确用户实际遇到的行为,再在整个仓库和上下游依赖中搜索同一接口、同一数据结构和同一处理模式,找出所有生产调用方与测试调用方。确认影响范围后,统一修改权威定义、调用代码、生成文件、测试和文档,并按 repo harness 规范同步更新相关切面。


stateDiagram-v2
    [*] --> 当前稳定版本
    当前稳定版本 --> 候选构建: 锁定源码与生成契约
    候选构建 --> 本地通过: 单测 / 真实数据库集成
    本地通过 --> 阻断: 依赖未对齐或缺发布权限
    本地通过 --> 预发布部署: 审批并冻结发布清单
    预发布部署 --> 环境校验: 迁移 / 对账 / 服务部署
    预发布部署 --> 阻断: 超时或不兼容
    环境校验 --> 受控放流: OAuth / tools/list / tools/call 验收
    环境校验 --> 阻断: 401 / 目录错位 / 业务失败
    受控放流 --> 生产观察: 真实用户路径与巡检
    生产观察 --> 新稳定版本: 达到验收门槛
    生产观察 --> 停流与协同恢复: 业务退化
    阻断 --> 修复或恢复: 核对已提交的迁移和发布内容
    修复或恢复 --> 当前稳定版本: 人工确认恢复
    停流与协同恢复 --> 当前稳定版本: 恢复并再次验收
    新稳定版本 --> [*]
sequenceDiagram
    autonumber
    participant DEV as 开发与 CI
    participant DB as 数据库 / 搜索投影
    participant REG as Registry / 授权服务
    participant API as API / MCP 网关
    participant CLI as 客户端 / 巡检
    participant REL as 发布负责人
    DEV->>DEV: 测试代码、共享 Schema 与生成校验
    DEV-->>REL: 候选制品和测试证据
    REL->>REL: 确认窗口、回退对象与授权
    REL->>DB: 迁移预检与必要回填
    DB-->>REL: 实际迁移状态
    REL->>REG: 发布不可变目录与授权策略
    REG-->>REL: 内容哈希和 publication 状态
    REL->>API: 部署匹配的服务制品
    API-->>REL: 镜像身份与健康状态
    REL->>CLI: 使用匹配客户端验收
    CLI->>API: OAuth / initialize / tools/list
    API-->>CLI: 当前目录
    CLI->>API: search → read → cursor
    API->>DB: 真实权限与源读取
    DB-->>API: 数据或受控拒绝
    API-->>CLI: 结果
    CLI-->>REL: 成功率、失败码与实际响应
    alt 验收成功
        REL->>REL: 小流量开放并观察
    else 关键阶段失败
        REL->>REL: 停止放流,核对已完成步骤
        REL->>DB: 评估迁移恢复影响
        REL->>REG: 恢复匹配的目录与授权
        REL->>API: 恢复匹配服务制品
        REL->>CLI: 重连并复验
    end

这套做法可以落实为五个具体动作:

  1. 先找完整调用链。 从入口向下追踪到数据源,也从数据结构向上查找所有消费者。仓库内的搜索结果只是起点,还要检查 SDK、独立客户端和上下游服务中的调用约定
  2. 明确唯一的权威定义。 工具名称、参数、权限和返回结构应由同一份契约维护,再生成服务端注册、客户端类型和测试基线,减少多处手工维护造成的漂移
  3. 按同一模式成组修改。 发现多个资源、接口或服务存在相同错误模式时,先确认它们属于同一类问题,再在本次改动中一并修正,避免只修复最先暴露的入口。
  4. 让测试覆盖真实边界。 单元测试验证局部逻辑,契约测试确认调用双方对同一份接口契约的理解一致,端到端测试验证真实客户端能否完成任务。测试数据、环境版本和调用轨迹也要对应到同一轮发布。
  5. 把发布状态记录清楚。 数据库迁移、工具目录更新和服务部署可能分别成功或失败。每一步都要有独立结果和可恢复方式,不能把整个发布过程当成一个可以自动整体回滚的动作
Prompt:依据现有目标、spec/tasks.md、design.md及日志推进,原地更新唯一任务清单,区分已验证子项(附证据)、未完成行为与外部阻塞,禁止平行计划。以独立可验收行为衡量进度,以闭环可验证模块修改为任务单位,不以局部修补替代整体验收。并行允许主Agent负责一个主要范围(推进任务)仅一个subagent负责独立范围生命周期(测试/审计/阅读大量内容汇总摘要等耗费上下文但是目的明确的实现动作)
明确文件归属,避免重复研究与交叉修改。关键设计取舍集中确认;已批准范围内的实现、测试与修复自主完成。改动前核对根因、最小方案、上下游影响、回滚及验收条件。严禁重复Happy Path、无效测试、凑覆盖率用例、冗余兜底、死代码、遗留兼容分支及无意义抽象,不要新增新的测试文件或函数,除非你有百分百把握其不测试后续必定会出现问题,多删少增测试,任何需求设计都考虑先给最小方案,并说明为什么不能更简单,并考虑是否存在过度设计,不保留兼容遗留代码
在需求设计和 BUG 修复中优先采用系统性、结构化的代码设计。提出需求或修复缺陷时,先在整个仓库及上下游依赖中检索同类模式,把同一问题涉及的切面一并纳入改动。优先从数据结构、调用契约和状态流转等根因入手,设计完整修复方案;兜底逻辑只用于明确的边界场景,并删除已经被替代的链路。环境仅预检一次,持续执行定向测试,修改范围完成后运行全量测试;缺陷先复现再修复,不得削弱断言或模拟成功。
每完成一项核对行为、删除项与证据并更新清单,做完一项勾选一项,以任务与最小可验证切面为推进单元,确保进度明确,外部阻塞仅暂停依赖步骤,其余继续推进;commit提交、pr, ci完全授权,按最佳实践/自我审计/最小修改,推动到所有task/pi-goal完整实施,文档与目标勾选,严格审计通过,mr前 

尤其需要谨慎处理兜底逻辑56,例如,旧工具调用失败后静默转发到另一个接口、权限读取失败时返回空列表、分页异常时直接截断正文,以上这些场景可能让表面流程继续运行,却掩盖了真实错误,用户最终拿到的可能是缺失内容,测试也可能因为没有明确失败而误判通过

八、小结:让记忆读取与工具演进形成闭环

Agent Memory 的核心,是让模型沿着稳定的读取动作访问分散的业务事实:先发现资源,再检索候选,最后回到源记录核验,资源声明与 Run 授权划定可见边界,搜索投影和分页支撑高效读取,工具迁移则提醒我们,内部抽象、客户端契约和发布版本必须协同演进;每次改动都要覆盖完整调用链,并用真实任务验证用户体验,才能平安上线

脚注

Footnotes

  1. MCP(Model Context Protocol,模型上下文协议):用于客户端发现和调用工具的开放协议。tools/list 返回当前可见工具,tools/call 执行指定工具。参见 MCP Tools 规范。 ↩ ↩2 ↩3

  2. Capability(公开能力定义):描述某个客户端可发现、可调用的工具及其参数和权限范围。它决定公开目录的边界,实际数据仍由服务端按当前授权读取。 ↩ ↩2 ↩3

  3. Episode(经历摘要):从录音或相关活动中整理出的内容单元,通常包含摘要、话题、时间范围和关联记录。它适合定位经历,逐字引用应回到来源 Transcript。 ↩ ↩2

  4. Todo(待办资源):记录待执行事项、状态和可选截止日期的业务对象。查询“本周到期”时,需要依据截止日期过滤;无截止日期的任务不能被当作某天到期。 ↩ ↩2

  5. Calendar(日程资源):描述事件时间、参与者及提醒等信息的业务对象。它的时间语义与 Todo 截止日期不同。 ↩ ↩2

  6. Transcript(转写工件):语音识别生成的文字及其时间、说话人等结构化信息。本文所说的精读会定位确切工件,避免不同版本的转写混用。 ↩ ↩2

  7. Stable Reference(稳定资源引用):由资源类型和对象身份组成的引用,例如 todo:<id>。它让搜索候选与精确读取指向同一个业务对象,也避免暴露数据库表名和内部主键结构。 ↩ ↩2

  8. Memo(整理后的记忆文本):本项目旧检索链路中的记忆文档或摘要载体。它适合按主题找候选,但命中 Memo 后仍可能需要追到对应业务对象,确认当前状态和精确来源。 ↩

  9. Prompt(提示词):提供给模型的任务目标、约束、工具说明和行为指引。Prompt 帮助模型理解任务并选择工具,实际权限与参数合法性仍由服务端校验。 ↩

  10. Tool Ergonomics(工具易用性):衡量工具名称、参数、说明和返回内容对模型正确调用的支持程度。参见 Anthropic 的 Writing effective tools for agents。本文借鉴其设计与评测思路,资源模型本身来自项目需求。 ↩

  11. Memory Resource(记忆资源):Agent 可以读取的一类业务对象,例如 Episode、Todo、Calendar 或 Contact。资源定义对象身份、可读字段和适用的过滤规则,具体读取动作由工具提供。 ↩

  12. Contact(联系人资源):保存人物身份、别名及关系的业务对象。检索时需要处理别名、合并和字段级可见性。 ↩

  13. Voice Memo(语音备忘):用户主动保存的语音笔记及其整理内容。它与一次完整录音产生的 Transcript 工件有不同的业务身份。 ↩

  14. Digest(归纳记忆):汇总一段时间或多次事件中的重要变化的派生内容。它便于回顾,但仍需关注来源和更新时间。 ↩

  15. Intent(待确认意图):Agent 准备执行的业务动作或提议。只有经过规定的确认和写入流程,才能将提议视为已执行的操作。 ↩

  16. User Profile(用户长期画像):保存长期偏好与个性化上下文的数据。它的敏感程度和可见范围可能高于一般业务记录。 ↩

  17. Data Relationship(DR,数据关系):描述不同业务对象之间如何关联,以及一个对象的生成、引用或读取依赖哪些上游数据。本文的图表达逻辑关系,不代表每条箭头都对应数据库外键。 ↩

  18. Resource Declaration(资源声明):描述某类资源的身份字段、可读字段、过滤条件和适用受众的机器可读定义。读取层依据声明校验请求,并生成对应的查询规则。 ↩

  19. Schema(接口结构契约):规定字段类型、必填参数、嵌套关系和请求合法性的机器可读定义。工具目录和客户端应使用同一份权威 Schema。 ↩

  20. Agent Runtime(Agent 运行时):负责启动和执行 Agent、维护本次调用上下文并调度工具的运行环境。本文中的 Runtime 为内部读取入口提供可信的用户、Thread、Run 和 Agent 身份。 ↩

  21. Thread(对话或任务上下文):串联多轮交互的上下文标识。它可以跨越多个 Run,因此授权校验还要确认当前 Run 与 Thread 的关联。 ↩ ↩2

  22. Config(Agent 权限配置):本项目中记录 Agent 可访问资源、操作和字段的当前配置。读取时会将当前配置与 Run 启动快照及系统上限求交。 ↩ ↩2

  23. Resource Reader(资源读取器):本项目内部统一的读取实现,接收可信身份、有效授权和资源请求,再调用各类源数据适配器。它统一读取入口和处理步骤,各类资源继续由自己的适配器访问源表与存储。 ↩

  24. OAuth(授权协议):让外部客户端通过用户授权取得有限访问能力的协议。OAuth 成功建立身份和授权关系后,业务服务仍需检查具体资源与字段权限。 ↩

  25. MCP Tools(MCP 工具能力):MCP 协议中供客户端发现和调用的工具接口。客户端通过 tools/list 获取工具定义,再通过 tools/call 执行指定工具;本文的 list / search / read 以这一能力形式对外提供。 ↩

  26. Run(一次 Agent 执行):Agent 从可信入口开始执行到结束的一次运行实例。一个 Thread 可以包含多次 Run;每次 Run 都有自己的身份与授权起点。 ↩

  27. Authorization Snapshot(授权快照):Run 启动时保存的资源、操作和字段权限集合。实际读取还会与当前 Config22 权限及系统上限求交。 ↩

  28. Field Grant(字段授权):授予特定调用方读取某个资源字段或嵌套字段路径的权限。检索阶段也使用这份授权确定哪些字段可以参与匹配,避免受限字段影响候选结果。 ↩

  29. JSON Pointer(JSON 路径标记):RFC 6901 定义的 JSON 节点定位语法,例如 /participants/0/display_name。本文用路径和偏移描述分片位置。参见 RFC 6901。 ↩ ↩2

  30. Search Projection(搜索投影):从业务源记录派生出的检索数据,用来维护对象的搜索资格并支持候选召回。对象当前的业务状态仍以源记录为准。 ↩

  31. Object Storage(对象存储):以对象 Key 保存音频、附件或长文本工件的存储方式,例如 S3。它通常无法与 PostgreSQL 共享一个原子事务。 ↩

  32. pg_trgm(三元字符组匹配):PostgreSQL 扩展,通过字符三元组计算字符串相似度,可用于模糊匹配。效果受语言、查询长度和阈值影响。参见 PostgreSQL 官方文档。 ↩

  33. Full-Text Search(全文检索):PostgreSQL 的词项检索能力。tsvector 表示可检索词项,tsquery 表示查询,ts_rank 计算词法相关度;相关度衡量文本匹配,不等于事实可信度。参见 PostgreSQL 文档。 ↩

  34. Rerank(重排):候选召回后,依据相关度、状态、时间、置信度或资源类型等信号重新排序。重排只能调整已经进入候选集的记录,无法找回在召回阶段被截断的答案。 ↩

  35. Embedding(向量表示):模型将文本映射成数值向量,检索时根据向量距离或相似度找候选。向量召回仍须应用相同的用户、字段和生命周期限制。 ↩

  36. BM25(Best Matching 25):基于词频、逆文档频率和文档长度归一化的经典词法相关度算法。它与向量语义相似度属于不同信号;旧检索路径曾提供独立策略入口。 ↩

  37. Retrieval Eval(检索评测):使用固定问题和期望证据集合,比较 Recall@K、Precision@K、MRR 或 NDCG,并记录权限拒绝、延迟和成本。评测应保留候选截断点,以区分召回失败和排序失败。 ↩

  38. Context Engineering(上下文工程):控制进入模型上下文的信息数量、结构、来源和时效。先检索候选、再按需读取长内容,可以减少无关文本占用。参见 Anthropic 的 Effective context engineering for AI agents。 ↩

  39. Cursor(分页游标):表示下一页位置的不透明令牌。客户端只需原样回传,不应自行修改或依赖其内部格式。 ↩

  40. HMAC(基于哈希的消息认证码):服务端使用密钥对游标内容计算签名,防止客户端篡改。签名不等于加密,因此游标不应承载秘密原文。 ↩

  41. Snapshot Read(一致性快照读取):跨多次查询固定同一数据版本。当前分页每次读取最新源内容,尚未提供跨页快照。 ↩

  42. MCP Resources 与业务 Resource:MCP 规范中的 Resources 是协议层的一种资源暴露能力;本文的 Memory Resource 是业务层的数据分类,并通过 MCP Tools 提供 list / search / read。两者名称相近,职责不同。参见 MCP Resources 规范。 ↩

  43. Echo(Agent 运行层):本项目中承载 Agent 调用与工作流执行的一部分运行能力。它通过内部工具访问业务数据,不直接替代源记录和授权服务。 ↩

  44. Enso(业务数据与资源读取入口):本项目中负责部分业务 API、资源读取及 PostgreSQL 数据访问的服务边界。它把不同源表适配到统一读取契约,具体资源仍由各自的数据模型维护。 ↩

  45. MCP Gateway(MCP 工具网关):接收外部 MCP 客户端的初始化和工具调用请求,并把通过身份与授权检查的调用交给相应执行器。 ↩

  46. Memory Eval(记忆评测平台):本项目用于构造 Memory 数据集、运行评测并查看 Case 与 Run 结果的评测入口。资源检索评测应固定输入、授权范围和期望证据,保留实际工具轨迹。 ↩

  47. Eval Case(评测用例):包含用户输入、允许访问的数据范围、期望结果和判定规则的一条可复现样本。Agent 评测还应保存实际工具调用轨迹和失败阶段。 ↩

  48. Capability Registry(能力注册中心):保存对外可发现的工具能力、关联授权和发布状态的控制面。它决定客户端能看到哪些公开操作,但具体数据读取仍由服务端执行。 ↩

  49. Manifest(发布清单):描述公开工具名称、Schema、授权预算及关联元数据的机器可读文件。它需要与服务端执行器和客户端使用的契约保持一致。 ↩

  50. Tool Catalog(工具目录):MCP 客户端通过 tools/list 发现的工具名称及参数描述。客户端可能缓存这份目录,所以服务端更换契约后还要验证重新初始化和重新发现的行为。 ↩

  51. 受众边界(Audience Boundary):服务端根据调用方身份和目标客户端确定可访问的能力范围。授权判断需要同时考虑用户身份、客户端类型和资源策略,登录状态本身不决定全部资源权限。 ↩

  52. Contract Drift(契约漂移):客户端、Schema、Registry 与服务端执行器各自使用不同版本的工具名称、参数或返回约定。它常表现为初始化成功,但真正调用时才失败。 ↩

  53. Tool Surface(工具暴露面):客户端实际能够发现和调用的工具集合,包括工具名称、参数、返回结构和可用范围。内部读取层可以保持统一,对外工具则可按用户任务组织。 ↩

  54. Client Consumer(客户端消费者):依赖公开工具契约的客户端或集成方,包括产品内客户端、外部 Agent、SDK 和自动化巡检。工具名称、参数与返回结构都可能成为它们的长期依赖。 ↩

  55. Backward Compatibility(向后兼容):新版本服务端继续接受旧客户端按既有工具名称、参数和返回格式发出的请求,让客户端能够分批升级,并在明确的迁移窗口后再下线旧接口。 ↩

  56. Fallback(兜底逻辑):主路径失败时触发的备用处理方式。它适用于明确、可预期的故障边界;若静默吞掉错误、返回不完整结果或掩盖权限问题,就会增加故障发现和定位的难度。 ↩

版权与许可

作者: Cooing

本文链接: https://blog.cooingcode.space/zh/blog/agent-memory-resource-and-tool/

许可协议:自由转载、引用,但请署名并注明出处。