1. 先摸清 Bruno 的脚本执行模型别再靠猜很多人第一次在 Bruno 里写脚本习惯性地把 Pre Request 和 Post Response 当成两个互不相干的小编辑器上面算个签名下面写个断言中间靠 copy-paste 撑着。等到接口一多签名逻辑复制了七份时间戳格式改了就要挨个改这时候才开始怀念那种一份代码到处用的日子。这篇内容就是解决这个问题的——Bruno 脚本执行怎么分层、跨阶段的代码共享怎么落地、共享层怎么维护才不至于越写越乱。先给没接触过的朋友交代一句Bruno 是一款离线优先的 API 调试工具它的集合直接以文件夹和.bru文本文件的形式存在本地所以天然适合进版本库。它在请求前后各挂了一个脚本入口用 JavaScript 写能读请求、改请求、读响应、做断言还能在多个请求之间传递变量。这套机制用好了一个集合可以当成一个轻量级的自动化测试工程来使用不好就是一堆复制粘贴的碎片。适合谁看已经在用 Bruno 调接口、写断言但脚本开始互相抄的想把接口调试链做成可回归的以及单纯想知道pre 里算的东西为什么 post 里拿不到的人。我先把结论亮出来Bruno 的跨阶段代码共享本质是两件事——数据共享和逻辑共享。数据靠运行时变量池传递逻辑靠集合级脚本和本地模块复用。绝大多数人卡住的是把这两件事混成一件试图直接把一个函数对象从 pre 递到 post然后发现对面是undefined。1.1 脚本到底有几个落点谁先谁后Bruno 的脚本不是只有一个地方能写。按作用范围从小到大排大致有这么几层集合级脚本挂在 Collection 上整个集合里每个请求执行时都会先跑它的 Pre Request 部分请求结束后跑 Post Response 部分。这是放公共逻辑最合适的位置。文件夹级脚本挂在 Collection 内部的某个文件夹上只对文件夹里的请求生效。适合按业务域切分比如订单域用一套鉴权初始化。请求级脚本单个请求自己的 Pre Request 和 Post Response最常用的落点。Tests它和 Post Response 属于同一阶段只是 Bruno 在界面上给出了一块更聚焦断言的编辑区执行时机上没有本质差异。执行顺序按嵌套由外向内集合级 Pre → 文件夹级 Pre → 请求级 Pre → 真正把请求发出去 → 请求级 Post含 Tests→ 文件夹级 Post → 集合级 Post。这个顺序的意义在于越靠外的层越适合做环境准备和全局收尾越靠内的层越适合做这个请求特有的活儿。注意文件夹级脚本在较新版本中支持得比较完整老版本可能只有集合级和请求级两层。你手上是什么版本先在集合设置里点一遍确认别照着别人的目录结构硬套然后怀疑自己配置错了。理解这个顺序有个很实际的用处你可以在集合级 Pre 里统一注入追踪头、统一生成请求 ID在请求级 Pre 里只做业务参数签名职责清清楚楚。而不是每个请求开头都复制三行req.setHeader(X-Trace, ...)。1.2 三个变量作用域用错就是值明明写了却读不到这是跨阶段共享的第一道坎我见过太多人在这里绕圈。Bruno 里常打交道的作用域有这么几个作用域写法示例生命周期是否落盘典型用途环境变量bru.getEnvVar/bru.setEnvVar跟随所选环境会写回环境配置baseURL、密钥占位、开关集合变量bru.getCollectionVar/bru.setCollectionVar跟随集合会写回集合文件跨请求复用的 token、缓存 ID运行时变量bru.setVar/bru.getVar本次会话或本次 runner 运行不落盘单条链路的中间结果、签名原文关键点在最后一列。运行时变量才是跨阶段共享最顺手的那把钥匙它在 Pre Request 里写、在 Post Response 里读同一个请求的两个阶段共享同一个池子不污染环境配置也不会跑完一次之后把敏感值留在文件里。反过来用环境变量传中间值就是自找麻烦。第一它会落盘签名原文、临时 token 全进了配置文件一不小心提交到仓库第二它在 runner 批量执行时会跨 iteration 残留上一轮的脏数据影响下一轮第三改环境变量的心智负担比想象中大尤其是多人协作时别人拉下来发现环境里多了一堆看不懂的键。所以我的建议很明确配置类的东西进环境变量跨请求的业务状态进集合变量单次请求内部的中间结果进运行时变量。三条线不要互相串。还有一个容易忽略的点这些变量池里存的都是可序列化的数据。字符串、数字、布尔、普通对象、数组都没问题。但你要是想把一个函数塞进去指望对面读出来直接调用那是不行的——跨脚本边界传递函数对象取决于运行时的隔离策略不同版本表现不一样。稳妥的做法是函数定义在它该在的地方变量池里只放函数算出来的结果。2. 三步搭起跨阶段共享的骨架把模型理清楚之后落地就三步收敛公共逻辑、打通阶段握手、让结果回流。这三步不是并列的三个技巧而是有先后依赖的——先把公共逻辑收敛了握手才有东西可握握手通了回流才有意义。2.1 第一步把公共函数收敛到集合级而不是散落在每个请求里最常见的乱象是这样的一个集合里八个请求要签名每个请求的 Pre Request 里都躺着一段一模一样的 HMAC 计算二十行。某天密钥的拼接规则从secret payload改成payload secret你得改八处改漏一处就等着线上联调时对不上。收敛的做法有两条路看你的团队习惯选。路线 A集合级脚本里集中定义请求级按需调用。把公共函数写在 Collection 的 Pre Request 脚本里请求级脚本里直接调。这条路的好处是零配置、纯 Bruno 内部解决局限是脚本隔离策略不同版本有差异函数能不能被内层直接看见需要你自己验证一遍——写个console.log(typeof myFn)打出来看比查文档快。路线 B把公共逻辑抽成本地模块脚本里require。Bruno 的集合本身就是一个文件夹你完全可以在集合根目录放一个scripts/目录里面写纯 JavaScript 模块脚本里用相对路径引进来。较新版本对require内置模块和本地文件的支持比较友好配合集合级的依赖声明还能用第三方包。// scripts/sign.js —— 与 .bru 文件放在同一集合目录下 const crypto require(crypto); function hmacSha256(secret, payload) { return crypto.createHmac(sha256, secret).update(payload, utf8).digest(hex); } function stamp() { return String(Date.now()); } function nonce(len 8) { return Math.random().toString(36).slice(2, 2 len); } module.exports { hmacSha256, stamp, nonce };// 请求级 Pre Request Script const { hmacSha256, stamp } require(./scripts/sign.js); const ts stamp(); const payload JSON.stringify({ sku: A-001, qty: 2, ts }); req.setHeader(X-Ts, ts); req.setHeader(X-Sign, hmacSha256(bru.getEnvVar(APP_SECRET), payload)); req.setBody(payload); // 关键把实际发出去的那份字符串存起来Post 阶段要回放 bru.setVar(sent_payload, payload);我倾向路线 B理由很实在模块是纯 JS 文件可以被单元测试覆盖可以在编辑器里跳转、可以被 lint改一处全集合生效。而挂在集合脚本里的函数你只能在 Bruno 的界面里读出了编辑器就没有工具链支持。实操心得scripts/目录一定要跟.bru文件一起进版本库但密钥不要进。签名函数里从环境变量取值环境文件本身加进.gitignore只提交一份带占位符的示例环境。这条规矩能省掉很多我这跑得好好的你那报 401的对话。2.2 第二步用运行时变量做阶段间的握手握手这件事说穿了就是一句话Pre 阶段把 Post 阶段需要的信息写进变量池Post 阶段按约定读出来。但这里有个高频误解必须先破除很多人在 Pre 里算完签名只做了bru.setVar(sign, sign)然后就以为请求带上了签名结果接口返回 401回头查了半天签名算法其实问题在于他只存了变量没有改请求。变量池是给脚本之间传递用的请求发出去用的是什么取决于你对req对象做了什么。所以 Pre 阶段的完整动作永远是两步改请求 留凭证。// 请求级 Pre Request Script —— 完整形态 const { hmacSha256, stamp } require(./scripts/sign.js); const secret bru.getEnvVar(APP_SECRET); // 1. 只序列化一次后面所有地方都用这同一个字符串 const payload JSON.stringify({ sku: bru.getVar(sku) || A-001, qty: Number(bru.getVar(qty) || 2), ts: stamp() }); // 2. 改请求头、体、URL 参数看接口要求 req.setHeader(Content-Type, application/json); req.setHeader(X-Sign, hmacSha256(secret, payload)); req.setBody(payload); // 3. 留凭证Post 阶段和后续请求要用的都放运行时变量池 bru.setVar(sent_payload, payload); bru.setVar(sent_at, Date.now());这里有个细节值得单独拎出来讲payload 只序列化一次。如果你在 Pre 里JSON.stringify一遍算签名又让 Bruno 在发送时把对象再序列化一遍两次生成的字符串很可能不一样——对象键的顺序、数字格式化、空格处理任何一点差异都会让服务端重算出来的签名和你的对不上。这种 bug 特别难受因为你盯着代码看算法完全正确就是过不了。解决办法就一句算出字符串存字符串发字符串。2.3 第三步让响应结果回流到断言和后续请求Post 阶段的价值不只是断言通过与否它还是整条链路的数据回流口把服务端返回的 token、订单号、状态机流转结果拿出来喂给下一个请求。// 请求级 Post Response Script // 1. 先断言失败早暴露 expect(res.getStatus()).to.equal(200); // 2. 安全地解析 body不同版本拿到的可能是字符串也可能是对象 const raw res.getBody(); const data typeof raw string ? JSON.parse(raw) : raw; // 3. 回放 Pre 阶段留下的凭证做请求-响应一致性校验 const sent bru.getVar(sent_payload); expect(sent).to.be.a(string); test(响应回显的载荷与发出的载荷一致, function () { expect(data.echo).to.equal(sent); }); // 4. 写回业务状态供后续请求使用 if (data.token) { bru.setVar(token, data.token); // 本次链路用 bru.setCollectionVar(last_token, data.token); // 跨会话留痕 } // 5. 记录耗时做粗粒度性能观察 const cost Date.now() - Number(bru.getVar(sent_at) || 0); console.log(round-trip(ms):, cost);注意第 4 步里我同时写了两处。这不是冗余是有意为之运行时变量给这一轮执行用集合变量给下次打开 Bruno 还能看到上次结果用。用途不同别混。再补一个容易忽略的细节res.getBody()的返回类型。有些版本直接给你解析好的对象有些给你原始字符串接口返回非 JSON 时还可能是一段 HTML。所以那段typeof raw string的判断不是防御性编程的洁癖是真的会踩。我遇到过一次网关报错返回 HTML 错误页JSON.parse直接抛异常把整个 Post 脚本带崩连带后面的断言一条都没跑排查时还以为是断言写错了。3. 完整实操一条带签名的下单链路怎么串起来光讲机制有点飘我拿一个虚构的电商下单链路走一遍。三个请求登录拿 token、查库存、提交订单需要签名。目标是整套流程能在 Runner 里一键跑完变量自动流转断言覆盖关键节点。3.1 目录结构与脚本落点先看集合长什么样order-flow/ ├── scripts/ │ └── sign.js # 公共签名与工具函数 ├── auth/ │ └── login.bru # 登录无签名 ├── stock/ │ └── query.bru # 查库存要 token ├── order/ │ └── create.bru # 下单要 token 签名 └── collection.bru # 集合配置与环境引用分层原则很直白scripts/放纯函数不碰bru对象.bru里的脚本负责跟 Bruno 交互集合级脚本只做全局准备比如生成一次性的 trace ID不塞业务逻辑。这么切的好处是纯函数可以脱离 Bruno 单独测——你写个 node 脚本跑一遍hmacSha256(k,v)对比预期值五分钟就能确认签名实现有没有问题不用每次都真发请求。3.2 登录最基础的变量回流login.bru的请求级 Post Responseexpect(res.getStatus()).to.equal(200); const body typeof res.getBody() string ? JSON.parse(res.getBody()) : res.getBody(); test(返回体包含 token, function () { expect(body.token).to.be.a(string); expect(body.token.length).to.be.greaterThan(8); }); bru.setVar(token, body.token); bru.setVar(uid, body.uid);这段没什么花活但它是整条链路的起点。token进了运行时变量池后面两个请求的 Pre 阶段直接bru.getVar(token)就能用。3.3 查库存把 token 落到请求头上// query.pre-request const token bru.getVar(token); if (!token) { throw new Error(token 缺失请先执行 login 请求); } req.setHeader(Authorization, Bearer token); req.setHeader(X-Trace, bru.getVar(trace_id) || na);这里我故意写了个throw。原因是缺 token 的时候如果不抛错请求照样发出去服务端返回 401然后你在 Post 里看到断言失败一路往回推最后才发现是执行顺序不对或者上一个请求失败了。在 Pre 阶段提前抛错报错信息直接告诉你缺什么定位成本从十分钟降到十秒。注意Pre Request 脚本抛异常会导致该请求根本不发送。这是特性不是 bug正好用来做前置条件校验。但也意味着你不能在 Pre 里写那种出错了就吞掉继续的 try-catch否则错误会被掩盖到更远的地方。3.4 下单签名、发送、回放三件套create.bru的 Preconst { hmacSha256 } require(./scripts/sign.js); const secret bru.getEnvVar(APP_SECRET); const token bru.getVar(token); if (!secret) throw new Error(APP_SECRET 未配置); if (!token) throw new Error(token 缺失); // 参数集中在一处方便对照接口文档 const params { sku: bru.getVar(sku) || A-001, qty: Number(bru.getVar(qty) || 1), uid: bru.getVar(uid), ts: String(Date.now()) }; const payload JSON.stringify(params); const sign hmacSha256(secret, payload); req.setHeader(Authorization, Bearer token); req.setHeader(Content-Type, application/json); req.setHeader(X-Sign, sign); req.setBody(payload); // 三个凭证Post 阶段逐一回放 bru.setVar(sent_payload, payload); bru.setVar(sent_sign, sign); bru.setVar(sent_ts, params.ts);对应 Postexpect(res.getStatus()).to.equal(201); const raw res.getBody(); const data typeof raw string ? JSON.parse(raw) : raw; test(订单创建成功并返回单号, function () { expect(data.orderId).to.be.a(string); expect(data.status).to.equal(CREATED); }); test(服务端回显的签名与本地一致, function () { expect(data.signEcho).to.equal(bru.getVar(sent_sign)); }); test(时间戳落在合理窗口内, function () { const drift Math.abs(Date.now() - Number(bru.getVar(sent_ts))); expect(drift).to.be.lessThan(5000); }); bru.setVar(order_id, data.orderId); bru.setCollectionVar(last_order_id, data.orderId);这里第三条断言看似随口一提其实是签名类接口最值得加的一条。签名对不上原因可能是密钥错、可能是拼接顺序错、也可能单纯是本地时钟偏了几分钟服务端时间窗校验直接拒掉。把 drift 打出来一眼就能区分是算法问题还是时钟问题。我吃过这个亏当时排查了快两个小时最后发现是虚拟机休眠后系统时间没同步。3.5 用 Runner 串起来跑顺便管好变量残留单条跑通只是及格线能批量跑才是这条链路的价值。Runner 里选中整个集合设定迭代次数可以挂一份数据文件驱动参数。几个要点执行顺序靠文件夹排序控制登录必须在最前面。别指望 Runner 自己推断依赖。数据文件按行驱动每行一组sku、qty脚本里用bru.getVar(sku)读取不到就退回默认值这样单跑和批量跑都不会崩。跨 iteration 的变量残留要主动清理。运行时变量在整次 runner 运行期间是活的第一轮算出来的order_id如果不清第二轮万一接口失败没写新值你读到的就是上一轮的旧数据断言还全绿——这种假绿是最危险的。在集合级 Post 脚本里收尾清一次或者每轮开头覆写一遍。// 集合级 Post Response —— 每轮结束收尾 bru.setVar(sent_payload, ); bru.setVar(sent_sign, ); bru.setVar(sent_ts, );清空比删除省事也避免了删了之后读取报 undefined的连锁问题。4. 踩坑实录这些错我基本都犯过脚本类工具的通病是错误信息不友好一个undefined背后可能藏着七八种原因。下面这几类是我在实际项目里反复遇到的配了定位路径。4.1 典型症状与定位路径症状一Post 阶段读 Pre 阶段的变量拿到 undefined。先别怀疑变量池按这个顺序查变量名有没有拼错包括大小写Pre 阶段是不是在setVar之前就抛错中断了读的到底是bru.getVar还是bru.getEnvVar两个池子不互通最后一种情况最常见——Pre 里用setVar写Post 里用getEnvVar读写读不在同一层永远读不到。症状二签名接口稳定返回 401本地算法看起来没错。按三同一排查同一份字符串、同一个密钥、同一个时间窗。字符串确认服务端签的是不是你真正发出去的那一份最直接的办法是把sent_payload打到日志里跟抓包看到的 body 逐字符比。密钥确认环境变量取到的是真值不是占位符console.log(secret.length)打长度最安全别直接把密钥打出来。时间窗看 drift 断言超了就是时钟问题。症状三脚本报Cannot read properties of undefined。九成是res.getBody()在请求失败时返回空JSON.parse(undefined)直接炸。养成习惯解析前先判类型解析用 try-catch 包一层catch 里把原始 body 打出来。网关返回 HTML 错误页的场景太常见了。症状四请求压根没发出去界面没反应。大概率是 Pre 脚本抛异常了。Bruno 的 Pre 阶段异常会阻断发送控制台里翻一下错误堆栈。这类问题有个特点是看起来像网络问题其实跟网络一点关系没有。症状五Runner 批量跑第一轮通过后面全挂。变量残留。要么是 token 过期后没重新登录要么是上一轮的脏数据覆盖了本轮的初始值。在每轮开头显式重置关键变量比事后猜哪一轮开始不对要省事得多。4.2 常见问题速查表现象高概率原因一分钟验证方式变量读不到写读用了不同作用域两边都换成setVar/getVar再试签名 401发送体与签名字符串不一致打印sent_payload与抓包 body 比对签名 401时序本地时钟漂移加 drift 断言看是否超时间窗请求未发送Pre 脚本抛异常看控制台堆栈逐行注释排查JSON 解析报错响应非 JSON 或为空先判typeof打印原始 body断言假绿变量上一轮残留每轮开头覆写关键变量函数未定义路径写错/模块未导出console.log(typeof fn)打一下依赖装不上集合级依赖未声明检查集合目录下的依赖配置还有两个不太起眼但会咬人的点顺手记一下。第一个是请求体的编码。req.setBody传字符串和传对象Bruno 的处理路径不完全一样。传对象时它会按 Content-Type 自动序列化传字符串时原样发。签名场景必须走字符串否则你签的和发的对不上。这个差异不会报错只会让你在 401 里反复怀疑人生。第二个是环境切换后的变量漂移。从测试环境切到预发环境环境变量跟着换了但集合变量和运行时变量不会自动重置。如果里面有环境相关的 ID比如某个环境特有的租户标识切换后请求就会串环境。我的做法是把所有环境相关的值全放环境变量集合变量只存跟环境无关的业务状态从源头上消灭这类问题。5. 让共享层能活过三个月的几个习惯脚本写出来不难难的是三个月后别人接手还能看懂或者你自己回头改还能五分钟定位。这几条是我踩完之后固化下来的习惯不一定每条都适合你但方向值得参考。给变量加前缀一眼看出归属。sent_开头的是 Pre 写给 Post 的凭证auth_开头的是鉴权相关biz_开头的是业务状态。变量池是个扁平的大筐没有命名约定就是一团乱麻。命名成本几乎为零收益是排查时的确定性。公共模块只做纯计算不碰bru对象。scripts/sign.js里只有入参和出参没有任何全局依赖。这样它可以被 node 直接跑、被单测覆盖、被别的集合复制走。一旦模块里出现bru.getEnvVar它就锁死在 Bruno 里了复用性归零。Pre 阶段的前置校验要抛错不要静默降级。缺 token 就抛缺密钥就抛参数类型不对就抛。降级处理会让错误漂移到离现场很远的地方排查成本成倍上升。用集合自身的版本管理能力。Bruno 的集合是纯文本目录git diff能看清每一次脚本改动。这比那些把集合存在云端数据库里的工具强太多——出问题能 bisect能回滚能在 PR 里 review 脚本逻辑。我现在的习惯是脚本改动和接口改动分开提交出问题时一眼能分清是逻辑变了还是参数变了。升级前跑一遍回归。脚本 API 在不同版本之间有过调整尤其是变量作用域、require支持范围、响应体返回类型这几块。升级前把整个集合 Runner 跑一遍比升级后在生产联调时发现问题要好得多。最后分享一个我个人用得很顺的小技巧给集合加一个自检请求它的唯一作用就是把当前环境的配置、关键变量是否存在、签名函数算出来的固定样例值全部打出来。联调前先跑它三十秒内就能确认环境对不对、密钥有没有配、函数有没有加载成功。这比一条条试错高效得多尤其是刚接手别人集合的时候跑一遍自检就知道这套东西能不能用。
企业数字化 ERP 产品动态
相关推荐
智慧医联体信息化架构解析与实施指南 1. 智慧医联体信息化解决方案概述"智慧医联体信息化解决方案"是当前医疗行业数字化转型的核心架构,其核心理念是通过"一个平台、十大中心、一项改造、两大体系"的框架设计,实现区域内医疗资源的整合与协同。这套方案正在全国范围内快… · 2026/11/4 22:50:29
Table嵌套Switch状态渲染问题解决方案 1. Table嵌套Switch状态变更的渲染问题解析前端开发中经常会遇到在表格内嵌交互组件的场景,其中Table嵌套Switch开关的状态变更渲染问题尤为典型。这个问题通常表现为:当表格数据中的状态字段发生改变时,Switch组件无法实时响应数据变化并更新… · 2026/11/2 8:36:45
EasyU优启通PE维护盘实战指南:制作、启动与核心操作全解析 1. 为什么一个PE维护盘值得单独拿出来聊装系统、修引导、救数据、清病毒,这些活儿但凡干过几次的人都知道,真正决定效率的往往不是你手速多快,而是手边那套PE环境靠不靠谱。EasyU(圈内习惯叫优启通)就是在这个场景下被… · 2026/11/5 16:49:35
Rust原来很简单二:所有权系统 Part 2 核心篇 —— 所有权系统
本篇覆盖 第 6 ~ 10 章:所有权、借用与引用、生命周期、字符串与切片、内存模型。 ⚠️ 这是全书最重要的一篇。 把它吃透,Rust 从此“很简单”;跳过它,你会处处碰壁。请慢下来,动手敲每一段代码。 第 6 章 所有权:Rust 的第一性原理
6… · 2026/11/5 16:26:08
基于Hadoop+Spark的民宿智能推荐系统设计与实现 1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享… · 2026/11/5 14:59:25
ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配RS485,解决远距离温度监控难题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07
多智能体协作系统架构设计与实践优化 1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39
大模型系统性入门实战指南:从原理到部署微调的全链路路线 提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39
鸿蒙文件系统沙箱机制解析与开发实践 1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07