2026年1月

注意:需要绑卡验证。

  1. 注册创建项目
    可使用 github、谷歌账号、邮箱等方式进行注册,注册地址

注册登录后,进入下面的页面


  1. 添加服务






  2. 查看部署情况和节点




关于部署的镜像:
镜像是 fork 佬王的项目 nodejs-argo 后,自己创建固定隧道,添加自己的固定隧道后,通过 github actions 构建的镜像。佬王项目地址
固定隧道:服务重启后节点不会变
临时隧道:服务重启节点会变,需重新导入


📌 转载信息
原作者:
cainiaoxue
转载时间:
2026/1/19 18:16:45

近期米家 APP 自动化(离家 / 到家)失效问题,发现最新版本新增了如图设备选项,原有自动化没有勾选设备,所以不会触发家居设备状态变化。

需要删掉自动化的触发条件,重新添加 个人状态变化 - 离家 / 回家 - 单设备判断 - 勾选手机 / 设备 - 圈定范围 - 确定后保存自动化。


📌 转载信息
原作者:
BeeThor
转载时间:
2026/1/19 18:16:40

在电商工具开发圈摸爬滚打这些年,淘宝图片搜索接口(识图接口)是我见过“最挑剔”的接口没有之一。它不像商品详情、关键字接口那样逻辑直白,反而对图片格式、编码方式、参数传递有着近乎苛刻的要求,还藏着不少“权限隐藏坑”——明明签名正确却返回403,相似商品混着无关内容,大促期间突然限额缩水,每一次踩坑都得熬夜返工。

6年来,我从第一次对接时的“传图就报错”,到现在能稳定支撑日均3万次识图调用,踩过的坑涵盖了图片处理、参数解析、权限校验全流程。今天就把这些血的教训、可直接复制复用的代码全抖出来,给做同款识别、货源查找、竞品图搜的朋友避避雷,少走我当年的弯路。

实例返回 测试url:[免费测试]

