从单点智能到集群协作:CAD 智能体的进化之路
在传统的 CAD 二次开发中,我们习惯了"点击按钮 - 执行命令"的线性交互。用户想要倒角,就点击倒角工具;想要统计面积,就运行面积统计插件。这种模式在单一任务场景下行之有效,但当面对复杂的工程设计------比如"找出所有厚度小于 0.1mm 的薄壁零件并生成修复建议报告"时,传统的脚本往往显得力不从心。它需要串联几何分析、规则判断、文档生成等多个环节,代码耦合度高且难以维护。
随着大语言模型(LLM)的爆发,AI Agent(智能体)为 CAD 软件带来了新的交互范式。但单个 Agent 的能力终究有限,真正的生产力飞跃来自于多个专业 Agent 的协同工作。这就引出了我们今天的主角:A2A(Agent-to-Agent)。本文将带你从零开始,构建一个基于 A2A 协议的 CAD 智能体集群,让不同的智能体像工程师团队一样分工协作,共同解决复杂的工程问题。
基础设施搭建:用 Elastic Agent Builder 打造数据大脑
构建智能体集群的第一步,是让 Agent 拥有"记忆"和"查询"能力。在 CAD 场景中,这通常意味着能够检索设计规范、历史案例或特定的几何参数。我们将利用 Elastic Agent Builder 快速创建一个具备数据查询能力的 Hello World Agent,作为集群中的"知识库节点"。
首先,你需要一个 Elastic Serverless 项目。登录 Elastic Cloud 创建新项目后,进入 Developer Tools 控制台。我们需要创建一个索引来存储文档数据,这里我们定义一个包含语义搜索能力的索引 my-docs:
json
PUT /my-docs
{
"mappings": {
"properties": {
"title": { "type": "text" },
"content": { "type": "semantic_text" },
"filename": { "type": "keyword" },
"last_modified": { "type": "date" }
}
}
}
索引创建完成后,注入一条测试数据。这条数据模拟了一份关于"问候语规范"的设计文档,后续我们的 Agent 将依据此文档进行回复:
json
PUT /my-docs/_doc/greetings-md
{
"title": "Greetings",
"content": "# Greetings\n## Basic Greeting\nHello!\n## Helloworld\nGreeting Hello World! 🌎\n## Not Greeting\nI'm only a greeting agent. 🤷",
"filename": "greetings.md",
"last_modified": "2025-11-04T12:00:00Z"
}
数据就位后,核心步骤是创建 Tool (工具)。在 Agent Builder 中选择 New Tool,类型设为 ES|QL。我们将定义一个名为 example.get_greetings 的工具,其作用是从索引中提取特定的问候语文档。配置如下 ES|QL 查询语句:
sql
FROM my-docs | WHERE filename == "greetings.md"
保存工具后,接着创建 Agent 。在 New Agent 表单中,设定 Agent ID 为 helloworld_agent,并在 Custom Instructions 中写入业务逻辑:如果用户输入包含"Hi"或"Hello",则返回文档中的基础问候语;若包含"Hello World",则返回对应的特定文本;否则返回默认提示。最关键的一步是在 Tools 标签页中,仅勾选刚才创建的 example.get_greetings 工具。
至此,一个能够理解自然语言并查询 Elastic 数据的独立 Agent 已经诞生。你可以在 Agent Builder 的聊天窗口中输入"hello world"进行测试,它会精准地返回预设的响应。但这只是单机智能,接下来我们要让它融入集群。
协议核心解析:读懂 Agent Card 与 Task 生命周期
要让上述 Elastic Agent 与其他 CAD 专用 Agent(如几何计算 Agent、渲染 Agent)协作,必须遵循统一的通信标准,即 A2A 协议 。A2A 的核心在于两个概念:Agent Card (能力名片)和 Task(任务对象)。
Agent Card:智能体的身份证
每个接入 A2A 网络的 Agent 都必须发布一个 JSON 格式的 Agent Card。这不仅是身份标识,更是能力说明书。Client 端通过读取这张"名片",决定何时调用该 Agent。一个标准的 CAD 几何分析 Agent 的 Card 可能长这样:
json
{
"name": "CAD Geometry Analyzer",
"description": "Analyzes STL models for thin walls and intersections",
"url": "https://cad-cluster.local/geometry-agent",
"version": "1.0.0",
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"skills": [
{
"id": "thin-wall-detection",
"name": "Thin Wall Detection",
"description": "Identifies triangles with thickness below threshold",
"inputModes": ["application/json"],
"outputModes": ["application/json"]
},
{
"id": "ray-intersection",
"name": "Ray Intersection",
"description": "Calculates intersection points for a given ray",
"inputModes": ["application/json"],
"outputModes": ["application/json"]
}
]
}
注意 skills 字段,它详细列出了该 Agent 能做什么。在集群中, orchestrator(编排者)会根据这些技能描述,自动将"检查薄壁"的任务分发给这个 Agent,而不是发给只负责查文档的 Elastic Agent。
Task 生命周期:协作的节拍器
A2A 中的协作不是简单的请求 - 响应,而是基于 Task 的状态机管理。一个 Task 拥有明确的生命周期状态:submitted(已提交)、working(处理中)、input-required(需要人工介入)、completed(完成)、failed(失败)。
当 Client 发起任务时,Remote Agent 不会立即返回最终结果,而是先返回一个 Task 对象,状态标记为 working。随后,Agent 可以通过 SSE(Server-Sent Events)流式推送中间状态或日志。这种机制对于耗时的 CAD 几何计算尤为重要------用户不必盯着旋转的光标等待,而是能实时看到"正在加载模型"、"正在构建 BVH"、"检测到 12 个薄壁面"等进度信息。
深度集成:将 C# 几何内核注册为网络服务
在 CAD 领域,核心的几何算法(如布尔运算、网格简化、射线检测)通常由高性能的 C++ 或 C# 代码实现。如何将这些本地能力暴露给 A2A 集群?我们需要构建一个 Geometry API Service,将其封装为符合 A2A 标准的 Remote Agent。
假设我们已经有一个经过优化的 C# 几何内核,支持零拷贝加载 STL 文件和基于 BVH 的射线相交检测。现在,我们要为其包裹一层 HTTP 服务,使其成为集群中的"几何专家"。
1. 定义数据结构与接口
首先,确保几何接口清晰且与渲染逻辑解耦。定义基础结构如 Point、Ray 和 Intersection,并提供统一的 API 类:
csharp
public class GeometryAPI
{
private ObjectPool<Triangle> _trianglePool;
private BVH _bvh;
public bool LoadModel(string filename)
{
// 零拷贝加载逻辑,直接填充内存池
if (!ParseSTLToPool(filename, _trianglePool)) return false;
_bvh = new BVH(_trianglePool.GetAll());
_bvh.Build();
return true;
}
public List<Intersection> GetIntersectingPoints(Ray ray)
{
if (_bvh == null) return new List<Intersection>();
var hits = _bvh.Intersect(ray);
// 按距离排序并返回
return hits.OrderBy(h => h.Distance).Select(h => h.ToIntersection()).ToList();
}
public List<Triangle> GetThinParts(float maxThickness)
{
// 遍历内存池,计算厚度并筛选
return _trianglePool.GetAll()
.Where(t => ComputeThickness(t) <= maxThickness)
.ToList();
}
}
2. 实现 A2A 服务端
接下来,使用 ASP.NET Core 创建一个 Web API,实现 A2A 协议要求的端点。你需要处理 /agent/card 请求以返回前述的 JSON 名片,并实现 /tasks/send 来处理任务提交。
在处理 tasks/send 时,服务端需解析传入的 JSON-RPC 请求。例如,当收到 thin-wall-detection 技能调用时,实例化 GeometryAPI,执行 GetThinParts 方法,并将结果封装成 A2A 标准的 Artifact 返回。
csharp
[HttpPost("/tasks/send")]
public async Task<IActionResult> SendTask([FromBody] A2ATaskRequest request)
{
var taskId = Guid.NewGuid().ToString();
var skillId = request.Params.Message.Parts.First().SkillId;
// 异步启动任务,立即返回 Task 对象(状态:working)
_ = Task.Run(() => ProcessGeometryTask(taskId, skillId, request.Params));
return Ok(new A2ATaskResponse
{
Id = taskId,
Status = new TaskStatus { State = "working" },
SessionId = request.Params.SessionId
});
}
在这个后台任务 ProcessGeometryTask 中,调用 C# 几何内核进行实际计算。计算完成后,通过 SSE 通道推送最终结果,或将状态更新为 completed 并附带包含检测结果的 Artifact。这样,无论底层是 C#、Python 还是 Go 实现的 Agent,只要遵循 A2A 协议,就能无缝对话。
集群编排:使用 Microsoft Agent Framework 串联工作流
有了数据查询 Agent(Elastic)和几何计算 Agent(C#),我们需要一个"指挥官"来协调它们。Microsoft Agent Framework 提供了强大的编排能力,允许我们在 Python 或 C# 中定义复杂的工作流。
以下是一个基于 Python 的编排示例,展示如何让两个 Agent 协作完成"查询规范并检测模型"的任务:
python
import asyncio
from a2a.client import A2ACardResolver
from agent_framework.a2a import A2AAgent
async def main():
# 1. 发现集群中的 Agent
resolver = A2ACardResolver(base_url="http://cad-cluster.local")
# 获取几何 Agent 的名片
geo_card = await resolver.get_agent_card("/geometry-agent.json")
geo_agent = A2AAgent(agent_card=geo_card)
# 获取文档 Agent 的名片
doc_card = await resolver.get_agent_card("/helloworld_agent.json")
doc_agent = A2AAgent(agent_card=doc_card)
# 2. 定义协作流程
user_prompt = "检查 engine.stl 是否有薄壁,并参考问候语文档格式输出报告"
# 第一步:调用几何 Agent 进行检测
print(">> 调用几何分析 Agent...")
geo_response = await geo_agent.run({
"file": "engine.stl",
"threshold": 0.1
})
thin_parts = geo_response.artifacts[0].data
# 第二步:调用文档 Agent 获取报告模板
print(">> 调用文档规范 Agent...")
doc_response = await doc_agent.run("How to format a report?")
template = doc_response.messages[0].text
# 第三步:本地整合结果(或由第三个总结 Agent 完成)
final_report = f"{template}\n\n检测结果:发现 {len(thin_parts)} 处薄壁风险。"
print(f"\n最终报告:\n{final_report}")
if __name__ == "__main__":
asyncio.run(main())
在这个流程中,Microsoft Agent Framework 充当了 Client 角色,它不需要知道几何计算的具体算法,也不需要关心文档存储在哪个 Elastic 索引中。它只需要根据 Agent Card 找到对应的服务,发送标准化的 Task,然后处理返回的 Artifact。这种解耦使得我们可以随时替换底层的几何引擎(比如从 C# 换成 Rust 实现),或者升级文档数据库,而无需修改上层业务逻辑。
异常处理与生产级考量
在实际落地中,网络波动、计算超时或参数错误不可避免。A2A 协议通过 Task 的状态机制提供了天然的容错基础。
当几何计算耗时过长导致 HTTP 超时时,服务端不应直接断开连接,而应保持 Task 状态为 working,并通过心跳包维持会话。若计算过程中发现模型文件损坏,Agent 应将 Task 状态更新为 failed,并在 Message 中附带详细的错误码(如 ERR_INVALID_STL),以便上游编排者决定是重试、跳过还是通知人工介入。
此外,安全性不容忽视。在生产环境中,Agent Card 应包含认证方案(如 OAuth2 或 API Key),并在 HTTP 头中校验权限。对于涉及核心设计数据的 CAD 集群,建议启用 HTTPS 并配置严格的 Roots 策略,限制 Agent 只能访问指定的文件目录或数据库表,防止越权操作。
从单个脚本到智能体集群,CAD 开发的范式正在发生深刻变化。通过 A2A 协议,我们将分散的工具、数据和算法连接成了一个有机的整体。无论是 Elastic 的数据检索,还是 C# 的高性能几何计算,都能在统一的协作框架下发挥最大价值。未来,随着更多专业 Agent 的加入,这个集群将不仅能执行命令,更能自主规划、协同创新,真正成为工程师的得力助手。