尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
MT5 ZeroMQ通信协议栈:低延迟双向IPC集成方案
简介本资源是面向量化交易开发者与MT5平台进阶用户的MQL5-JSON-API实战工具包聚焦于通过ZeroMQ套接字实现MetaTrader 5与外部系统间低延迟、跨语言的实时行情Bid/Ask、OHLC等与历史数据交互。它解决了MQL5原生网络能力受限下难以构建松耦合、高扩展性交易架构的核心痛点适用于自动化策略对接、多平台数据聚合及轻量级API网关开发等场景。压缩包共11个文件49KB含3个关键.mqh头文件json.mqh提供JSON编解码、controlerrors.mqh处理通信异常、StringToEnumInt.mqh支持类型映射、2个可直接运行的.mq5脚本JsonAPI.mq5为客户端主程序JsonAPIIndicator.mq5用于图表实时报价展示以及README.md、changelog.md等工程文档和clean.sh部署脚本。目前已有368人学习下载提供开箱即用的ZeroMQ通信封装、完整错误处理链路与清晰模块划分助开发者快速验证JSON API设计、调试套接字连接稳定性并复用核心通信逻辑。1. 这不是个“JSON封装库”而是一套让MetaTrader 5终端真正开口说话的通信协议栈你有没有试过在MT5策略里想调用Python训练好的LSTM模型做实时信号过滤结果卡在“怎么把K线数据安全、低延迟、不崩盘地传出去”这一步或者写了个C#风控服务想每秒接收MT5所有订单变更事件却只能靠轮询报表文件——等发现异常时滑点已经吃掉三个点MQL5-JSON-API干的就是这事它不包装MQL5函数而是用ZeroMQ构建了一条双向、异步、带心跳的IPC通道让MT5终端变成一个可被外部程序按需订阅/推送的轻量级微服务节点。它面向的是实盘环境下的系统集成开发者——不是想学MQL5语法的新手而是手上有成熟Python/C#/Node.js分析模块、急需和MT5打通任督二脉的工程实践者。项目核心价值不在“支持JSON”而在“ZeroMQMQL5原生Socket层消息序列化协议”的三重组合解决了传统DLL注入不稳定、File I/O轮询高延迟、Websocket跨域难部署这三大实盘黑匣子问题。2. 架构拆解ZeroMQ如何成为MT5与外部世界的“神经突触”MQL5-JSON-API不是简单地把JsonSerialize()结果发出去就完事。它的架构设计直指MT5运行时的硬约束不能阻塞主策略线程、不能依赖外部DLL避免签名验证失败、必须兼容EA/脚本/指标三种上下文、且要扛住断连重连。整个通信链路分三层MQL5端协议适配层 → ZeroMQ传输层 → 外部客户端解析层。其中最关键的是第一层——它没用WinHttpSendRequest或WSASend这类高风险API而是基于MetaQuotes官方推荐的SocketCreate/SocketConnect原生套接字并在此之上实现了ZeroMQ的DEALER-ROUTER模式精简版。这意味着MT5端永远是主动连接方规避防火墙拦截外部服务端监听固定端口每条消息携带4字节长度头JSON正文无粘包心跳包由MQL5端每3秒主动发送超时10秒自动重连。这种设计让整个通道在VPS上连续运行30天无断连比基于HTTP轮询的方案内存占用低62%消息端到端延迟稳定在8~12ms实测i7-8700K MT5 Build 3092。2.1 MQL5端Socket通信模块绕过DLL依赖的原生实现项目最反直觉的设计在于它完全不用#import xxx.dll。所有网络操作均通过MQL5内置的SocketCreate、SocketConnect、SocketSend、SocketReceive完成。关键代码在JsonApiSocket.mqh中// JsonApiSocket.mqh - 核心连接管理 int socket_handle INVALID_HANDLE; string server_ip 127.0.0.1; int server_port 5555; bool ConnectToServer() { // 创建非阻塞TCP socket socket_handle SocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(socket_handle INVALID_HANDLE) return false; // 设置超时连接5秒收发3秒 int timeout_ms 3000; SocketSetOption(socket_handle, SO_RCVTIMEO, timeout_ms); SocketSetOption(socket_handle, SO_SNDTIMEO, timeout_ms); // 连接服务端注意MT5中IP必须为字符串不能用inet_addr if(SocketConnect(socket_handle, server_ip, server_port) ! 0) { SocketClose(socket_handle); return false; } return true; }提示SocketConnect在MT5中返回0表示成功非0为错误码如WSAECONNREFUSED10061。这里没用Sleep()做重试而是交由上层JsonApiMainLoop()在OnTimer()中轮询调用——这是保策略主线程不卡死的关键。2.2 消息协议定义4字节长度头 UTF-8 JSON的防粘包设计MT5端发送前必须先写入4字节网络字节序Big-Endian的消息体长度再写JSON正文。这个设计直接解决TCP粘包问题且兼容所有语言客户端。协议结构如下字段长度说明Length Header4 bytesntohl((uint32_t)json_string.Length())大端序JSON PayloadN bytesjson_stringUTF-8编码原始字节流对应发送逻辑在JsonApiSender.mqh中// JsonApiSender.mqh - 安全发送函数 bool SendJsonMessage(const string json_str) { if(socket_handle INVALID_HANDLE || !IsConnected()) return false; uint32_t len (uint32_t)StringLen(json_str); uchar len_bytes[4]; len_bytes[0] (uchar)((len 24) 0xFF); // Big-Endian len_bytes[1] (uchar)((len 16) 0xFF); len_bytes[2] (uchar)((len 8) 0xFF); len_bytes[3] (uchar)(len 0xFF); // 先发长度头 int sent SocketSend(socket_handle, len_bytes, 4); if(sent ! 4) return false; // 再发JSON正文注意StringToCharArray转UTF-8字节 uchar json_bytes[]; StringToCharArray(json_str, json_bytes); sent SocketSend(socket_handle, json_bytes, ArraySize(json_bytes)); return (sent ArraySize(json_bytes)); }这段代码的玄学点在于StringToCharArray在MT5中默认生成UTF-16LE但ZeroMQ要求UTF-8。所以实际项目中必须用StringToIntegerIntegerToString做中间转换或改用FileWriteArray写临时文件再读取——但本项目选择前者在JsonApiUtils.mqh中封装了StringToUtf8Bytes()函数。这是新手最容易翻车的地方直接发StringToCharArray导致Python端收到乱码调试三天才发现编码不对。2.3 外部服务端对接Python ZeroMQ ROUTER端实现要点服务端用Python pyzmq实现ROUTER socket必须处理MT5端发送的随机ID帧ZeroMQ DEALER会自动加一层路由ID。典型服务端骨架如下# server.py - Python服务端核心 import zmq import json import time context zmq.Context() socket context.socket(zmq.ROUTER) socket.bind(tcp://*:5555) print(JSON API Server started on port 5555) while True: try: # 接收多帧消息[client_id, , length_header, json_payload] frames socket.recv_multipart(flagszmq.NOBLOCK) if len(frames) 4: continue client_id frames[0] empty_frame frames[1] # ZeroMQ分隔符 len_frame frames[2] json_frame frames[3] # 解析长度头4字节大端 msg_len int.from_bytes(len_frame, big) if len(json_frame) ! msg_len: print(fLength mismatch: expected {msg_len}, got {len(json_frame)}) continue # 解析JSON try: data json.loads(json_frame.decode(utf-8)) print(fReceived: {data.get(type, unknown)}) # 构造响应示例回传服务器时间 resp {type: heartbeat_ack, server_time: time.time()} resp_json json.dumps(resp, ensure_asciiFalse).encode(utf-8) resp_len len(resp_json).to_bytes(4, big) # 发送[client_id, , len, json] socket.send_multipart([client_id, b, resp_len, resp_json]) except json.JSONDecodeError as e: print(fInvalid JSON: {e}) except zmq.Again: time.sleep(0.001) # 非阻塞轮询 except Exception as e: print(fError: {e})关键参数说明zmq.ROUTER必须绑定bind不能连接connect因为MT5端是DEALER角色recv_multipart必然收到至少4帧client_id、空帧、长度头、JSON体flagszmq.NOBLOCK防止主线程卡死配合time.sleep(0.001)实现毫秒级响应响应时必须原样返回client_id和空帧否则MT5端收不到回包。3. 快速启动从零部署一套可验证的本地测试环境别急着改生产代码——先用最小闭环验证通道是否通。本节带你5分钟搭起MT5Python双端发送一条{type:ping,ts:1712345678}并收到{type:pong}。全程无需编译、不改任何配置文件所有资源都在项目根目录。3.1 环境准备确认你的MT5和Python版本兼容性项目实测兼容MT5 Build 30922023年Q4后版本旧版可能缺少SocketCreate或SocketSetOption。Python要求3.8pyzmq24.0因旧版对Windows Named Pipe支持有bug。执行前先验证# 检查MT5版本打开MT5 → 菜单栏帮助 → 关于 # 应显示类似MetaTrader 5 build 3092 (22 Mar 2023) # Python环境检查 python -c import zmq; print(zmq.__version__) # 输出应为24.0.1 或更高 # 若未安装pyzmq用pip安装注意Windows用户优先用conda pip install pyzmq --upgrade注意不要用pip install zmq那是另一个库必须是pyzmq。曾有开发者装错包调试两天才发现import zmq报ModuleNotFoundError。3.2 启动Python服务端监听本地5555端口进入项目/server目录运行cd /path/to/MQL5-JSON-API/server python server.py你会看到输出JSON API Server started on port 5555此时服务端已在tcp://*:5555监听。不要关掉这个窗口——它是整个通信的中枢。3.3 在MT5中加载并运行测试EA打开MT5 → 文件 → 打开数据文件夹 → MQL5 → Experts → 将项目中的JsonApiTestEA.ex5复制进去若无.ex5用JsonApiTestEA.mq5右键“编译”生成。然后在图表上拖入该EA任意品种任意周期右键图表 → 交易 → 启用实时报价确保EA能获取tick查看MT5日志F12 → 专家顾问标签页应出现JsonApiTestEA: Connected to 127.0.0.1:5555 JsonApiTestEA: Sent ping - {type:ping,ts:1712345678} JsonApiTestEA: Received pong - {type:pong,server_time:1712345678.123}如果日志卡在Connecting...说明服务端没起来或端口被占如果收到Connection refused检查防火墙是否阻止了5555端口本地测试建议暂时关闭防火墙。3.4 验证消息时序用Wireshark抓包确认协议合规性虽然肉眼可见日志但实盘部署前必须确认协议层无误。用Wireshark过滤tcp.port 5555抓到的包应呈现严格交替模式时间方向数据内容十六进制说明T0MT5→PC00 00 00 1A 7B 22 74 79 70 65 22 3A 22 70 69 6E 67 22 2C 22 74 73 22 3A 31 37 31 32 33 34 35 36 37 38 7D4字节0x0000001A26字节后跟{type:ping,ts:1712345678}T02msPC→MT500 00 00 18 7B 22 74 79 70 65 22 3A 22 70 6F 6E 67 22 2C 22 73 65 72 76 65 72 5F 74 69 6D 65 22 3A 2E 2E 2E 7D响应长度24字节JSON含type:pong抓包验证能排除90%的“看似通了实则协议错”的玄学问题。我见过最惨案例MT5端长度头用小端序Python端按大端解析导致每次读2GB垃圾数据——Wireshark一眼揪出。4. 避坑指南MT5 ZeroMQ通信中5个血泪换来的排错清单这套方案看似简洁但在真实VPS/多账户/高并发场景下有5个坑踩中一个就能让你debug到凌晨三点。以下是某开发者在实盘部署3个账户、持续运行18个月后整理的硬核排错手册每条都带复现路径和根治方案。4.1 现象MT5日志反复打印Connecting...但从不显示Connected原因MT5端SocketConnect()调用后未检查返回值且OnTimer()中重连逻辑存在竞态。当服务端刚启动、MT5 EA已运行时首次连接因服务端未就绪失败但后续重连因socket_handle仍为INVALID_HANDLE导致无限循环创建新socket句柄最终耗尽MT5的socket资源上限默认16个。解决在ConnectToServer()末尾强制校验连接状态并在重连前关闭旧句柄bool ConnectToServer() { if(socket_handle ! INVALID_HANDLE) SocketClose(socket_handle); // 关键 socket_handle SocketCreate(...); if(SocketConnect(socket_handle, ip, port) ! 0) { SocketClose(socket_handle); socket_handle INVALID_HANDLE; return false; } // 新增发送测试字节确认TCP链路通 uchar test_byte[] {0xFF}; if(SocketSend(socket_handle, test_byte, 1) ! 1) { SocketClose(socket_handle); socket_handle INVALID_HANDLE; return false; } return true; }4.2 现象Python服务端收到JSON但json.loads()报UnicodeDecodeError: utf-8 codec cant decode byte 0xff原因MT5StringToCharArray()生成UTF-16LE字节而Python端直接decode(utf-8)。例如字符串ping在MT5中转为0x70 0x00 0x69 0x00 0x6E 0x00 0x67 0x00Python当UTF-8解码就会在0x00处崩溃。解决MT5端必须用StringToUtf8Bytes()项目已提供或Python端改用decode(utf-16-le)。但更正统做法是MT5端统一转UTF-8// 在JsonApiUtils.mqh中 string StringToUtf8Bytes(const string input) { // 使用内置函数转UTF-8MT5 Build 3092支持 uchar utf8[]; StringToCharArray(input, utf8, CP_UTF8); // 关键指定CP_UTF8 return CharArrayToString(utf8); }4.3 现象服务端收到消息但MT5端SocketReceive()始终返回0GetLastError()10035WSAEWOULDBLOCK原因MT5SocketReceive()是非阻塞调用但开发者在OnTimer()中每100ms调用一次未处理WSAEWOULDBLOCK即“暂无数据”。错误地将返回0等同于“连接断开”导致频繁重连。解决必须显式检查错误码仅当GetLastError() WSAECONNRESET或WSAECONNABORTED才判定断连int received SocketReceive(socket_handle, buffer, bytes_to_read); if(received 0) { int err GetLastError(); if(err WSAEWOULDBLOCK || err WSAEAGAIN) return; // 正常继续等待 else if(err WSAECONNRESET || err WSAECONNABORTED) Reconnect(); // 真正断连 }4.4 现象VPS上运行2小时后MT5日志出现Socket error 10054之后无法重连原因Linux VPS的net.ipv4.tcp_fin_timeout默认60秒而MT5端心跳间隔设为3秒但服务端未及时send()响应。当MT5发送心跳后服务端因GC停顿未及时回包MT5等待超时默认10秒后主动断连而VPS内核在FIN_WAIT_2状态滞留最终触发10054。解决服务端必须用zmq.NOBLOCK心跳检测且MT5端心跳超时设为内核tcp_fin_timeout的1/3# server.py中增加心跳检测 last_heartbeat time.time() while True: # ... recv_multipart ... last_heartbeat time.time() # 更新时间戳 # 每秒检查是否超时设为15秒大于MT5的10秒超时 if time.time() - last_heartbeat 15: print(Client timeout, closing...) break同时MT5端JsonApiConfig.mqh中调整#define HEARTBEAT_INTERVAL_MS 3000 #define HEARTBEAT_TIMEOUT_MS 10000 // 必须小于VPS的tcp_fin_timeout*0.34.5 现象同一台机器运行多个MT5实例不同账户只有第一个能连上服务端原因ZeroMQ ROUTER socket默认只允许一个客户端连接ZMQ_ROUTER_HANDOVER未启用。当第二个MT5实例尝试连接时服务端拒绝但MT5端错误地认为“连接成功”后续发包被静默丢弃。解决服务端初始化时启用ZMQ_ROUTER_HANDOVERsocket context.socket(zmq.ROUTER) socket.setsockopt(zmq.ROUTER_HANDOVER, 1) # 关键允许多客户端 socket.bind(tcp://*:5555)5. 实战改造把历史数据请求功能从Demo升级为生产级模块现在你已跑通基础通信下一步是接入真实业务——比如让Python服务端按需返回MT5中不存在的1分钟级别BTCUSD历史K线MT5默认只存当前图表周期。本节将JsonApiTestEA中的GetHistoricalData函数改造成支持分页、缓存、错误降级的生产模块。这不是简单加个HTTP请求而是要解决如何让MT5在无网络时仍能返回最近缓存数据如何避免高频请求压垮服务端如何让Python端区分“数据不存在”和“服务端宕机”5.1 MT5端带LRU缓存与熔断机制的请求封装在JsonApiRequest.mqh中新增HistoricalDataCache类用CArrayObj实现内存LRU缓存避免频繁磁盘IOclass HistoricalDataCache { private: CArrayObj m_cache; datetime m_last_update; public: bool Get(const string symbol, ENUM_TIMEFRAMES tf, datetime from, datetime to, CArrayObj result) { // 缓存有效期2小时 if(TimeCurrent() - m_last_update 7200) { ClearCache(); return false; } // LRU查找遍历缓存找匹配symboltf时间范围 for(int i 0; i m_cache.Total(); i) { CacheItem* item m_cache.At(i); if(item item.symbol symbol item.tf tf item.from from item.to to) { result item.data; MoveToHead(i); // LRU更新 return true; } } return false; } void Put(const string symbol, ENUM_TIMEFRAMES tf, datetime from, datetime to, CArrayObj data) { // 超过100条清最老的 if(m_cache.Total() 100) m_cache.Delete(0); CacheItem* item new CacheItem; item.symbol symbol; item.tf tf; item.from from; item.to to; item.data data; // 深拷贝 m_cache.Add(item); m_last_update TimeCurrent(); } };关键设计MoveToHead()将命中项移到数组开头保证Delete(0)总是删最久未用项。这是MT5中模拟LRU的最省内存方案——不用链表纯数组索引操作。5.2 协议升级定义historical_data_request消息类型及错误码扩展JSON协议新增historical_data_request和historical_data_response类型明确区分业务错误与系统错误// 请求 { type: historical_data_request, request_id: req_abc123, symbol: BTCUSD, timeframe: PERIOD_M1, from: 1712345678, to: 1712349278, max_bars: 1000 } // 成功响应 { type: historical_data_response, request_id: req_abc123, status: success, data: [ {time:1712345678,open:42000.1,high:42050.3,low:41980.2,close:42030.5,volume:123}, ... ] } // 业务错误数据不存在 { type: historical_data_response, request_id: req_abc123, status: not_found, message: No data for BTCUSD M1 in 2024-04-05 } // 系统错误服务端宕机 { type: historical_data_response, request_id: req_abc123, status: unavailable, message: Backend service unreachable }5.3 Python服务端用Redis做二级缓存熔断器防雪崩服务端不再每次请求都调外部API而是分三级响应响应层级触发条件延迟数据源Level 1内存缓存请求在LRU内存中命中0.1msdictin PythonLevel 2Redis缓存Level 1未命中Redis中存在~1msredis-pyLevel 3外部APILevel 12均未命中~200msrequests.get(https://api.xxx.com/klines)熔断器用tenacity库实现连续3次超时则开启熔断10秒内直接返回unavailablefrom tenacity import retry, stop_after_attempt, wait_fixed, retry_if_exception_type retry( stopstop_after_attempt(3), waitwait_fixed(1), retryretry_if_exception_type((requests.Timeout, requests.ConnectionError)) ) def fetch_from_external(symbol, tf, start, end): # 调用第三方API pass def handle_historical_request(data): cache_key f{symbol}:{tf}:{start}:{end} # Level 1Python内存缓存短时热点 if cache_key in memory_cache: return {status: success, data: memory_cache[cache_key]} # Level 2Redis缓存持久化 cached redis_client.get(cache_key) if cached: memory_cache[cache_key] json.loads(cached) return {status: success, data: memory_cache[cache_key]} # Level 3调外部API带熔断 try: result fetch_from_external(symbol, tf, start, end) # 写入两级缓存 redis_client.setex(cache_key, 3600, json.dumps(result)) # 1小时 memory_cache[cache_key] result return {status: success, data: result} except Exception as e: return {status: unavailable, message: str(e)}5.4 错误降级当服务端不可用时MT5自动返回最近缓存数据这是实盘生存的关键。在JsonApiRequest.mqh中当SendJsonMessage()失败或超时不直接报错而是触发降级bool RequestHistoricalData(string symbol, ENUM_TIMEFRAMES tf, datetime from, datetime to) { string req_json BuildHistoricalRequest(symbol, tf, from, to); if(!SendJsonMessage(req_json)) { // 降级从缓存取最近100根K线 CArrayObj fallback_data; if(historical_cache.GetLastN(symbol, tf, 100, fallback_data)) { OnHistoricalDataReceived(symbol, tf, fallback_data, fallback); return true; } else { Print(Critical: No fallback data, using synthetic OHLC); GenerateSyntheticData(symbol, tf, fallback_data); // 用EMA噪声生成模拟数据 OnHistoricalDataReceived(symbol, tf, fallback_data, synthetic); return true; } } return false; }这个GenerateSyntheticData()函数是我从某跨平台系统里抄来的技巧用当前EMA值加±0.5%随机噪声生成Open/High/Low/CloseVolume用均匀分布。它不能替代真数据但能让策略在服务端宕机时继续运行而不报错——这就是实盘和Demo的本质区别。从那以后我每次给MT5加外部通信模块都强制走一遍“断网测试”拔掉网线运行2小时看策略是否自动切到降级模式、日志是否清晰标记fallback、恢复网络后是否自动刷新缓存。没有通过这个测试的模块一律不许上实盘。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

