XunbuOS Podcast:Shopify 工程深度技术周报
今天的 Shopify 工程博客为我们带来了十篇深度技术文章,覆盖了从 LLM Agent 优化、移动端测试稳定性到 Checkout 扩展迁移、巨型商品聚类等多个前沿领域。其中,关于 Gisting 上下文压缩 和 Sidekick 持续学习循环 的两篇文章揭示了 Shopify 在 AI 基础设施上的核心技术积累,而 移动端 E2E 测试 98% 稳定性 的重建方案则对任何移动团队都具有极高的参考价值。
Gisting:压缩 LLM Agent 上下文,吞吐量提升 16%
将 6000 token 系统提示词压缩至 1500
Gisting 将上下文压缩为一组学习得到的 token,在保持质量的同时让模型运行更快、成本更低。Gist token 是在训练时通过知识蒸馏学习嵌入的特殊 token,推理时直接替换系统提示词即可。Shopify 将 Sidekick GraphQL agent 的系统提示词从约 6,000 个 token 压缩至约 1,500 个 gist token(4:1 压缩比),预测质量无损。
关键优化
- 初始化:将 gist 嵌入初始化为提示词分块的均值(而非随机噪声),初始损失降低了 7 倍
- 压缩比:4:1 是质量开始下降的临界点
- 批处理平均损失:按整个批次取平均而非每个响应 token 取平均,保留了长响应中的信号
- 训练速度:预计算教师 logits 和数据预分词,将训练时间从三十小时缩短到六小时
性能收益
在 350 RPM 负载下,TTFT 下降 19%(438ms → 354ms),端到端延迟下降约 38%(6.8s → 4.2s),吞吐量提升 16%(20.2 QPS → 23.4 QPS)。实际生产中减少了 14% 的 GPU 用量。Gisting 与前缀缓存优化效果可以叠加,且能很好地融入持续学习循环。
移动端 E2E 测试稳定性从 50% 提升至 98%
旧框架的根因
旧测试基于 Appium 和 WebdriverIO,使用 React Native Test ID 定位元素。开发者常硬编码 pause(1000) 等待界面渲染,导致测试频繁闪崩,最终不得不从 PR 检查中移除。更糟的是,测试验证的是组件树中的节点存在,而非用户体验。
两层核心设计
Builder 风格测试 API:每个步骤必须携带断言,声明操作后屏幕应处于何种状态。提供可复用的切片(如 logIntoApp),逃生舱口显式标记为 UNSAFE_ 以便 code review 识别。
计算机视觉替代 Test ID:每个测试步骤截图,通过视觉查找目标。文本识别使用 PaddleOCR,图标匹配使用 OpenCV 将 Polaris SVG 与截图进行灰度匹配。Test ID 仍可作为后备,但需显式选择 UNSAFE_testID。
关键设计原则
- 对 AI Agent 友好:API 表面小、语法可预测,AI 可根据屏幕内容直接生成正确的测试代码
- 自诊断失败原因:每次运行生成带注释的视频,展示每个步骤在查找什么、在哪里查找
- 新测试在进入阻塞套件前,需在专用管道中多次运行,失败率超过阈值则被拒绝
Sidekick 的持续学习循环:成本削减 96%
飞轮核心架构
Shopify 的 GraphQL agent 每天从生产环境的失败中学习,通过自修复管道将失败对话转化为训练信号。一组前沿推理模型批评每个失败,仲裁者合并批评为单一修复指令注入用户回合之前,重放对话并由评判者打分,通过的轨迹成为强化学习的训练数据。
质量定义与评判者校准
质量始于评分准则(rubric),划分完整性、执行、响应质量和安全性等维度。地面真值应包含随机采样流量而非仅精选示例。使用 Cohen's kappa 衡量标注者间一致性,低分说明准则含糊不清。团队推崇使用 DSPy 和基于反思的优化器(GEPA、ACE)进行校准,并保持每个评判者小而专注。
实际效果
GraphQL agent 每秒服务高达 2,000 个请求。微调模型成本接近 100 万美元,相比前沿模型服务每年约 2700 万美元的成本,服务成本降低了 96%。Gisting 将系统提示词从约 6,000 tokens 压缩到约 1,500 个 gist tokens,TTFT 下降约 19%,端到端延迟下降约 38%,GPU 用量减少约 14%。
构建超越模型生命周期的 Agentic 框架
扫描工作流与成果
Shopify 应用安全团队构建了名为 Dispatch 的框架编排器,组织漏洞狩猎智能体进行安全扫描。在约六周内完成了超过 80 个应用的完整扫描,产生 300 多个发现,保守估值相当于超过 40 万美元的漏洞悬赏奖金,其中两个发现被评为"严重"(Critical)。
关键流程阶段
- 测试引导:找到正确的测试命令并验证测试套件可运行
- 架构文档化:生成数据模型、API、授权模式的共享描述
- 分区:将文件按领域分组,控制 token 数在上下文窗口的 20-30%
- 漏洞狩猎:每个分区并行运行狩猎智能体
- 验证:用不同模型顺序执行验证器,编写并执行测试
- 修复:自动创建分支并编写 PR
测试预言机原则
对于 IDOR 验证器,要求从公开调用站点反向推导、创建属于两个不同租户的测试夹具、不能仅依赖 Model 层单元测试、测试必须提取有影响力的跨租户数据、不得 stub 重要的上下游控制。无法证实的发现将被拒绝或降级。
成本控制
完整应用扫描成本在 50 至 300 美元之间(取决于模型选择和应用规模),增量 diff 扫描仅需约 5 至 50 美元。分区策略相比非分区方法提升了准确率和召回率,同时保持相对较低的成本。
Checkout Blocks 迁移到 Polaris Web Components,包体积缩减 85%
性能结果
所有扩展的传输包体积降幅在 40% 到 85% 之间。payment-icons 降低 84.4%,static-content 降低 54.1%,custom-field 降低 44.6%。按结账量加权的扩展加载时间(ELT),P50 提升 8%,P90 提升 7%。
架构变化
从 React 切换到 Preact,使用 remote-dom 将真实 DOM 节点从沙箱镜像到宿主环境。s-* 组件替换 Polaris React 组件树,代码库顺带改为 TypeScript。迁移采用"增量、最小优先"策略,在旧 core 旁新建 core-next 库,逐个扩展升级后合并。
64KB 包体积限制挑战
主要减重来源:切换到 Preact 后免除 react-reconciler(约 89KB);自研精简版 Liquid 解析器 "droplet" 替代 liquidjs(约 73KB,gzip 后仅 13KB);小型内部日期工具替代 dayjs(约 12KB)。markdown-to-jsx 因风险太高未替换,通过 pnpm workspace catalog 将 React 别名指向 Preact。
迁移中修复的 Bug
- ID 碰撞导致生产回滚:Preact 的
useId()生成确定性 ID,同一扩展的两个实例生成相同 ID,导致第二个字段的链接错误打开第一个字段的弹窗。修复方案是编写useStableIdhook 生成随机且实例唯一的 ID - textAlignment 属性移除:在 ui-extensions 中重新开放了 Paragraph 的
textAlignment属性 - s-checkbox label 只接受纯字符串:重构组件,新增接受字符串或经过消毒的 HTMLElement 的 label slot
Hack Days 39:为产品页面构建音乐播放器
平台级 Audio 媒体类型
团队在五个 PR 中构建了真正的 Audio 媒体类型:新增 audios 数据库表、Merchandising::Audio 模型、Audio GraphQL 类型、admin-web 拖放支持、Liquid drops(AudioDrop、AudioSourceDrop)以及自定义 audio-visualizer Web 组件。{{ product.media | media_tag }} 渲染音频播放器的方式与渲染 3D 模型完全相同。
应用与店面
Shop Sounds 用 React Router、Polaris 和 Shopify App framework 构建,发布流程是约按顺序协调的七个 GraphQL 变更。店面端曲目列表播放器通过全局音频单例解决跨页面播放问题:应用嵌入块在页面主体中注入一个单独的 audio 元素和停靠的迷你播放器。
可视化器
六个自定义 GLSL fragment shader,共享相同 uniform 接口(uBass、uMid、uTreble、uEnergy),支持七种余弦调色板配色方案。六个 shader 乘以七种调色板,为艺术家提供 42 种视觉标识。由音频中频乘以时间驱动的 midShift 参数让调色板随音乐播放缓慢演变。
架构洞察
使用 Shopify 自身的 metaobjects 和 metafields 作为数据层,无需自定义数据库。发布作品是 metaobject,曲目列表是 JSON metafield,每首曲目的音频文件、可视化器配置等是产品 variant 上的 metafields。具有 storefront: PUBLIC_READ 访问权限的 Metafields 可自动通过 Liquid 提供给主题扩展。
Catalog API:对数十亿商品进行智能体语义聚类
核心价值主张框架
产品被购买的核心价值是什么?如果属性不改变核心价值,它就是变体;如果改变,就应拆分至不同的 UPI。蛋白质粉的风味不改变核心价值,巧克力和香草是变体;油漆的颜色改变核心价值,分属不同产品。这一原则被直接编码进提示词。
两阶段 LLM 管道
先由 singleton detector 做确定性预过滤:若主题已有明确的跨产品链接结构(metafield 引用、标签分组、collection 查询),直接分配 UPI,无需 LLM 处理。需要 LLM 时,用 FAISS HNSW 构建余弦相似度图(每个商品连接 100 个最近邻),再以稀疏平均链聚类(阈值 0.25,块大小上限 200)。
阶段 1(提案):LLM 输出严格 JSON,为每个产品 ID 分配 brand 和 model 字符串。先输出 patterns 数组(观察到的命名模式),再标注产品,强制模型在上下文中推理。
阶段 2(批判):对提案簇进行异常值检测,默认保留原簇除非有明确证据表明核心价值不同。
动态结构化输出突破
通过 OpenAI Structured Output 严格强制 JSON schema,让每个输入产品 ID 都在输出中拥有必需属性。将商品 ID 重映射为规范化 ID 提高缓存命中率,清理输出中的非 ASCII 标记在评测数据上将召回率提升 8%。
教 Sidekick 学会拒绝:LLM 评审共识
问题的表现
生产训练数据只包含成功查询,零拒绝样本,导致技能模型过于"乐于助人"。面对无法实现的查询(如查找是医生的客户,Shopify 并不存储职业数据),模型生成返回零结果的查询,给商家造成"零个客户匹配"的错误印象。
解决方案:评审共识管道
将小型 Toloka 数据集(约 600 条标准查询 + 602 条拒绝注释)作为种子数据,运行四个前沿 LLM 作为自动化数据评审。四个模型各自独立评估,只有在决策和推理两方面都达成一致才接受标签变更,分歧样本被过滤而非仲裁。
分类体系严格互斥:需要更多上下文、缺少能力、错误的技能、存在歧义。这些类别必须互斥,否则评审分歧会向下游传播。
结果
启用拒绝能力后,细分技能评估分数从 0.619 提升到 0.798(相对提升 28.9%)。拒绝准确率 86.3%,误报率 4.6%。评审模型与真实标签种子数据的一致性很强:四个模型的预测准确率接近 90%,Cohen's kappa 均在 0.75 以上。
经验教训
- 小型种子数据集的价值远超其规模,评审模型会继承种子数据中的偏差
- 分类体系的互斥性不容妥协
- 在早期管道中,全体一致共识优于单一模型的置信度分数
- 拒绝是产品功能,不是失败
Ruvy:将 Ruby 编译为 WebAssembly,性能提升 20%
预初始化带来性能优势
Ruvy 构建在 ruby.wasm 之上,在构建 Wasm 模块时就完成了 Ruby 虚拟机的预初始化。使用 Wasmtime 运行时,"Hello world" 场景耗时从 56.262ms 降至 44.543ms。Cranelift 编译器将 Wasm 模块编译为原生代码的耗时减少了约 70%(1.659s → 446.31ms)。
运行时无需指定参数
Ruvy 生成的 Wasm 模块无需在运行时提供文件路径作为 WASI 参数,这使其能兼容无法为启动函数配置额外 WASI 参数的计算环境,如边缘计算服务。这对希望将 Shopify Scripts 中的 Ruby 逻辑复用到 Shopify Functions 中的 Partners 尤其有吸引力。
构建 ShopifyQL 代码编辑器:ANTLR 到 Lezer 的桥梁
核心挑战:标记偏移转换
ShopifyQL 语言服务器基于 ANTLR TypeScript 目标构建,遵循 LSP 协议。CodeMirror 使用自己的 Lezer 解析引擎,两者偏移系统不兼容:ANTLR 的偏移值是增量式的(相对于前一个标记),而 CodeMirror 中一切都是相对于文档顶部的。
解决方案:自定义 TokenIterator
TokenIterator 接收文档并计算每行长度(含换行符),追踪当前行和当前字符,将 ANTLR 风格的行、字符和标记长度描述符转换为 CodeMirror 风格的起始和结束偏移。核心逻辑简洁:moveToLine 累加行数并重置字符位置,moveToCharacter 在当前行内增量移动,getStartOffset 对前面所有行长度求和再加上当前字符位置。
适配器架构
ShopifyQL 查询传递给语言服务器,服务器返回标记流,适配器将标记转换为 Lezer 节点类型,构建描述文档的缓冲区,再从缓冲区生成 Lezer 树。前端所有 ShopifyQL 语言功能(分词、解析、补全、悬停提示、代码检查)都封装在 TypeScript 语言服务器中,由 ANTLR 语法生成,并通过 Protobuf 在 Go 和 TypeScript 之间共享类型。
播客全文
女:Hello 大家好,欢迎收听XunbuOS Podcast,我是小雅。
男:大家好,我是老冯。
女:今天咱们聊的话题有点特别,不是某个单一的功能更新,而是 Shopify 整个 AI 基础设施和工程文化层面的一些大动作。我看了几篇技术博客,感觉信息量非常大。
男:确实,这几篇放在一起看很有意思。从给 LLM 压缩上下文的 Gisting,到移动端测试框架的重建,再到那个把生产环境失败变成训练数据的持续学习飞轮,最后还有黑客松里搞出来的音乐播放器。感觉是从底层优化到上层应用全都覆盖了。
女:那咱们得好好捋一捋。先从那个 Gisting 说起吧?这个名字我第一次听,它到底解决什么问题?
男:好,咱们从这儿开始。Gisting 解决的问题很直接:系统提示词太长了。你想,Sidekick 这种 agent,它的系统提示词可能有几千个 token。每个请求都得带着这老长的提示词去推理,速度又慢,成本又高。
女:这个我懂,就像你每次进一家店,店员都得先给你背一遍店规,然后才开始服务。时间都浪费在背书上了。那 Gisting 是怎么解决的?
男:它训练了一组特殊的 token,叫做 gist token。简单说,就是把那六千多个 token 的提示词,压缩成大概一千五百个 token。压缩比是 4:1,而且质量几乎无损。具体做法是用知识蒸馏,让模型学习这组特殊 token 的嵌入,推理的时候就用它们替换掉完整提示词。
女:压缩四倍,质量还没损失?这个听着有点神啊。
男:数据不会骗人。在每分钟 350 个请求的负载下,首 token 延迟从 438 毫秒降到了 354 毫秒,端到端延迟从 6.8 秒降到了 4.2 秒。吞吐量也提升了 16%。这些数字在生产环境里就是实打实的 GPU 成本节省,他们大概减少了 14% 的 GPU 用量。
女:少用 14% 的 GPU,这省下来的钱可不少。不过我之前想过一个问题,现在很多服务都有前缀缓存,系统提示词不是可以在缓存里复用吗?为什么还需要 Gisting?
男:好问题。前缀缓存确实能避免重复计算系统提示词的 KV 缓存,但它消除不了解码成本。模型每生成一个 token,都要跟序列里的每个键做注意力计算,这个开销是省不掉的。而且随着批次变大,KV 缓存读取的成本会线性增长,这对吞吐量的影响特别大。Gisting 就是把这些成本也给压下来了。关键是他们两个还可以叠加用,不是在二选一。
女:明白了,Gisting 和前缀缓存是朋友不是敌人。那接下来聊聊第二个话题,移动端 E2E 测试。我记得以前很多人抱怨移动端测试特别容易挂,你们这是从 50% 的稳定性做到了 98%?
男:对,这是个特别有代表性的案例。原来他们用 Appium 和 WebdriverIO,用 React Native Test ID 定位元素。听起来挺灵活,但问题就是太灵活了,开发者很容易写出一堆脆弱的测试。最常见的就是点完一个元素,马上就想点下一个,根本不等待界面渲染完。然后开发者就硬塞 pause(1000) 这种等待去修,在本地碰巧能过,到了 CI 上就随机挂。
女:听着就头大。那你们是怎么修的?
男:他们做了一次很彻底的框架重建。核心是两个设计:一个是严格的 Builder 风格 API,另一个是用计算机视觉去找元素。先说 Builder API,它有个很关键的设计:每个步骤都必须携带断言。不管是点击、等待还是输入,你都得声明这一步之后屏幕应该是什么状态。如果状态不对,测试就在出错的这一步直接失败,就不会出现后面一连串的连锁崩溃了。
女:每一步都要求带断言?这个要求挺严格的,但对测试质量确实有好处。
男:对,而且他们还有一套切片机制,比如 logIntoApp 这种常见的步骤序列,可以复用。最妙的是他们还留了逃生舱口,但明确命名为 UNSAFE_ 开头,就是在代码审查的时候让你一眼就能看出来这个测试用了危险操作。
女:这个设计思路很聪明。那计算机视觉又是怎么做的?
男:他们把 Test ID 给降级了。现在测试步骤会截图,然后像人一样通过视觉去找目标。文本识别用 PaddleOCR,图标匹配用 OpenCV,把 Polaris 设计系统里的 SVG 跟截图做灰度匹配。比如你想点保存按钮,就直接写 touch({ text: 'Save' }),看着模拟器就能写测试,不用再去翻组件树了。
女:不用再为了一个测试去加 testID 了?这个开发体验好太多了。
男:那可不。而且还有个额外的好处:测试失败的时候,系统会生成一段带注释的视频,展示每个步骤在找什么、在哪找。你看一眼视频就知道哪里出了问题,不用反复重新跑测试。对开发者来说,这省下的时间可不止一点点。
女:从 50% 到 98%,这个跨越还是很有说服力的。那咱们再聊聊那个 Sidekick 的持续学习飞轮?这个题目听着就很硬核,每天把生产环境的失败变成模型权重?
男:这个文章信息量特别大,但核心思想其实很清晰:前沿模型是启动产品的最快路径,但它们不会自动从生产环境学习。用户的更正、被拒绝的输出、反复出现的失败,这些都不会自动改善下一次响应。文章里有个比喻很形象,已部署的前沿模型是冻结的,而改进只能积累在提示词编辑、检索示例这些离散的工件里。飞轮的目标,就是把生产经验压缩进模型权重的连续空间。
女:那这个飞轮具体是怎么转起来的?
男:分几个环节。首先你得定义什么是"质量",把产品需求转成一个评分准则,然后让人类专家去标注数据。注意,这里特别强调要随机采样流量,而不是精选案例,因为黄金集测试的是你已知要找的情况,但随机样本才能揭示生产中真实的好坏。
女:那标注完数据之后呢?
男:用 DSPy 校准一个评判者,把它当成离线指标。然后先用这个评判者去改进现有的框架系统,比如提示词、工具定义、编排代码。等框架改进趋于平稳了,就进入参数空间,从生产流量里挖困难负样本,让一个由前沿模型组成的自修复管道去批评失败,再合并成修复指令,重放对话,通过评判者打分。
女:听起来很复杂,但逻辑是通的。那最关键的,效果怎么样?
男:他们用 GraphQL agent 做了个案例分析。这个 agent 每秒服务高达两千个请求。用前沿模型服务这种流量,每年成本估计要 2700 万美元。他们微调出来的模型,成本接近 100 万美元,也就是说服务成本降低了 96%。再加上刚才说的 Gisting,模型还更快了。在质量上,经过持续学习,专用模型也超越了前沿模型的性能。
女:96% 的成本削减?这已经不是优化了,这是天壤之别啊。
男:所以说,如果流量规模大到一定程度,前沿模型反而不是最优解。微调一个专用小模型,加上持续学习的飞轮,才是能规模化运营的路子。而且文章里有个特别重要的观点:持续学习的复利效应就在于,每个周期都是从更有能力的模型开始,而不是仅仅更精细的框架。
女:这个思维很关键。说到这个,还有一篇关于拒绝数据的文章,我觉得跟飞轮是配套的。生产数据只包含成功案例,模型学不会什么时候该拒绝。你们用 LLM 评审共识来自动整理数据,这个思路也很特别。
男:对,这个问题特别隐蔽。你想象一下,当你问 Sidekick 一个查不到的问题,比如"找一下是医生的客户",但 Shopify 并不存储职业数据。这个时候模型不会直接拒绝,它会生成一个返回零结果的查询。商家看到的就是"零个客户匹配",而不是"这个请求根本无法完成"。这两者给用户的暗示完全不一样。
女:这确实是产品体验的巨大差异。那这个数据整理管道的核心是什么?
男:简单说,就是不再依赖手动标注去扩大数据集,而是用一组前沿 LLM 作为自动化数据评审。四个评审模型各自独立评估查询,只有当四者对裁决和推理都达成一致,标签才能通过共识门槛。分歧的样本就过滤掉,而不是仲裁。这个设计很有意思,他们在刻意牺牲覆盖率来换精确率。
女:为什么要这么做?
男:因为如果四个独立模型对同一个样本都无法达成一致,那就说明这个样本太模糊了,应该由人来做决定。他们还设计了一个四类互斥的分类体系:需要更多上下文、缺少能力、错误的技能、存在歧义。这四个类别必须完全互斥,不然评审之间会产生分歧,标签不一致,然后向下游不断放大。
女:听起来是花了很大功夫去定义这个分类体系。效果怎么样?
男:他们的细分技能评估分数从 0.619 提升到了 0.798,相对提升了 28.9%。人工验证下来,拒绝准确率 86.3%,误报率只有 4.6%。而且这个过程形成了数据飞轮:改进后的模型上线,它的生产流量成为下一轮的采样池,新模式被评审集成标注并加入语料库,下一轮就从一个更大更干净的基线开始。
女:把生产反馈变成训练数据,再变成更好的模型,这个闭环确实非常强大。
男:对,这篇文章结尾那句话挺有意思的:"我们花了大量时间教模型何时说是。当初应该从何时说不开始。"我觉得这句话对任何做 AI 产品的人来说都值得记住。
女:好了,技术和基础设施聊得够多了,咱们换换口味。最后一篇文章讲的是 Shop Sounds,一个在黑客马拉松里做出来的音乐播放器项目。这个我印象很深刻,因为黑客马拉松我参加过,那种三天出原型的感觉真的很爽。
男:没错,这个项目特别有创意。他们发现音乐人在 Shopify 上卖 T 恤、黑胶唱片卖得很好,但数字音乐产品页面通常只是一张图片和一个购买按钮,连试听功能都没有。而音乐和录音制品这个类目,一年能产生超过 10 亿美元的 GMV。
女:十亿美金的 GMV,这个市场确实不容忽视。那他们折腾出了什么?
男:三天时间,十三个人,搞出了个完整的应用,包括发布管理、曲目列表播放器、GLSL shader 可视化器,还有同步歌词。最有意思的是那个可视化器,六个不同的 shader,全部由音频数据实时驱动。低音、中音、高音、整体能量,四个参数传入,每个 shader 创造出完全不同的视觉世界。
女:六个 shader?都是什么风格的?
男:有像颜料在水中流动的流体效果,有随节拍脉动的同心多边形环,有带视差深度的多层星空,还有镜像振荡的波形,甚至还有一个无限向前奔涌的隧道。而且每个 shader 还支持七种配色方案,这样一算,六乘以七,42 种视觉风格,还不用动参数。
女:看起来视觉效果真的很叼。那他们是怎么实现跨页面播放的?音乐不能因为你从产品页跳到首页就停了吧?
男:对,这正是关键点。他们做了一个全局音频单例。通过一个应用嵌入块,在页面主体注入一个单独的 audio 元素,还有停靠的迷你播放器。每个页面上的播放器都读写这个共享的音频实例,你在产品页开始播放,导航到其他页面,音乐继续。
女:从产品角度来说,客户体验确实很重要。那数据层是怎么设计的?
男:文章里有个特别值得借鉴的点:他们没建自定义数据库,而是用 Shopify 自己的 metaobjects 和 metafields 作为数据层。一个发布物是一个 metaobject,曲目列表是上面的 JSON metafield,每首曲目的音频文件、可视化器配置、歌词,都是 product variant 上的 metafields。
女:全部用 Shopify 原语?这个思路很聪明,不用自己管基础设施了。
男:对,而且数据能跟平台的其余部分协同工作,比如集合、搜索、webhooks、Storefront API。他们甚至做了个地理定位的巡演日期模块,用 Haversine 公式计算距离,显示"附近的演唱会"。
女:三天做出来这么丰富的东西,黑客马拉松的力量真是不可小觑。
男:所以说,重点不在于项目能不能上线,而在于它证明了平台能力的边界是可以被推的。比如 Jamie 那五个 PR,证明了原生音频媒体在 Shopify 平台架构上是可行的,从数据库到店面都通。这就是黑客松的实验文化带来的价值。
女:说到平台边界,咱们之前聊的那个安全团队的智能体代码审计框架,不就是把边界往安全方向推了一大步吗?80 多个应用,300 多个发现,保守估值相当于 40 万美元的漏洞悬赏奖金。
男:对,那个框架相当扎实。他们构建了一套完整的扫描流程,由 Dispatch 编排器驱动,分阶段进行:测试引导、架构文档化、文件编目、分区、漏洞狩猎、验证、后处理、报告、修复。系统会在初次扫描时做代码分区,并行部署多个狩猎智能体。后续增量扫描只关注 diff,大幅降低 token 消耗。
女:这个分区策略挺有意思,为什么能提升准确率?
男:他们做了个基准测试,把已知漏洞重新引入本地应用做对比。发现分区后准确率和召回率都提升了,而且成本还更低。因为把 token 控制在模型上下文窗口的 20% 到 30%,给代码探索和工具使用留出了足够空间。至于成本,一次完整应用扫描大概 50 到 300 美元,增量扫描只要 5 到 50 美元。
女:这个成本控制很有参考价值。那咱们也差不多收个尾了。最后再聊聊那个 Ruvy 和 ShopifyQL Notebooks?这两个相对小众一点。
男:Ruvy 是个有意思的小工具,把 Ruby 代码编译成 WebAssembly 模块,然后可以直接跑。它构建在 ruby.wasm 之上,做了两个优化:一是在构建时就预初始化 Ruby 虚拟机,二是免除运行时指定 WASI 参数。实测下来,执行"Hello world"比原来的方案快约 20%,编译时间更是减少了 70% 左右。
女:预初始化这个优化很巧,如果把启动时间都省掉了,那在一些边缘计算环境部署就方便多了。
男:对,特别是那些不支持额外参数的平台。至于 ShopifyQL Notebooks,这篇文章其实讲的是怎么把 ShopifyQL 语言服务器接到 CodeMirror 上。这是个很工程化的故事,核心挑战是语言的离线偏移和 CodeMirror 的绝对偏移之间的转换。他们写了个 TokenIterator 类来逐个调整。
女:这个听着也有点像做翻译官的感觉,把 ANTLR 世界的语言翻译给 Lezer 听。
男:没错,而且他们发现了一个特别诡异的坑:在某些情况下,注释的行号是负数。因为 ANTLR 解析注释是最后才做的,指针要往回挪,行号就成了 -2。这类边缘情况只有真正去实现一遍才会碰到,很典型。
女:听了这么多,感觉今天的信息量确实大。从 Gisting 的 token 压缩,到移动端测试的计算机视觉革命,再到持续学习的飞轮和拒绝数据的管道,最后还有黑客松的创意爆发。老冯,你觉得这些故事背后有什么主线吗?
男:我觉得主线很清楚:Shopify 在把 AI 从"demo 能跑"推向"生产能扛"。Gisting 解决推理效率,持续学习飞轮解决模型进化,拒绝数据管道解决模型的行为边界,安全智能体框架解决漏洞发现的质量与成本。这些不是孤立的技术点,而是一个系统性的工程能力建设。真正硬核的不是单点突破,而是把这些点连成一套可持续运行的基础设施。
女:说得好。从基础设施到应用,从模型优化到数据反馈。对了老冯,你觉得这里面哪一项给独立站开发者的启发最大?
男:要我说的话,那个移动端 E2E 测试的重建故事最值得借鉴。它说得特别清楚:测试不稳定不是天生的,而是 API 设计的问题。你只要坚持每步断言、像用户一样查找元素、杜绝"脚枪",就能从 50% 的稳定性做到 98%。这个方法论是通用的,不限于移动端。另外,那个 Shop Sounds 项目用 metaobjects 当数据层的思路,对做应用的开发者来说,也是一个非常实用的指南。
女:行,那我们这一期就差不多到这里。如果大家想了解更多细节,可以在 Shopify Engineering 博客上找到这些文章的原文。有想聊的话题也可以在评论区告诉我们。
男:对,我们下期再见。别忘了订阅 XunbuOS Podcast,这样就不会错过每一期精彩内容了。
女:拜拜!
男:拜拜!
参考链接
- Gisting: Compressing LLM Agent context to ↑ throughput and ↓ cost
- How we raised mobile end-to-end test stability to 98%
- Sidekick's continual learning loop
- Building an agentic harness that outlasts the model
- Upgrading Checkout Blocks app to Polaris web components
- Inside Shopify Hack Days: Building a prototype for music-playing pages
- Clustering billions of products for agentic commerce with Catalog API
- Teaching Sidekick to say no: automated data curation with LLM judge consensus
- Introducing Ruvy
- Building a ShopifyQL Code Editor
迅步科技专注Shopify独立站,Shopify应用及无头网站开发,为您的品牌提供快速、灵活、个性化的电商体验,提升用户转化率与运营效率。欢迎咨询合作。