"items": {
    "pagecount": 3,
    "total_results": 60,
    "real_total_results": 60,
    "item": [
      {
        "title": "李宁篮球袜男款专业实战短筒运动袜子女夏跑步中长精英美式毛巾底",
        "pic_url": "https://img.alicdn.com/imgextra/O1CN01i8iOpV1rZSiOwyUYE_!!2068455645.jpg",
        "promotion_price": "25.00",
        "price": "25.00",
        "num_iid": "785697155584",
        "is_tmall": "false",
        "area": "淄博",
        "detail_url": "//item.taobao.com/item.htm?id=785697155584"
      },
      {
        "title": "likeid篮球袜美式中长筒袜男夏季专业实战精英袜训练防滑运动袜子",
        "pic_url": "https://img.alicdn.com/imgextra/O1CN01tzf4lC1RMTNN4FCVw_!!623082097.jpg",
        "promotion_price": "19.90",
        "price": "19.90",
        "num_iid": "706255675043",
        "is_tmall": "false",
        "area": "佛山",
        "detail_url": "//item.taobao.com/item.htm?id=706255675043"
      },
      {
        "title": "黑人月精英专业篮球袜加厚毛巾底中筒高筒运动袜子男",
        "pic_url": "https://img.alicdn.com/imgextra/TB2luFSbTIlyKJjSZFMXXXvVXXa_!!546743164.jpg",
        "promotion_price": "12.90",
        "price": "12.90",
        "num_iid": "543839578944",
        "is_tmall": "false",
        "area": "深圳",
        "detail_url": "//item.taobao.com/item.htm?id=543839578944"
      },
      {
        "title": "李宁运动袜子男短筒专业跑步篮球羽毛球女款棉透气防臭毛巾底秋冬",
        "pic_url": "https://img.alicdn.com/imgextra/O1CN01i8iOpV1rZSiOwyUYE_!!2068455645.jpg",
        "promotion_price": "29.00",
        "price": "29.00",
        "num_iid": "751100232040",
        "is_tmall": "false",
        "area": "淄博",
        "detail_url": "//item.taobao.com/item.htm?id=751100232040"
      },
      {
        "title": "实战篮球袜精英款训练斯坦斯stance篮球袜子高筒559中筒359毛巾底",
        "pic_url": "https://img.alicdn.com/imgextra/O1CN01GJGJlc1fwZNLhfdEe_!!2215130674071.jpg",
        "promotion_price": "43.90",
        "price": "43.90",
        "num_iid": "835954755321",
        "is_tmall": "false",
        "area": "石家庄",
        "detail_url": "//item.taobao.com/item.htm?id=835954755321"
      },
      {
        "title": "袜子男防臭防脚气夏季薄款运动抑菌中筒袜吸汗毛巾底篮球袜四季fm",
        "pic_url": "https://img.alicdn.com/imgextra/O1CN01Y89HYb2KdErlFuBmQ_!!4059759579.jpg",
        "promotion_price": "20.71",
        "price": "20.71",
        "num_iid": "787279891547",
        "is_tmall": "false",
        "area": "金华",
        "detail_url": "//item.taobao.com/item.htm?id=787279891547"
      },
      {
        "title": "斗牛篮球精英袜男款高帮加厚毛巾底专业实战长筒防滑透气运动袜子",
        "pic_url": "https://img.alicdn.com/imgextra/O1CN011VQ3pa1nRthROBUCP_!!2212944505087.jpg",
        "promotion_price": "19.80",
        "price": "19.80",
        "num_iid": "751809323321",
        "is_tmall": "true",
        "area": "金华",
        "detail_url": "//item.taobao.com/item.htm?id=751809323321"
      },
      {
        "title": "包邮皇马主客场足球袜灰色白色紫色球袜毛巾底长筒过膝足球袜",
        "pic_url": "https://img.alicdn.com/imgextra/TB2kc0RbwJlpuFjSspjXXcT.pXa_!!85536437.jpg",
        "promotion_price": "16.00",
        "price": "16.00",
        "num_iid": "528042874974",
        "is_tmall": "false",
        "area": "郑州",
        "detail_url": "//item.taobao.com/item.htm?id=528042874974"
      },
      {
        "title": "科比欧文加厚专业篮球袜男中筒精英袜高帮毛巾底防滑透气吸汗防臭",
        "pic_url": "https://img.alicdn.com/imgextra/O1CN01YjiY1A1eHyLEsYqae_!!2908813847.png",
        "promotion_price": "29.90",
        "price": "29.90",
        "num_iid": "636638770741",
        "is_tmall": "true",
        "area": "广州",
        "detail_url": "//item.taobao.com/item.htm?id=636638770741"
      },
      {
        "title": "情缘  袜子男士  足球袜  运动袜  提花袜  定制袜  长筒袜男定制",
        "pic_url": "https://img.alicdn.com/imgextra/O1CN0130nTa524Rl8JXBJqz_!!0-item_pic.jpg",
        "promotion_price": "12.80",
        "price": "12.80",
        "num_iid": "845255066462",
        "is_tmall": "false",
        "area": "鹤壁",
        "detail_url": "//item.taobao.com/item.htm?id=845255066462"
      },

一、初次翻车:Base64漏加前缀+图片超限,调试到凌晨四点

第一次对接淘宝图片搜索接口,是帮客户做“同款货源查找工具”——用户上传商品图,工具返回淘宝上的相似款和价格。我照着文档把图片转成Base64编码,直接拼接参数发起请求,结果连续9小时返回两种错误:要么是40001签名错误,要么是40013参数无效。

翻遍淘宝开放平台文档和开发者社区,才摸清两个致命坑:

  1. Base64编码必须带格式前缀:淘宝识图接口要求图片Base64串必须加上“data:image/jpeg;base64,”前缀(根据图片格式调整),我只传了纯编码串,导致接口无法识别图片类型,直接判定参数无效;
  2. 图片大小和格式有严格限制:仅支持JPG/PNG格式,单张图片大小不能超过2M,且分辨率不能低于300*300。我测试用的图是5M的PNG,虽然转了编码,但接口直接拒收,错误信息却只显示“参数无效”,完全不提示图片超限。

更坑的是,识图接口的签名逻辑比商品详情接口多了“图片参数单独编码”要求——Base64串里的“+”“/”必须URL编码,否则签名计算时会被截断。那天对着官方示例反复调试,逐字符对比编码结果,终于磨出能跑通的签名和图片处理函数:

import hashlib
import time
import urllib.parse
import base64
from PIL import Image
import io

def compress_image(image_bytes, max_size=2*1024*1024, min_resolution=(300, 300)):
    """
    压缩图片:适配淘宝识图接口要求(JPG/PNG,≤2M,≥300*300)
    :param image_bytes: 图片字节流
    :param max_size: 最大大小(字节)
    :param min_resolution: 最小分辨率(宽,高)
    """
    img = Image.open(io.BytesIO(image_bytes))
    # 检查分辨率
    if img.size[0] < min_resolution[0] or img.size[1] < min_resolution[1]:
        raise ValueError("图片分辨率过低,需≥300*300")
    # 压缩质量(逐步降低直到符合大小)
    quality = 90
    while True:
        img_byte_arr = io.BytesIO()
        img.save(img_byte_arr, format=img.format if img.format in ["JPEG", "PNG"] else "JPEG")
        img_byte_arr = img_byte_arr.getvalue()
        if len(img_byte_arr) <= max_size or quality <= 30:
            break
        quality -= 10
    return img_byte_arr, img.format

def generate_taobao_image_sign(params, app_secret, image_bytes=None):
    """
    生成淘宝图片搜索API签名(Base64带前缀+图片单独编码!)
    :param params: 请求参数(不含sign)
    :param app_secret: 应用密钥
    :param image_bytes: 图片字节流(用于生成Base64)
    """
    # 1. 处理图片,生成符合要求的Base64编码
    if image_bytes:
        img_bytes, img_format = compress_image(image_bytes)
        img_base64 = base64.b64encode(img_bytes).decode()
        # 必须加格式前缀,否则接口无法识别
        params["image"] = f"data:image/{img_format.lower()};base64,{img_base64}"
    
    # 2. 强制添加淘宝识图接口必传参数
    params["format"] = "json"
    params["v"] = "2.0"
    params["timestamp"] = time.strftime("%Y-%m-%d %H:%M:%S")
    params["method"] = "taobao.image.search.query"  # 识图接口固定方法名
    params["fields"] = "num_iid,title,price,similarity,image_url,shop_type"  # 显式指定返回字段
    
    # 3. 过滤sign,按参数名ASCII升序排序,图片Base64需单独URL编码
    sign_params = {}
    for k, v in params.items():
        if k != "sign":
            # 图片参数单独编码,避免特殊字符截断
            if k == "image":
                sign_params[k] = urllib.parse.quote(v, safe='')
            else:
                sign_params[k] = v
    sorted_params = sorted(sign_params.items(), key=lambda x: x[0])
    
    # 4. 拼接参数,首尾加密钥,SHA1加密转大写
    query_str = "&".join([f"{k}={v}" for k, v in sorted_params])
    sign_str = f"{app_secret}{query_str}{app_secret}"
    return hashlib.sha1(sign_str.encode()).hexdigest().upper(), params

# 示例调用:上传本地图片发起识图
if __name__ == "__main__":
    app_key = "your_app_key"
    app_secret = "your_app_secret"
    # 读取本地图片为字节流
    with open("test.jpg", "rb") as f:
        image_bytes = f.read()
    # 生成签名和参数
    params = {"app_key": app_key}
    sign, final_params = generate_taobao_image_sign(params, app_secret, image_bytes)
    final_params["sign"] = sign
    print("请求参数准备完成,图片Base64前缀:", final_params["image"][:50])

在这里插入代码片作者:高阔

1. 背景

这可能是全网第一篇完整讲解鸿蒙端使用CANN部署AI模型的文章, 满满干货。

社区作为用户交流、信息传递的核心载体,图片内容(如理财产品截图、投资经验分享配图、用户互动评论图片等)的展示质量直接影响用户的信息获取效率与平台信任感。从京东金融App社区的业务需求来看,当前用户上传图片普遍存在多样性失真问题:部分用户通过老旧设备拍摄的图片分辨率较低,部分用户为节省流量选择低画质压缩上传,还有部分截图类内容因原始来源清晰度不足导致信息模糊(如理财产品收益率数字、合同条款细节等),这些问题不仅降低了内容可读性,还可能因信息传递不清晰引发用户误解。

京东金融App团队已完成Real-ESRGAN-General-x4v3超分辨率模型在安卓端的部署,能够针对性提升评论区、内容详情页、个人主页等核心场景的图片清晰度,从视觉体验层面优化用户留存与互动意愿。

ESRGAN-General-x4v3模型在安卓端的部署,采用的是ONNX框架,该方案已有大量公开资料可参考,且取得显著业务成效。但鸿蒙端部署面临核心技术瓶颈:鸿蒙系统不支持ONNX框架,部署端侧AI仅能使用华为自研的CANN(Compute Architecture for Neural Networks)架构,且当前行业内缺乏基于CANN部署端侧AI的公开资料与成熟方案,全程需技术团队自主探索。接下来我会以ESRGAN-General-x4v3为例, 分享从模型转换(NPU亲和性改造)到端侧离线模型部署的全部过程。

2. 部署前期准备

2.1 离线模型转换

CANN Kit当前仅支持Caffe、TensorFlow、ONNX和MindSpore模型转换为离线模型,其他格式的模型需要开发者自行转换为CANN Kit支持的模型格式。模型转换为OM离线模型,移动端AI程序直接读取离线模型进行推理。

2.1.1 下载CANN工具

从鸿蒙开发者官网下载 DDK-tools-5.1.1.1, 解压使用Tools下的OMG工具,将ONNX、TensorFlow模型转换为OM模型。(OMG工具位于Tools下载的tools/tools\_omg下,仅可运行在64位Linux平台上。)



2.1.2 下载ESRGAN-General-x4v3模型文件

https://aihub.qualcomm.com/compute/models/real\_esrgan\_general\_x4v3下载模型的onnx文件.

注意: 下载链接中的a8a8的量化模型使用了高通的算子(亲测无法转换), CANN工具无法进行转换, 因此请下载float的量化模型。

下载后有两个文件:

•model.onnx文件 (模型结构): 包含计算图、opset版本、节点配置等,文件较小。

•model.data文件 (权重数据): 包含神经网络参数、权重等,文件较大。

现在我们需要把这种分离文件格式的模型合并成一个文件,后续的操作都使用这个。

合并文件:

请使用JoyCode写个合并脚本即可, 提示词: 请写一个脚本, 把onnx模型文件的.onnx和.data文件合并。

2.1.3 OM模型转换

1. ONNX opset 版本转换

当前使用CANN进行模型转换, 支持ONNX opset版本7\~18(最高支持到V1.13.1), 首先需要查看原始的onnx模型的opset版本是否在支持范围, 这里我们使用Netron(点击下载)可视化工具进行查看。
在这里插入图片描述



目前该模型使用的opset版本是20, 因此我们需要把该模型的opset版本转成18, 才可以用CANN转换成鸿蒙上可部署的模型。请使用JoyCode写个opset转换脚本即可, 提示词: 请写一个脚本, 把onnx模型文件的opset版本从20转换成18。



2. OM离线模型****

命令行中的参数说明请参见OMG参数,转换命令:

./tools/tools_omg/omg --model new_model_opset18.onnx --framework 5 --output ./model

转换完成后, 生成model.om的模型文件, 该模型文件就是鸿蒙上可以正常使用的模型文件

2.2 查看模型的输入/输出张量信息

部署AI模式时, 我们需要确认模型的输入张量和输出张量信息, 请使用JoyCode编写一个脚本, 确定输入输出张量信息, 提示词: 写一个脚本查看onnx模型的输入输出张量信息。

在这里插入图片描述

2.2.1 输入张量

BCHW格式, 是深度学习中常见的张量维度排列格式, 在图像处理场景中:

•B (Batch): 批次大小 - 一次处理多少个样本。

•C (Channel): 通道数 - 图像的颜色通道数。

•H (Height): 高度 - 图像的像素高度。

•W (Width): 宽度 - 图像的像素宽度。

由此可以得出结论, 该模型1个批次处理1张宽高为128*128的RGB图片(因为C是3,因此不包含R通道)。



2.2.2 输出张量

该模型1个批次输出1张宽高为512*512的RGB图片。



2.2.3 BCHW和BHWC格式的区别:

超分模型中的BCHW和BHWC是两种不同的张量存储格式,主要区别在于通道维度的位置:



BCHW格式(Batch-Channel-Height-Width)

◦维度顺序:[批次, 通道, 高度, 宽度]

◦内存布局:通道维度在空间维度之前

◦常用框架:PyTorch、TensorRT等

示例: 形状为 (1, 3, 256, 256) 的RGB图像

内存中的存储顺序: R通道的所有像素 -> G通道的所有像素 -> B通道的所有像素

tensor_bchw = torch.randn(1, 3, 256, 256)
访问第一个像素的RGB值需要跨越不同的内存区域
pixel_0_0_r = tensor_bchw[0, 0, 0, 0]  # R通道
pixel_0_0_g = tensor_bchw[0, 1, 0, 0]  # G通道  
pixel_0_0_b = tensor_bchw[0, 2, 0, 0]  # B通道

BHWC格式(Batch-Height-Width-Channel)

◦维度顺序:[批次, 高度, 宽度, 通道]

◦内存布局:通道维度在最后,像素的所有通道连续存储

◦常用框架:TensorFlow、OpenCV等

示例:形状为 (1, 256, 256, 3) 的RGB图像

内存中的存储顺序:像素(0,0)的RGB -> 像素(0,1)的RGB -> ... -> 像素(0,255)的RGB -> 像素(1,0)的RGB...

tensor_bhwc = tf.random.normal([1, 256, 256, 3])
# 访问第一个像素的RGB值在连续的内存位置
pixel_0_0_rgb = tensor_bhwc[0, 0, 0, :]  # [R, G, B]



3. 鸿蒙端部署核心步骤

3.1 创建项目

1.创建DevEco Studio项目,选择“Native C++”模板,点击“Next”。

在这里插入图片描述



2.按需填写“Project name”、“Save location”和“Module name”,选择“Compile SDK”为“5.1.0(18)”及以上版本,点击“Finish”。

在这里插入图片描述

3.2 配置项目NAPI

CANN部署只提供了C++接口, 因此需要使用NAPI, 编译HAP时,NAPI层的so需要编译依赖NDK中的libneural\_network\_core.so和libhiai\_foundation.so。



头文件引用

按需引用NNCore和CANN Kit的头文件。

#include "neural_network_runtime/neural_network_core.h"
#include "CANNKit/hiai_options.h"

编写CMakeLists.txt

CMakeLists.txt示例代码如下。

cmake_minimum_required(VERSION 3.5.0)
project(myNpmLib)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include)

include_directories(${HMOS_SDK_NATIVE}/sysroot/usr/lib)
FIND_LIBRARY(cann-lib hiai_foundation)

add_library(imagesr SHARED HIAIModelManager.cpp ImageSuperResolution.cpp)
target_link_libraries(imagesr PUBLIC libace_napi.z.so
    libhilog_ndk.z.so
    librawfile.z.so
    ${cann-lib}
    libneural_network_core.so
    )

3.3 集成模型

模型的加载、编译和推理主要是在native层实现,应用层主要作为数据传递和展示作用。模型推理之前需要对输入数据进行预处理以匹配模型的输入,同样对于模型的输出也需要做处理获取自己期望的结果

在这里插入图片描述

3.3.1 加载离线模型

为了让App运行时能够读取到模型文件和处理推理结果,需要先把离线模型和模型对应的结果标签文件预置到工程的“entry/src/main/resources/rawfile”目录中。

在这里插入图片描述



在App应用创建时加载模型:

1.native层读取模型的buffer。

const char* modelPath = "imagesr.om";
RawFile *rawFile = OH_ResourceManager_OpenRawFile(resourceMgr, modelPath);
long modelSize = OH_ResourceManager_GetRawFileSize(rawFile);
std::unique_ptr<uint8_t[]> modelData = std::make_unique<uint8_t[]>(modelSize);
int res = OH_ResourceManager_ReadRawFile(rawFile, modelData.get(), modelSize);

2.使用模型的buffer, 调用OH\_NNCompilation\_ConstructWithOfflineModelBuffer创建模型的编译实例

HiAI_Compatibility compibility = HMS_HiAICompatibility_CheckFromBuffer(modelData, modelSize);
OH_NNCompilation *compilation = OH_NNCompilation_ConstructWithOfflineModelBuffer(modelData, modelSize);

3.(可选)根据需要调用HMS\_HiAIOptions\_SetOmOptions接口,打开维测功能(如Profiling)。

const char *out_path = "/data/storage/el2/base/haps/entry/files";
HiAI_OmType omType = HIAI_OM_TYPE_PROFILING;
OH_NN_ReturnCode ret = HMS_HiAIOptions_SetOmOptions(compilation, omType, out_path);     

4.设置模型的deviceID。

size_t deviceID = 0;
const size_t *allDevicesID = nullptr;
uint32_t deviceCount = 0;
OH_NN_ReturnCode ret = OH_NNDevice_GetAllDevicesID(&allDevicesID, &deviceCount);

for (uint32_t i = 0; i < deviceCount; i++) {
    const char *name = nullptr;
    ret = OH_NNDevice_GetName(allDevicesID[i], &name);
    if (ret != OH_NN_SUCCESS || name == nullptr) {
        OH_LOG_ERROR(LOG_APP, "OH_NNDevice_GetName failed");
        return deviceID;
    }
    if (std::string(name) == "HIAI_F") {
        deviceID = allDevicesID[i];
        break;
    }
}

ret = OH_NNCompilation_SetDevice(compilation, deviceID);

5.调用OH\_NNCompilation\_Build,执行模型编译。

ret = SetModelBuildOptions(compilation);
ret = OH_NNCompilation_Build(compilation);

6.调用OH\_NNExecutor\_Construct,创建模型执行器。

executor_ = OH_NNExecutor_Construct(compilation);

7.调用OH\_NNCompilation\_Destroy,释放模型编译实例。



3.3.2 准备输入输出****Tensor

1.处理模型的输入,模型的输入为13128*128格式(BCHW) Float类型的数据, 需要把RGB 数据转成BCHW格式并进行归一化。

从图片中读取的RGB数据为BHWC,需要转换成模型可以识别的BCHW
/**
 * 把bhwc转成bchw
 */
uint8_t *rgbData = static_cast<uint8_t*>(data);
uint8_t *floatData_tmp = new uint8_t[length];
for (int c = 0; c < 3; ++c) {
    for (int h = 0; h < 128; ++h) {
        for (int w = 0; w < 128; ++w) {
            // HWC 索引: h * width * channels + w * channels +c 
            int hwc_index = h * 128 * 3 + w * 3 + c;
            // CHW 索引: C * height * width + h* width + W
            int chw_index = c * 128 * 128 + h * 128 + w;
            floatData_tmp[chw_index] = rgbData[hwc_index];
        }
    }
}
//归一化
float *floatData = new float[length];
for (size_t i = 0; i < length; ++i) {
    floatData[i] = static_cast<float>(floatData_tmp[i])/ 255.0f;
}

2.创建模型的输入和输出Tensor,并把应用层传递的数据填充到输入的Tensor中

// 准备输入张量
size_t inputCount = 0;
OH_NN_ReturnCode ret = OH_NNExecutor_GetInputCount(executor_, &inputCount);
for (size_t i = 0; i < inputCount; ++i) {
    NN_TensorDesc *tensorDesc = OH_NNExecutor_CreateInputTensorDesc(executor_, i);
    NN_Tensor *tensor = OH_NNTensor_Create(deviceID_, tensorDesc);
    if (tensor != nullptr) {
        inputTensors_.push_back(tensor);
    }
    OH_NNTensorDesc_Destroy(&tensorDesc);
}


ret = SetInputTensorData(inputTensors_, inputData);

// 准备输出张量
size_t outputCount = 0;
ret = OH_NNExecutor_GetOutputCount(executor_, &outputCount);

for (size_t i = 0; i < outputCount; i++) {
    NN_TensorDesc *tensorDesc = OH_NNExecutor_CreateOutputTensorDesc(executor_, i);
    NN_Tensor *tensor = OH_NNTensor_Create(deviceID_, tensorDesc);
    if (tensor != nullptr) {
        outputTensors_.push_back(tensor);
    }
    OH_NNTensorDesc_Destroy(&tensorDesc);
}
if (outputTensors_.size() != outputCount) {
    DestroyTensors(inputTensors_);
    DestroyTensors(outputTensors_);
    OH_LOG_ERROR(LOG_APP, "output size mismatch.");
    return OH_NN_FAILED;
}



3.3.3 进行推理

调用OH\_NNExecutor\_RunSync,完成模型的同步推理。

OH_NN_ReturnCode ret = OH_NNExecutor_RunSync(executor_, inputTensors_.data(), inputTensors_.size(),
                                                 outputTensors_.data(), outputTensors_.size());

说明

•如果不更换模型,则首次编译加载完成后可多次推理,即一次编译加载,多次推理。

•所有关于模型的操作, 均无法多线程执行。



3.3.4 获取模型输出并处理数据

1.调用OH\_NNTensor\_GetDataBuffer,获取输出的Tensor,在输出Tensor中会得到模型的输出数据。

// 获取第一个输出张量
NN_Tensor* tensor = outputTensors_[0];

// 获取张量数据缓冲区
void *tensorData = OH_NNTensor_GetDataBuffer(tensor);

// 获取张量大小
size_t size = 0;
OH_NN_ReturnCode ret = OH_NNTensor_GetSize(tensor, &size);

float *tensorDataOutput = (float*)malloc(size);
// 将tensorData的数据一次性复制到tensorDataOutput中
memcpy(tensorDataOutput, tensorData, size);



2.对Tensor输出数据进行相应的处理

把模型输出的BCHW转成BHWC, 并进行反归一化处理



//把模型输出的BCHW转成BHWC
float *outputResult = static_cast<float *>(tensorData);
float *output_tmp = new float[size/sizeof(float)];
for (int h = 0; h < 512; ++h) {
    for (int w = 0; w < 512; ++w) {
        for (int c = 0; c < 3; ++c) {
            output_tmp[h * 512 * 3 + w* 3 + c] = outputResult[c * 512 * 512 + h * 512 + w];
        }
    }
}
std::vector<float> output(size / sizeof(float), 0.0);
for (size_t i = 0; i < size / sizeof(float); ++i) {
    output[i] = output_tmp[i];
}
delete [] output_tmp;


 // 计算总的数据大小
size_t totalSize = output.size();

// 分配结果数据内存
std::unique_ptr<uint8_t[]> result_data = std::make_unique<uint8_t[]>(totalSize);

// 将float数据转换为uint8_t (反归一化)
size_t index = 0;
for (float value : result) {
    // 将float值转换为uint8_t (0-255范围)
    float scaledValue = value * 255.0f;
    scaledValue = std::max(0.0f, std::min(255.0f, scaledValue));
    result_data[index++] = static_cast<uint8_t>(scaledValue);
}

result_data 就是最终的超分数据,可以正常显示



4. 总结与技术展望

京东金融App在鸿蒙端部署Real-ESRGAN-General-x4v3超分辨率模型的完整实践过程,成功解决了ONNX模型到OM离线模型转换、BCHW与BHWC张量格式处理、以及基于CANN Kit和NAPI的完整部署链路等关键技术难题。

展望端智能的未来发展,随着芯片算力的指数级增长、模型压缩技术的突破性进展以及边缘计算架构的日趋成熟,端侧设备将从单纯的数据采集终端演进为具备强大推理能力的智能计算节点,通过实现多模态AI融合、实时个性化学习、隐私保护计算和跨设备协同等核心能力,将大语言模型、计算机视觉、语音识别等AI技术深度集成到移动设备中,构建起无需联网即可提供智能服务的自主计算生态,推动人机交互从被动响应向主动感知、预测和服务的范式转变,最终开启真正意义上的普惠人工智能时代。

作者:王元

1. 背景与痛点:存量代码的“多语言噩梦”

在前端开发中,将一个成熟的中文存量项目进行国际化多语言(i18n)改造,往往面临着以下困境:

•工作量巨大: 项目包含数百个 .vue/.js/.ts 等文件,散落着成千上万个硬编码的中文字符串。

•人工易错: 手动提取容易遗漏,且极其枯燥,极易产生 Copy/Paste 错误。

•命名困难: 为每一个中文词条想一个语义化的英文 Key(如 homePageTitle)不仅耗时,而且难以保证团队风格统一。

•维护成本高: 翻译文件(zh.ts/en.ts)的维护和代码中的替换需要同步进行,稍有不慎就会导致报错。

如果按照传统的人工查找替换方式,预计需要耗费数周的人力。为了打破这一僵局,我决定利用 JoyCode 结合我开发的 i18n-mcp 工具,打造一套自动化的国际化多语言解决方案。



2. 解决方案:JoyCode + i18n-mcp

我基于 MCP (Model Context Protocol) 开发了一个工具 i18n-mcp,通过 JoyCode 的 AI 能力来调度和执行以下三个核心步骤,实现了从“提取”到“替换”的全链路自动化。

流程图

以下是i18n-mcp的流程图(由JoyCode生成)

在这里插入图片描述

核心流程拆解

第一步:智能提取中文与去重

i18n-mcp 自动扫描所有源文件。利用正则或 AST(抽象语法树)精准识别代码中的中文字符串(包括 Template、Script 和 JSX 部分)。

•全量扫描(full-project-scan工具): 文件过多的时候,全量扫描会有问题。可以通过指定文件夹的方式,扫描该文件夹下面的文件。

•增量扫描(git-change工具):针对git变更的文件,进行扫描。精准定位变更文件,仅处理本次变更涉及的代码,大幅提升效率。

•智能去重: 对提取出的文本进行去重,确保相同的中文文案(如“确认”、“取消”)只生成一个 Key,避免冗余。

第二步:AI 辅助翻译与文件生成

•翻译缓存: 优先查询 数据存储层 中的 Translation Cache,已翻译过的文案直接复用,显著降低 Token 消耗并加速流程。

•自动化翻译: 提取的中文列表没有在缓存中或zh文件中的,被发送给 LLM,自动翻译成英文。

•语义化 Key 生成: 区别于传统 Hash 值,LLM 根据代码上下文(Context)自动生成符合语义的 Key(如将“请输入密码”生成为 pleaseInputPassword),提升代码可读性。

•文件落地: 自动在 lang 文件夹下生成标准的 zh.tsen.ts 文件。



生成示例: zh.ts: { "pleaseSelect": "请选择" } en.ts: { "pleaseSelect": "Please Select" }





第三步:一键代码替换

•变更预览 (Preview): 在实际修改前,可调用 preview-changes 工具展示即将变更的代码对比,确保修改符合预期。

•AST 节点替换: 使用 extract-and-replace 工具,将源代码中的硬编码字符串精准替换为国际化方法(如 $t('pleaseSelect'))。

•无损格式保持: 基于 AST 的替换策略能够完美保留原代码的缩进、换行和注释,修改后的代码无需二次 Lint 即可直接提交。





3. 成果与收益:从“数周”到“数小时”

通过引入 JoyCode + i18n-mcp 的实践,我在项目的国际化改造中取得了显著的成效:

📊 定量收益

维度传统人工方式JoyCode + i18n-mcp提升幅度
单页面改造耗时约 10-30 分钟< 1 分钟效率提升 90%+
词条遗漏率质量显著提升
变量命名耗时需人工构思AI 秒级生成完全自动化

💡 定性收益

1.解放生产力: 从枯燥的“搬运工”工作中解脱出来,可以专注于业务逻辑和核心功能的开发。

2.代码规范统一: AI 生成的 Key 风格高度统一(全驼峰),避免了“千人千面”的命名混乱。

3.可维护性增强: 建立了自动化的语言包管理机制,后续新增词条只需运行脚本即可。



4. i18n-mcp开发

i18n-mcp是我首次开发MCP,整体难度相对较低。对于前端部分,基于github模板进行开发,随后发布至公司NPM私服即可。

核心代码主要由JoyCode的编码功能协助完成。按照上述核心流程步骤通过问答交互的方式,引导JoyCode完成核心代码的开发工作。

整个i18n-mcp架构图如下所示(架构图亦由JoyCode生成)。

在这里插入图片描述



MCP配置如下

{
  "mcpServers": {
    "i18n-mcp": {
      "autoApprove": [],
      "disabled": true,
      "timeout": 180,
      "command": "npx",
      "type": "stdio",
      "transportType": "stdio",
      "args": [
        "-y",
        "@jd/i18n-mcp@latest"
      ],
      "env": {}
    }
  }
}

效果

配置之后,输入prompt “调用i18n-mcp的auto-i18n-process方法”

效果如下:

在这里插入图片描述

5. 总结

尽管目前 i18n-mcp 仍存在一些不足,例如在全面扫描大量文件时可能出现连接错误、翻译和替换结果不够准确等问题,仍需人工进行二次校验,但其在短时间内辅助开发的价值依然显著。在本次实践过程中,我主要通过 JoyCode 的交互式问答完成开发工作。JoyCode 不仅在代码补全方面发挥了重要作用,更凭借其强大的智能调度和自动化执行能力,成为高效处理复杂任务的核心中枢。结合 i18n-mcp 的开发,AI技术的深度赋能得以充分体现,大幅提升了开发的效率。

后续,我将持续研究 AI 在前端开发中的落地场景,充分发挥 AI 辅助开发的强大能力。通过深入探索和应用 AI 技术,进一步释放其在业务创新与效率提升方面的巨大潜力。

作者:桑伟杰

在这里插入图片描述

一、背景

当前在传统设计环节,设计师与研发之间存在大量的关于样式等视觉层的理解偏差,从而会出现大量的重复且无效的细节像素调整工作,由于项目时间紧、细节多设计走查环节会给各方角色诸多额外负担,在AI涌现后设计师尝试使用AI\_Code直接还原设计稿件,并且从传统交付静态界面设计图片转为交付可运行的实现方案,但在多数团队的认知里,AI\_Code仍停留在“氛围编程”阶段:能写出代码,但不符合框架规范,改动越多问题越多。通过不断摸索总结出一套稳定可用的 Design to Code (D2C) 解法:设计师借助 AI - IDE工具以及设计工具,通过MCP打通设计数据与研发数据,实现将设计稿直接转译为符合开发规范、可上线的前端代码,极大缩短交付周期。

D2C核心效果:设计师第一次拥有了对实现效果的“直接控制权”工程师从繁琐的像素级样式修改中解放出来团队整体迭代速度大幅提升

在这里插入图片描述

传统链路VSD2C链路

二、效果展示

案例1:PC端\_WMS6.0工艺配置

通过D2C流程从【组件生成】→【页面生成】,完成PC端工艺流程配置功能代码输出,实现了卡片拖拽、卡片状态自动变更、放置位置判断等核心功能;实现项目完整交付在测试环境中可直接运行,研发无需对前端代码进行修改,D2C代码输出总耗时0.5人/日,项目整体效率提升26%

在这里插入图片描述

WMS6.0\_Vue2.0实现效果

案例2:移动端\_PDA上架到容器

通过D2C流程链接设计数据与研发数据,【直接调用研发组件库代码】,按照代码仓库标准代码输出规范的前端页面,实现多页面跳转,逻辑判断,查询等核心功能,达到像素级还原并符合团队规范。D2C代码输出总耗时0.5人/日,项目整体效率提升50%

在这里插入图片描述

PDA\_Flutter实现效果

三、设计思维转变

D2C 并非“让设计师写代码”,而是促使设计师提升工程化思维:使设计师从传统的设计界面转向当前的设计容器,从而更好的让AI能够读懂设计数据实现D2C流程



传统设计思维 ➔ 工程化思维

传统设计思维:

步骤:1.设计全部视觉元素 ➔ 2.在页面进行元素相对位置的排布 ➔ 3.完成设计内容的产出

特点:元素之间仅包含相对关系没有结构层的动态属性,与页面实现的框架不一致

工程化思维:

步骤:1.设计组织分层关系 ➔ 2.设计分层容器布局规则 ➔ 3.设计容器所需设计元素 ➔ 4.完成设计内容的产出

特点:先有组织容器再有容器内容,组织容器具备布局规则等动态属性,更符合页面实现的框架。

四、实现路径

D2C的核心方法:D2C的核心法则是在保证幻觉与Token限制的条件下,通过稳定与可靠的方法,尽量多的将设计数据与研发数据进行链接,让AI充分理解两端数据并完成翻译

在这里插入图片描述

优劣势对比

稳定的D2C链接方法:

通过Figma MCP获取全部设计数据,包括颜色、圆角、间距、图层名称、文本信息、图片资源、代码数据、页面截图;将设计数据传递给AI-IDE工具,通过rules和Prompt控制设计数据解析标准,规定AI按照解析结果与代码数据对应,实现代码输出优势:即有设计元属性,又包含截图以及基础代码信息,AI可以更好的关联研发数据实现完美还原

并且针对不同页面构成,总结并执行不同的D2C步骤,用于还原设计内容,由于D2C的核心是链接,所以重点在于如何制造稳定链接,我们可以通过Code Connect或者让AI通过图层命名检索的方式实现稳定链接

在这里插入图片描述

D2C设计流程图

针对已有组件:

逻辑:通过调整设计组件名称与变体与研发组件名称和属性建立映射链接

步骤:提供界面截图 ➔ 工程师维护组件映射表 ➔ 设计师调整设计组件与研发组件结构一致 ➔ 还原页面内容

重点:工程师维护的组件映射表需包含组件名称及组件属性,设计师需保持设计组件与研发组件的结构相同

案例:PDADesign组件映射表

针对无组件场景:

逻辑:按照设计组件的名称与结构按照研发代码编写规则输出组件建立映射链接

步骤:设计师需采用工程化思维绘制组件 ➔ AI阅读代码仓库组件书写规范 ➔ 按照规范将设计组件输出为研发组件 ➔ 通过MCP获取设计组件并关联已经转为代码的研发组件

重点:与工程师对齐结构规范,若仓库中有Token数据再设计组件绘制时也需要保持一致



五、结语

D2C 是一次 团队角色和流程的升级,更是一场认知的跃迁:设计师不再只是交付界面,而是交付“可运行的实现方案”AI 成为设计师和工程师之间的“实时翻译器”最终实现:更快迭代、更少摩擦、更强共创。

在这条由 AI 驱动的设计到代码之路上,设计师不再是单纯的界面构建者,而是系统规则的定义者、智能逻辑的编织者。他们与 AI 一起,共同塑造一个能“理解意图、自动生成、持续学习”的设计生态。

当设计稿不再停留于视觉表达,而成为可以被机器直接理解的语言,设计师便跨越了传统的边界——从视觉思考者,走向了系统架构的参与者;从界面呈现者,走向了智能生产力的创造者。

AI 不会取代设计师,但会放大他们的思考维度,让人类的创造力从重复劳动中解放出来,去关注更本质的价值:如何让设计更智能、更高效、更具生命力。 在未来,D2C 不仅是“设计到代码”的捷径,更是“人机共创”的起点—— 让每一位设计师,都能成为 AI 时代的工程合作者,让设计真正成为推动产品智能演化的核心力量。

精简 Excel 工作簿、删除多余或不再使用的工作表,是一种非常有效的整理方式。通过移除无关内容,可以减少冗余信息,使文件结构更加清晰,只保留最有价值的数据。删除不必要的工作表不仅有助于释放存储空间,还能让工作簿的浏览与管理更加高效、直观。

在本文中,你将学习如何使用 Spire.XLS for .NET 库,通过 C# 从 Excel 工作簿中删除指定的工作表。

安装 Spire.XLS for .NET

首先,你需要将 Spire.XLS for .NET 包中包含的 DLL 文件添加为 .NET 项目的引用。你可以通过提供的下载链接手动下载 DLL 文件并引入项目,或者直接使用 NuGet 进行安装。

PM> Install-Package Spire.XLS

在 C# 中通过索引删除工作簿中的工作表

Spire.XLS for .NET 提供了 WorksheetsCollection.RemoveAt(int index) 方法,可根据工作表在工作簿中的索引位置删除指定的工作表。

具体示例代码如下:

using Spire.Xls;
using Spire.Xls.Collections;

namespace RemoveWorksheetByIndex
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个 Workbook 对象
            Workbook wb = new Workbook();

            // 加载 Excel 文件
            wb.LoadFromFile(@"C:\Users\Administrator\Desktop\Input.xlsx");

            // 从工作簿中获取工作表集合
            WorksheetsCollection worksheets = wb.Worksheets;

            // 根据索引删除指定的工作表
            worksheets.RemoveAt(0);

            // 将工作簿保存为新的 Excel 文件
            wb.SaveToFile("RemoveByIndex.xlsx", ExcelVersion.Version2016);

            // 释放资源
            wb.Dispose();
        }
    }
}

