本文是「Agent 基本概念」系列第四篇。
前三篇分别讲了 Agent 是什么 、由哪些组件构成 、如何决策与规划 。这一篇聚焦 Agent 最核心的两个能力基座:记忆 与工具 。
系列规划:① 什么是 AI Agent → ② 核心组件 → ③ 决策与规划 → ④ 记忆与工具 → ⑤ 架构与落地。
前言:记忆与工具,决定 Agent 的能力边界
前一篇我们讨论了 Agent 的四种决策与规划模式。但无论用哪种模式,Agent 都需要两个能力基座:
- 记忆:让 Agent 记住过去、积累经验、保持一致;
- 工具:让 Agent 从"会说"变成"会做"。
没有记忆,Agent 每轮都从零开始;没有工具,Agent 只能空谈。
这篇文章的主线是:
从"上下文窗口"这个最原始的记忆开始,一路讲到"MCP"这个标准化的工具接入协议,理解 Agent 如何"记住"和"动手"。
一、Agent 记忆:不只是把对话塞进上下文窗口
1.1 为什么上下文窗口不够用?
最早的 LLM 应用,记忆方案非常朴素:把所有对话历史都塞进上下文窗口。
这能解决短期对话的连贯性问题,但面对长期任务很快崩溃:
- 上下文窗口有限:再大的窗口也有上限,几十轮对话后就会爆;
- 成本线性增长:每轮推理都要重新处理全部历史,token 费用越来越高;
- 噪声干扰:无关的旧对话稀释了模型对当前任务的注意力;
- 无法跨会话:关掉页面,一切归零。
一篇 2025 年 12 月的 ACM 教程论文指出,LLM 的固定上下文窗口从根本上限制了它们在持续、复杂的人机交互中的实用性,导致遗忘之前的对话轮次、缺乏一致的人格以及无法执行长周期推理等问题。这正是 Agent 记忆系统需要解决的核心问题。
1.2 记忆的类型:从认知科学借来的分类
Agent 记忆的设计,大量借鉴了认知科学对人类记忆的分类。当前研究中,最主流的分类方式是按功能划分:
| 记忆类型 | 作用 | 对应技术方案 | 例子 |
|---|---|---|---|
| 短期记忆 | 当前对话上下文 | 上下文窗口 | 用户刚说的"不要转机" |
| 工作记忆 | 当前任务状态 | 内存/状态对象/注意力机制 | 已选航班、支付进度 |
| 情景记忆 | 过去发生过什么 | 向量存储 + 语义检索 | 上次订票时用户抱怨过转机 |
| 语义记忆 | 事实和知识 | 知识图谱 | 北京的首都机场代码是 PEK |
| 程序记忆 | 如何做某件事 | 工具定义 / 提示模板 | 订票的标准流程 |
一篇 2026 年 8 月发表的综述对 2025-2026 年出现的主流记忆架构做了系统性的基准测试,比较了三种架构:向量存储情景记忆 在原始存储和快速检索方面表现出色,但在高更新频率下会急剧退化;知识图谱语义记忆 提供了可解释的关系和下游推理优势,代价是更高的索引延迟;基于注意力的工作记忆 提供动态上下文权重,但在长程依赖解析上存在困难。研究结果表明,分层混合架构------在语义图谱之下铺设情景存储,再用注意力机制增强------是通用 Agent 最稳健的设计。
1.3 记忆的四层管理机制
根据一篇关于 Agent 原生记忆系统的综述,Agent 记忆的管理包括四个核心组件:
(1)记忆表示与存储
记忆的逻辑表示决定了系统如何搜索、组合和使用历史上下文。主要方案包括:
- Token 级序列表示:直接用文本序列存储,最简单但检索效率低;
- 向量嵌入表示:把记忆编码为向量,支持语义检索,但丢失结构信息;
- 图结构表示:用实体和关系构建知识图谱,支持多跳推理,但构建成本高。
物理存储则可以选择内存中的上下文寄存器、稠密向量引擎或图数据库。
(2)记忆检索与路由
根据当前查询上下文,动态识别相关的记忆子集。检索机制包括:原生注意力检索、语义 Q-近邻搜索、拓扑子图遍历、通过 LLM 规划的自主 Agent 路由,以及多阶段混合执行。
(3)记忆维护
治理记忆的动态生命周期,包括三个子操作:
- 冲突解决与版本管理:处理矛盾信息,通过多版本、失效或优先级规则;
- 容量管理:通过硬淘汰(如 FIFO、token 限制)或基于分数的优先级淘汰(如时间衰减)来限制增长;
- 语义整合:用 LLM 合并冗余断言为密集摘要,或通过工具调用执行 CRUD 操作。
(4)记忆治理
这是最容易被忽视但最重要的部分。记忆必须支持遗忘------不仅要忘掉不再需要的信息,还要能撤销已经授予的访问权限,并将记忆回滚到之前的状态,覆盖缓存、派生工件和主存储。
隐私研究提出了 Memory-Aware Retention Schema(MaRS) 框架,结合六种有理论基础的遗忘策略,在性能、隐私和计算效率之间取得平衡。配套的 FiFA 基准从叙事连贯性、目标完成、社交回忆准确性、隐私保护和成本效率五个维度评估 Agent 的记忆表现。
1.4 记忆与 RAG 的本质区别
很多人把 Agent 记忆和 RAG 混为一谈。它们确实有交集,但本质不同。
RAG(Retrieval-Augmented Generation) 是一种无状态的、只读的检索原语:给定一个查询,从静态语料库中获取相关段落,以增强单次生成步骤。RAG 回答的是"资料里说了什么"。
Agent Memory 则是一个持久且可更新的基础设施,用于管理 Agent 特定的状态。它管理完整的长期记忆生命周期,包括记忆的表示、存储、检索和维护,而不仅仅是填充当前上下文窗口。Agent Memory 回答的是"Agent 该记住什么"。
用一个更通俗的比喻:
- RAG 让它会查资料;
- Agentic RAG 让它更会查资料,把检索集成到自主决策循环中,由 LLM Agent 主动控制何时检索、如何检索;
- Memory 让它能带着过去的上下文继续工作。
这三者组合起来,才能构建一个既有知识、又有记忆的完整 Agent。
1.5 代码实战:三层记忆的最小实现
下面用 Python 实现一个最小的三层记忆系统,包含短期记忆、工作记忆和长期记忆。
python
import json
from datetime import datetime
from typing import Any
from dataclasses import dataclass, field
@dataclass
class ShortTermMemory:
"""短期记忆:当前对话上下文,受 token 预算约束"""
messages: list[dict] = field(default_factory=list)
max_tokens: int = 8000
def add(self, role: str, content: str):
self.messages.append({
"role": role,
"content": content,
"time": datetime.now().isoformat()
})
self._trim()
def _trim(self):
"""软裁剪:保留最近的消息,超出预算时从最早的开始删"""
# 简化估算:1 token ≈ 4 字符
while self._estimate_tokens() > self.max_tokens and len(self.messages) > 2:
self.messages.pop(0)
def _estimate_tokens(self) -> int:
return sum(len(m["content"]) // 4 for m in self.messages)
def get_context(self) -> list[dict]:
return self.messages
@dataclass
class WorkingMemory:
"""工作记忆:当前任务状态,结构化存储"""
state: dict[str, Any] = field(default_factory=dict)
def set(self, key: str, value: Any):
self.state[key] = {
"value": value,
"updated_at": datetime.now().isoformat()
}
def get(self, key: str, default=None):
item = self.state.get(key)
return item["value"] if item else default
def snapshot(self) -> dict:
return {k: v["value"] for k, v in self.state.items()}
class LongTermMemory:
"""长期记忆:基于向量检索的跨会话记忆"""
def __init__(self, vector_store):
self.vector_store = vector_store # 任意向量库:Chroma/Pinecone/pgvector
def write(self, content: str, metadata: dict | None = None):
"""写入记忆"""
self.vector_store.add_texts(
texts=[content],
metadatas=[{
"time": datetime.now().isoformat(),
**(metadata or {})
}]
)
def retrieve(self, query: str, top_k: int = 5) -> list[str]:
"""检索相关记忆"""
results = self.vector_store.similarity_search(query, k=top_k)
return [doc.page_content for doc in results]
def forget(self, memory_id: str):
"""遗忘:删除指定记忆"""
self.vector_store.delete([memory_id])
使用示例:
python
# 初始化
short = ShortTermMemory()
working = WorkingMemory()
long_term = LongTermMemory(vector_store=my_vector_store)
# 用户说了一句话
short.add("user", "帮我订明天去北京的机票,要靠窗")
# 提取任务状态到工作记忆
working.set("destination", "北京")
working.set("date", "明天")
working.set("preference", "靠窗")
# 工具调用后发现用户历史偏好
long_term.write(
"用户通常喜欢早班机,且对价格敏感",
metadata={"type": "user_preference", "user_id": "u123"}
)
# 下一轮对话时检索长期记忆
prefs = long_term.retrieve("用户出行偏好", top_k=3)
1.6 代码实战:上下文窗口的自动压缩
当上下文使用率超过阈值时,调用 LLM 将历史消息压缩为摘要:
python
def auto_compact(
messages: list[dict],
llm,
threshold: float = 0.85,
max_tokens: int = 8000
) -> list[dict]:
"""当上下文使用率超过阈值时,自动压缩历史消息"""
used = sum(len(m["content"]) // 4 for m in messages)
usage = used / max_tokens
if usage < threshold:
return messages
# 保留最近 4 条消息,其余压缩为摘要
recent = messages[-4:]
old = messages[:-4]
if not old:
return messages
old_text = "\n".join(
f"{m['role']}: {m['content']}" for m in old
)
summary = llm.invoke(
f"请将以下对话历史压缩为简洁摘要,"
f"保留关键事实、用户偏好和未完成任务:\n\n{old_text}"
)
return [
{"role": "system", "content": f"[历史摘要] {summary}"},
*recent
]
1.7 常见工程坑
- 记忆污染:把错误信息写进长期记忆,之后一直错。
- 检索不准:向量检索召回不相关内容,干扰决策。
- 上下文超限:不压缩、不遗忘,最终撑爆窗口。
- 隐私与合规:长期记忆可能存敏感信息,需要加密、脱敏、可删除。
- 记忆不一致:短期记忆和长期记忆冲突,模型不知道该信谁。
- 高更新场景退化:向量存储情景记忆在高频更新下性能急剧下降。
二、工具调用:从 Function Calling 到 MCP
2.1 工具调用的标准流程
LLM 本身不能发请求、读文件、点按钮。工具调用(Function Calling / Tool Calling)是与外部世界交互的标准接口。
根据 OpenAI 官方文档,工具调用是一个多步骤的对话流程,包含五个高层步骤:
- 向模型发起请求,附带它可以调用的工具定义;
- 接收模型返回的工具调用:模型根据用户输入和工具定义,生成调用请求;
- 在应用侧执行代码:用工具调用中的参数执行实际的函数逻辑;
- 将工具输出返回给模型:发起第二次请求,携带工具执行结果;
- 接收最终响应:模型基于工具输出生成最终回答,或发起更多工具调用。
用伪代码表示:
text
# 第 1 步:定义工具
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "城市名"}
},
"required": ["location"],
"additionalProperties": false
},
"strict": True
}
}]
# 第 2 步:发起请求,模型返回工具调用
response = model.request(messages, tools=tools)
# → tool_call: get_weather({"location": "paris"})
# 第 3 步:应用侧执行
result = get_weather("paris")
# → {"temperature": 14, "unit": "celsius"}
# 第 4 步:返回工具输出
# 第 5 步:模型生成最终响应
2.2 代码实战:一个完整的工具调用循环
下面用 Python 实现一个完整的工具调用循环,包含工具注册、模型调用、执行和结果回传:
python
import json
# ---------- 1. 工具定义与注册 ----------
TOOL_REGISTRY = {}
def tool(name: str, description: str, parameters: dict):
"""装饰器:注册工具"""
def decorator(func):
TOOL_REGISTRY[name] = {
"type": "function",
"function": {
"name": name,
"description": description,
"strict": True, # strict 在 function 级别
"parameters": parameters,
"_fn": func # 实际执行函数
}
}
return func
return decorator
@tool(
name="get_weather",
description="获取指定城市的实时天气。当用户询问天气时使用。",
parameters={
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名,如 '北京' 或 'Paris'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位,默认摄氏度"
}
},
"required": ["location", "unit"], # strict 模式下所有字段必须 required
"additionalProperties": False
}
)
def get_weather(location: str, unit: str = "celsius"):
# 实际调用天气 API
return {"location": location, "temperature": 14, "unit": unit}
# ---------- 2. 执行工具调用 ----------
def execute_tool(tool_call) -> dict:
"""执行模型返回的工具调用"""
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
if name not in TOOL_REGISTRY:
return {"error": f"未知工具: {name}"}
try:
result = TOOL_REGISTRY[name]["_fn"](**args)
return {"result": result}
except Exception as e:
return {"error": str(e)}
# ---------- 3. 完整调用循环 ----------
def run_with_tools(user_input: str, model, max_steps: int = 10):
messages = [{"role": "user", "content": user_input}]
tools = [
{k: v for k, v in t.items() if k != "_fn"}
for t in TOOL_REGISTRY.values()
]
for _ in range(max_steps):
response = model.chat(messages, tools=tools)
msg = response.choices[0].message
# 没有工具调用 → 直接返回
if not msg.tool_calls:
return msg.content
# 把 assistant 的工具调用加入历史
messages.append(msg)
# 逐个执行工具调用
for tool_call in msg.tool_calls:
result = execute_tool(tool_call)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})
return "达到最大步数,任务未完成"
这个循环就是前一篇讲的 ReAct 模式在工具调用层面的具体落地。
注意 :
strict: True必须放在function对象级别,与parameters平级。开启 strict 模式后,所有字段必须在required中列出,且additionalProperties必须设为False。
2.3 工具设计的核心原则
工具设计得好不好,直接决定 Agent 的可靠性。综合 Anthropic 和 OpenAI 的官方指南,核心原则如下:
原则一:少而精,不要一对一枚举 API
不要为每个 API 端点都包装一个工具。Anthropic 的官方建议是:实现搜索导向的工具 (如 search_contacts),而不是全量列表工具 (如 list_contacts)。工具应该围绕 Agent 实际需要完成的工作来设计,而不是简单地映射底层 API。
原则二:严格的参数 Schema
用 JSON Schema 定义参数类型、必填项、枚举值,并设置 additionalProperties: false,开启 strict: true。这能显著减少模型编造参数值的情况。
原则三:工具描述是给模型看的文档
工具描述要说明四件事:做什么、什么时候用、输入输出是什么、有什么限制。好的描述要聚焦于 Schema 本身无法表达的歧义点。
原则四:错误处理要结构化
工具失败时,返回结构化错误,而不是抛异常:
json
{"error": "FLIGHT_SOLD_OUT", "message": "该航班已售罄"}
原则五:权限最小化与幂等
只给 Agent 必要的权限。能读就不能写,能查就不能删。支付、下单等操作要支持幂等,避免重复执行。
2.4 从 Function Calling 到 MCP:工具生态的标准化
Function Calling 解决了"模型如何调用单个函数"的问题。但当工具数量增长到几十个、上百个时,新的问题出现了:
- N×M 碎片化:N 个 LLM 应用 × M 个工具 = N×M 个适配器,每个组合都需要单独维护;
- 工具发现困难:模型如何在大量工具中选择正确的那个?
- 上下文膨胀:把所有工具定义都塞进上下文,token 消耗巨大。
MCP(Model Context Protocol) 正是为了解决这些问题而生的。
根据 MCP 官方规范,MCP 是一个开放协议 ,实现 LLM 应用与外部数据源和工具之间的无缝集成。它基于 JSON-RPC 2.0 消息格式,采用 Host-Client-Server 架构:Host 是发起连接的 LLM 应用(如 Claude Desktop、IDE 插件),Client 是 Host 内部负责与单个 Server 建立连接的连接器,Server 是提供工具、资源和提示的服务端。一个 Host 可以创建多个 Client,分别连接不同的 Server,每个 Client 与对应的 Server 保持 1:1 的专用连接。
MCP 采用两层协议模型 :数据层使用 JSON-RPC 2.0,传输层支持 Stdio 和 Streamable HTTP。数据层定义了三个核心原语:
- Tools(工具) :执行状态变更操作的可执行函数;
- Resources(资源) :只读的数据检索;
- Prompts(提示) :模板化消息和工作流。
Anthropic 在 2024 年 11 月将 MCP 开源,并于 2025 年 12 月 9 日将其捐赠给 Linux 基金会下的 Agentic AI Foundation(AAIF) ,与 Block 的 goose 和 OpenAI 的 AGENTS.md 一起成为创始项目。
2.5 代码实战:一个最小 MCP Server
下面用 Python 实现一个最小的 MCP Server,暴露一个查询数据库的工具:
python
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import json
app = Server("demo-server")
@app.list_tools()
async def list_tools() -> list[Tool]:
"""告诉客户端:我提供哪些工具"""
return [
Tool(
name="query_orders",
description="根据用户 ID 查询订单列表。当用户询问订单时使用。",
inputSchema={
"type": "object",
"properties": {
"user_id": {
"type": "string",
"description": "用户 ID"
},
"limit": {
"type": "integer",
"description": "返回条数,默认 10",
"default": 10
}
},
"required": ["user_id"],
"additionalProperties": False
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""执行工具调用"""
if name == "query_orders":
user_id = arguments["user_id"]
limit = arguments.get("limit", 10)
# 实际查询数据库
orders = db.query(
"SELECT * FROM orders WHERE user_id = ? LIMIT ?",
[user_id, limit]
)
return [TextContent(
type="text",
text=json.dumps(orders, ensure_ascii=False)
)]
raise ValueError(f"未知工具: {name}")
async def main():
async with stdio_server() as (read, write):
await app.run(read, write, app.create_initialization_options())
if __name__ == "__main__":
import asyncio
asyncio.run(main())
提示 :不同版本的 MCP Python SDK 在
TextContent构造方式和装饰器用法上可能略有差异,请以官方 SDK 文档为准。
客户端连接这个 Server 后,就能自动发现 query_orders 工具,无需手写适配代码:
python
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
server_params = StdioServerParameters(
command="python",
args=["demo_server.py"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# 自动发现工具
tools = await session.list_tools()
print("可用工具:", [t.name for t in tools.tools])
# 调用工具
result = await session.call_tool(
"query_orders",
{"user_id": "u123", "limit": 5}
)
print("订单:", result.content[0].text)
这就是 MCP 的核心价值:工具定义与 Agent 应用解耦,一次开发,处处可用。
2.6 MCP 解决了什么,又带来了什么新问题?
解决了什么:
- 标准化了模型与工具、数据源之间的连接方式,把 N×M 问题变成了 N+M;
- 工具可以独立开发、独立部署,像微服务一样组合;
- 支持状态连接和能力协商。
带来了什么新问题:
MCP 赋予了 Agent 强大的执行能力,同时也引入了严重的安全挑战。RAG-MCP 论文(2025 年 5 月发表)提出,通过对工具元数据进行语义检索,可以将 prompt token 开销降低 50% 以上,并将工具选择准确率从 13.62% 提升到 43.13%,超过三倍。
安全方面,OWASP 发布的 MCP Top 10 安全漏洞清单中,Token 管理不善与密钥泄露 被列为头号风险。其余条目涵盖模型错误绑定、上下文欺骗、提示状态操纵、不安全的内存引用和隐蔽信道滥用等。此外还有 MCP rug-pull 攻击 ------恶意服务器在初始审批通过后修改工具行为,以及 Intent Inversion 攻击------半诚实的 MCP 服务器通过分析工具调用模式推断用户隐私信息。
缓解措施包括:人在回路确认 (对敏感工具调用要求人工确认)、服务器沙箱化 、供应链检查 和实时提示注入检测。
2.7 常见工程坑
- 工具误用:模型选了不合适的工具。
- 参数幻觉:模型编造不存在的参数值。
- 权限过大:Agent 能删库、能转账,风险极高。
- 工具描述冲突:多个工具功能相似,模型不知道选哪个。
- 上下文膨胀:工具定义太多,挤占了任务上下文的 token 预算。
- 副作用不可逆:发了邮件、下了单,无法撤回。
三、记忆与工具的协同:1+1 > 2
记忆和工具不是孤立的两个模块,它们的协同才是 Agent 真正强大的地方。
工具调用产生记忆:每次工具调用的结果(成功或失败)都应该被写入记忆系统,成为后续决策的依据。
记忆指导工具选择:记忆中的用户偏好(如"喜欢早班机")可以指导 Agent 在选择工具参数时做出更好的决策。
下面用一个完整的例子展示两者的协同:
python
def is_important(tool_call, result) -> bool:
"""判断工具调用结果是否值得写入长期记忆"""
# 示例:错误结果、用户偏好、关键状态变更 → True
return False # 实际实现根据业务规则
class MemoryAugmentedAgent:
"""带记忆增强的工具调用 Agent"""
def __init__(self, model, short, working, long_term):
self.model = model
self.short = short
self.working = working
self.long_term = long_term
def run(self, user_input: str):
# 1. 写入短期记忆
self.short.add("user", user_input)
# 2. 从长期记忆检索相关偏好
memories = self.long_term.retrieve(user_input, top_k=3)
memory_context = "\n".join(memories) if memories else "无相关记忆"
# 3. 构建带记忆的上下文
messages = [
{"role": "system", "content": f"用户历史偏好:\n{memory_context}"},
*self.short.get_context()
]
# 4. 调用模型(可能触发工具调用)
response = self.model.chat(messages, tools=get_all_tools())
# 5. 如果模型调用了工具,执行并记录
if response.tool_calls:
for call in response.tool_calls:
result = execute_tool(call)
# 工具结果写入短期记忆
self.short.add("tool", json.dumps(result, ensure_ascii=False))
# 重要结果写入长期记忆
if is_important(call, result):
self.long_term.write(
f"调用 {call.function.name},结果:{result}",
metadata={"type": "tool_result"}
)
# 更新工作记忆
self.working.set(
f"last_tool_{call.function.name}",
result
)
# 6. 返回最终结果
return response.content
Reflexion 的情景记忆缓冲区:前一篇讲到的 Reflexion 框架,其核心机制就是将反思文本保存在情景记忆缓冲区中,引导后续试验中更好的决策。这是记忆与决策循环深度耦合的典型案例。
MCP + Memory 的组合:MCP 提供工具接入的标准协议,Memory 提供状态持久化的基础设施。两者结合,Agent 既能"记住"过去的交互,又能"使用"当前的工具。
四、总结
回到主线:
记忆让 Agent 不再"金鱼脑",工具让 Agent 从"会说"变成"会做"。
记忆方面,记住三条原则:
- 分层设计:短期、工作、长期三层协同,不要试图用一种方案解决所有问题。
- 主动治理:记忆不只是写入和检索,还要有冲突解决、容量管理和遗忘策略。
- 区分 RAG:RAG 是无状态的只读检索,Agent Memory 是持久可更新的状态管理,两者互补而非替代。
工具方面,记住三条原则:
- 少而精:不要一对一枚举 API,围绕 Agent 的实际工作需要设计工具。
- 标准接口:MCP 正在成为工具接入的事实标准,关注它的演进和安全实践。
- 安全优先:权限最小化、人在回路确认、服务器沙箱化,是工具调用的铁律。
下一篇,我们会进入本系列的最后一篇:Agent 的架构、多智能体与落地。从单 Agent 到多 Agent 协作,从 LangGraph 到 CrewAI,从评估指标到生产环境的最佳实践,给出一个完整的落地路线图。
本文是「Agent 基本概念」系列第 4 篇。
如果你觉得有帮助,欢迎点赞、收藏、关注。
下一篇:《Agent 的架构、多智能体与落地:从单 Agent 到生产系统》