Agent面试详解(中):记忆、工具与多用户并发
原创 · 约 17 分钟阅读 · 阅读 --

Agent面试详解(中):记忆、工具与多用户并发

作者: Alex Xiang


《Agent面试详解》系列:上篇:运行时与上下文 · 中篇:记忆、工具与并发 · 下篇:评测、安全与落地

上篇搭好了 Agent 的运行时骨架:状态机驱动循环,checkpoint 负责恢复,上下文按 Token 预算动态组装。接下来要把它放进真实的多用户环境。

这一步比接入一个向量数据库或 MCP Server 难得多。长期记忆会重复、冲突和过期;工具会超时、越权并产生不可逆副作用;两个 Worker 可能同时执行同一个步骤。中篇集中处理这些数据一致性和并发问题。

把所有历史对话做 embedding,再按相似度取 Top K,不等于长期记忆。它很快会遇到重复、冲突、过期、越权和无限膨胀。

先把记忆分层

层次生命周期例子
Working memory单次模型调用当前 Prompt、最近 observation
Session state当前 runplan、step、checkpoint、预算
Episodic memory跨 run 的经历某次部署失败及其原因
Semantic memory稳定事实用户偏好、系统约束、领域知识
Procedural memory长期规则Skill、操作步骤、验收规范

LangChain 的上下文工程文档也区分 runtime context、会话 state 和跨会话 store,并建议通过生命周期中间件完成摘要、工具筛选和状态更新。Context Engineering

分层的价值不只是便于命名,而是让每类信息有不同的写入条件和淘汰策略。一次工具返回属于 observation,不应因为与用户问题相似就自动升级成长期事实;一次成功操作也不应直接变成永远有效的流程规则。

给记忆设计“遗忘”

真正的 Memory Service 至少要支持写入、检索、合并、修订和遗忘:

  • 相同实体、相同事实去重。
  • 新事实覆盖旧版本,同时保留审计链。
  • 临时信息设置 TTL。
  • 根据重要性、置信度和使用频率衰减。
  • 多条 episode 定期合并成 semantic memory。
  • 长期不命中的内容进入冷存储。
  • 每个租户和用户设置容量配额。
  • 用户可以查看、纠正和删除自己的记忆。

长期记忆从事件写入到去重、合并、过期、分层存储和过滤检索的完整生命周期

“删除”也不能只删向量索引。原始对象、派生摘要、向量、缓存和搜索索引都要通过同一条删除链路处理,否则用户删除了一条记忆,Agent 仍可能从旧摘要里把它找回来。

检索先过权限,再谈相似度

每条记忆应带上来源、时间、租户、用户、权限、置信度和版本。检索不能只看向量距离:

WHERE tenant_id = :tenant_id
  AND permission_scope && :allowed_scopes
  AND valid_from <= :now
  AND (expires_at IS NULL OR expires_at > :now)
ORDER BY relevance_score DESC, confidence DESC, last_used_at DESC
LIMIT :budgeted_k

长期记忆先按租户、权限和有效期做硬过滤,再进入语义排序并截取 Top K

先做硬过滤,再做语义排序。否则“很相似但属于别的客户”的记忆,正是最危险的数据泄漏。检索结果还要受本轮 Token 预算约束,Top K 只是候选数量,不表示候选必须全部进入上下文。

工具层要和模型彻底解耦

一个生产工具不能只是 Python 函数名加 description。它需要明确的执行合同:

name: orders.refund
version: 2
input_schema: RefundRequest
output_schema: RefundResult
timeout_ms: 10000
retry_policy: no_automatic_retry
idempotency: required
permission: orders.refund
side_effect_level: irreversible
approval: required
error_taxonomy:
  - validation_error
  - permission_denied
  - provider_timeout
  - business_rejected

Tool Registry 负责描述和发现,Tool Gateway 负责控制执行。模型只提交结构化调用,不直接获得第三方 Token,也不直接连数据库。

Tool Gateway 在执行前校验结构、租户权限、风险、超时、重试、幂等和审批,并隔离不同工具

这里有一个容易混淆的边界:Tool Registry 回答“有哪些工具、参数是什么”,Tool Gateway 回答“当前租户能不能调用、这次调用是否需要审批、失败能不能重试”。把两者合在模型侧,会让工具描述、凭证和策略同时暴露给一个不完全确定的决策者。

大结果不要原样返回模型

工具执行后可以同时生成四种结果:

  • 原始结果:存对象存储,供审计和下载。
  • 结构化结果:供程序继续处理。
  • 模型摘要:只包含下一步决策需要的信息。
  • 类型化错误:驱动重试、改参、重规划或停止。

例如数据库查询返回 20 万行时,模型不需要看到全部内容。它需要字段、行数、统计摘要、异常样本和原始结果引用。

