首页 / 资讯中心 / 文章详情

AI与仿真软件集成:基于TCP和MCP的自动化方案

AI与仿真软件集成:基于TCP和MCP的自动化方案 ★ FEATURED ARTICLE
1. 为什么要在仿真软件里塞进一个 AI做过仿真的人都有一个共同的体感软件本身很强但用起来很累。以 Maxwell、eSim、Altium Designer 这类工具为例一次完整的仿真流程往往要经历建模、赋材料、设边界条件、划网格、配求解器、跑批、后处理这一长串动作每一步都对应着几十个参数面板。老手靠肌肉记忆和脚本模板撑着新手则常常卡在这个参数到底填多少上反复试错一天下来可能只跑通一个算例。我最初动这个念头是因为团队里新来的同事问了我一个问题能不能像跟人说话一样把需求描述给软件让它自己把参数填好当时我第一反应是这不现实但仔细一想其实仿真软件早就提供了脚本接口和命令行入口缺的只是一个能理解自然语言、并且能安全调用这些接口的中间层。这个中间层就是后来我们落地的这套方案用一条 TCP 通道把 AI 和仿真软件连起来再通过 MCPModel Context Protocol把仿真软件的能力暴露成 AI 可以调用的工具。这套东西解决的核心问题有三个。第一是降低操作门槛让不熟悉软件的人也能通过自然语言完成建模和求解配置第二是提升批量效率把重复性的参数扫描、结果提取交给 AI 编排第三是沉淀经验把老师傅脑子里的这个场景该用哪种网格变成可复用的工具描述。它适合谁适合有一定仿真基础、又想折腾自动化的工程师也适合做 CAE 工具链集成的开发者。哪怕你只是想给自己的日常工作省点力气这套思路也能直接抄。需要先说明的是下面讲的所有实现细节都是基于我们团队实际踩过的坑总结出来的涉及具体参数的地方我会给出计算过程涉及工具选型的地方我会解释为什么这么选。你完全可以根据自己的软件环境做替换。2. 整体架构设计与选型思路拆解2.1 三层结构AI 层、协议层、仿真层整套方案我把它拆成三层这样职责清晰任何一层出问题都好定位。最上面是AI 层也就是大模型和它的 Agent 框架。它负责理解用户的自然语言、规划任务步骤、决定调用哪个工具、解析工具返回的结果。这一层不直接碰仿真软件它只跟协议层打交道。中间是协议层核心就是 MCP Server。MCP 本质上是一套标准化的工具描述和调用协议它规定了工具叫什么名字、接受什么参数、返回什么格式。AI 层通过 MCP 客户端发现这些工具协议层则把 AI 的调用翻译成仿真软件能听懂的命令。最下面是仿真层包括仿真软件本体、它的脚本接口比如 Maxwell 的 IronPython 脚本、Altium 的脚本系统、以及我们额外挂上去的一条 TCP 通道。这条 TCP 通道是关键它让协议层和仿真层解耦——协议层不需要知道仿真软件装在哪台机器上只要知道 IP 和端口就行。为什么这么分层因为仿真软件往往跑在性能强劲的工作站上而 AI 调用可能来自另一台机器甚至另一个网络环境。用 TCP 做传输天然支持跨机器、跨进程而且调试的时候可以用 telnet 或 nc 直接手动发消息验证非常方便。2.2 为什么选 TCP 而不是别的传输方式有人会问为什么不用 HTTP 或者直接进程内调用我的考虑是这样的。进程内调用比如直接把 AI 的 Python 库和仿真脚本跑在同一个进程里看起来最简单但问题很大仿真软件通常是重量级 GUI 程序它的脚本引擎往往绑定在特定版本的 Python 上而 AI 框架对 Python 版本和依赖有自己的一套要求两者很容易打架。一旦版本冲突排查起来非常痛苦。HTTP 也可以但 HTTP 是请求-响应模式服务端无法主动推送消息。仿真跑一个算例可能要几十分钟我们希望仿真软件能在求解过程中主动把进度推给 AI让 AI 决定要不要继续等、要不要调整策略。TCP 是全双工的天然支持这种双向流式通信。至于为什么不用 WebSocket其实 WebSocket 底层也是 TCP只是多了一层握手和帧封装。在我们这个场景里通信双方都是自己控制的程序不需要浏览器兼容性直接用裸 TCP 反而更轻、更好调试。实测下来一条 TCP 长连接在局域网内的往返延迟通常在 1 毫秒以内完全够用。2.3 MCP 在这里扮演什么角色MCP 是这两年 AI 工具集成领域比较火的一个概念简单说它就是给 AI 用的 USB 接口。传统做法是每个 AI 应用都要为每个工具写一套适配代码工具一多就变成 N×M 的维护噩梦。MCP 把这层抽象出来了工具方只需要实现一个 MCP Server声明自己有哪些能力AI 方只需要实现一个 MCP Client就能自动发现并调用所有符合协议的工具。在我们的方案里MCP Server 负责把仿真软件的能力包装成一个个工具比如create_geometry、set_material、assign_boundary、run_simulation、extract_result。每个工具都有清晰的参数 schemaAI 看到 schema 就知道该怎么填。这样一来用户说一句帮我建一个 50mm×30mm 的矩形材料用铜跑个静电场AI 就能自动拆解成若干个工具调用依次执行。这里有个经验工具粒度不要太细也不要太粗。太细的话AI 一次任务要调用几十次容易出错也慢太粗的话参数会变得极其复杂AI 反而填不对。我们的做法是按一个完整的物理动作来切分比如设置材料是一个工具设置边界条件是另一个而不是把设置材料的电导率单独拆出来。3. 核心细节解析与实操要点3.1 TCP 通道的消息格式设计TCP 是字节流没有消息边界所以我们必须自己定义一套分帧规则。我试过几种方案最后选的是长度前缀 JSON 载荷。具体格式是前 4 个字节是一个大端序的 32 位整数表示后面 JSON 载荷的字节长度紧接着是 UTF-8 编码的 JSON 字符串。接收方先读 4 字节拿到长度 N再读 N 字节就得到一条完整消息。这个方案的好处是实现简单、跨语言通用Python、C#、Java 都能轻松处理。消息体本身用 JSON结构大致是这样{ id: req-001, type: command, action: set_material, params: { object: box1, material: copper, conductivity: 5.8e7 }, timestamp: 1730000000 }id用于请求和响应的配对type区分是命令、响应还是事件推送action是动作名params是参数。响应消息里会带一个status字段取值ok、error或progress。注意长度前缀一定要用大端序并且要处理粘包和半包问题。所谓粘包就是一次 recv 读到了两条消息半包就是一条消息只读到一半。标准做法是维护一个接收缓冲区循环判断缓冲区里是否有完整的消息。3.2 仿真软件侧的脚本桥接仿真软件本身不会说 TCP所以我们需要在它内部跑一个脚本负责监听 TCP 端口、解析消息、调用软件 API、再把结果发回去。以 Maxwell 为例它支持 IronPython 脚本我们可以写一个常驻脚本用 .NET 的TcpListener起一个服务。这里有个坑仿真软件的脚本引擎通常是单线程的而且和 GUI 主线程绑定。如果你在脚本里阻塞式地等 TCP 消息GUI 会卡死。解决办法是用异步回调或者定时器轮询。我的做法是起一个后台线程专门收消息收到后把任务丢进一个队列主线程在空闲时从队列取任务执行。这样 GUI 保持响应任务也不会丢。另一个坑是异常处理。仿真 API 抛出的异常如果不捕获会直接让脚本崩溃TCP 连接也就断了。所以每个 API 调用都要包在 try-catch 里把异常信息序列化成错误响应发回去让 AI 知道哪一步失败了。3.3 MCP 工具的描述与参数设计MCP 工具的描述质量直接决定了 AI 能不能用对。我总结了几条经验。第一工具名要动词开头、语义明确。create_rectangle比rect好run_simulation比run好。AI 是靠名字和描述来匹配意图的模糊的名字会让它选错工具。第二参数要有类型、单位、取值范围和默认值。比如长度参数要写清楚单位是毫米还是米是整数还是浮点。我们吃过亏有一次 AI 把长度 50 理解成了 50 米结果建出来的模型大得离谱。后来我们在参数描述里强制写上单位mm问题就没了。第三枚举类型的参数要把所有合法值列出来。比如材料类型不要只写string而要写enum: [copper, aluminum, ferrite, air]。这样 AI 就不会瞎编一个不存在的材料名。第四给每个工具写清楚它依赖哪些前置条件。比如run_simulation必须在几何、材料、边界都设置好之后才能调用。这个信息可以写在工具描述里AI 规划任务时会参考。3.4 自然语言到工具调用的映射策略用户说的话和工具调用之间隔着一次意图解析 任务规划。我们的做法是让 AI 先输出一个结构化的任务计划再逐步执行。比如用户说建一个 50×30 的矩形材料铜跑静电场把最大场强告诉我AI 会先规划成调用create_rectangle参数 width50, height30调用set_material参数 objectrectangle1, materialcopper调用setup_solver参数 typeelectrostatic调用run_simulation调用extract_result参数 quantitymax_field_strength这个计划会先展示给用户确认可选然后逐步执行。每一步的结果都会反馈给 AI如果某一步失败AI 可以决定重试、调整参数还是中止。实操心得让 AI 在规划阶段就把所有参数填全不要留待定。因为执行阶段再回头问用户体验会很割裂。如果某个参数用户没提AI 应该用工具描述里的默认值并在结果里说明我用了默认值 X。4. 实操过程与核心环节实现4.1 环境准备与依赖清单先把环境列清楚避免你踩版本坑。我们这套方案实测可用的组合是组件版本说明Python3.10AI 层和 MCP Server 用IronPython2.7仿真软件脚本引擎.NET Framework4.7.2IronPython 运行依赖MCP SDK最新协议实现仿真软件Maxwell 2023 R1其他版本 API 可能不同Python 选 3.10 是因为它在 AI 生态里兼容性最好很多库对 3.11、3.12 的支持还不完善。IronPython 选 2.7 是因为仿真软件内置的就是这个版本没法换。4.2 搭建 TCP 服务端仿真侧服务端跑在仿真软件里用 IronPython 写。核心逻辑是起一个监听线程循环 accept 连接每个连接再起一个线程处理消息。import clr clr.AddReference(System) from System.Net import IPAddress from System.Net.Sockets import TcpListener, NetworkStream from System.Threading import Thread, ThreadStart import json HOST 0.0.0.0 PORT 9527 def read_exact(stream, n): buf [] while n 0: chunk stream.ReadByte() if chunk 0: raise Exception(connection closed) buf.append(chunk) n - 1 return bytes(bytearray(buf)) def handle_client(client): stream client.GetStream() while True: header read_exact(stream, 4) length int.from_bytes(header, big) payload read_exact(stream, length) msg json.loads(payload.decode(utf-8)) resp dispatch(msg) data json.dumps(resp).encode(utf-8) stream.Write(bytes(bytearray(len(data).to_bytes(4, big))), 0, 4) stream.Write(bytes(bytearray(data)), 0, len(data)) def start_server(): listener TcpListener(IPAddress.Parse(HOST), PORT) listener.Start() while True: client listener.AcceptTcpClient() t Thread(ThreadStart(lambda: handle_client(client))) t.IsBackground True t.Start()dispatch函数就是路由根据action字段调用对应的仿真 API。这里要注意IronPython 的int.from_bytes在 2.7 里可能没有需要自己实现一个字节转整数的函数。4.3 搭建 MCP Server协议侧MCP Server 用标准 Python 写它对外暴露工具对内通过 TCP 客户端跟仿真软件通信。核心是定义工具列表和调用处理函数。from mcp.server import Server from mcp.types import Tool, TextContent import socket, json, struct app Server(simulation-mcp) TOOLS [ Tool( namecreate_rectangle, description创建一个矩形几何体单位毫米, inputSchema{ type: object, properties: { width: {type: number, description: 宽度单位mm}, height: {type: number, description: 高度单位mm} }, required: [width, height] } ), Tool( nameset_material, description给指定对象赋材料, inputSchema{ type: object, properties: { object: {type: string}, material: {type: string, enum: [copper, aluminum, ferrite, air]} }, required: [object, material] } ), # 其他工具省略 ] def send_tcp(action, params): s socket.create_connection((127.0.0.1, 9527), timeout300) msg json.dumps({id: 1, type: command, action: action, params: params}) data msg.encode(utf-8) s.sendall(struct.pack(I, len(data)) data) header s.recv(4) length struct.unpack(I, header)[0] body b while len(body) length: body s.recv(length - len(body)) s.close() return json.loads(body.decode(utf-8)) app.call_tool() async def call_tool(name, arguments): result send_tcp(name, arguments) return [TextContent(typetext, textjson.dumps(result))]这里timeout300是给长时间求解留的余量。如果仿真要跑更久可以设成 0 表示不超时但那样一旦卡死就没法恢复所以我建议设一个合理上限配合进度推送来监控。4.4 参数计算实例网格尺寸怎么定AI 帮用户填参数时最容易被质疑的就是你凭什么填这个值。以网格尺寸为例我给它设计了一个基于物理的计算逻辑。假设用户要做的是电磁场仿真频率 f1GHz材料是空气。电磁波在空气中的波长 λ c/f 3e8 / 1e9 0.3m 300mm。按照经验网格尺寸应该小于波长的 1/10也就是 30mm如果要精度高一点取 1/20即 15mm。这个计算过程我们写进了工具描述里AI 在调用set_mesh时会先问用户频率然后自动算出建议值。用户如果坚持用自己的值也可以覆盖。注意不同物理场的最优网格准则不一样。静电场看几何曲率磁场看趋肤深度热场看温度梯度。不要用一套准则套所有场景否则结果会失真。4.5 端到端跑通一次完整流程把三层都启动起来实际跑一次。启动顺序是先开仿真软件并加载脚本确认 TCP 端口在监听再启动 MCP Server最后启动 AI 客户端并连接 MCP Server。用户输入建一个 50×30 的矩形材料铜跑静电场告诉我最大场强。AI 解析后依次调用工具每一步的响应都会打印出来。实测下来从用户输入到拿到结果整个流程大约 40 秒其中建模和配置只占 5 秒剩下 35 秒是求解时间。相比手动操作配置环节至少省了 80% 的时间。如果中途某一步失败比如材料名拼错了AI 会收到错误响应然后自动纠正重试。我们测试了 20 次AI 自主纠错的成功率在 85% 左右剩下 15% 需要人工介入。5. 常见问题与排查技巧实录5.1 连接类问题速查现象可能原因排查方法连接被拒绝仿真侧服务没启动用 netstat 看端口是否监听连接超时防火墙拦截检查入站规则连上就断消息格式不对抓包看前 4 字节长度是否合理响应乱码编码不一致统一用 UTF-8连接问题里最常见的是防火墙。Windows 上默认会拦截非标准端口的入站连接第一次启动服务时会弹窗询问如果点了取消后面就一直连不上。解决办法是手动在防火墙里加一条入站规则放行对应端口。5.2 仿真 API 调用失败的处理仿真 API 报错的原因五花八门我整理了几类高频的。第一类是对象不存在。比如 AI 调set_material时传的 object 名字和实际创建的名字不一致。这通常是因为 AI 在规划时假设了名字但创建工具返回的实际名字带了后缀。解决办法是让创建工具把实际名字返回给 AI后续调用都用返回的名字。第二类是参数越界。比如网格尺寸设成了负数或者频率设成了 0。这类问题要在工具层做校验参数不合法就直接返回错误不要传给仿真软件。第三类是状态依赖。比如没设边界就调求解。这类问题靠工具描述里的前置条件说明来规避AI 规划时会检查。5.3 长任务超时与进度推送仿真跑几十分钟是常事如果 TCP 连接一直干等中间任何网络抖动都可能导致断连。我们的做法是让仿真侧定期推送进度事件。具体实现是仿真侧每完成一个求解步就主动往连接里写一条typeprogress的消息带上当前进度百分比和已用时间。AI 侧收到后可以选择继续等、或者根据进度决定是否中止。这样即使任务很长连接也一直有数据流动不容易被中间设备判定为空闲而断开。实操心得进度推送的频率不要太高否则会淹没真正的结果消息。我们设的是每 5% 推一次实测体验比较平衡。5.4 AI 幻觉导致的错误调用AI 有时候会自作主张调用不存在的工具或者给参数填一个离谱的值。这是大模型的通病没法完全消除但可以缓解。我们的做法有三条。一是工具列表要精简不要一次性暴露几百个工具AI 选择越多越容易选错。二是参数校验要严格非法值直接拒绝不给 AI 试错的机会。三是关键操作要二次确认比如删除几何体、覆盖已有结果这类不可逆操作让 AI 先问用户一句。实测下来加了这三条之后错误调用率从最初的 20% 降到了 5% 以下。5.5 性能瓶颈定位如果整套流程跑起来很慢先分清是 AI 慢、网络慢还是仿真慢。最简单的办法是在每一层打时间戳。我们实测的数据是AI 解析意图平均 2 秒TCP 往返平均 1 毫秒仿真 API 调用平均 50 毫秒求解时间取决于算例本身。所以如果总时间很长八成是求解本身慢而不是集成方案的问题。这时候该优化的是网格和求解器设置而不是通信层。6. 后续可以怎么扩展这套方案跑通之后能扩展的方向其实挺多。我目前在做的是把多个仿真软件都接到同一个 MCP Server 后面让 AI 根据任务类型自动选择用哪个软件。比如静电场用 MaxwellPCB 布线用 Altium这样用户只需要描述需求不用关心底层用哪个工具。另一个方向是把历史算例做成知识库AI 在填参数时先检索相似算例参考历史值。这个对提升参数准确性帮助很大尤其是那些靠经验才能定好的参数。还有一个我觉得很有意思的点把仿真结果反过来喂给 AI 做分析。现在 AI 只是帮我们跑仿真跑完之后的结果解读还是靠人。如果能让 AI 直接读出场强分布、温度曲线然后给出这个设计哪里可能有问题的判断那整个闭环就完整了。我试过让 AI 读结果数据它对趋势的判断基本靠谱但对绝对值的物理意义理解还不够深这块还需要再调。最后分享一个小技巧调试这套系统时强烈建议先用 nc 或 telnet 手动发几条 JSON 消息确认仿真侧能正确响应再去接 AI。因为 AI 的不确定性会掩盖底层的问题先把手动链路跑通能省掉大量排查时间。
阅读完成 · 觉得有帮助?
咨询建站