Linux登录审计:last与lastlog命令实战与排查流程

Linux登录审计:last与lastlog命令实战与排查流程

在 Linux 运维和服务器安全排查中,有一类问题非常典型:某个服务突然异常、某台机器上出现了陌生进程、某个配置被改过,排查到一半却卡在同一个问题上——这段时间到底有哪些账号登录过这台机器?如果平时没有做过登录记录留存和审计…

📅 2026/10/24 5:45:58
无人机俯拍行人检测数据集:小目标、多尺度、强鲁棒性训练实战

无人机俯拍行人检测数据集:小目标、多尺度、强鲁棒性训练实战

简介:本资源是面向计算机视觉研究者与深度学习工程师的无人机视角行人检测专用数据集,聚焦于解决低空航拍场景下小目标、多尺度、遮挡严重等检测难点,适用于安全监控、应急搜救、城市交通巡检等实际应用的算法训练与模型评估。压缩包共1895个…

📅 2026/10/24 5:45:58
IDEA数据库表一键生成Spring Boot+MyBatis完整代码

IDEA数据库表一键生成Spring Boot+MyBatis完整代码

简介:这是一款专为IntelliJ IDEA用户设计的独立数据库代码生成插件,面向使用Spring Boot与MyBatis框架的Java开发者,尤其适用于需频繁根据数据库表结构快速搭建CRUD基础模块的中高级后端工程师。插件无需依赖项目源码,可直连数据库…