在 C# 中通过工作表名称删除工作簿中的工作表

如果你已经知道需要删除的工作表名称,可以使用 WorksheetsCollection.Remove(string sheetName) 方法,直接按名称从工作簿中移除对应的工作表。

具体示例代码如下:

using Spire.Xls;
using Spire.Xls.Collections;

namespace RemoveWorksheetByName
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个 Workbook 对象
            Workbook wb = new Workbook();

            // 加载 Excel 文件
            wb.LoadFromFile(@"C:\Users\Administrator\Desktop\Input.xlsx");

            // 从工作簿中获取工作表集合
            WorksheetsCollection worksheets = wb.Worksheets;

            // 根据工作表名称删除指定的工作表
            worksheets.Remove("sheet2");

            // 将工作簿保存为新的 Excel 文件
            wb.SaveToFile("RemoveByName.xlsx", ExcelVersion.Version2016);

            // 释放资源
            wb.Dispose();
        }
    }
}

在 C# 中一次性删除工作簿中的所有工作表

如果需要一次性移除工作簿中的所有工作表,可以使用 WorksheetsCollection.Clear() 方法快速清空工作表集合。

具体示例代码如下:

using Spire.Xls;
using Spire.Xls.Collections;

namespace RemoveAllWorksheets
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个 Workbook 对象
            Workbook wb = new Workbook();

            // 加载 Excel 文件
            wb.LoadFromFile(@"C:\Users\Administrator\Desktop\Input.xlsx");

            // 从工作簿中获取工作表集合
            WorksheetsCollection worksheets = wb.Worksheets;

            // 删除所有工作表
            worksheets.Clear();

            // 将工作簿保存为新的 Excel 文件
            wb.SaveToFile("RemoveAllWorksheets.xlsx", ExcelVersion.Version2016);

            // 释放资源
            wb.Dispose();
        }
    }
}

申请临时许可证

如果你希望移除生成文档中的评估提示信息,或解除功能限制,请申请一个 为期 30 天的试用许可证。

最近项目组做了一次安全项目,在联动讨论中,我们团队提出攻克一个一直被“模糊处理”的问题:如何在不引入复杂流量解密、不严重影响性能的前提下,更可靠地识别潜在的 C2通信行为。

其实在我看来这个问题并不新,在往常的项目中异常端口、可疑域名、频繁外联、策略命中日志等检测点都是这个问题。而这些信号单独存在时,误报率高,且很难形成可执行的处置结论。而本次讨论的关键转折点,正是在“出站 IP 本身是否具备明确恶意属性”这一角度上。

一、从“流量行为”转向“通信对象本身”的判断

在复盘既往处置案例时,阿孙提到一个共性:多数被确认为C2 的样本不是因为流量形态极其复杂,而是其通信目标本身就具备明显的基础设施风险特征,比如位于高风险国家或地区、IP 段频繁出现在僵尸网络、钓鱼、木马回连情报中、长期驻留在 VPS/云主机/异常 ASN、多项目/多情报源重复命中等

但这些信息在现有体系中是割裂的,地理信息在 IP 归属系统中,恶意标签在威胁情报平台中,而F火墙/EDR却只能看到“一个外联IP”,由此,我门讨论是否可以多采用IP离线库植入威胁情报,手动拓宽我们的“威胁情报”,提出这正是我们提出使用IP离线库和威胁情报进行融合。

二、 为什么必须引入 IP 离线库,而不是完全依赖情报 API

最初也有人提出直接调用在线威胁情报 API 即可,但在技术评估阶段,很快暴露出几个不可回避的问题:

1. 出站连接频率高,实时 API 成本与延迟不可控
在核心业务网段,单节点每天的外联 IP 数量级在百万级,实时查询并不可行。

2. 部分安全系统处于内网或半隔离环境
核心日志分析、审计系统无法直接访问外部情报接口。

3. IP 基础属性缺失会削弱判断上下文
单一“是否恶意”的结论,无法解释风险来源,例如:

1. 这是一个海外 IDC 正常业务 IP?

2. 还是位于高风险区域的小型自治系统?

3. 是否属于动态拨号或代理出口?

因此,我们的思路是先通过本地 IP 离线库快速完成“背景定性”,再用威胁情报完成“恶意定量”  。

三、 IP离线库在 C2 识别中的实际定位

在方案中,IP 离线库并不直接承担“是否 C2”的判断,而是用于回答以下关键问题:

1. 该出站 IP 的国家/地区/城市是否与业务场景匹配

2. 是否命中云厂商、数据中心、匿名网络、异常 ASN

3. 是否存在明显的跨国、跨区域跳变特征

4. 是否属于历史上极少访问、但突然频繁出现的地理位置

在我们实际部署中,使用的是IP数据云离线库,它覆盖IPv4/IPv6的本地IP 离线库,字段不仅包括国家、省市,还包含 ASN、运营商类型、IDC/住宅网络标识,方便后续识别C2通信,评估出站IP是否为已知恶意地址。
【场景:识别C2通信】评估出站IP是否为已知恶意地址,方法:IP离线库+威胁情报融合2.png

四、 威胁情报融合的方式,而非简单“命中即拦截”

第二层才是威胁情报,但这里我们刻意避免了“黑名单式”的粗暴使用方式。

具体做法是:

第一步,多源情报聚合:商业情报 + 社区情报 + 历史处置数据

第二步,情报置信度分级:区分活跃 C2、历史恶意、关联基础设施

第三步,时间衰减机制:避免因陈旧情报导致长期误判

当某个出站 IP在 IP 离线库中表现为、海外小众地区、云主机 / VPS、非业务白名单 ASN,该IP同时在威胁情报中命中已知 C2 或僵尸网络关联,被多个情报源低频标注,我们才将其提升为  “高置信度可疑 C2 通信”  ,进入阻断或人工复核流程。

五、 实际落地的处理流程拆解

在最终方案中,整体流程被拆解为清晰的四个阶段:

1. 出站连接采集
从F火墙、NDR、EDR 中统一采集目的 IP、端口、协议、频率。

2.IP 离线库快速画像

1. 地理位置

2. ASN / 网络类型

3. 是否云主机 / IDC

4. 是否偏离正常业务访问分布

3.威胁情报关联评分

1. 是否命中恶意标签

2. 情报来源数量

3. 最近活跃时间

4. 策略与响应联动

1. 自动阻断(高置信度)

2. 降权监控(中置信度)

3. 留痕审计(低置信度)

这种方式的一个明显变化是我们不再“猜测流量是不是 C2”,而是在判断“这个通信对象是否值得被当作 C2 对待”  。

项目讨论中最被认可的是①不依赖深度包检测;②不影响现有网络性能;③可解释性强,适合审计与复盘;④能在离线、内网环境稳定运行。【场景:识别C2通信】评估出站IP是否为已知恶意地址,方法:IP离线库+威胁情报融合1.png

Python动态类型机制所带来的编码自由度,是吸引无数开发者深耕于此的核心魅力,却也如同一把双刃剑,在消解静态类型繁琐约束的同时,埋下了类型契约模糊、行为边界失范的隐性隐患,传统测试手段始终被困在“预设输入-验证输出”的点覆盖逻辑里,面对动态类型环境中对象属性动态绑定、参数类型多元兼容、逻辑分支随运行时状态灵活演化的复杂场景,往往显得捉襟见肘,而属性测试的横空出世,恰好为突破这一技术困局提供了全新的实践路径,它不再执着于单一用例的精准匹配,而是从被测试对象的核心行为特征出发,提炼出那些不因输入变化、环境调整、版本迭代而转移的普适性属性共识,通过海量衍生场景的自动化探索,验证对象在动态变化全过程中的行为稳定性与一致性,这种从“点验证”到“面验证”的思维跃迁,恰恰切中了动态类型环境下测试有效性的核心诉求。在个人长期的开发实践与技术复盘过程中,我逐渐意识到,动态类型的灵活绝非放任代码类型混乱的借口,而是需要更高级的测试方法论来守护代码的可靠性底线,属性测试正是这样一种方法论,它不依赖于代码层面的类型注解或静态检查工具的表层扫描,而是深入到代码运行时的行为本质,通过挖掘那些支撑业务逻辑的核心不变量,构建起动态类型环境下的信任基石,这种信任基石的搭建,远比零散的单元测试用例更具抗脆弱性,也更能适应Python动态特性带来的代码演化需求,更重要的是,它让开发者在享受动态类型便利的同时,不必承担隐性错误扩散的风险,真正实现了灵活与可靠的双向平衡。

动态类型环境中最常见且最易被忽视的痛点,莫过于函数或对象对参数的隐式约定,这种约定往往不会以显性的类型声明或文档注释的形式呈现,而是潜藏在代码逻辑的深处,成为只有开发者本人才能意会的“潜规则”,传统测试只能基于开发者的经验与认知预设有限的测试用例,却很难覆盖那些边缘的、非常规的输入组合,而这些组合恰恰是动态类型代码最容易出现问题的重灾区,很多时候,这些非预期输入并不会触发语法错误或程序崩溃,而是会产生不符合业务预期的隐性结果,这种结果在测试阶段难以被察觉,却会在生产环境中引发连锁反应,造成难以估量的损失。属性测试的核心优势就在于它能够基于预设的生成策略,自动生成海量多样化的输入数据,这些数据不仅涵盖常规的合法输入,更包括那些边界值、异常值和类型兼容但行为存疑的输入,在个人实践过程中,我曾针对一个处理复杂层级数据结构的工具类展开测试,最初采用传统单元测试的思路,设计了二十余组覆盖常规场景的用例,测试通过率达到100%,但当引入属性测试后,通过提炼“数据转换前后核心特征不变”“异常输入触发合规反馈而非隐性错误”等关键属性,测试工具在短时间内自动生成了数千组输入数据,成功暴露了多个隐藏在动态类型兼容场景下的行为漏洞,比如当输入数据中混合了字符串与数字类型的键名时,工具类会出现键值映射错位的问题,当输入嵌套层级超过预设阈值时,会出现数据结构扁平化不彻底的问题,这些漏洞在常规测试中完全无法被发现,因为它们既不触发报错信息,也不会导致程序终止,只是会在特定条件下产生偏离预期的输出,而这种隐性问题,在动态类型项目中往往会随着代码迭代不断放大,最终演变成难以排查的系统故障。

