第 6 篇(11-12 章):智能代理协议与 AI 代理的上下文工程

第 6 篇(11-12 章):智能代理协议与 AI 代理的上下文工程

第 6 篇(11-12 章):智能代理协议与 AI 代理的上下文工程

文章目录

  • 第 6 篇(11-12 章):智能代理协议与 AI 代理的上下文工程
  • 一、第 11 章:使用智能代理协议(MCP、A2A 和 NLWeb)
    • 1. 模型上下文协议(MCP)
    • 2. 代理间协议(A2A)
    • 3. 自然语言网页(NLWeb)
  • 二、第 12 章:AI 代理的上下文工程
    • 1. 什么是上下文工程?
    • 2. 有效上下文工程的策略
    • 3. 检查上下文
    • 4. 常见上下文失败
  • 三、代码示例讲解:上下文如何被"记住"与"管理"
    • 示例一:上下文感知代理(第 12 章)
      • ① 创建上下文感知代理
      • ② 实际运行结果(多轮上下文保持,译为中文节选)
      • ③ 上下文压缩:总结工具
      • ④ 实际运行结果(总结工具 + 基于记录推荐,译为中文节选)
      • ⑤ 机制总结:上下文工程如何工作
      • ⑥ 完整代码(示例一)
    • 示例二:外部能力接入(第 11 章协议思想落地)
      • ① 定义外部工具
      • ② 完整代码(示例二)

本系列「微软《AI Agents for Beginners》实战解读」基于微软官方课程,逐章讲解并结合实践扩展。本篇覆盖第 11 章《使用智能代理协议(MCP、A2A 和 NLWeb)》第 12 章《AI 代理的上下文工程》
文中子标题沿用官方课程原文;示例基于 Microsoft Agent Framework(MAF),模型后端为 DeepSeek(OpenAI 兼容协议)。
本篇代码讲解基于真实运行结果(结果已译为中文),每个示例末尾附完整可运行代码。


一、第 11 章:使用智能代理协议(MCP、A2A 和 NLWeb)

随着代理使用增多,标准化、安全与开放创新的协议需求上升。本章介绍三种协议:MCP(连接 LLM 与工具/数据)、A2A(连接代理与代理)、NLWeb(连接代理与网站)。

1. 模型上下文协议(MCP)

模型上下文协议(MCP)是开放标准,为应用向 LLM 提供上下文与工具提供标准化方法,使代理通过"通用适配器"一致地连接不同数据源与工具。

为什么需要 MCP。MCP 出现之前,每个工具/数据源(数据库、搜索服务、内部 API)都需一套专属集成代码,且彼此不通用——模型无法"即插即用"外部能力。MCP 的目标是统一"模型 ↔ 外部能力"的接入协议:任何服务只要实现一个 MCP 服务器,任何支持 MCP 的代理即可发现并使用它,无需为每个服务编写定制适配代码。

MCP 协议是什么样。技术上,MCP 基于JSON-RPC 2.0消息格式,采用客户端-服务器架构

  • 主机(Host):启动与 MCP 服务器连接的 LLM 应用(如代码编辑器 VSCode)。
  • 客户端(Client):主机应用内维护与服务器一对一连接的组件,负责发起请求。
  • 服务器(Server):提供具体功能的轻量级程序,可运行于本地(stdio)或远程(HTTP)。

连接与使用遵循固定生命周期:

① initialize(初始化握手)→ 双方交换协议版本与能力 ② 能力协商 → 客户端了解服务器支持哪些功能 ③ 工具发现 tools/list → 客户端获取可用工具清单(名称/描述/参数 Schema) ④ 工具调用 tools/call → 客户端把模型选中的工具名与参数发给服务器 ⑤ 服务器执行真实逻辑 → 返回结果 → 注入模型上下文

协议定义三类核心原语作为服务器的能力:

  • 工具(Tools):代理可调用的离散动作,服务器公布名称、描述与输入/输出格式。
  • 资源(Resources):服务器提供的只读数据项或文档,客户端按需获取(文本或二进制)。
  • 提示(Prompts):预定义模板,支持更复杂的工作流。