Anthropic 在工具工程实践中强调,工具应该定义清楚、能够组合、谨慎占用上下文,并通过评测反复改进。Writing Effective Tools for AI Agents

MCP 解决了工具如何暴露给不同客户端的问题,但不自动解决租户隔离、审批、幂等、执行沙箱和企业审计。这些仍然属于 Harness。

多用户并发冲突怎么处理

Agent 一旦支持多用户,就会遇到三类冲突:

  1. 同一用户打开两个会话,同时修改同一 run 或同一文档。
  2. 不同用户操作同一个业务对象,例如工单、代码分支或订单。
  3. 两个 Worker 误以为自己都拥有同一个 run,重复产生副作用。

这三类冲突不能只靠“每个用户一个 session”解决。session 隔离的是交互上下文,业务对象、run 所有权和外部副作用仍然可能共享。

先让每一层都认识租户

Run、step、message、memory、tool call、trace 和 artifact 都必须带租户边界:

tenant_id / user_id / session_id / run_id / step_id / tool_call_id

不能只在 API 入口检查一次,然后让内部队列和记忆库丢掉 tenant_id。租户隔离必须贯穿入口、状态、队列、工具、存储和日志。

状态冲突用版本,不用长锁

更新 run 时携带版本号:

UPDATE agent_runs
SET state = :new_state,
    version = version + 1
WHERE id = :run_id
  AND tenant_id = :tenant_id
  AND version = :expected_version;

更新行数为 0,说明状态已经变化。此时重新读取并判断是否重规划,而不是覆盖别人的结果。

数据库悲观锁只应该包围毫秒级的原子更新,不能跨模型调用和网络调用。让事务等模型思考 30 秒,会直接把连接池和并发拖垮。

Worker 使用租约和心跳

Worker 获取的是有过期时间的 lease,不是永久所有权。执行期间续租;崩溃后 lease 到期,其他 Worker 才能接管。接管者从最后一个已提交 checkpoint 继续,而不是猜测前一个 Worker 做到哪里。

Worker A 崩溃后停止心跳,Worker B 必须等待租约过期,之后才能取得新租约并从 checkpoint 恢复

租约时间需要覆盖一次正常心跳抖动,但不能长到故障后迟迟不能接管。比较实用的起点是:租约时长为心跳间隔的三到五倍,续租失败后立即停止领取新步骤;真正执行副作用前,再确认租约和状态版本仍然有效。

副作用必须幂等

idempotency_key = run_id + step_id + tool_call_id

同一个 key 的退款、发送、创建和写入只能成功一次。Agent 的“恰好一次执行”通常无法端到端保证,更现实的做法是 at-least-once 调度,加业务侧幂等。

多租户从入口隔离、单飞去重、乐观锁、分区队列、Worker 租约到工具幂等的并发链路

如果数据库状态更新和事件发布必须同时成功,可以使用 transactional outbox:事务内只写业务状态和 outbox,独立 relay 再把事件送入队列。

Planning 不等于先写一份漂亮清单

静态计划在第一步执行后就可能过时。一个有效 plan 至少包含:

  • 最终可观察目标。
  • 步骤依赖。
  • 每一步验收证据。
  • 当前状态和未解决阻塞。
  • 失败后的恢复策略。

当工具返回与假设冲突、依赖不可用、预算明显不足或连续两次没有产生新证据时,应触发重规划。重规划不是把原计划再说一遍,而是明确撤销哪些假设、保留哪些证据、为什么换路径。

单 Agent 是默认选择

Subagent 适合并行检索、独立 review、上下文隔离和不同领域分析。Multi-Agent 只适合任务真正可分、角色确实不同、结果可验证的场景。

多 Agent 最常见的问题不是智力不足,而是:

  • 重复搜索和重复调用工具。
  • 状态版本不同步。
  • 子 Agent 互相强化错误。
  • 通信 Token 超过实际工作。
  • 最终没有人对结果负责。

委派协议应明确输入、输出 schema、预算、截止时间和完成条件。子 Agent 返回的不是“我研究完了”,而是一份可验证的结果包。

中篇留下的状态

到这里,Agent 的记忆有生命周期,工具有执行合同,多租户状态有版本,Worker 有租约,外部副作用有幂等键。它已经不容易因为重试或并发直接写坏数据。

但“没有写坏”还不等于“做对了”。下篇要回答最后一组问题:怎样还原一次 run,怎样测试不确定的执行轨迹,安全边界放在哪里,以及哪些 Agent 场景值得真正投入。

继续阅读:下篇:评测、安全与落地

《Agent面试详解》系列:上篇:运行时与上下文 · 中篇:记忆、工具与并发 · 下篇:评测、安全与落地

打开原图 ↗