属性测试的有效性,本质上取决于开发者对被测试对象核心属性的提炼能力,这也是属性测试区别于传统测试的关键所在,更是考验开发者对业务逻辑理解深度的试金石,在动态类型环境中,对象的属性并非一成不变,部分属性是与代码实现细节强耦合的边缘属性,会随着版本迭代或逻辑调整发生变化,而另一些属性则是支撑对象存在的核心骨架,是与业务目标直接相关的稳定属性,属性测试的第一步,就是要精准区分这两类属性,剥离那些易变的、非核心的边缘属性,聚焦于那些稳定的、决定对象价值的核心属性,这些核心属性往往表现为行为层面的不变量,比如对象经过特定操作后状态的一致性、不同输入序列下输出结果的可复现性、参数类型兼容转换后的行为等价性等。在实践中,我曾针对一个动态生成业务配置对象的模块设计属性测试,最初因为对核心属性的认知模糊,过度关注配置项的具体数值与默认参数,导致测试用例频繁失效,每当配置项的默认值调整或新增配置字段时,测试就需要大面积修改,不仅增加了维护成本,也失去了测试的意义,后来我调整思路,重新梳理模块的业务目标,提炼出“配置对象的键值映射关系与输入源完全一致”“配置项的优先级规则在动态添加与删除过程中始终生效”“配置对象序列化与反序列化后核心业务属性保持无损”这三个核心属性,这一调整让测试用例的稳定性提升了80%以上,也让测试从对实现细节的过度依赖中解脱出来,真正成为守护业务逻辑的屏障,更重要的是,这种属性提炼的过程,本身就是对代码逻辑的深度复盘,能够倒逼开发者更清晰地梳理动态类型对象的行为边界,让原本模糊的隐式约定变得显性化、结构化,从而降低团队协作中的沟通成本,避免因认知偏差引发的开发失误。

验证属性测试在动态类型环境中的有效性,需要建立多维度的评估体系,而不是简单以测试通过率作为唯一标准,单一的通过率指标往往具有极强的迷惑性,无法反映测试的真实价值,只有从多个维度进行综合评估,才能全面衡量属性测试的有效性与实用性。第一个维度是覆盖深度,这不仅包括代码行的覆盖,更重要的是行为路径的覆盖,通过分析属性测试生成的输入数据所触发的代码执行路径,可以清晰看到哪些路径是传统测试从未触及的,这些路径往往对应着动态类型代码中最复杂的逻辑分支,比如参数类型的强制转换逻辑、异常情况的兜底处理逻辑等,在实践中,我曾对比过同一模块的传统单元测试与属性测试的路径覆盖情况,结果显示传统测试仅覆盖了65%的行为路径,而属性测试的路径覆盖率达到了92%,那些未被覆盖的路径,恰恰是最容易滋生隐性错误的区域。第二个维度是行为一致性,即在不同版本迭代中,核心属性的验证结果是否保持稳定,在个人实践中,我发现当对一个动态类型工具库进行重构时,传统单元测试需要修改超过60%的用例才能适配新的实现逻辑,而属性测试仅需调整少量输入生成策略,核心属性的验证逻辑完全无需改动,这充分体现了属性测试在应对代码演化时的超强适应性,因为它关注的是业务行为而非实现细节,只要核心业务逻辑不变,测试就无需大动干戈。第三个维度是问题发现效率,属性测试能够在代码提交后的自动化测试阶段,快速定位那些因动态类型特性引发的隐性问题,相比传统测试依赖人工复现的低效模式,属性测试可以直接输出触发问题的输入特征,极大缩短了问题排查的周期,比如一次重构后,工具库出现了罕见的配置项丢失问题,传统测试花费了两天时间仍未定位到根源,而属性测试在运行后立即输出了触发问题的输入组合,开发者仅用一小时就找到了问题所在,这种效率上的提升,对于追求快速迭代的动态类型项目而言,具有不可替代的价值。

在动态类型环境中践行属性测试,需要警惕一些容易陷入的实践误区,这些误区往往源于开发者对动态类型特性与属性测试本质的理解偏差,一旦踩入,不仅无法发挥属性测试的价值,反而会增加不必要的开发负担,甚至误导测试方向。第一个常见误区是过度抽象属性,将一些非核心的、与业务无关的特征纳入属性范畴,导致测试用例与代码实现过度耦合,一旦代码细节调整,测试就会失效,这种脱离业务本质的属性设计,完全违背了属性测试的初衷,比如在测试一个动态序列化工具时,我曾错误地将序列化后的字符串长度纳入核心属性,结果当工具引入压缩算法后,字符串长度大幅变化,导致测试全面失效,后来我将属性调整为“序列化与反序列化后内容完全一致”,才解决了这个问题。第二个误区是忽视动态类型的灵活性,用静态类型的思维设计属性测试,比如强行限制输入数据的类型范围,这不仅浪费了属性测试的场景探索能力,也与Python动态类型的设计哲学相悖,比如测试一个支持多类型输入的字符串处理函数时,若强行将输入限制为字符串类型,就会错过数字、布尔值等类型隐式转换为字符串后的行为测试,从而遗漏潜在问题。第三个误区是低估输入生成策略的重要性,简单采用默认的随机生成规则,导致生成的输入数据要么过于单一,无法覆盖复杂场景,要么过于杂乱,难以触发有价值的代码路径,在个人实践中,我曾因依赖默认生成策略而导致属性测试长期无法发现潜在问题,后来通过结合业务场景定制输入生成规则,比如针对动态数据结构设置嵌套深度阈值、针对参数类型设置兼容转换规则、针对业务逻辑设置输入数据的分布权重,才让属性测试的效能得到充分释放,这些实践误区的踩坑与复盘,让我深刻认识到,属性测试不是一个可以无脑套用的工具,而是需要结合动态类型特性与业务场景灵活调整的方法论,只有避开这些误区,才能真正发挥它的价值。

属性测试在Python动态类型环境中的应用,绝不仅限于测试层面,更能反向推动代码设计的优化与升级,实现测试与开发的双向赋能,在动态类型环境中,代码的可读性与可维护性很大程度上取决于行为契约的清晰度,而属性测试提炼核心属性的过程,正是对行为契约的显性化定义,这种显性化定义能够让团队成员更准确地理解代码的设计意图,减少因类型模糊引发的沟通成本,提升协作效率。在长期实践中,我发现引入属性测试的动态类型项目,代码的内聚性会显著提升,开发者会不自觉地规避那些行为模糊、属性混乱的设计,转而追求核心属性清晰、行为边界明确的代码结构,比如在设计一个动态数据验证工具时,因为属性测试要求核心验证规则稳定不变,开发者会主动将验证规则与输入数据的类型处理逻辑解耦,从而提升代码的可复用性与可维护性,这种由测试驱动的设计优化,远比单纯的代码评审更具约束力,也更能从根源上提升代码质量。

Python生态的生命力源于其极致的灵活性与丰富的库资源,这种特性让开发者能快速搭建各类应用、适配多元场景,却也为模糊测试的普及埋下了深层矛盾。模糊测试的核心价值在于通过非预设输入的探索性验证,捕捉常规测试难以触及的隐性风险,但其在Python生态中始终未能像单元测试工具那样融入主流开发流程,并非工具本身不够成熟,而是生态的碎片化特性、开发者的认知偏差、工具与开发节奏的适配失衡等多重因素交织,形成了一道难以逾越的普及壁垒。这种壁垒并非显性的技术难题,而是隐藏在工具选型、学习路径、流程整合等日常开发场景中的隐性阻碍,需要从生态特性与测试需求的本质矛盾出发,才能看清其核心症结——模糊测试的设计逻辑与Python开发者的使用习惯、项目的迭代节奏、生态的兼容模式之间存在着未被弥合的缝隙,这些缝隙共同构成了普及路上的隐形鸿沟。Python生态的独特性在于第三方库的爆发式增长,不同领域的库在设计理念、数据结构、执行逻辑上差异巨大,而模糊测试工具往往基于通用逻辑开发,难以兼顾各类库的特性,比如面向结构化数据处理的库与面向异步网络请求的库,对输入数据的格式、类型、边界条件的要求截然不同,模糊测试工具若缺乏针对性的适配策略,生成的输入数据要么无法触发核心逻辑,要么因格式不兼容被直接过滤,无法发挥探索性测试的真正价值,这种适配的复杂性让很多开发者在初期尝试后便选择放弃。

工具生态的碎片化适配困境,是模糊测试在Python生态中普及的首要障碍。Python生态中存在大量功能各异的框架、库与开发范式,从Web开发、数据处理到自动化脚本,不同场景下的项目架构、接口设计、数据流转逻辑差异极大,而现有模糊测试工具大多缺乏普适性的适配能力,往往针对特定场景设计,难以兼容多元开发模式。例如面向Web框架的模糊测试工具,在应对数据科学领域的矩阵运算库时,会因输入生成逻辑与数据结构不匹配而失效;针对同步代码设计的工具,在处理异步协程项目时,又会出现执行流程错乱的问题。更关键的是,Python库的版本迭代频繁,部分库的接口在迭代中缺乏向后兼容,导致模糊测试工具需要持续跟进适配,而多数工具维护团队规模有限,难以覆盖全生态的版本更新,这就使得开发者在使用时往往需要投入大量精力进行定制化改造,从输入生成规则的调整到执行逻辑的适配,一系列繁琐的适配工作让很多开发者望而却步,最终放弃引入模糊测试。以某主流模糊测试工具为例,其最初针对传统同步Web框架开发,当异步框架逐渐成为主流后,工具未能及时更新协程兼容逻辑,开发者若要在异步项目中使用该工具,需要手动修改工具的执行引擎,添加协程调度的适配代码,这不仅要求开发者熟悉工具的内部实现,还需要掌握异步编程的核心原理,对于专注于业务开发的团队而言,这种额外的技术投入远超预期收益,自然会将模糊测试排除在核心测试流程之外。

开发者的认知阈值与使用惯性,构成了模糊测试普及的深层阻碍。在Python开发群体中,多数开发者更倾向于轻量化、即时反馈的测试方式,单元测试的“编写用例-执行验证-快速迭代”模式已深入人心,形成了稳定的使用惯性。而模糊测试的核心逻辑与这种惯性存在天然差异,它需要开发者跳出“预设场景”的思维定式,转向“探索性验证”的逻辑,这种思维转换本身就存在一定的认知门槛。更重要的是,模糊测试的价值呈现方式较为间接,它无法像单元测试那样即时反馈用例通过率,而是需要通过长期运行、海量输入探索才能发现潜在风险,这种“慢反馈”特性与Python项目快速迭代的开发节奏形成了鲜明冲突。很多开发者在初期尝试时,因短期内看不到明显效果,便认为模糊测试“性价比低”,忽视了其在捕捉隐性风险、提升代码鲁棒性上的长期价值。此外,行业内对模糊测试的宣传多聚焦于复杂场景的深度验证,导致很多开发者形成“模糊测试只适用于大型项目”的认知偏差,而Python生态中大量的中小型项目、工具类库开发者,往往因这种认知而不愿尝试引入。比如一个开发轻量级数据解析库的团队,开发者习惯用单元测试覆盖常见的解析场景,当尝试引入模糊测试时,连续运行数小时未发现明显问题,便觉得模糊测试对小型项目没有价值,却忽略了那些极端数据格式可能引发的解析逻辑漏洞,这些漏洞在日常使用中出现概率低,但一旦出现就会导致整个解析流程瘫痪,而这种隐性风险的预防价值,恰恰是模糊测试的核心优势所在。

学习路径的陡峭与优质资源的匮乏,进一步加剧了模糊测试的普及难度。模糊测试本身涉及输入生成策略、覆盖准则设计、结果分析等多个专业维度,而Python生态中针对这些维度的系统化、入门级学习资源严重不足。现有资源大多偏向工具的基础使用说明,缺乏对核心逻辑、场景化适配思路的深度拆解,导致开发者在使用时往往只知其然,不知其所以然。例如很多教程仅介绍如何调用工具生成随机输入,却未讲解如何结合项目业务逻辑设计高效的输入生成规则,如何根据不同数据类型调整探索策略,这就使得开发者在面对复杂项目时,即便掌握了基础操作,也难以发挥模糊测试的真正效能。同时,Python生态中缺乏统一的实践标准与最佳案例库,不同项目的模糊测试实施路径差异较大,开发者难以借鉴成熟经验,只能在试错中摸索,这不仅增加了学习成本,还容易因初期的错误实践导致对模糊测试的误解,进一步阻碍了其普及。比如针对机器学习模型的输入验证场景,模糊测试需要生成符合特征维度、数值范围的输入数据,才能有效测试模型的鲁棒性,但现有教程几乎没有涉及这类场景的适配方法,开发者只能盲目使用默认的随机输入生成规则,导致生成的大量数据因不符合特征要求被模型直接拒绝,测试效率极低,这种低效的实践体验会让开发者对模糊测试的价值产生怀疑,最终放弃深入探索。

资源消耗与执行效能的错配,是模糊测试在Python生态中落地的现实障碍。Python作为解释型语言,运行速度本身就低于编译型语言,而模糊测试需要生成海量输入数据并反复执行被测试代码,这会带来显著的资源消耗——大量的CPU占用、内存开销以及漫长的执行时间,这种消耗在中小型项目、资源有限的开发环境中尤为突出。例如在处理复杂数据结构或逻辑密集型代码时,模糊测试的执行速度可能是单元测试的数十倍,一次完整的测试往往需要数小时甚至数天,这与Python项目快速迭代、频繁测试的开发模式严重不符。更关键的是,模糊测试的执行效能与输入生成策略的精准度密切相关,若输入生成缺乏针对性,大量无效输入会进一步拉长测试周期、浪费资源,而精准输入策略的设计又需要开发者投入额外精力,这种“高投入-低效能”的错配让很多团队在权衡成本后,选择放弃引入模糊测试,即便认可其价值,也只能将其作为边缘测试手段,难以融入核心开发流程。以一个处理复杂数学运算的工具库为例,其核心函数包含多层嵌套的逻辑判断,模糊测试需要生成大量不同数值范围、精度的输入数据,在普通的开发电脑上,一次完整的测试需要占用80%以上的CPU资源,持续运行超过12小时,期间开发者无法进行其他开发工作,这种资源占用与时间成本,对于追求快速迭代的小型团队而言,显然是难以承受的,最终只能将模糊测试从日常测试流程中剔除。

生态级集成支持的缺失,让模糊测试难以形成顺畅的使用闭环。在Python生态中,单元测试工具已与主流IDE、CI/CD平台、代码管理工具形成深度集成,开发者可以在编码过程中即时编写用例、提交代码后自动触发测试、通过平台直观查看结果,这种无缝集成的体验极大降低了使用门槛。而模糊测试工具在这方面的集成支持严重不足,多数工具仍以独立运行的命令行形式存在,缺乏与主流开发工具链的适配,导致开发者需要在不同工具之间手动切换、传递数据,打破了原有的开发流程闭环。例如在CI/CD流程中,模糊测试的配置复杂且缺乏标准化,不同平台的适配方式各异,需要开发者编写大量定制化脚本才能实现自动触发;在结果分析环节,多数工具输出的日志格式杂乱,缺乏与代码调试工具的联动,开发者需要手动定位问题关联的代码片段,排查效率极低。这种集成层面的断层,让模糊测试无法像单元测试那样融入日常开发的每一个环节,只能作为额外的“附加操作”,自然难以得到广泛普及。比如在GitHub Actions中配置模糊测试,开发者需要手动编写yaml配置文件,指定工具的安装路径、执行命令、输出目录,还需要处理不同操作系统的兼容性问题,而单元测试只需调用内置的测试命令即可自动运行;在结果分析时,模糊测试工具输出的日志仅包含输入数据与执行结果,开发者需要手动将输入数据代入代码调试,才能定位问题根源,这种繁琐的操作流程,让模糊测试的使用体验远不如单元测试流畅,难以被开发者广泛接受。

价值转化的模糊性与评估体系的缺失,是模糊测试普及的终极桎梏。模糊测试的核心价值在于预防潜在风险、提升代码鲁棒性,但这种价值难以量化评估,不像功能测试那样可以通过用例通过率、缺陷修复率等明确指标衡量。在Python生态中,多数项目缺乏对模糊测试价值的评估标准,开发者无法直观判断引入模糊测试后代码质量的提升幅度,也难以向团队或管理层证明其投入的合理性。例如模糊测试发现的隐性风险,若未发生实际影响,往往被认为是“无关紧要的问题”,其预防价值被严重低估;而即便发现了关键风险,也因缺乏前后对比数据,难以量化其避免的损失。这种价值转化的模糊性,导致很多团队在资源分配时优先选择价值明确的测试手段,而将模糊测试排在次要位置。此外,行业内尚未形成统一的模糊测试效果评估框架,不同项目对“有效测试”的定义各异,进一步加剧了价值认知的混乱,让开发者在引入时缺乏明确的目标导向,最终难以坚持长期使用,也阻碍了模糊测试在Python生态中的广泛普及。

引言:一个“非随机”的选择困境

当你向ChatGPT、DeepSeek或文心一言提问:“2026年最适合程序员的轻薄本是哪款?”时,AI生成的答案中,为何总是那几款品牌被反复推荐,而其他性能相近甚至更具性价比的产品却踪迹全无?