具体网络交互示例。以调用"搜索航班"工具为例,客户端向服务器发送 JSON-RPC 请求:

POST /mcp HTTP/1.1 Host: flights.example.com Content-Type: application/json Authorization: Bearer <token> { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "search_flights", "arguments": {"origin": "PDX", "destination": "HNL", "date": "2026-06-15"} } }

服务器执行真实逻辑后返回结果,供客户端回传给模型:

{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"找到 3 个航班:UA 231 08:00-11:30 $450;HA 47 09:15-12:00 $380;AS 12 13:20-16:45 $520"}],"isError":false}}

MCP 的优势。动态工具发现(代理可获取服务器可用工具列表,无需静态编码集成)、跨 LLM 互操作性(可切换核心模型)、标准化安全(统一认证方法,简化多服务器访问管理)。

MCP 示例。以 AI 助手预订航班为例:助手(MCP 客户端)连接航空公司 MCP 服务器 → 通过tools/list发现"搜索航班/预订航班"工具 → 用户请求时通过tools/call调用搜索工具 → 服务器作为包装层调用真实预订 API → 返回结果 → 用户选定后调用预订工具完成预订。

理论扩展:MCP 与直接 API 的对比。直接 API 集成需为每个服务编写适配代码,且 API 变更需更新代码;MCP 以"一次集成、动态发现"取代之——新增服务只需其提供 MCP 服务器,代理即可发现并使用,显著降低集成成本。MCP 已捐赠给 Linux 基金会,成为工具连接的事实标准。

2. 代理间协议(A2A)

代理间协议(A2A)实现不同 AI 代理之间的通信与协作,将不同组织、环境与技术栈的代理连接起来共同完成共享任务。

为什么需要 A2A。MCP 解决了"模型 ↔ 工具"的问题,但代理之间如何协作仍是空白——不同组织/技术栈的代理(如一家公司的订票代理与另一家公司的酒店代理)缺乏统一通信方式。A2A 的目标是统一"代理 ↔ 代理"的协作协议:让不同厂商的代理能互相发现、发起任务、交换消息与成果,从而把原本孤立的代理组织成协作网络。

A2A 协议是什么样。A2A 采用基于 JSON 的 HTTP 协议,围绕"代理卡 + 任务 + 消息"建模:

  • 代理卡(Agent Card):代理的"名片",以 JSON 描述名称、任务描述、技能列表、端点 URL、版本与能力——其他代理通过它了解"该找谁、它擅长什么、怎么联系"。
  • 任务(Task):一次协作单元,由发起方向接收方创建;任务状态(进行中/完成/失败)驱动协作流程。
  • 消息(Message):任务内交换的上下文(用户聊天内容、中间结果)。
  • 工件(Artifacts):远程代理完成任务的成果,包含结果、描述与文本上下文。
  • 事件队列:处理任务完成前的更新与消息传递,防止长任务期间连接被关闭。

协作流程:

① 通过代理卡发现/选择远程代理(了解其能力与端点) ② 发起方创建任务(Task)并传递用户上下文(Message) ③ 接收方以自身 LLM 解析请求、调用自身工具执行 ④ 通过事件队列推送进展;完成后产出工件(Artifacts) ⑤ 发起方汇总结果返回用户

具体网络交互示例。先通过代理卡发现酒店代理的能力与端点:

GET /.well-known/agent.json HTTP/1.1 Host: hotel-agent.example.com
{"name":"HotelBookingAgent","description":"预订酒店房间","skills":[{"id":"hotel.search","name":"搜索酒店","description":"按城市与日期查找酒店"}],"endpoint":"https://hotel-agent.example.com/a2a","version":"1.2"}

随后发起协作任务并传递用户上下文:

POST /a2a/task HTTP/1.1 Host: hotel-agent.example.com Content-Type: application/json { "jsonrpc": "2.0", "id": 1, "method": "tasks/send", "params": { "task_id": "task-9041", "message": {"role": "user", "content": "预订火奴鲁鲁酒店,6月15-22日,2位客人"} } }

A2A 的优势。增强协作(跨厂商与平台代理互动共享上下文)、模型选择灵活性(每个代理可自主选择 LLM)、内置认证(协议内建安全框架)。

