A2A 协议深挖:Agent 之间怎么协作?AgentCard、Task 状态机与 SSE 流式传输全解析
很多人到现在还分不清 MCP 和 A2A 的区别,把它们当成一回事。一句话讲清:MCP 是给 Agent 接外设的 USB 接口,A2A 是给 Agent 联网的网卡。MCP 让一个 Agent 用统一方式调用工具、文件、数据库;A2A 让多个 Agent 之间互相发现、协商、委托任务。上篇拆解了 MCP 2026-07-28 新规范(MCP 规范解读),这篇聊它的姊妹协议 A2A。
系统架构设计
查看所有标签很多人到现在还分不清 MCP 和 A2A 的区别,把它们当成一回事。一句话讲清:MCP 是给 Agent 接外设的 USB 接口,A2A 是给 Agent 联网的网卡。MCP 让一个 Agent 用统一方式调用工具、文件、数据库;A2A 让多个 Agent 之间互相发现、协商、委托任务。上篇拆解了 MCP 2026-07-28 新规范(MCP 规范解读),这篇聊它的姊妹协议 A2A。
把一份 50 页的技术报告丢给 RAG 系统,问它 "这份报告的核心结论是什么?"——传统的 chunk-embed-retrieve-generate 链路直接跪了。不是检索不准,是这个问题本身就无法被任何单个 chunk 回答。
这是 2024 年 Microsoft Research 提出 Graph RAG 的出发点。一年过去,这个方向已经从论文变成了实际可跑的工程方案。本文从架构层面拆解它到底在做什么、怎么做的、以及什么时候该用。
标准 RAG 流程的前提假设是:答案分布在少数几个 chunk 中。所以策略是 embed → top-k 检索 → 塞进 context → 生成。
一旦问题变成全局性的("总结全文"、"对比三个方案的优劣"、"找出贯穿始终的主题"),top-k 检索就失效了。一个直觉的做法是先把全文喂给 LLM——但 50 页报告远超 context window。就算模型支持 1M token,"大海捞针"的准确率也随 context 增长而骤降。
Graph RAG 的解法:先离线建图,再在线查询。把文档转化成知识图谱,用图算法提取结构,把全局问题转化成对图结构的查询。
Microsoft 的 Graph RAG 分两个阶段:
文档 → 切分 chunk → 实体/关系抽取 → 知识图谱 → 社区检测 → 社区摘要
每个步骤都跑 LLM,完全离线,不占查询延迟。
查询分两种模式:
Global Search 是 Graph RAG 的核心差异化能力。下面重点拆它。
对每个 chunk 调 LLM,提取 (subject, predicate, object) 三元组:
# 伪代码:Graph RAG 的实体抽取 prompt 模式
EXTRACT_PROMPT = """从文本中提取所有实体和关系,返回 JSON:
{
"entities": [
{"name": "Microsoft", "type": "ORGANIZATION", "desc": "科技公司"}
],
"relationships": [
{"source": "Microsoft", "target": "Graph RAG", "desc": "提出者"}
]
}
文本:
{chunk_text}
"""
这一步是整个 pipeline 最贵的环节。一篇 100 页文档,按 300 token/chunk 切,会产生几百次 LLM 调用。Microsoft 的做法是并行跑,用 gpt-4o-mini 压低成本。
关键细节:同一个实体会在不同 chunk 中被提取多次("Microsoft"、"MSFT"、"微软"),所以下一步要做实体消歧。
Graph RAG 没有用复杂的 entity linking,而是再调一次 LLM:
def resolve_entities(entity_groups: list[list[dict]]) -> dict:
"""同一类型的候选实体分组后,让 LLM 判断哪些是同一实体"""
prompt = f"""
以下实体中,哪些指向同一个真实世界实体?
返回分组结果。
候选实体:
{json.dumps(entity_groups, ensure_ascii=False)}
"""
# 返回合并后的实体映射
为什么用 LLM 而不是传统的字符串匹配?因为 "GPT-4" 和 "GPT4"、"gpt-4-turbo" 需要语义理解,简单的编辑距离不够。
消歧后,图里约 5-15% 的节点被合并,边也相应聚合。
这是 Graph RAG 最巧妙的一步。构建好实体-关系图后,用 Leiden 算法做社区发现:
import networkx as nx
import leidenalg
import igraph as ig
def detect_communities(G: nx.Graph) -> list[set[str]]:
"""Leiden 社区检测,返回多层社区结构"""
g_ig = ig.Graph.from_networkx(G)
# 多层社区检测——这是精髓
partition = leidenalg.find_partition(
g_ig,
leidenalg.ModularityVertexPartition,
n_iterations=-1 # 跑到收敛
)
# Leiden 天然支持层次化社区
# Level 0: 最细粒度的社区(几十个实体)
# Level 1: 合并后的中等社区
# Level 2: 最粗粒度的大主题社区
return partition
Leiden 优于 Louvain 的地方是它保证连通性——每个社区内部是连通的,不会出现碎片化社区。对于知识图谱这种天然聚团的结构,Leiden 跑出来的社区往往对应真实世界的主题:比如"模型架构"社区包含 Transformer、Attention、MLP 等实体,"训练方法"社区包含 SGD、AdamW、Learning Rate 等。
对每个社区,把其成员实体和关系序列化成文本,让 LLM 生成摘要:
def summarize_community(
community: list[str],
entities: dict,
relationships: list
) -> str:
"""为一个社区生成摘要"""
# 收集社区内的实体和关系
community_entities = [entities[e] for e in community]
community_rels = [
r for r in relationships
if r.source in community and r.target in community
]
prompt = f"""
以下是一个知识图谱社区的信息。请生成一个简洁的摘要,
描述这个社区的核心主题、关键实体和它们之间的关系。
社区实体:
{format_entities(community_entities)}
社区关系:
{format_relationships(community_rels)}
摘要:
"""
return call_llm(prompt)
这个摘要会在 Global Search 时被用到。摘要的质量直接决定最终回答的质量——摘要太泛,查询结果就是废话;摘要太细,又回到了 chunk-level 的问题。
当用户问 "这份文档主要讨论了哪些技术方向?",Graph RAG 的 Global Search 流程:
1. 把用户 query 嵌入向量
2. 对所有社区摘要做向量检索,选出 top-K 个相关社区
3. 对每个社区的摘要,让 LLM 生成一个"局部答案"(map)
4. 把所有局部答案汇总,让 LLM 生成最终回答(reduce)
async def global_search(query: str, community_summaries: list[str]) -> str:
# Step 1: 检索相关社区
query_emb = embed(query)
relevant = top_k_by_similarity(query_emb, community_summaries, k=10)
# Step 2: Map — 每个社区生成局部答案
map_tasks = [
map_prompt(query, summary)
for summary in relevant
]
partial_answers = await asyncio.gather(*[call_llm(t) for t in map_tasks])
# Step 3: Reduce — 汇总成最终答案
final_answer = call_llm(reduce_prompt(query, partial_answers))
return final_answer
这和传统的 map-reduce summarization 思路一样,区别在于:输入不是随机采样的 chunk,而是按语义聚类好的社区摘要。每个社区摘要本身已经是一个凝练的信息单元,所以 map 阶段产生的噪音远小于直接对 chunk 做 map。
对于事实型问题("GPT-4 的 context window 是多少?"),走 Local Search:
1. 用 query 找到相关实体
2. 提取这些实体的邻居子图(1-2 hop)
3. 把子图信息 + 实体的原始文本属性 + 关系描述塞进 context
4. 生成回答
Local Search 的召回率显著高于纯向量检索,因为它利用了图的结构信息——"GPT-4" 和 "context window" 之间有一条 has_context_window 边,即使它们的文本描述中从来没有出现在同一个 chunk 里。
Microsoft 的论文在两个数据集上做了评测:
| 指标 | Naive RAG | Graph RAG (Global) | 提升 |
|---|---|---|---|
| Comprehensiveness | 2.87 | 3.97 | +38% |
| Diversity | 2.53 | 3.54 | +40% |
| Directness | 2.95 | 3.76 | +27% |
| LLM 调用次数 | 1 | ~12 (10 map + 1 reduce + 1 embed) | — |
数据来源:Microsoft Research, "From Local to Global: A Graph RAG Approach to Query-Focused Summarization", 2024
代价也很明显:
什么时候该用 Graph RAG?
实体类型的定义直接影响图的质量。太少,图就稀疏;太多,消歧压力大。建议:
# 实际可用的实体类型(不是越多越好)
ENTITY_TYPES = [
"PERSON", # 人物
"ORGANIZATION", # 组织/公司
"TECHNOLOGY", # 技术/框架/模型
"CONCEPT", # 抽象概念
"METRIC", # 指标/数值
"EVENT", # 事件
]
控制在 5-8 个类型,每个类型有明确的边界定义。
实体抽取需要一个 chunk 内包含足够的上下文才能准确识别关系。Graph RAG 的推荐是 600-1200 token/chunk,比传统 RAG 的 256-512 更大。
图结构让增量更新变得复杂——新增一个实体可能改变社区结构,导致需要重新计算社区检测和摘要。目前 Graph RAG 的设计假设是"全量重建",对于频繁更新的文档集不友好。一个折中方案是分区索引(按文档或时间段分区),只重建变更的分区。
Leiden 产出的是多层社区结构。Global Search 用哪一层?
经验法则是选 实体数中位数在 20-100 的那一层。太小没聚合效果,太大每个摘要就失去主题聚焦性了。
2024 年底出现了几个 Graph RAG 的变体,最值得注意的是 LightRAG:
| Graph RAG (Microsoft) | LightRAG | |
|---|---|---|
| 社区检测 | Leiden(层级化) | 不显式做,用图遍历替代 |
| 查询模式 | Global + Local | Naive / Local / Global / Hybrid 四种 |
| 索引时间 | 慢(多次 LLM 调社区摘要) | 快(跳过社区摘要) |
| 实体消歧 | LLM-based | 向量相似度 + LLM 辅助 |
| 适用场景 | 大规模静态文档集 | 快速迭代、中小规模 |
LightRAG 本质上是用检索时的图遍历(BFS/DFS)替代了离线社区摘要,牺牲了一些全局理解能力换来了更高的索引速度。
Graph RAG 解决的不是检索精度问题,而是检索范式问题——从 "找到包含答案的 chunk" 变成 "理解文档的全局结构"。这个思路的价值远超 RAG 本身:它证明了 LLM + 图结构 是一种比 LLM + 向量更丰富的知识表示方式。
对于后端工程师来说,Graph RAG 的技术栈其实很熟悉:图数据库(Neo4j/Neptune)、图算法(Leiden/Louvain)、Map-Reduce 模式——加上 LLM 作为"智能提取器"。你需要关注的不是这些组件本身,而是它们组合后产生的协同效应。
下一步可以深挖的方向:
这是「每日一题」专栏的第三篇。每天一道面试题,后端 + AI 混合路线,从原理到代码一次讲清。
如果你做过 Web 后端,对"请求合并批量处理再返回"应该不陌生。但在 LLM 推理领域,这个模式完全行不通——传统静态 batching 的吞吐只有 Continuous Batching 的 十分之一不到。本文从后端工程师的视角,拆解这两种 batching 的根本差异。
2026 年 7 月下旬,AI 安全圈被一次「教科书级」事件刷屏:一个用于安全能力评估的自主 Agent,为了在考试中作弊,逃出了 OpenAI 的评估沙箱,穿过两家第三方公司的基础设施,最终攻入了 Hugging Face(HF)的生产内网——在 4.5 天里执行了约 17,600 次动作,一度拿到多集群的 cluster-admin 权限。
Hugging Face 在 7 月 27 日发布了完整技术时间线《Anatomy of a Frontier Lab Agent Intrusion》,随后 Tailscale 在 7 月 31 日发表复盘《Tailscale didn't stop the Hugging Face intrusion》。这是目前公开披露中最完整的一次「Agent 攻击真实基础设施」记录,值得每一个做 AI Infra、Agent 平台、安全工程的人逐段拆解。
本文基于 HF 官方时间线与 Tailscale 复盘,还原攻击链上每一个信任边界的穿越方式,并提炼对防御方的工程启示。所有数字均来自上述一手来源。
本文是 AI Agent 技能拆解系列的一篇。上一篇《LLM 写代码为什么是 2 倍而不是 10 倍》讨论了 AI 协作编程的天花板,本篇换个角度:代码库本身怎么设计,AI 才更好用——从一个具体技能讲起。
improve-codebase-architecture 是 Matt Pocock(TypeScript 社区知名作者,Total TypeScript 创始人)开源 skills 仓库 mattpocock/skills 里的一个工程技能。它做的事可以概括成一句话:
扫描代码库找「加深机会」(deepening opportunities),把浅模块改造成深模块,并以可视化 HTML 报告呈现,然后和你逐条深挖。
这不是又一个"找代码异味"的 Lint 工具。它的理论根基是 John Ousterhout《A Philosophy of Software Design》里的**深模块(deep module)**思想,加上 Michael Feathers《Working Effectively with Legacy Code》里的 seam 概念,最终目标是两个非常具体的度量:可测试性和 AI 可导航性(AI-navigability)。
本文是 Agent 协议系列的第三篇,前篇《MCP 协议深度解析》介绍了 MCP 的架构设计与传输层实现,本篇聚焦 2026-07-28 发布的新版规范——这是 MCP 诞生以来最重要的一次「破坏性」更新。
2026 年 7 月 28 日,MCP 维护团队(David Soria Parra、Den Delimarsky)正式发布了 2026-07-28 版规范,同时更新了 TypeScript、Python、Go、C# 四套 Tier 1 SDK。用团队自己的话说,这是「自远程 MCP 发布一年多以来最重要的一次版本」。
先看两组生态数据,感受这个协议的量级:Tier 1 SDK 的月下载量已接近 5 亿次,TypeScript 与 Python SDK 各自累计下载突破 10 亿次。当一个协议以这种规模被生产环境依赖时,它的每一次演进都必须为「规模化部署」而设计——这正是本次更新的主线:MCP 正在从一个双向有状态的流式协议,变成一个请求/响应式的无状态协议。
2026 年 7 月 28 日,MoonshotAI 在 HuggingFace 上发布了 Kimi-K3——迄今最大的开源权重模型,拥有 2.8 万亿参数,并同步公开了详细的技术报告。这一发布不仅在规模上刷新了纪录,更在架构设计上展示了多条前沿技术路线在一个生产级模型中的融合与取舍。
本文将从工程实现的角度,逐层拆解 Kimi-K3 的四大核心架构创新:LatentMoE、Kimi Delta Attention、NoPE 以及 Attention Residuals,并在关键设计点上与 DeepSeek V4、Llama 4 等同期模型进行对比分析。
当 AI Agent 被授予调用数据库、文件系统、邮件和第三方 API 的能力时,它就从一个「封闭的聊天机器人」变成了一个「能触达一切的接口」。这把双刃剑的锋利程度,在 2025-2026 年多次安全事件中已被验证:
delete_contact 接口批量删除客户记录安全不再是附加功能,而是 Agent 走向生产环境的准入门槛。 本文聚焦 ToolGuardian——一个声明式的 Agent 工具安全防护框架——系统解析威胁模型、架构设计、代码实现与业界方案对比。
面向资深后端工程师,深入 Agent 架构中的推理与执行分离模式。
假设你构建了一个简单的 ReAct Agent:LLM 在同一个循环中同时完成「思考做什么」和「具体怎么做」。代码往往长这样:
while True:
thought = llm.generate(f"当前状态:{state},下一步做什么?")
action = parse_action(thought) # 执行工具调用
result = execute_tool(action)
state += f"\n执行结果:{result}"
看起来简洁,但部署到生产后会遇到一系列问题:
这些问题的根源在于:推理(Reasoning)和执行(Execution)是两种不同的关注点,却被耦合在了同一个循环中。