这个看似“智能”的推荐,背后绝非随机选择。它是一场发生在高维向量空间、由复杂概率计算主导的精密博弈。你的品牌未被提及,不是因为产品不好,而是因为在大模型的“世界模型”里,你的信息未被有效地编码、关联,或在最终生成阶段被其他更高权重的信息“挤掉”。

本文将以技术侦探的视角,试图拆解大模型生成答案的“黑盒”流程,并逆向推演一套名为 GEO(生成式引擎优化) 的技术体系,如何通过系统工程方法,科学、可度量地提升品牌信息在这一链条中的 引用概率。

第一章:逆向工程——大模型生成答案的“三层漏斗”

尽管各大模型的内部权重与训练数据是核心机密,但根据公开论文(如Transformer架构、RAG系统原理)及可观测现象,我们可以将其生成包含外部信息的答案过程,简化为一个 “召回-排序-生成” 的三层漏斗模型。

1. 召回(Recall):从“信息宇宙”中捕捞候选集

发生了什么? 当模型解析你的问题(Query)后,它并非从完整训练数据中逐字扫描,而是将问题转化为一个高维向量(Embedding),并在其内部的索引或关联的外部知识库中,进行近似最近邻搜索(ANN),快速召回一批语义相关的信息片段(Chunks)。这些片段可能来自训练数据中的网页、文档、问答对,或实时检索的结果。

技术挑战: 如果你的品牌内容(官网、评测、技术文档)在语义上与用户的高频提问方式向量距离过远,或在数据索引中权重过低、特征不明显,就会在召回层被直接过滤掉。这是“零推荐”的根本原因之一。

2. 排序(Ranking):对候选信息进行“价值评估”

发生了什么? 召回的上百条候选信息,将进入一个复杂的排序环节。模型会综合评估每条信息的:

相关性(Relevance): 与问题的语义匹配度。

权威性(Authority):信源本身的权重(如知名媒体、官方机构、高权威域名)。

新鲜度(Freshness): 信息的时效性。

流行度(Popularity): 在训练数据中被引用的广泛程度。

技术挑战:即使被召回,如果你的内容在权威性(未被高质量信源引用)、新鲜度(信息陈旧)、流行度(网络声量小)等维度上得分不足,其综合排序也会靠后,难以进入最终生成的候选名单。

3. 生成(Generation):基于概率采样构造最终答案

发生了什么? 模型根据排序靠前的信息片段作为核心上下文,结合其预训练的世界知识,通过自回归的方式逐词生成答案。在此过程中,它会对提及的具体实体(如品牌名、产品型号)进行概率采样。排序更高、在上下文中出现更连贯、更符合模型“认知”的实体,被采样的概率自然更大。

技术挑战:生成环节的随机性背后是概率的博弈。如果你的品牌信息未能与“理想答案”的上下文强绑定,或者表述方式(如昵称、别称)未被模型良好对齐,也可能在最后一刻“落选”。

第二章:GEO的理想框架——在“三层漏斗”中施加技术干预

要系统性地提升引用概率,就必须针对上述三层漏斗,设计一套可工程化的技术干预框架。一个理想化的GEO系统应包含以下核心模块:

垂直诊断模型(用于理解与预测):

目标: 逆向诊断目标大模型(如DeepSeek、GPT-4)在特定领域的偏好与逻辑。它需要理解:对于某类问题,模型倾向于召回什么类型的内容?排序时更看重什么信号?

技术实现猜想: 可能需要通过海量的问答对进行对比学习,或对开源模型进行针对性微调,构建一个能够模拟目标模型部分决策行为的“镜像模型”。

向量化运营数据库(用于优化召回与排序):

目标: 不再将内容视为孤立的文本,而是将其结构化、向量化存储。运营重点是将品牌内容的关键信息,以更易被模型“召回”和“理解”的方式重新组织。

技术实现猜想: 建立行业知识图谱,将产品特性、使用场景、用户痛点映射为标准化的向量表示。同时,需要追踪哪些外部高权威信源引用了品牌,并优化这些“引用锚点”的内容。

实时反馈控制系统(用于验证与迭代):

目标: 构建一个分钟级监测系统,能够量化每一次优化动作(如发布一篇技术白皮书、获得一个权威媒体引用)对最终AI引用概率的影响。

技术实现猜想: 需要自动化地模拟海量用户提问,抓取AI答案,并通过NLP技术解析其中品牌露出的位置、情感和上下文,形成归因分析报告,驱动策略迭代。

第三章:从理论到实践——万数科技的“工程应答”

当我们把视线投向业界,会发现 万数科技 提出的技术栈,几乎是对上述理想GEO框架的一次精准工程实现。他们的方案不是功能罗列,而是针对每个工程挑战的深度解决方案。

1. 对“垂直诊断模型”的应答:DeepReach大模型

设计原理揭秘: DeepReach并非一个通用的聊天模型,而是一个专门针对 “预测并提升被主流模型引用概率” 这一任务进行优化的垂直模型。其技术栈深入Transformer堆栈的中间层表示、高维向量空间的几何关系以及温度参数对生成随机性的影响。简单说,它通过技术手段(可能包括对抗性训练、梯度信号分析等)尝试“学习”目标模型的内部打分机制,从而能更准确地诊断:优化哪些内容、以何种形式呈现,最能撬动目标模型的排序权重。

2. 对“向量化运营数据库”的应答:量子数据库 + 翰林台平台

设计原理揭秘:

量子数据库 解决了“如何高效组织与检索海量优化语料”的问题。它通过系统化多级行业数据向量化编码和分布存储,不仅存储内容,更存储内容之间的语义关联和优化归因。它支持大模型数据混合学习,意味着优化行动产生的新数据(如一次成功的AI推荐案例)能被拆解、归因,并反哺给DeepReach模型,形成一个自我强化的学习闭环。

翰林台AI定制内容平台 则是将诊断结果和数据库知识,转化为标准化作战动作的“兵工厂”。它基于DeepReach的理解,自动生成在特定模型看来权威性更高、相关性更强、更易被集成的跨模态内容(技术文档、Q&A对、场景化评测),并确保内容格式符合不同AI平台的偏好(多模态适配化)。

3. 对“实时反馈控制系统”的应答:天机图数据分析系统

设计原理揭秘: 这是将GEO从“艺术”变为“科学”的关键。天机图系统实现了对优化效果的定量数据化监测。它能:

洞察意图演化: 分析用户提问模式的变迁,提前布局内容。

分钟级追踪效果: 当一个新的优化内容被部署后,系统能快速监测到它在目标AI答案中排名或提及率的变化。

归因分析: 将“效果波动”与“运营动作”在时间线上关联,明确是哪些具体操作(如更新了某核心页面的Schema标记、在某高权重论坛发布了深度帖)驱动了引用概率的提升。

方法论闭环:GRPO法则
其独创的 GRPO法则 正是将上述三项技术组件串联起来的“操作系统级”工作流。它规定了从 表达结构化(G)、多模态适配化(R)、定量数据化(P) 到 整体优化(O) 的标准作业程序,确保整个干预过程是严谨、可重复、可度量的工程实践,而非依赖灵感的随机尝试。

结论:从“黑盒猜测”到“白盒干预”

GEO的终极目标,是将在海量参数中运行的、非确定性的AI生成过程,通过一套外部的、系统性的工程技术框架,变得更具可预见性和可影响力。

它不再是对“黑盒”的盲目猜测,而是通过 垂直模型(DeepReach)进行深度诊断、利用 向量数据库(量子数据库)重构信息资产、并通过 实时反馈系统(天机图)构建控制闭环 的“白盒化”干预尝试。万数科技 的技术栈展示了一条清晰的路径:将影响大模型引用概率这一宏大课题,分解为一个个可被测量、可被优化的工程子任务。

对于技术团队而言,理解这套框架的价值在于:当你们在选择GEO服务商或考虑自研时,可以不再被模糊的承诺所迷惑,而是能够尖锐地提问:你们的技术,究竟是在召回、排序还是生成层发挥作用?你们的模型,是简单调用API,还是真正具备逆向诊断能力?你们的数据,是散乱的文档,还是结构化的、可归因的向量网络?

答案,将决定你的品牌是永远在AI的“黑盒”外徘徊,还是能够深入其内部逻辑,赢得这场关于未来注意力的关键战争。

MES制造执行系统是精益生产的重要支撑工具,它能够帮助企业实现生产过程的数字化、智能化和精细化管理,提高生产效率和质量,降低生产成本,为企业创造更大的价值。

MES制造执行系统是一种集生产计划、物料管理、工艺执行、设备控制、质量管理等功能于一体的软件系统。它通过实时监控生产过程、收集并分析生产数据,为管理层提供决策支持,同时为操作层提供指导和帮助。

image.png

制造行业在生产过程中所面临的挑战

1、无法预测生产线需求使用

随着工厂订单量的增多,在生产前,人工往往不知道或无法快速预知在一条产线中应该做哪个订单的那些工序,所需量是多少,要提前准备何种物料。

2、不能及时掌握生产情况

每天的生产数据需要人工事后填写和统计,管理层不能及时掌握订单在车间的最新生产情况。无法及时得知当前每个生产订单、每个工序的生产进度如何、哪些未按计划开始、哪些未按计划完工、特急件是哪些、良品数、不良品数分别多少等等问题。

3、没有对比,无竞争感

因为没有即时的目视指令和电子看板,现场人员没有绩效对比和竞争,没有紧迫感。不知道当前谁的效率高?谁的效率低?

4、无法及时得知当前机台产线情况

当前哪些机台产线是在工作或是停机?机台、产线有多少时间在生产,多少时间在停转和空转?利用率是多少?这些都是无法及时得知,只能通过记录得知。

5、无法及时得知致错原因

无法及时得知过去几小时之内,车间出现最多的不良品是什么原因造成的?不良率有多高等问题。

6、无法追溯源头

用户投诉产品不良时,如何立即追溯该产品的历史生产过程信息?如:是谁、在什么时间、在哪台机器上、用什么材料做的?该产品加工过程经过了哪些工序?当时的工艺参数是怎样的等问题。

MES系统对企业生产管理有哪些改进?

在精益生产的背景下,MES制造执行系统发挥着至关重要的作用,具体来说,MES制造执行系统在精益生产中的改善企业生产的五大方法:

image.png

1、全面的生产能力平衡分析

在企业生产过程中,不同的人员和设备都有着不同的生产能力,不同的产品有着不同的生产能力需求,若采用同一种生产任务分配模式,容易造成车间生产能力与完成计划所需能力之间的不协调,直接导致车间生产现场混乱,且难以合理调整各工作中心的生产分配量。

MES系统拥有最直观的图形和文字,可以为企业提供最精准的设备任务负荷分析、部门/班组任务负荷分析等数据。通过详细的数据逐级查询和分析,还能够帮助企业计划和调度人员进行生产任务的外协和均衡,并实现最优的生产计划排程。

2、高效的生产计划管理

在没有使用MES系统之前,企业生产信息的获取,只能通过人员填写的报表反馈或者电话汇报。这样,信息获取不够及时,影响企业管理层及时有效地下达管理指令,制约了管理措施的有效实施。

MES系统能够全面管理企业订单的整个生产流程,通过生产信息的采集和多维度的看板展现,让每个订单、每个零件、每道工序等实时信息,及时展现给车间管理人员,使企业各级领导更加便捷地掌握生产任务执行状况,并迅速做出生产决策,确保实现生产任务按时、按节点完成。

3、便捷的任务派工管理

生产订单的执行往往需要通过多道不同的工序来完成。未使用MES系统时,开始阶段可能对生产任务执行的进度比较了解,但一旦工序并行作业,就无法完成工单的追踪和管理了。MES系统拥有强大的任务动态调度能力,能够及时响应生产现场各种状态的变化。

在生产计划完成后,系统还能够自动生成任务派工单,并通过条码扫描向生产现场自动输送加工程序、零件图纸、工艺指导文档等,大大节约工作人员在生产现场来回奔波的时间。

4、完善的产品质量管理

制造企业把产品质量视为企业的生命,全面的质量管理保障高品质的产品。MES系统通过对原材料、生产过程以及在用户使用中的产品的整个生命周期进行数字化、网络化和动态化的管理,实现对产品质量的管控和追溯。

这样,MES系统就可以通过持续不断的改进,帮助企业完善全面的质量管理体系,进而有效控制生产成本。

5、最优的车间库存管理

库存管理是每个企业在生产过程中都不可或缺的环节。合理的库存控制,可以有效避免生产停滞,为企业建立良好的生产环境。MES系统支持原材料、成品、半成品、工具等的库存管理,所有流程通过条码扫描操作,既准确又便捷。

MES系统彻底改善了企业车间生产管理流程,实现车间管理无盲点,生产管控一体化的新模式。其最前沿的信息技术在各大制造企业间得到强烈反响,并成为支撑制造企业高速发展的内在动力。

成功实施MES系统需先完善管理基础

将MES系统导入到企业的运作体系之中,企业需要先完善管理基础,根据自身情况选择好“合身”的MES软件系统,而后采用科学的实施方法充分准备,才能促使MES正式运行、发挥效用。

要想成功实施MES,企业必须先在管理上下功夫——与MES密切相关的工作,包括车间环境、职责分工以及人员保障等方面。

1、定置,改善车间环境

定置,是指通过对生产和工作环境的分析,把生产和工作需要的物品按照工艺的需要科学地确定位置。定置管理,则是指对现场物品定置的设计、组织、实施、控制,使现场管理达到科学化、规范化、经常化的全过程。定置管理为生产者在较短的时间内用较低的成本制造出高质量产品提供良好的客观条件。通过定置管理,理顺物流,可以为MES的实施提供良好的车间环境。

2、合理分工,明确职责

“计算机能够解决一切管理问题。”——这是相当一部分企业领导在实施MES时的一个误区。事实上,许多企业面临的管理问题是不可能靠计算机来解决的,必须靠企业自身通过科学的组织、严格的规章及有效的控制来解决。计算机只能通过信息的获取与加工、一定的流程控制来支持企业管理思想的贯彻。有些企业的组织结构不合理、职能相互重叠,其结果是责任不清、相互扯皮。

这一方面妨碍了MES的顺利实施,另一方面也难以保证MES正常高效地运行。因此,在实施MES时,必须对企业的业务流程进行合理重组,去除重叠的部门职能,减少无效劳动,合理分工、明确责职。这样既可以简化MES软件的权限设置和流程控制,又能够保证信息处理的及时性,为MES的实施提供组织保证。

3、提供人员保证

MES系统实施,通常涉及到计算机人员、企业管理人员、车间现场操作人员和具体业务人员等方面。不仅涉及面广,而且各类人员的文化水平、业务能力、计算机应用水平也参差不齐。因此,为了保证MES的顺利实施,必须对相关人员进行足够的培训。对不同类型、不同层次人员的培训方式应有所不同。

实际上,在整个MES实施过程中,培训工作是贯彻始终的。不仅要在实施准备阶段进行原理培训,而且在实施准备、模拟运行与试运行、切换运行、新系统运行过程中也要进行有关培训,如软硬件产品培训、系统管理员培训和持续扩大培训等。只有通过培训让企业员工对MES软硬件产品有了一定的了解,才能够保证系统最终的顺利实施和应用。

【声明】:以上所发文章仅供大家学习参考,请不要作商业用途;MES系统的专业性很强,文中难免有错误,一旦发现,请联系我们及时更正;文中部分图片源于网络,侵删。

最后感谢图片内容的提供方:织信MES,该厂商专注企业信息化系统管理10年余,坚持传播生产管理知识,自研低代码开发底座,基于B/S架构,可帮助企业快速构建生产管理所需的各项功能,可根据客户实际作业流程和管理要求实现定制化开发,系统内置自定义开放接口OpenAPI,能够对接所有的管理信息系统,广泛应用于国内外各行各业。

概要

云服务器市场的供应商激增,如今许多厂商经常将自己的服务器宣传得像天花乱坠,夸大其性能优势。这让许多新手用户在选择时感到困惑,往往难以判断选择合适的服务器,或者说如何科学地面对服务器的实际性能。这种情况,我们需要借助命然而,对于大多数新手来说,高效命令行的使用可能不太熟悉,这也使得他们在判断服务器性能时面临技术领先。为了帮助大家发现这些陷阱,博主特地整理了一些命令,帮助你快速检测服务器的真实性能,确保不再“踩坑”!

脚本1

wget -qO- bench.sh | bash

脚本2

  wget -qO- https://raw.githubusercontent.com/oooldking/script/master/superbench.sh | bash

看上下行

脚本3

(wget -qO- wget.racing/nench.sh | bash; wget -qO- wget.racing/nench.sh | bash) 2>&1 | tee nench.log

脚本4

curl -s bench.wget.racing | bash

脚本5

curl -s https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/yabs.sh | bash

可比较真实的测试服务器带宽

脚本6

curl -fsL https://ilemonra.in/LemonBenchIntl | bash -s fast

可测试是否支持Netflxi等(不一定准确)

脚本7

wget -N --no-check-certificate https://raw.githubusercontent.com/veip007/hj/master/hj.sh && chmod +x hj.sh && bash hj.sh

全能,测速、加速 DD系统等

脚本8

(curl -s wget.racing/nench.sh | bash) 2>&1 | tee nench.log