A2A 示例。用户请求"预订下周飞往火奴鲁鲁的全程旅行":旅游代理协调 → 用 A2A 连接航空代理、酒店代理、租车代理 → 各专职代理运行自身 LLM 与工具完成预订 → 旅游代理汇总结果返回用户。

理论扩展:协议分层。MCP 与 A2A 解决不同层级问题:MCP 是"能力接入层"(LLM ↔ 外部能力),A2A 是"协作编排层"(代理 ↔ 代理)。二者可组合使用——A2A 连接的各专职代理内部,可再通过 MCP 接入各自的数据源。

3. 自然语言网页(NLWeb)

NLWeb为任何网站带来自然语言界面,使代理能够发现并与内容互动,将网站纳入更广泛的"代理生态系统"。

为什么需要 NLWeb。网站存储了海量信息,但只能由人通过菜单、表单与页面导航访问——AI 代理无法直接"读懂"或"查询"网站内容。NLWeb 的目标是让网站具备对代理可访问的自然语言接口:网站不再只是"给人看的页面",而是"也可被 AI 查询的数据源",从而让代理能利用互联网上已有的海量内容。

NLWeb 协议是什么样。NLWeb 为网站定义一个统一接口,核心要素:

  • NLWeb 应用:处理自然语言查询的系统,是网站的"自然语言引擎"。
  • NLWeb 协议:网站自然语言交互的规则集,响应以 JSON 返回(常用 Schema.org 结构化数据);网站在.well-known/nlweb暴露配置供代理发现。
  • MCP 服务器端点:每个 NLWeb 配置也充当 MCP 服务器——通过ask方法向其他 AI 系统暴露查询能力。
  • 嵌入模型与向量数据库:把网站内容转为向量并存储,检索时按相似度返回结果。

工作机制:

① 内容摄取:网站产品目录以 Schema.org/RSS 提供 → 嵌入 → 存入向量库 ② 查询处理:用户/代理以自然语言提问 → NLWeb 在向量库检索相关片段 ③ 生成响应:LLM 结合检索结果,生成引用真实内容的 JSON 响应 ④ ask 方法:外部代理通过 MCP `ask()` 直接查询网站

具体网络交互示例。外部代理通过 NLWeb 的自然语言接口查询网站:

POST /nlweb HTTP/1.1 Host: travel-site.example.com Content-Type: application/json { "query": "火奴鲁鲁带泳池的适合家庭的酒店" }

网站返回引用真实内容的 Schema.org 结构化响应:

{"@context":"https://schema.org","results":[{"@type":"Hotel","name":"Waikiki Beach Resort","address":{"addressLocality":"Honolulu","addressRegion":"HI"},"amenityFeature":[{"name":"Pool"},{"name":"Kids Club"}]},{"@type":"Hotel","name":"Aloha Garden Hotel","address":{"addressLocality":"Honolulu"},"amenityFeature":[{"name":"Pool"}]}]}

NLWeb 示例。旅行网站以 Schema.org 格式提供产品目录 → 用户以自然语言查询"找一个有泳池的适合家庭的火奴鲁鲁酒店" → NLWeb 处理查询、在向量库搜索相关酒店 → 生成引用真实酒店的自然语言响应 → 外部代理可通过 MCPask方法直接查询该网站。

理论扩展:向量检索在 NLWeb 中的角色。NLWeb 的检索机制依赖嵌入与向量数据库——将网站内容嵌入为向量,语义相近的查询与内容向量距离近,从而以相似度排序返回结果。这与第 05 章 RAG 管线的检索阶段同构。


二、第 12 章:AI 代理的上下文工程

上下文驱动代理制定行动计划。上下文工程是确保代理拥有完成任务所需正确信息的实践——上下文窗口大小有限,需管理系统化地添加、删除与压缩信息。

1. 什么是上下文工程?

提示工程与上下文工程的区别。提示工程专注于静态指令,用规则引导代理;上下文工程管理包含初始提示在内的动态信息集合,确保代理随时间推移拥有所需信息,核心是使过程可重复且可靠。