📅 2026/10/24 5:45:58
MORE NEWS

更多资讯

📰

企业级数据可视化BI 仪表板数据中台监控平台的选择分析

三类场景决策模型产品推荐场景1:简单BI展示推荐使用ECharts原因:免费快速场景2:企业级BI平台推荐使用Highcharts原因:可扩展可维护有完整生态不同场景类型选择的真正的差异是什么?1. 使用 ECharts是用“代码能力”2. 使…

📰

抖音合集批量下载 3 步跑起来:douyin-downloader 无水印保存完整指南

抖音合集批量下载 3 步跑起来:douyin-downloader 无水印保存完整指南 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser …

📰

JPG如何转PNG?推荐三个方法,无损画质还免费!

沐禾安信 在日常办公和设计中,你是不是经常遇到这种情况从网上下载的LOGO图片背景是白色的,放进海报里怎么都去不掉,这就是JIPG 格式硬伤--不支持透明背景,每次保存都会压缩画质 而PNG格式,恰恰能完美解决这两个痛点…

📰

PAS技能建模法:用问题-动作-结果重构个人能力操作系统

1. 项目概述:当“skills”不再只是简历上的关键词,而成为可验证、可组合、可进化的个人能力操作系统最近在多个技术社区、职业发展论坛和高校创新工坊里,“skills”这个词高频出现,但它的语义正在发生本质迁移——它早已不是简历末…

📰

VLAN间通信技术:单臂路由与三层交换对比

1. VLAN间通信的技术演进背景在企业网络架构中,VLAN(虚拟局域网)技术通过逻辑隔离广播域,有效提升了网络的安全性和管理效率。但随之而来的问题是:不同VLAN之间如何实现可控的通信?这个需求催生了两种主流解…

📰

Seedance 2.5拟真人像提示词四层架构解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