脚本9

screen -S uping
wget -N --no-check-certificate https://raw.githubusercontent.com/FunctionClub/uPing/master/uping.py
python uping.py

服务器延迟监测
脚本10

wget -qO- --no-check-certificate https://raw.githubusercontent.com/qd201211/Linux-SpeedTest/master/superbench.sh | bash

系统配置、国内速度等

脚本11

wget --no-check-certificate https://zhujiwiki.com/wp-content/uploads/2018/07/unixbench.sh

chmod +x unixbench.sh

./unixbench.sh

UnixBench跑分,测试主机性能

运行10-30分钟后(根据CPU内核数量,运算时间不等)得出分数,越高越好

脚本12
访问:https://netflix.com/title/80018499
测试是否可以观看Netflix(奈飞)

脚本13

bash <(curl -Lsk https://raw.githubusercontent.com/BigMangos/speedtest-go-script/master/install.sh)

测试本地速度speedtest go版本的一键安装脚本

脚本14

bash <(curl -Lso- http://yun.789888.xyz/speedtest.sh)
        或者
 bash <(curl -Lso- https://zhujiwiki.com/wp-content/uploads/2021/12/speedtest.sh)

一键测试三网速度

脚本15

curl https://raw.githubusercontent.com/zhucaidan/mtr_trace/main/mtr_trace.sh|bash

一键测试TCP三网回程线路

脚本16

curl https://raw.githubusercontent.com/zhanghanyun/backtrace/main/install.sh -sSf | sh

一键测试TCP三网回程线路

脚本17

bash <(curl -Lso- https://bench.im/hyperspeed)
 bash <(curl -Lso- https://2life.top/speedtest.sh)

国内三网速度

脚本18

`curl -sL yabs.sh | bash
`
yabs,系统性能测试

脚本19

wget -O box.sh https://raw.githubusercontent.com/BlueSkyXN/SKY-BOX/main/box.sh && chmod +x box.sh && clear && ./box.sh

综合工具箱

脚本20

 wget -q https://github.com/Aniverse/A/raw/i/a && bash a

独服测试

脚本21

wget -qO- benchy.pw | sh

curl -Ls benchy.pw | sh

已开源:https://github.com/L1so/benchy

22、系统信息和测速

含国内、亚洲、国际等节点,可选节点

1、面向全球

wget -qO- network-speed.xyz | bash

2、限定区域,包括国内

curl -sL network-speed.xyz | bash -s -- -r region_name

中国

curl -sL network-speed.xyz | bash -s -- -r china

亚洲

curl -sL network-speed.xyz | bash -s -- -r asia

region_name = na, sa, eu, asia, middle-east, india, china, iran

23、TCP三网回程

bash <(curl -Ls https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh) && nexttrace -F -T

24、VPS一键脚本工具

curl -fsSL https://raw.githubusercontent.com/eooce/ssh_tool/main/ssh_tool.sh -o ssh_tool.sh && chmod +x ssh_tool.sh && ./ssh_tool.sh

wget -qO ssh_tool.sh https://raw.githubusercontent.com/eooce/ssh_tool/main/ssh_tool.sh && chmod +x ssh_tool.sh && ./ssh_tool.sh

总结

在使用性能测试脚本时,绝对不能随便运行从网上找到一个就直接在自己的服务器上。很多正常的脚本,背后可能被某些不法分子侵入了后门或病毒,这些恶意代码可能会窃取你的资源和数据,甚至利用你的服务器进行恶意攻击,导致你所在的服务区域瘫痪,影响其他用户的正常使用。因此,安全性作为首要,必须确保所用的测试脚本来源可靠、无害。博主在这里为大家收集的脚本,都是经过检验的。欢迎大家使用,之后博主还会不断的更新!

vLLM 是一款专为大语言模型推理加速而设计的框架,实现了 KV 缓存内存几乎零浪费,解决了内存管理瓶颈问题。

更多 vLLM 中文文档及教程可访问 →https://vllm.hyper.ai/

*在线运行 vLLM 入门教程:零基础分步指南

源码 examples/offline_inference/rlhf_utils.py

import torch


def stateless_init_process_group(master_address, master_port, rank, world_size,
                                 device):

    """
    vLLM 提供 `StatelessProcessGroup` 来创建进程组,
    无需考虑 torch.distributed 中的全局进程组。
    建议先创建 `StatelessProcessGroup`,然后初始化
    外部(训练进程)与 vLLM 工作进程之间的数据平面通信(NCCL)。
    """
    from vllm.distributed.device_communicators.pynccl import PyNcclCommunicator
    from vllm.distributed.utils import StatelessProcessGroup
    pg = StatelessProcessGroup.create(host=master_address,
                                      port=master_port,
                                      rank=rank,
                                      world_size=world_size)
    pynccl = PyNcclCommunicator(pg, device=device)
    return pynccl


class WorkerExtension:

    """
    vLLM 工作进程的基类。
    通过定义扩展类,无论底层工作进程类是什么,代码都能正常工作。
    这种方式使代码能同时兼容 vLLM V0 和 V1。
    注意:我们在单独模块中定义此类,主模块应将完整限定名
    作为 `worker_extension_cls` 参数传递。
    """

    def init_weight_update_group(self, master_address, master_port,
                                 rank_offset, world_size):
        from vllm.distributed.parallel_state import get_world_group
        rank = get_world_group().rank + rank_offset
        self.model_update_group = stateless_init_process_group(
            master_address,
            master_port,
            rank,
            world_size,
            self.device,
        )

    def update_weight(self, name, dtype, shape):
        weight = torch.empty(shape, dtype=dtype, device="cuda")
        self.model_update_group.broadcast(weight,
                                          src=0,
                                          stream=torch.cuda.current_stream())

        self.model_runner.model.load_weights(weights=[(name, weight)])

        del weight

    def check_weights_changed(self):
        """
        Check if the weights are updated to 0.
        """
        """
        检查权重是否已更新为 0。
        """
        weights_updated = True
        for name, p in self.model_runner.model.named_parameters():
            weights_updated = weights_updated and torch.allclose(
                p, torch.zeros_like(p))
        return weights_updated


class ColocateWorkerExtension:

    """
    vLLM 工作进程在协同部署场景下的基类。
    通过定义扩展类,无论底层工作进程类是什么,代码都能正常工作。
    这种方式使代码能同时兼容 vLLM V0 和 V1。
    注意:我们在单独模块中定义此类,主模块应将完整限定名
    作为 `worker_extension_cls` 参数传递。
    """

    def report_device_id(self) -> str:
        from vllm.platforms import current_platform
        self.device_uuid = current_platform.get_device_uuid(self.device.index)
        return self.device_uuid

    def update_weights_from_ipc_handles(self, ipc_handles):
        handles = ipc_handles[self.device_uuid]
        device_id = self.device.index
        weights = []
        for name, handle in handles.items():
            func, args = handle
            list_args = list(args)
            # the key is to change device id to the current device id
            # in case two processes have different CUDA_VISIBLE_DEVICES
            # 关键是将设备 ID 改为当前设备 ID,
            # 以防两个进程有不同的 CUDA_VISIBLE_DEVICES
            list_args[6] = device_id
            tensor = func(*list_args)
            weights.append((name, tensor))
        self.model_runner.model.load_weights(weights=weights)
        torch.cuda.synchronize()

    def check_weights_changed(self):

        """
        检查权重是否已更新为0。
        """
        weights_updated = True
        for name, p in self.model_runner.model.named_parameters():
            weights_updated = weights_updated and torch.allclose(
                p, torch.zeros_like(p))
        return weights_updated

Triton 是一种用于并行编程的语言和编译器。它旨在提供一个基于 Python 的编程环境,以高效编写自定义 DNN 计算内核,并能够在现代 GPU 硬件上以最大吞吐量运行。

更多 Triton 中文文档可访问 →triton.hyper.ai/

triton.language.where(condition, x, y)

根据 condition 返回来自 x 或 y 元素的张量。
注意:无论 condition 的值是什么,x 和 y 总是会被求值。
如果希望避免意外的内存操作,请使用 triton.load 和 triton.store 中的 mask 参数。
x 和 y 的形状都会被广播到 condition 的形状。x 和 y 必须具有相同的数据类型。
参数

  • conditiontriton.bool 的块)- 当为 True(非零)时,产生 x,否则产生 y。
  • x - 在条件为 True 的索引处选择的值。
  • y - 在条件为 False 的索引处选择的值。

作为一名求职中的中年软件工程师,由于地域和年龄限制,我的选择空间其实就那么几家。我经常需要反复查看自己感兴趣公司的招聘页面。这一过程既耗时又枯燥,尤其是在需要同时跟踪多家公司职位的情况下。虽然许多招聘网站都提供基于邮件的职位提醒,但这些提醒通常要么依赖于对已提交简历进行不透明的 AI 匹配,要么只是简单的关键词匹配。在这两种情况下,我对实际的匹配条件几乎没有控制权。

为了解决这个问题,我决定利用一个 AI Agent 来自动 Watching 招聘页面,并在发布符合我自己定义条件的新职位时通知我。在本文中,我将介绍一个用于验证这一想法的概念验证(PoC)。

在这个 PoC 中,我展示了如何使用 AI Agent 来 Watching 一家公司招聘页面上的软件开发岗位。该 Agent 能够自动浏览招聘网站、搜索相关职位、提取结构化信息,并将结果存储到 SQLite 数据库中,以便后续查询和跟踪。

PoC

职位数据来源

在本次实验中,我选择了一家其招聘页面基于 Eightfold AI 平台构建的公司。如果你的目标公司同样使用 Eightfold AI,那么只需做很少的修改即可复用该 PoC。

Eightfold AI 是一个人才智能平台,利用人工智能支持招聘、员工留任以及劳动力发展。它基于技能和经验将候选人与开放职位进行匹配,目前已被包括 Vodafone、Morgan Stanley 和 Chevron 在内的 100 多家公司使用。该平台覆盖 155 多个国家。

尽管 Eightfold AI 平台本身已经提供了基于 AI 的职位提醒订阅功能,但我仍希望对匹配逻辑和采集到的数据拥有更细粒度的控制,因此才希望有一套自定义解决方案。

Agent 设计

我在 VS Code Copilot Chat 环境中实现了该 PoC,并使用了以下工具和提示词。

MCP 工具

  • 浏览器工具browsermcp:用于导航和操作招聘网站页面。
  • SQLite 数据库工具genai-toolbox:用于持久化存储提取到的职位数据。

./vscode/mcp.json

{
  "servers": {
    "browsermcp": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "@browsermcp/mcp@latest"
      ]
    },
    "sqlite": {
      "command": "~/genai-toolbox/toolbox",
      "args": [
        "--prebuilt",
        "sqlite",
        "--stdio"
      ],
      "env": {
        "SQLITE_DATABASE": "~/jobs/jobs.db"
      }
    }
  }
}

数据库 Schema

CREATE TABLE IF NOT EXISTS xyz_company_jobs (
    job_id TEXT PRIMARY KEY, -- 职位的唯一标识
    req_id TEXT,             -- 招聘需求 ID
    job_title TEXT,          -- 职位名称
    location TEXT,
    date_posted TEXT,        -- 格式:'YYYY-MM-DD'
    business_department TEXT,
    job_description_url TEXT,
    job_description TEXT     -- 职位描述的主要内容
);

Agent 提示词

你是一个可以访问浏览器工具和 SQLite 数据库工具的 AI Agent。你的任务是从 XYZ_Company 的招聘网站中收集与软件开发相关的职位信息,并将提取到的数据存储到 SQLite 数据库中。当前浏览器中打开的页面是 XYZ_Company 的职位搜索门户。

数据库:
```sql
CREATE TABLE IF NOT EXISTS xyz_company_jobs (
    job_id TEXT PRIMARY KEY, -- 职位的唯一标识
    req_id TEXT,             -- 招聘需求 ID
    job_title TEXT,          -- 职位名称
    location TEXT,
    date_posted TEXT,        -- 格式:'YYYY-MM-DD'
    business_department TEXT,
    job_description_url TEXT,
    job_description TEXT     -- 职位描述的主要内容
)
```

规则:
- 在调用一次 `click()` 操作后,必须等待 10 秒,确保页面完全加载,然后再调用 `snapshot` 捕获当前页面状态。
- 在向 `xyz_company_jobs` 表插入新记录之前,需要检查 `job_id` 是否已经存在,以避免重复数据。
- 在生成 INSERT SQL 语句时,确保对值中的单引号进行正确转义。
- 不要收集或点击位于 `document > main > group Similar Position` 区域下的职位。

用户已经准备的环境:
- 浏览器中打开的页面是 XYZ_Company 的职位搜索门户。

你的安装过程:
1. 如果 `xyz_company_jobs` 表不存在,则创建该表。在执行 SQL 时,需保留 SQL 代码块中的注释行。

执行过程:
1. 每一个文本匹配 "$Job_Title$ 于 $time_since_publication$ 前发布" 模式的按钮都代表一个职位。通过 `今天 - time_since_publication` 计算 `Date Posted`。
2. 点击职位按钮以打开职位详情页面,该页面的 URL 即为 `Job Description URL`。
3. 从职位详情页面中提取:职位名称、工作地点、Job ID、业务部门(可选)、Req ID 以及职位描述的主要内容。
4. 仅收集与软件开发相关的职位。
5. 将每个收集到的职位插入 `xyz_company_jobs` 表,并基于 `job_id` 确保不产生重复记录。
6. 如有需要,点击 `更多职位` 按钮以加载更多职位。
7. 至少收集并存储 10 个职位。

运行

  1. 打开一个 Chrome 浏览器标签页,访问 XYZ 公司的招聘门户网站。在该标签页上激活 Browser MCP Chrome 扩展程序。
  2. 在 VS Code 中启动一个新的 Copilot Chat 会话,并使用上述 MCP 配置和提示。

总结

通过上述配置,我成功实现了一个 AI Agent,它能够自动 Watching 目标公司的招聘页面,发现新的软件开发职位。该 Agent 可以自动浏览招聘网站、识别相关职位、提取结构化数据,并将其存储到 SQLite 数据库中,从而方便后续访问和长期跟踪。

后续工作

该 PoC 还可以在多个方面进行扩展:

  • 引入更复杂的匹配逻辑,例如简历解析和基于语义的技能匹配。
  • 将收集到的职位信息导出为 RSS 或邮件摘要,从而构建一个完全自托管的职位提醒系统。
  • 添加通知机制,在发现新的匹配职位时立即提醒我。

为了标准化 iOS 和 Android 平台的事件工具,Uber 工程团队重新设计了其移动分析架构,解决了所有权分散、语义不一致和跨平台数据不可靠的问题,目标是简化工程工作,提高数据质量,并为骑手和司机应用的产品和数据团队提供可靠的洞察。

 

根据 Uber 工程师的说法,移动分析对于决策、功能采用和衡量用户体验至关重要。随着应用程序和团队的增长,工具变得分散。功能团队独立定义并发出事件,共享 UI 组件常常缺乏分析钩子,类似的交互在不同的团队中有不同的记录方式。其结果是,超过 40%的移动事件属于自定义或临时事件,这不仅增加了分析复杂度,还降低了聚合指标的可信度。

 

为了应对这些挑战,工程师将核心分析职责从功能级代码转移到了共享基础设施。他们与产品、设计和数据科学团队合作,定义了点击、展示和滚动等标准事件类型。这些事件基于共享模式通过代码生成,在 UI 组件层进行监控,通过集中式报告层输出,由后端服务进行数据增强,并通过 Uber 的分析管道进行消费。

Uber 移动分析系统架构(图片来源:Uber博客

 

其中一项关键决策是将分析逻辑嵌入到平台级 UI 组件中。工程师引入了分析构建器,用于管理事件生命周期、元数据附件和事件发出逻辑,使功能团队可以开展标准化的分析工作而无需编写自定义工具。他们对包含 100 个展示记录组件的示例应用做了性能测试,结果显示,CPU 使用率或帧率没有退化,这是在性能敏感设备上推广该工具的先决条件。

ImpressionAnalyticsBuilder 类事件生成的数据流图(图片来源:Uber博客

 

该平台还实现了常见的元数据收集。应用级元数据(如接送地点或餐厅 UUID)会自动记录,而事件类元数据(包括列表索引、行标识符、滚动方向和视图位置)则由 AnalyticsBuilder 捕获。界面通过 Thrift 模型实现了标准化,可以确保容器视图、按钮和滑块的日志记录保持一致。

分析元数据金字塔概览(图片来源:Uber博客

 

为了验证平台有效性,工程师通过新旧 API 对两个功能进行了 dual-emitted 分析。查询结果表明,跨平台事件量、元数据及界面是匹配的,而像滚动开始/停止计数和视图位置等语义也保持了一致。试点应用揭示了平台和记录方法的差异,并突出了列表增强的好处——将多个行事件合并为单个标准化事件,简化了查询并提高了可测试性。功能团队还采用了可见性检查机制,减少了自定义实现。

 

试点应用之后,Uber 分析团队进行了旧事件到标准化 API 的迁移,使得产品团队可以专注于他们的路线图。在需要支持的地方,他们创建了自动化脚本,扫描 iOS 和 Android 代码,评估高优先级事件,并生成适合迁移的列表。平台团队还添加了一个 linter,目的是拦截使用非标准 API 新建的点击或展示事件,防止它们进一步漂移。根据工程师的反馈,跨平台一致性得到提升,元数据和语义保持了统一,工具代码量减少,展示计数更可靠,并实现了可扩展的开箱即用 UI 交互覆盖功能。

 

展望未来,Uber 工程师正在通过组件化增强分析功能,为按钮和列表等 UI 元素分配唯一 ID,以便标准化事件命名和元数据,进一步减少开发人员的工作量。

原文链接:

https://www.infoq.com/news/2026/01/uber-mobile-analytics-platform/

TVM 现已更新到 0.21.0 版本,TVM 中文文档已经和新版本对齐。

Apache TVM 是一个深度的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 →https://tvm.hyper.ai/

在部署 TVM 运行时模块时,无论目标是 CPU 还是 GPU,TVM 最终只需要一个动态共享库(dynamic shared library) 。实现这一点的关键就在于 统一的模块序列化机制。本文将介绍 TVM 模块序列化的格式标准与实现细节。

序列化(Serialization)

入口 API 为 tvm.module.Module 的 export_library。在此函数内部,我们会执行以下步骤:

  1. 收集所有 DSO 模块(例如 LLVM 模块和 C 模块)。
  2. 在获得 DSO 模块后,调用 save 函数将它们保存到文件。
  3. 随后检查是否存在已导入的模块(imported modules),例如 CUDA、OpenCL 等。这里对模块类型不做限制。
    如果存在导入模块,我们将创建一个名为 devc.o / dev.cc 的文件(用于将这些导入模块的二进制数据打包进最终的动态库中),然后调用 _PackImportsToLLVM 或 _PackImportsToC 来执行模块序列化。
  4. 最后,调用 fcompile,其内部会调用_cc.create_shared,生成动态共享库。

备注

  1. 对于 C 源码模块(CSourceModule),我们会将它们编译并与 DSO 模块一同进行链接。
  2. 是否使用 _PackImportsToLLVM 或 _PackImportsToC取决于 TVM 是否启用了 LLVM。它们本质上实现的是相同的目标。

序列化底层机制与格式标准

序列化主要发生在 _PackImportsToLLVM 或_PackImportsToC 中。它们都会调用 SerializeModule 来序列化 runtime module。在 SerializeModule 函数中,我们首先会构造一个辅助类 ModuleSerializer。它会以 module 为输入进行初始化,例如分配模块索引。随后可以调用其 SerializeModule 方法执行序列化。

为了更好地理解,让我们更深入地挖掘这个类的实现。

下面的代码用于构造 ModuleSerializer

explicit ModuleSerializer(runtime::Module mod) : mod_(mod) {
  Init();
}
private:
void Init() {
  CreateModuleIndex();
  CreateImportTree();
}

在 CreateModuleIndex() 中,我们使用 DFS 遍历模块的导入关系并为每个模块分配索引。根模块固定为索引 0

例如:

llvm_mod:imported_modules
  - cuda_mod

因此,LLVM 模块的索引将是 0,CUDA 模块的索引将是 1。

在构建完模块索引之后,我们将尝试构建导入树(CreateImportTree()),该导入树会在我们重新加载导出的库时用于恢复模块之间的导入关系。在我们的设计中,我们使用 CSR 格式来存储导入树,每一行对应父节点索引,而子数组中的索引对应其子模块索引。在代码中,我们使用 import_tree_row_ptr_ 和import_tree_child_indices_ 来表示它们。

在完成初始化之后,我们就可以使用 SerializeModule 函数来序列化模块。

在该函数的逻辑中,我们假设序列化格式如下所示:

binary_blob_size
binary_blob_type_key
binary_blob_logic
binary_blob_type_key
binary_blob_logic
...
_import_tree
_import_tree_logic

binary_blob_size 是我们在本次序列化步骤中将会包含的 blob 数量。在我们的示例中会有三个 blob,分别对应 LLVM 模块、CUDA 模块以及 _import_tree

binary_blob_type_key 是模块的 blob 类型键。 对于 LLVM / C 模块,其 blob 类型键为 _lib。对于 CUDA 模块,其类型键为 cuda,可以通过 module->type_key() 获取。

binary_blob_logic 是处理该 blob 的逻辑。 对于大多数 blob(例如 CUDA、OpenCL),我们会调用 SaveToBinary 函数将 blob 序列化为二进制。然而,对于 LLVM / C 模块,我们只会写入 _lib,用于表示这是一个 DSO 模块。

备注
是否需要实现 SaveToBinary 虚函数取决于模块的使用方式。例如,如果模块中包含我们在重新加载动态共享库时需要的信息,那么我们就应该实现该函数。像 CUDA 模块,在重新加载动态共享库时我们需要将其二进制数据传递给 GPU 驱动,因此我们需要实现 SaveToBinary 来序列化其二进制数据。但对于主机侧模块(如 DSO 模块),在加载动态共享库时我们并不需要额外信息,因此不需要实现 SaveToBinary。不过,如果未来我们希望记录一些关于 DSO 模块的元信息,我们也可以为 DSO 模块实现 SaveToBinary

最后,除非我们的模块中仅有一个 DSO 模块并且它位于根位置,否则我们会写入一个键 _import_tree。该键用于在重新加载导出的库时恢复模块导入关系,如前文所述。import_tree_logic 的内容则是将 import_tree_row_ptr_ 和 import_tree_child_indices_ 写入到流中。

在上述步骤完成后,我们会将最终结果打包进一个符号 runtime::symbol::tvm_ffi_library_bin,该符号可在动态库中恢复。

现在,我们已经完成序列化部分。正如你所看到的,我们理论上可以支持导入任意模块。

反序列化

入口 API 是 tvm.runtime.load。实际上,该函数会调用 _LoadFromFile。 如果进一步展开,可以看到其对应的是 Module::LoadFromFile

在我们的示例中,文件是 deploy.so。根据其函数逻辑,我们会在 dso_library.cc 中调用 module.loadfile_so,关键代码如下:

// Load the imported modules
const char* library_bin = reinterpret_cast<const char*>(
   lib->GetSymbol(runtime::symbol::tvm_ffi_library_bin));
Module root_mod;
if (library_bin != nullptr) {
   root_mod = ProcessLibraryBin(library_bin, lib);
} else {
   // Only have one single DSO Module
   root_mod = Module(n);
}```

如前所述,我们会将 blob 打包进符号 `runtime::symbol::tvm_ffi_library_bin`· 中。
在反序列化阶段,我们会检查它。如果存在 `runtime::symbol::tvm_ffi_library_bin`,我们将调用 `ProcessLibraryBin`,其逻辑如下:

```c++
READ(blob_size)
READ(blob_type_key)
for (size_t i = 0; i < blob_size; i++) {
    if (blob_type_key == "_lib") {
      // construct dso module using lib
    } else if (blob_type_key == "_import_tree") {
      // READ(_import_tree_row_ptr)
      // READ(_import_tree_child_indices)
    } else {
      // call module.loadbinary_blob_type_key, such as module.loadbinary_cuda
      // to restore.
    }
}
// Using _import_tree_row_ptr and _import_tree_child_indices to
// restore module import relationship. The first module is the
// root module according to our invariance as said before.
return root_module;

完成上述步骤后,我们会将 ctx_address 设置为 root_module, 以便能够从根模块查找符号(使所有符号可见)。

最终,我们就完成了反序列化部分。

二十年,是一个坐标。从 Web 2.0 的萌芽,到移动互联网的爆发,再到云原生时代的重塑,D2 技术大会伴随开发者走过了整整二十载风雨。

今天,我们站在了一个更加宏大的分水岭。AI 不再是遥远的科幻逻辑,它正以一种近乎“重构”的姿态,系统性地改写终端技术的底层范式:从代码生成的协作,到架构设计的逻辑,再到交互体验的边界。

第 20 届 D2 技术大会,年度主题定为——「AI 新」。

它既是我们的时代判断,也是我们的集体宣言。它是 AI 驱动的创新,也是终端人对技术边界追逐的热爱之新

此刻,我们正式向全球开发者、架构师、技术领袖及创新实践者发出邀请:来 D2,分享你对 AI 时代终端技术的独到见解,共同定义下一个二十年的生产力!


七大核心专场,期待你的真知灼见

我们渴望真实工程中的突破,珍视深度思考后的落地,让技术回归解决问题的初衷。

01 AI Coding:从写代码开始,重构工程本身

这是本届 D2 的主干专场。AI 正在从“辅助助手”升级为“协作伙伴”。

征集方向:

  • AI Agent 编程工具的研发与设计

侧重 Agent 型 AI 编程工具在本地与远程形态下的架构与产品设计。征集议题包括 IDE 深度集成、上下文采集与记忆管理、代码库索引检索、任务规划与工具调用、执行沙箱与权限控制、审计与回放、可观测性、成本/延迟优化与多模型策略等。重点关注可靠性与可控性:减少误改、支持规范化交付与团队协作。

  • AI-Native 开发实践

聚焦真实项目中 AI 编程的可复用方法。征集包含 Spec 驱动开发(结构化需求/验收标准/契约/测试)、AI 编程 Workflow 探索(从需求到 PR/发布的流水线)、以及团队级 AI 驱动研发实践(流程改造、提示/模板沉淀、质量门禁、效率与质量度量、失败复盘)。重点是“怎么做得稳、做得快”。

  • AI Coding 前沿研究与技术趋势

关注下一代 AI Coding 的关键技术与趋势。征集议题包括长上下文与复杂依赖、代码语义理解与程序分析结合、自动化评测与基准、对齐与安全、多智能体协作、可靠性与可解释性增强等。重点探讨研究如何走向工程落地与可验证的效果提升。

02 AI 创新体验:当交互正在被重写

终端是 AI 被感知的最前线。交互范式的巨变已经发生。

征集方向:

  • UI 范式重塑

探讨从 GUI 向 LUI 或 AUI 的代际演进。聚焦 Agent 驱动下的意图识别、动态 UI 生成及个性化界面即时构建。征集议题包括主动交互设计、多 Agent 协作下的用户反馈回路、以及如何利用 AI 简化复杂业务流的操作门槛。

  • 空间智能体验

聚焦多模态感知与空间计算的深度融合。涵盖视觉、语音、触觉在 3D/XR 环境下的集成交互,以及 AI 驱动的实时场景理解与数据可视化。重点探讨如何利用空间智能让数字世界更符合自然认知,实现高沉浸感的智能反馈。。

  • 具身交互探索

关注 AI 进入物理世界后的交互挑战,从 AI Wearables、AI PC 到机器人具身智能。探讨硬件约束下的自然语言处理、人机交互(HRI)实践及环境感知反馈。重点关注如何通过端侧智能赋予硬件产品生命力,解决真实场景下的交互痛点,探索用户真正愿意买单的终端新价值点。

03 AI 语言 & 框架:模型时代,语言与框架如何进化

当 AI 成为“默认能力”,底层技术如何适配?

征集方向:

  • 语言与编译器演进

探讨编程语言如何适配“人机共写”新常态。征集议题涵盖 LLM 友好型语法设计、智能化类型系统、AI 辅助的编译优化与静态分析等。重点研究如何通过语言特性的进化,提升 AI 生成代码的质量、安全性与复杂逻辑表达力。

  • Agent 框架重构

当 Agent 成为系统编排者,探讨传统框架的抽象层重塑。征集议题涵盖声明式意图驱动的框架设计、元数据驱动的界面自动生成、以及为 AI 重新设计的组件模型。重点关注框架如何提供更高级别的抽象,以支持多 Agent 在复杂业务逻辑中的无缝协作、状态同步与逻辑自治。

  • 智能运行时与内核

推动 AI 从工具层下沉为系统的核心能力。聚焦内置 AI 推理能力的运行时引擎、模型与容器/内核的深度集成,以及 AI 驱动的动态资源调度策略。重点探讨端云协同背景下,如何模糊开发与运行、模型与逻辑的边界,实现具备自适应、自进化能力的智能运行基座。

04 AI 智能测试:质量与效率,不再只能二选一

测试不再是滞后的环节,而是 AI 介入最深、收益最显性的战场。

征集方向:

  • 用例生成与自愈

探讨利用 LLM 实现测试全生命周期的自动化。征集议题包括基于语义理解的单元/集成测试生成、复杂业务场景下的测试数据合成,以及 UI 自动化脚本的自愈(Self-healing)机制。

  • 风险洞察与优化

聚焦利用 AI 提升质量保障的精准度与效率。征集议题涵盖基于变更分析的智能回归测试缩减、线上异常的实时检测与根因定位,以及多维度的质量风险预测模型。探讨如何利用算法在海量代码变更中快速锁定高风险区域,解决快速迭代与质量稳定性之间的核心矛盾。

  • 治理与角色演进

关注 AI 引入后测试流程与组织效能的系统性重构。核心议题包括 AI 测试工具的 ROI 分析、人机协同模式下的 QA 职责重定义,以及在规模化工程中构建“默认内置 AI”的质量防线。探讨如何通过技术赋能,打破质量与效率的零和博弈,重塑技术团队的质量文化与评价体系。

05 AI 智能生产:从工具走向生产系统

关注 AI 在真实业务落地时的“最后一公里”。

征集方向:

  • 业务深度嵌入

探讨 AI 如何从外部辅助工具进化为业务逻辑的核心。寻找在复杂业务场景中的落地架构案例,关注如何处理模型输出的不确定性以交付“确定性”结果。重点探讨 AI 对传统业务流程的深度重构,在提升用户价值的同时,确保生产系统的稳定性、安全性与商业收益。

  • 规模化生产交付

聚焦 AI 从原型验证(PoC)走向规模化交付的工程拐点。征集议题涵盖支持大规模 AI 应用的工程底座、端到端 AI 生产平台的演进、以及 FinOps 成本分析与合规治理。探讨如何构建标准化的平台能力,支撑 AI 跨团队、跨业务的高效迁移与持续稳定运行,实现技术普惠。

  • 全链路协同提效

关注覆盖需求、设计、交付及运维的 AI 全链路闭环。核心议题包括新一代人机协作下的流程重塑、领域专用 Agent 的生产环境编排,以及科学的效能度量方法。探讨如何通过技术与组织的双重演进,实现软件生产体系的跨越式提效,将 AI 潜能真正转化为规模化的实际业务产能。

06 终端技术:重构 AI 时代的性能底座

底层基础设施如何承载高算力与高响应需求?

征集方向:

  • 架构适配与演进

探讨终端架构如何重构以深度兼容 AI 能力,重点研究如何调整传统的软件拓扑结构,以支持 AI 在终端侧的无缝集成、高效编排与复杂的应用状态管理,提升端侧智能的响应实时性。

  • 运行时与性能优化

聚焦通过底层技术突破 AI 运行的性能瓶颈。征集议题涵盖面向 AI 指令集优化的编译器技术、异构算力的极致加速实践,以及轻量化端侧容器演进。探讨如何通过运行时与系统内核的深度协同,在有限的硬件资源限制下,实现极致的推理速度与能效比。

  • 端侧工程与协同

核心议题包括模型量化、蒸馏与剪枝的终端实战、端云协同推理架构,以及隐私安全约束下的端侧学习。探讨如何构建高效的端云配比方案,在保障响应速度与数据隐私的同时,实现计算成本与用户体验的帕累托最优。

07 一人公司:技术人的个体放大器

这是最具时代情绪的专场。AI 正在让“超级个体”成为可能。

征集方向:

  • 全栈生产力飞跃

探讨 AI 如何打破专业壁垒,实现“一个人就是一支团队”。分享利用 AI 协同完成从需求定义、全栈开发、交互设计到市场增长的全链路实践。

  • 商业闭环与实战

聚焦超级个体的商业化落地与可持续经营之道。征集独立开发者的 AI 实战案例,涵盖极致成本控制下的产品生存策略、AI 辅助的商业决策与自动化运营。探讨在 AI 时代,个体如何构建轻量化、高利润的商业模式,并成功应对从单兵作战到规模化营收的真实挑战。

  • 职业路径重构

探讨从“专项开发者”向“产品主理人”转型的思维重构、AI 时代的个人品牌经营,以及个体长期竞争力的构建。研究在组织边界日益模糊的未来,技术人如何利用 AI 工具集寻找更具自主性的创作路径,定义下一代极简且高效的职业范式。


顶尖出品人矩阵:为议题深度护航

本届 D2 各专场由行业资深专家领衔,他们不仅是评审者,更是议题的“合伙人”。

我们寻找的不仅是一个演讲者

更是一个在 AI 工程深水区挣扎过、思考过、最终破局的见证者

  • 隐风| 淘天集团-用户 &内容终端技术负责人

  • 云谦| 蚂蚁集团-高级前端技术专家

  • 悟石| 淘宝闪购-消费者端技术负责人

  • 渚薰| 前淘宝互动游戏专家

  • 偏右| 蚂蚁集团-支付宝体验技术前端平台负责人

  • 张磊| 字节跳动 Web Infra 技术负责人

  • 泠乐| 淘天集团-淘宝终端质量负责人

  • 茹炳晟| CCF TF 研发效能 SIG 主席 / 复旦大学 CodeWisdom 成员

  • 达峰| 蚂蚁集团-平台体验技术部负责人

  • 穆宸| AliExpress-终端技术负责人 / D2 负责人

  • 永霸| 淘天集团-交易终端技术负责人

  • 崔红保| DCloud CTO / uni-app 跨平台框架负责人

  • 秦粤| 阿里云-数据库高级前端专家

  • 梓骞 | 启智云图 CEO / Lovrabet 产品创始人

出品人寄语:“在 D2,我们致力于将前沿的 AI 实践提炼为系统化的技术范式。我们期待与你一同锚定 AI 时代的工程坐标,让每一份实战洞察都汇聚成定义未来的行业基准。”


🌟 为什么来到 D2 舞台

  1. 顶尖技术影响力:D2 是国内终端技术的风向标,线下规模 2000+,线上覆盖数十万专业开发者。

  2. 二十周年里程碑:参与第 20 届这一极具纪念意义的盛会,与业内最具创新精神的技术人同频共振。

  3. 常态化社区联动:优质内容将同步至稀土掘金、InfoQ、AI 产品榜等联合承办方平台,获得持续的行业曝光与认可。

🗓️ 议题提交指南

  • 截止时间: 2026 年 1 月 23 日(请关注官网最新动态)

  • 议题要求:内容具有前瞻性、实战性或深度思考;拒绝纯广告,强调技术细节与真实的踩坑经验

图片

扫码提交议题

二十年是一个里程碑,更是重新出发的起点。在「AI 新」的浪潮中,让我们一起,用 AI 驱动创新,用终端之心热爱创新。


*本文由极客时间企业版代发

度小满引入 Apache Doris 替换原有 Greenplum,实现整体查询效率提升 82%,与此同时,集群缩减 2/3、年省数百万的巨大效益。本文将分享度小满如何基于 Doris 从 0 到 1 构建超大规模数据分析平台,并围绕平滑迁移、异地多活容灾等方面,分享实践经验。

本文整理自度小满 Doris 数据库负责人汤斯在 Doris Summit 2025 中的演讲,并以演讲者第一视角进行叙述。

度小满金融(原百度金融)作为一家覆盖现代财富管理、支付、金融科技等多板块的科技公司,数据的分析处理对其极为重要,已经深度融入业务生命周期的每个环节,是进行风险控制、商业决策、用户体验优化及运营提效的基石。

随着业务高速发展,度小满原有基于 Greenplum 搭建的 OLAP 平台,逐渐暴露出三大痛点:

  • 规模与稳定性瓶颈:存储已接近饱和,扩容至百余台已接近硬件规模的承载上限,如果继续扩容,将面临更严重的稳定性挑战。

  • 性能与体验不佳:Greenplum SQL 查询执行速度慢,且经常出现 “计算时间远小于排队时间” 的情况,严重影响业务分析效率。

  • 缺失技术支持:当前使用的 Greenplum 6 版本技术架构已显得陈旧,并且 2024 年 Greenplum 宣布将停止开源,后续的技术支持与迭代升级将无法保障。

为了应对这些痛点,度小满金融迫切寻找更为高效、稳定且具备现代化技术架构的数据处理解决方案,以支持其未来的业务发展。

Apache Doris:高吞吐、快查询

面对日益增长的业务体量与复杂多变的分析需求,选用一个高效、可靠的数据库系统,已成为支撑业务稳健发展与快速创新的关键。Apache Doris 以其出色的性能表现与高度灵活的架构,成为众多场景下的优选方案。为深入验证其在海量数据与复杂分析场景中的能力,我们展开了一系列性能测试,关键结果如下:

  • 查询性能:在 1TB TPC-DS 标准测试集中, Apache Doris的查询速度约是 Greenplum 6 的 20-30 倍

  • 导入性能:在基于 Flink 写入的 TPS 测试中,基于单分片导入,压测最大 TPS 为:5000W/s

  • JSON 数据处理:针对新推出的 Variant JSON 数据类型,测试显示:存储 2-3 万 Key 时,其空间占用仅为普通 JSON 的 1/10 甚至更低,查询效率则提升至 10 倍以上

综上可知,Apache Doris 在写入吞吐、响应速度及存储效率上表现卓越,有力证明了其应对大规模、实时化、半结构化数据分析挑战的坚实技术基础。

基于 Apache Doris 的大规模数据分析平台

在上述详实的选型调研之后,我们决定采用 Apache Doris 替代原有 Greenplum 集群,构建超大规模数据分析平台。

为验证 Apache Doris 在真实业务场景中的表现,我们先进行了小范围试点,部署了少量 Doris 集群,并先行接入几个关键业务方。试点期间,系统在性能、稳定性和易用性方面获得高度评价。基于这一积极反馈,我们稳步扩展 Doris 集群规模,最终在效率与成本上实现大幅提升:

  • 整体效率:端到端分析任务耗时从 274 秒降至 47 秒,效率提升 82%,任务超时查杀比例从 1.3%骤降至 0.11%,降幅达 91%,彻底解决高峰期排队问题实现 0 排队,使分析师的工作不再因拥堵而中断,体验和生产力均有极大提升。

  • 集群成本:在同等资源成本下, Doris 仅以 1/3 的集群数量即可提供与 Greenplum 同等的服务能力,存储性能提升 200%。截至目前,已完成 百余台原 Greenplum 服务器的清退工作,以更少的硬件资源支撑了更高的计算与存储需求,实现年度硬件成本节约数百万元

从 0-1 数据平台建设经验

我们基于 Apache Doris 成功替换了 Greenplum,完成了从 0-1 的数据平台重构,覆盖架构设计、数据流转与业务协同的系统性工程。以下将围绕快速平滑迁移、异地多活容灾与全链路生态集成三个核心环节,展开具体实践。

01 快速迁移

为保障业务连续性与数据安全,我们开发了自动化迁移工具 SqlGlot,将大规模数据从原有 GP 集群迁移至 Doris 集群。整个过程历经半年,累计迁移 PB 级规模数据,全程业务无感知。

  • 表结构迁移:在表结构迁移阶段,团队从 GP 系统中导出表结构及相关元数据,借助 SqlGlot 工具实现字段映射与语法适配,并在此基础上完成分区构建与分桶策略设计,确保每个分桶数据量控制在 1G~3G 的合理范围内。该流程最终成功转换超过 20,000 张表,并保障了所有表的分区与分桶结构符合业务与性能要求。

  • 表数据迁移:我们通过分布式导出将 GP 数据并行迁移至 Doris 机器,并基于 Doris 官方推荐的 Stream Load 进行并发控制,以文件流式加载的方式高效导入数据至 Doris 集群。整个过程累计完成 PB 级规模数据迁移,稳定支持了 5000+ 次数据同步任务。

  • SQL 迁移:为解决因业务规模庞大、场景复杂而导致的官方工具语法支持不全的问题,我们基于 SqlGlot 并结合正则匹配能力,将 PostgreSQL SQL 高效转换为 Doris SQL。整个迁移流程包括“转换成功 → 执行成功 → 数据一致” ,累计完成约 47 万个 SQL 的转换,实现 95% 的执行成功率 与 92% 的数据一致率

02 异地双机房灾备

为保障数据安全并实现集群高可用,我们基于 Apache Doris 构建了异地双机房灾备架构,确保数据与服务具备跨机房容灾与双活能力。核心设计如下:

我们将所有 Doris 集群节点均匀部署于 A 与 B 两个异地机房,通过设置 tag.location 属性明确节点所属机房。用户账号按机房绑定,访问请求通过轮询机制自动分配,实现负载均衡(例如首次请求路由至 A 机房,第二次则路由至 B 机房)。建表时通过配置 location 参数,确保每张表在双机房各保留 2 个副本,从而达成数据异地双活与故障自动切换。

关键配置示例

  1. 设置节点机房标签

alter system modify backend ”BE1:9050" set ("tag.location" = "group_a");alter system modify backend ”BE2:9050" set ("tag.location" = "group_b");
复制代码

  1. 建表时指定双机房副本分布

CREATE TABLE ubevent (ts DATETIME, uid INT, ...) DUPLICATE KEY(ts) DISTRIBUTED BY HASH(uid) BUCKETS 10PROPERTIES ("replication_allocation" = "tag.location.group_b: 2, tag.location.group_a: 2");
复制代码

03 生态整合

为构建高效、稳定、易用的数据平台,我们还围绕 Apache Doris 进行系统性生态整合:

  • 计算引擎无缝集成:通过 Doris 官方提供的 Spark Connector 与 Flink Connector,实现了与现有 Spark、Flink 计算引擎的高效对接,保障了数据流水线稳定运行。

  • 运维体系化与自动化:集成 Prometheus、Grafana 及 Doris Manager,构建了覆盖监控、告警、管理与调优的自动化运维体系,全面提升集群稳定性与运维效率。

优化经验

为进一步提升数据平台的效率及资源利用率,在实际落地过程中,围绕集群、负载、存储等多维度总结了以下优化经验:

01 集群隔离

当前我们有多个 Doris 集群,为合理承接不同业务方的接入需求,我们主要依据业务成本与稳定性要求两大维度进行评估与路由。通常而言,稳定性越高,对应成本也越高。

新建集群时,稳定性最优,但相应成本也最高。为在成本与稳定性之间取得平衡,我们大多场景是基于 Workload Group 资源硬隔离方案,对 CPU 与内存进行资源组级别的隔离,有效减少不同业务负载间的资源竞争。若业务对稳定性的要求超出共享集群所能提供的范围,则仍需要通过新建独立集群来满足。

02 存储压力

在 Apache Doris 的落地与运维过程中,我们曾面临因业务快速增长带来的高达 80%-90% 的磁盘存储压力。针对这一问题,进行了一系列优化:

  • 控制表生命周期:部分业务或因对动态分区相关语法不熟悉,未主动采用该策略。为此,集成动态分区的参数配置,简化了开发难度,并提供统一注册入口,业务开发人员仅需选择是否开启、保留天数即可。

  • 修改压缩格式:将默认压缩算法从 LZ4 切换为 ZSTD。实测表明,存储空间平均节省约 50%,虽带来约 20%~30% 的 CPU 与内存负载上升,但整体 ROI 仍然较高。

  • 存储指标监控告警:为预防因误操作或异常行为导致的存储激增,建立了针对“人员”与“表”双维度的监控体系。环比分析业务人员数据占用趋势及单表每日增长量,可自动识别异常(如单日增长飙升至日常 10 倍),并及时触发告警及通知。

  • Hive 与 Doris 打通:在基于 Kerberos 认证的 Hive 环境中,对 Doris Hive Catalog 功能进行了二次开发,实现跨系统的直接数据访问,无需依赖 Flink 等同步工具,简化了架构并提升了数据使用效率。

03 负载均衡

为确保系统在负载高峰期的稳定运行,特别是应对异常 SQL 与大查询带来的资源压力,应对措施如下:

  • 双机房负载均衡:基于已有的异地双机房架构,通过轮询机制实现业务流量在 A 与 B 机房之间的自动分发:首个 SQL 请求路由至 A,次个请求则导向 B,以此循环,确保双机房负载均衡,避免单点资源过载。

  • SQL 参数限制:通过 enable_query_memory_overcommit = falseexec_mem_limit = 256 * 1024 * 1024 * 1024 等参数将最大占用内存限制为 256G,避免集群被打满,后续计划降至 60G。

  • Workload 资源队列动态调整:基于任务类型划分资源队列,配置 CPU 的软隔离和内存的硬隔离,并支持错峰调度。比如:例行任务通常在夜间执行,为其创建专门资源队列,数据分析等公共任务大多在白天执行,将配置更大的资源队列,随着白天/夜间需求的变化动态调整资源。此外,依据各队列负载设定并行度与并发数,控制任务排队时长。

  • 异常 SQL 拦截:实时识别与拦截异常 SQL,避免其影响 BE 节点稳定性。初期使用 Doris 内置正则规则进行拦截,但规则复杂导致 CPU 开销上升。为此,我们将拦截逻辑外移至平台层执行,以避免正则匹配及超大 JOIN 导致的 CPU 负载过高。

04 集群稳定性

随着集群规模不断扩大,保障 FE、BE 节点稳定性成为运维工作的核心挑战,为此,我们构建了以下保障体系:

  • 分层触达+全维度覆盖:根据不同指标优先级设置通知电话、短信、飞书提醒,P0 监控准确率 ≥80%;

  • 自动异常处理:为 FE 和 BE 的宕机重启设置了自动化处理方案,在识别到服务卡住时,系统会自动重启进程。此外,对于磁盘掉线,将自动下线故障盘并触发副本补齐。

我们同时采用对战分析、火焰图和日志查看等方法进行详细记录,以便后续调优。此外,编写了 SOP 手册,涵盖不同场景的应对措施,并进行了异常处理演练。

结束语

截至目前,我们已搭建 3 个基于 Doris 2.1.10 版本的线上集群,其中最大规模的集群达万 core 级别、上百 TB 内存和 PB 级磁盘。目前仍在扩容中,计划在年底前新增百余台 CN 节点和数十台 Mix 节点。未来,我们将重点关注并探索以下能力:

  • 存算分离:重点关注 Doris 3.X 版本的存储分离架构,推动落地实践。

  • 湖仓一体:全面打通数据湖与数据仓库,目前已小规模试点 Paimon;此外,针对数据外置场景,计划通过异步物化视图提升查询性能。

  • 智能物化视图探索:引入语义建模与 AI 智能分析,降低研发与业务沟通门槛,并对智能推荐与模板化方案进行探索与实践。

Agoda 近日分享了他们如何将多个独立的数据管道整合为一个基于Apache Spark的集中式平台,以消除财务数据中的不一致性的。该公司构建了一个多层质量保障框架,结合自动化校验、基于机器学习的异常检测以及与上游团队签订的数据契约(data contracts),确保用于财务报表和战略规划的财务指标准确无误,同时每天处理数百万笔预订交易。

 

这一问题源于一个典型的企业架构模式,Agoda 的数据工程、商业智能(BI)和数据分析团队各自开发了独立的财务数据管道,并使用不同的逻辑和定义。尽管这种做法在初期提供了简单性和清晰的责任边界,却导致了重复计算和全公司范围内指标不一致的问题。正如 Agoda 工程团队的Warot Jongboondee所解释的那样,这些差异“可能对 Agoda 的财务报表产生实质性的影响”。

独立的财务数据管道 (图片来源)

 

为了解决这一挑战,Agoda 推出了名为 Financial Unified Data Pipeline(FINUDP)的统一财务数据管道,作为销售、成本、收入和利润率等关键财务数据的单一事实来源(single source of truth)。该系统基于Apache Spark构建,每小时向下游团队提供更新,用于对账和财务规划。整合过程耗费了大量的精力:协调产品、财务和工程等多个利益相关方就统一的数据定义达成共识耗费了很长的时间;初始版本的运行时间长达五小时,后通过查询优化和基础设施调整,最终缩短至约 30 分钟。

财务统一数据管道(FINUDP)的架构(图片来源)

 

Agoda 的质量保障框架采用了多重防御机制。自动化校验会检查数据表中的空值、数值范围约束和数据完整性。一旦关键业务规则校验失败,管道会自动暂停,以防处理可能错误的数据。团队使用Quilliup来比对源表与目标表。与上游团队的数据契约(Data Contracts)会明确约定数据格式、内容和质量要求,任何违反契约的行为会立即触发告警。机器学习模型会持续监控数据模式,识别潜在异常。三级告警系统确保通过邮件、Slack 通知以及内部工具实现快速响应,如果数据更新延迟,系统会自动升级至 Agoda 的 7×24 小时网络运营中心(Network Operations Center,NOC)。

 

这一做法契合了行业的整体趋势。根据最新的行业调研,64%的组织将数据质量问题视为最大挑战。Gartner 指出,数据契约正成为“管理、交付和治理数据产品的一种日益流行的方式”。这类生产者与消费者之间的正式协议,明确定义了数据模式(schema)和质量标准。

 

当然,集中化也带来了明确的权衡取舍(trade-offs)包括,开发速度下降,因为任何变更现在都需要对整个管道进行测试。数据依赖,管道必须等待所有上游数据集就绪后才能启动。详尽的文档编写和广泛的干系人共识拖慢了落地进度,却建立了跨团队的信任。Jongboondee 表示,集中化“要求在每个环节都进行更紧密的协作和审慎的变更管理”。

 

目前,该系统已经实现了 95.6%的可用性,并朝着 99.5%的目标迈进。所有变更均需经过影子测试(shadow testing),也就是,在合并请求中,新旧版本的查询会并行运行,并自动比对结果。此外,还有一个与生产环境完全一致的专用 staging 环境,允许团队在正式发布前进行充分的验证。

 

FINUDP 项目表明,当企业处理大规模关键业务数据时,正逐步从零散的、事后补救式的质量检查,转向架构层面强制执行的、端到端的可靠性体系。这种体系优先保障数据的一致性与可审计性,而非单纯的开发速度,这一转变在财务数据日益支撑报表生成、机器学习模型训练和监管合规流程的今天,显得尤为关键。

 

原文链接:

How Agoda Unified Multiple Data Pipelines Into a Single Source of Truth