上下文类型。上下文并非单一内容,代理需管理多种来源:

  • 指令:规则——提示、系统消息、少量示例、工具描述。
  • 知识:事实、数据库检索结果、长期记忆(含 RAG 系统)。
  • 工具:外部函数/API/MCP 服务器定义及其反馈结果。
  • 对话历史:持续对话,随时间增长并占用窗口空间。
  • 用户偏好:随时间学到的偏好,可在关键决策时调用。

理论扩展:上下文窗口的有限性。模型的上下文窗口以 Token 计,是硬约束。上下文工程的本质是对有限窗口的分配问题——将有限的 Token 预算分配给"当前任务最相关的信息",而非历史全量。

2. 有效上下文工程的策略

规划策略。良好上下文工程始于规划,三步:定义明确的结果(代理完成任务后的状态)、绘制上下文图谱(完成任务需要哪些信息、信息在何处)、创建上下文流水线(代理如何获取信息——RAG、MCP 服务器、工具)。

实用策略。信息流入窗口后的管理手段:

  • 代理便签(Scratchpad):单次会话内记笔记,存于上下文窗口外,需要时检索。
  • 记忆(Memories):跨会话存储与检索(摘要、偏好、反馈)。
  • 压缩上下文:窗口接近极限时用摘要与裁剪技术(保留最相关信息、删除旧消息)。
  • 多代理系统:每个代理有独立窗口,规划如何共享与传递上下文。
  • 沙箱环境:代理在沙箱执行代码/处理大文档,仅将结果读入窗口。
  • 运行时状态对象:复杂任务逐步存储子任务结果,使上下文仅关联当前子任务。

3. 检查上下文

应用策略后,应检查模型实际收到的内容。关键调试问题:代理加载了过多、错误还是缺少所需上下文?生产环境优选包含计数、ID、哈希与策略标签的小型检查记录(选择、压缩、隔离、记忆与 RAG、安全隐私),而非记录原始提示或输出,兼顾可审计性与隐私。

4. 常见上下文失败

失败类型定义缓解
上下文中毒幻觉或错误进入上下文并被反复引用上下文验证与隔离;验证失败开启新线程
上下文分心上下文过大,模型过度关注历史而非当前任务上下文摘要,定期压缩重置注意力
上下文混淆工具过多导致模型调用错误工具工具载入管理,用 RAG 动态只选最相关工具
上下文冲突上下文含矛盾信息导致推理不一致上下文修剪覆盖旧指令;便签先调和冲突

理论扩展:上下文失败的共性。四类失败共同指向一个原则——上下文应当是"当前决策的最小充分集":过少则信息不足,过多则引入噪声与矛盾。上下文工程即在"充分"与"精炼"之间寻求平衡。


三、代码示例讲解:上下文如何被"记住"与"管理"

以下代码取自课程官方示例(已适配 DeepSeek 后端),并附实际运行结果(译为中文)。重点回答:多轮对话中,代理如何保持上下文连续性?上下文过长时又如何压缩?

示例一:上下文感知代理(第 12 章)

① 创建上下文感知代理

系统提示明确要求代理"对话变长时总结前文、跟踪早期偏好、引用先前决策":

agent=client.as_agent(name="ContextAwareAgent",instructions="""你是具备优秀记忆管理的旅行规划助手。 当对话变长时: 1. 将先前上下文总结为要点 2. 跟踪之前提到的用户偏好 3. 引用先前的决策而不重复全部细节 始终在简洁的前提下保持连续性。""",)session=agent.create_session()# 会话线程 = 上下文容器

② 实际运行结果(多轮上下文保持,译为中文节选)

输入请求依次为:规划日本之旅(寿司/寺庙/摄影)→ 预算 3000 美元、单人 10 天 4 月 → 综合所有信息推荐必去之处 → 偏好传统日式旅馆 → 改期 10 月。代理各轮输出(译为中文节选):

第 1 轮:日本是同时满足你三大爱好的绝佳目的地。寿司:别局限于筑地市场, 试试传送带寿司或高级 omakase;寺庙:京都是精神中心,推荐金阁寺与伏见稻荷大社;摄影:清晨是你的好朋友…… 第 2 轮:这个预算和时间非常可行!3000 美元 10 天 4 月——4 月是樱花旺季,价格偏高、需求大,尽早预订是关键。 骨架计划:东京(4晚)+ 京都(4晚)+ 大阪(2晚);预算指导:住宿 $300-450、餐饮 $500-600、JR Pass…… 第 3 轮:综合你告诉我的所有信息,我最推荐的是京都伏见稻荷大社的日出—— 既是寺庙,又是摄影胜地,下山后还能去锦市场吃新鲜寿司。4 月的樱花让画面更完美。 第 4 轮:传统日式旅馆很棒,但日本 ryokan 一般不算平价。策略是"混搭": 东京住商务酒店省预算,京都 2 晚 ryokan + 2 晚经济旅馆,大阪住经济型。京都预算 ryokan 约 $80-120/晚…… 第 5 轮:改到 10 月很棒!10 月秋色渐起、光线更柔和、人群少于樱花季, 而且秋刀鱼和肥金枪鱼正当时令——你的寿司时机很幸运。枫叶推荐东福寺、清水寺…… 第 6 轮:你的完整旅行计划总结:日本(东京/京都/大阪),预算 3000 美元(机票是否包含待确认), 10 天单人,10 月出行,兴趣为寿司/寺庙/摄影,住宿偏好 ryokan(京都 2 晚)……

观察要点:六轮对话中,代理始终记得第 1 轮的"寿司/寺庙/摄影"、第 2 轮的"3000 美元/10 天/4 月"、第 4 轮的"ryokan 偏好"、第 5 轮的"改期 10 月"——这是会话线程(Session)提供的短期记忆在起作用。但历史越长,Token 成本越高,这正是上下文压缩的必要性所在。

③ 上下文压缩:总结工具

对话增长后,用总结工具把积累的偏好压成紧凑摘要,即使旧消息被丢弃也能保留关键信息:

@tool(approval_mode="never_require")defsummarize_preferences(conversation_notes:str)->str:"""把积累的用户偏好总结为紧凑格式。"""returnf"[SUMMARY] User preferences recorded:{conversation_notes}"

④ 实际运行结果(总结工具 + 基于记录推荐,译为中文节选)

(用户:我要去希腊,喜欢海鲜、历史与跳岛游。预算 4000 美元两周,6 月和伴侣同行。请用总结工具记录这些偏好。) 代理:已记录!你的偏好已保存:希腊、海鲜/历史/跳岛、预算 4000 美元、两周 6 月、与伴侣同行。 (用户:基于你记录的,推荐 3 个必去的岛屿。) 代理:基于你的记录——海鲜、历史、跳岛、4000 美元双人 6 月——我的三大推荐: 1. 克里特岛:历史重镇,克诺索斯宫殿、干尼亚威尼斯老城,海鲜出色且岛大更实惠 2. 圣托里尼:火山口景观 + 阿克罗蒂里(被火山灰保存的米诺斯古城),但价格偏高,建议最多 3-4 晚 3. 纳克索斯:完美的"松弛中间值",波塔拉古城门、极佳性价比烤鱼、主要渡轮枢纽

观察要点:总结工具把散落各处的偏好浓缩为一条记录,后续推荐直接基于该记录——这正是"摘要 + 便签"策略:即使对话历史被裁剪,关键信息仍在。

⑤ 机制总结:上下文工程如何工作

结合代码与运行结果:

会话线程(Session)→ 短期记忆,多轮保持上下文 → 总结工具(summarize_preferences)→ 把积累偏好压缩为紧凑摘要 → 便签/记忆 → 关键信息持久化,可跨历史裁剪存活 → 防止"上下文分心/中毒/冲突"四类失败

三个关键机制

  1. 会话线程是短期记忆create_session()让多轮对话共享上下文,代理才能"记得"早期偏好。
  2. 总结工具是压缩杠杆summarize_preferences把长历史压成摘要,降低 Token 成本(对应第 10 章成本管理),同时保留要点。
  3. 压缩防失败:对话无限增长会导致"上下文分心"——总结工具定期"重置注意力",正是四类上下文失败的标准缓解。

⑥ 完整代码(示例一)

importasynciofromagent_frameworkimporttoolfrom_deepseekimportmake_clientasyncdefmain():client=make_client()# 上下文感知代理:多轮对话保持连续性agent=client.as_agent(name="ContextAwareAgent",instructions="""你是具备优秀记忆管理的旅行规划助手。 当对话变长时: 1. 将先前上下文总结为要点 2. 跟踪之前提到的用户偏好 3. 引用先前的决策而不重复全部细节 始终在简洁的前提下保持连续性。""",)session=agent.create_session()print(awaitagent.run("I'm planning a trip to Japan. I love sushi, temples, and photography.",session=session))print("\n"+awaitagent.run("My budget is $3000 and I'll be traveling solo for 10 days in April.",session=session))print("\n"+awaitagent.run("Based on everything I've told you so far, what's the one thing you'd recommend I not miss?",session=session))# 总结工具:压缩上下文@tool(approval_mode="never_require")defsummarize_preferences(conversation_notes:str)->str:"""把积累的用户偏好总结为紧凑格式。"""returnf"[SUMMARY] User preferences recorded:{conversation_notes}"summarizing_agent=client.as_agent(name="SummarizingTravelAgent",instructions="""你是主动管理对话上下文的旅行规划助手。 1. 收集几条偏好后,调用 summarize_preferences() 记录紧凑摘要 2. 用户要求回忆时,引用已记录的摘要 3. 保持回答简洁,避免复述全部历史""",tools=[summarize_preferences],)summary_session=summarizing_agent.create_session()print("\n"+awaitsummarizing_agent.run("I want to visit Greece. I love seafood, history, and island hopping. ""Budget is $4000 for two weeks. Traveling with my partner in June. ""Please record these preferences using your summarization tool.",session=summary_session,))print("\n"+awaitsummarizing_agent.run("Now, based on what you've recorded, suggest the top 3 islands we should visit.",session=summary_session,))if__name__=="__main__":asyncio.run(main())

示例二:外部能力接入(第 11 章协议思想落地)

① 定义外部工具

将外部能力封装为工具,模拟"通过标准接口连接外部数据源"——这是 MCP 将外部服务统一为可调用工具的思想:

@tool(approval_mode="never_require")defsearch_hotels(query:Annotated[str,"查询位置/设施/标签"])->str:"""查询酒店数据库中的匹配房产。"""hotels=[{"name":"Le Meurice Paris","location":"Paris","price":850,"tags":["luxury","romantic"]},{"name":"Four Seasons Maui","location":"Maui","price":695,"tags":["beach","family"]},]q=query.lower()matches=[hforhinhotelsifqinh["location"].lower()orany(qintfortinh["tags"])]returnjson.dumps(matchesorhotels[:2],indent=2)

② 完整代码(示例二)

importasyncio,jsonfromtypingimportAnnotatedfromagent_frameworkimporttoolfrom_deepseekimportmake_client@tool(approval_mode="never_require")defsearch_hotels(query:Annotated[str,"查询位置/设施/标签"])->str:"""查询酒店数据库中的匹配房产。"""hotels=[{"name":"Le Meurice Paris","location":"Paris","price":850,"tags":["luxury","romantic"]},{"name":"Four Seasons Maui","location":"Maui","price":695,"tags":["beach","family"]},{"name":"Hotel Sacher Vienna","location":"Vienna","price":420,"tags":["historic","accessible"]},]q=query.lower()matches=[hforhinhotelsifqinh["location"].lower()orany(qintfortinh["tags"])]returnjson.dumps(matchesorhotels[:2],indent=2)asyncdefmain():agent=make_client().as_agent(name="旅行代理",instructions="你是旅行代理,通过 search_hotels 工具为用户查找酒店,并给出推荐。",tools=[search_hotels],)print(awaitagent.run("帮我找一家巴黎的浪漫酒店"))if__name__=="__main__":asyncio.run(main())

运行前提:已按第 1 篇完成环境配置——创建.envDEEPSEEK_API_KEY填入开发者自有密钥)与_deepseek.py共享客户端,并执行pip install agent-framework python-dotenv openai