包含关键字 typecho 的文章

纯原生适配!ArkTS 开发 DormMate新生系统欢迎界面全解析

在这里插入图片描述

前言

随着高校信息化建设的推进,传统的宿舍管理模式存在效率低、信息孤岛多、交互体验差等问题。新生入住宿舍是学校管理中非常关键的环节,从分配床位、办理入住手续,到查询宿舍信息,管理流程繁杂。

本篇文章以 HarmonyOS 6.0 原生开发 为基础,分享 DormMate 新生宿舍管理系统中“欢迎区域”模块的实现方法。重点解析 ArkTS 声明式 UI 构建、多端适配以及鸿蒙原生组件使用技巧,为想基于 HarmonyOS 6.0 进行原生应用开发的读者提供参考。


在这里插入图片描述

背景

  1. 传统管理痛点

    • 手工登记信息,易出错
    • 新生对流程不熟悉,需人工指导
    • 信息更新慢,难以实时共享
  2. 系统设计目标

    • 简洁友好的欢迎界面:让新生第一眼就感受到服务功能
    • 高可扩展性:欢迎区域可以轻松添加活动信息、公告、快捷入口
    • 跨端统一体验:手机、平板、桌面端界面一致
  3. 技术选型

    • HarmonyOS 6.0:原生分布式操作系统,提供多端统一的应用开发框架
    • ArkTS:鸿蒙原生声明式开发语言,支持跨设备 UI 一致性渲染
    • ArkUI:鸿蒙原生 UI 框架,提供丰富的组件库和布局能力

HarmonyOS 6.0 原生开发介绍

HarmonyOS 6.0 基于“一次开发,多端部署”的核心理念,提供了 分布式软总线分布式数据管理统一的 ArkUI 框架。ArkTS 作为其原生开发语言,具备以下优势:

特性HarmonyOS 6.0 原生开发
跨端开发✅ 天然支持手机、平板、智慧屏、桌面端等多终端部署
UI 构建✅ 声明式 UI 语法,与 相近但更贴合鸿蒙系统
性能✅ 系统级深度优化,原生渲染性能更佳
系统能力✅ 全面调用 HarmonyOS 分布式能力、系统服务

在 DormMate 系统中,我们将利用 ArkTS + ArkUI 构建原生界面,充分发挥 HarmonyOS 6.0 的分布式特性,实现多端统一的欢迎页面。


开发核心代码:欢迎区域实现

下面是“欢迎区域”的核心实现代码,以及逐行解析。该模块的功能包括:

  • 欢迎新生文字提示
  • 简介功能
  • 当季入住信息
  • 图标装饰

完整代码

@Entry
@Component
struct WelcomeSection {
  // 获取系统主题
  @State theme: ThemeConstants = getThemeConstants();

  build() {
    Column() {
      this.buildWelcomeCard()
    }
    .padding(16)
    .width('100%')
    .backgroundColor(this.theme.backgroundColor)
  }

  /**
   * 构建欢迎区域卡片
   */
  @Builder
  buildWelcomeCard() {
    Row() {
      // 文字内容区域
      Column() {
        // 欢迎标题
        Text('欢迎使用新生宿舍管理系统')
          .fontSize(this.theme.headlineSmall.fontSize)
          .fontWeight(FontWeight.Bold)
          .fontColor(this.theme.onSurface)
          .margin({ bottom: 8 })

        // 功能描述
        Text('为新生提供便捷的宿舍分配、入住流程管理和宿舍信息查询服务')
          .fontSize(this.theme.bodyMedium.fontSize)
          .fontColor(this.theme.onSurfaceVariant)
          .margin({ bottom: 16 })
          .maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })

        // 入住季标签
        Text('2024届新生入住季')
          .fontSize(this.theme.labelLarge.fontSize)
          .fontWeight(FontWeight.Bold)
          .fontColor(this.theme.primary)
          .backgroundColor(this.theme.primaryContainer)
          .padding({ left: 16, right: 16, top: 8, bottom: 8 })
          .borderRadius(20)
      }
      .alignItems(ItemAlign.Start)
      .flexGrow(1) // 占据剩余空间,适配多端

      // 装饰图标区域
      Stack() {
        Text('宿')
          .fontSize(this.theme.displayLarge.fontSize)
          .fontWeight(FontWeight.Bold)
          .fontColor(this.theme.primary)
      }
      .width(100)
      .height(100)
      .backgroundColor(this.theme.primaryContainer)
      .borderRadius(20)
      .justifyContent(FlexAlign.Center)
      .margin({ left: 16 })
    }
    .width('100%')
    .padding(24)
    // 渐变背景
    .backgroundImage(
      LinearGradient.createLinearGradient(
        { x: 0, y: 0 }, // 起始点
        { x: 1, y: 0 }, // 结束点
        [
          this.theme.surfaceVariant + '80', // 带透明度的表面变体色
          this.theme.surface + 'CC'         // 带透明度的表面色
        ]
      )
    )
    .borderRadius(16)
  }
}

/**
 * 主题常量定义(模拟系统主题,实际开发可通过AbilityStage获取)
 */
interface ThemeConstants {
  backgroundColor: string;
  surface: string;
  surfaceVariant: string;
  onSurface: string;
  onSurfaceVariant: string;
  primary: string;
  primaryContainer: string;
  headlineSmall: { fontSize: number };
  bodyMedium: { fontSize: number };
  labelLarge: { fontSize: number };
  displayLarge: { fontSize: number };
}

/**
 * 获取主题常量(简化实现,实际项目建议使用主题管理)
 */
function getThemeConstants(): ThemeConstants {
  // 亮色主题示例,实际可根据系统设置动态切换
  return {
    backgroundColor: '#f9f9f9',
    surface: '#ffffff',
    surfaceVariant: '#f0f0f0',
    onSurface: '#1d1d1f',
    onSurfaceVariant: '#6e6e73',
    primary: '#007aff', // 鸿蒙系统蓝色
    primaryContainer: '#007aff1a', // 主色透明变体
    headlineSmall: { fontSize: 24 },
    bodyMedium: { fontSize: 16 },
    labelLarge: { fontSize: 14 },
    displayLarge: { fontSize: 64 }
  };
}

在这里插入图片描述

逐行解析

1. 组件结构与入口
@Entry
@Component
struct WelcomeSection {
  @State theme: ThemeConstants = getThemeConstants();

  build() {
    Column() {
      this.buildWelcomeCard()
    }
    .padding(16)
    .width('100%')
    .backgroundColor(this.theme.backgroundColor)
  }
  • @Entry:标记该组件为应用入口组件
  • @Component:声明这是一个 ArkUI 组件
  • @State:状态装饰器,用于管理组件内部状态(此处存储主题信息)
  • build():组件的构建方法,返回 UI 结构
  • 外层 Column 作为根布局,提供基础的页面边距和背景色
2. 欢迎卡片构建器
@Builder
buildWelcomeCard() {
  Row() {
    // 文字内容区域
    Column() { ... }
    .flexGrow(1)
    
    // 装饰图标区域
    Stack() { ... }
    ...
  }
  .width('100%')
  .padding(24)
  ...
}
  • @Builder:构建器装饰器,用于封装可复用的 UI 片段
  • Row:水平布局容器,对应 Row 组件
  • flexGrow(1):让文字区域占据剩余空间,实现自适应布局
  • Stack:堆叠容器,用于实现装饰图标区域( Container + Center)
3. 文字内容区域
Column() {
  // 欢迎标题
  Text('欢迎使用新生宿舍管理系统')
    .fontSize(this.theme.headlineSmall.fontSize)
    .fontWeight(FontWeight.Bold)
    .fontColor(this.theme.onSurface)
    .margin({ bottom: 8 })

  // 功能描述
  Text('为新生提供便捷的宿舍分配、入住流程管理和宿舍信息查询服务')
    .fontSize(this.theme.bodyMedium.fontSize)
    .fontColor(this.theme.onSurfaceVariant)
    .margin({ bottom: 16 })
    .maxLines(2)
    .textOverflow({ overflow: TextOverflow.Ellipsis })

  // 入住季标签
  Text('2024届新生入住季')
    .fontSize(this.theme.labelLarge.fontSize)
    .fontWeight(FontWeight.Bold)
    .fontColor(this.theme.primary)
    .backgroundColor(this.theme.primaryContainer)
    .padding({ left: 16, right: 16, top: 8, bottom: 8 })
    .borderRadius(20)
}
.alignItems(ItemAlign.Start)
.flexGrow(1)
  • Column:垂直布局容器,对应 Column 组件
  • Text:文本组件,支持 fontSize、fontWeight、fontColor 等样式配置
  • maxLines + textOverflow:实现文本超出两行时的省略号效果
  • 所有样式均基于主题常量,保证多端风格统一
4. 装饰图标区域
Stack() {
  Text('宿')
    .fontSize(this.theme.displayLarge.fontSize)
    .fontWeight(FontWeight.Bold)
    .fontColor(this.theme.primary)
}
.width(100)
.height(100)
.backgroundColor(this.theme.primaryContainer)
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.margin({ left: 16 })
  • Stack 配合 justifyContent(FlexAlign.Center) 实现文字居中效果
  • 直接通过链式调用设置宽高、背景色、圆角等样式,语法更简洁
  • margin({ left: 16 }) 实现与文字区域的间距
5. 渐变背景实现
.backgroundImage(
  LinearGradient.createLinearGradient(
    { x: 0, y: 0 }, // 起始点
    { x: 1, y: 0 }, // 结束点
    [
      this.theme.surfaceVariant + '80', // 80对应16进制的透明度(0.5)
      this.theme.surface + 'CC'         // CC对应16进制的透明度(0.8)
    ]
  )
)
  • 使用 LinearGradient 创建线性渐变背景
  • 鸿蒙中通过 16 进制后缀表示透明度(80=0.5,CC=0.8)
  • 渐变方向从左到右(x从0到1)
6. 主题管理
interface ThemeConstants { ... }

function getThemeConstants(): ThemeConstants {
  return {
    backgroundColor: '#f9f9f9',
    surface: '#ffffff',
    surfaceVariant: '#f0f0f0',
    onSurface: '#1d1d1f',
    onSurfaceVariant: '#6e6e73',
    primary: '#007aff',
    primaryContainer: '#007aff1a',
    headlineSmall: { fontSize: 24 },
    bodyMedium: { fontSize: 16 },
    labelLarge: { fontSize: 14 },
    displayLarge: { fontSize: 64 }
  };
}
  • 通过接口定义主题常量结构,保证类型安全
  • 实际项目中可结合 AbilityStageConfiguration 实现深色/浅色主题动态切换
  • 主色使用鸿蒙系统默认蓝色(#007aff),符合系统设计规范

多端适配说明

在 HarmonyOS 6.0 中,该组件可通过以下方式实现多端自适应:

  1. 尺寸适配:使用百分比宽度(width('100%'))和 flexGrow 实现不同屏幕尺寸适配
  2. 字体适配:可结合 vp 单位(虚拟像素)替代固定像素值,自动适配不同屏幕密度
  3. 布局适配:通过媒体查询(@Media)为不同设备类型定制布局:

    // 平板/桌面端适配示例
    @Media(minWidth: 800) {
      .buildWelcomeCard() {
     Row() {
       // 平板端可调整布局比例
       Column() { ... }.flexGrow(2)
       Stack() { ... }.width(120).height(120)
     }
      }
    }

心得

  1. HarmonyOS 6.0 原生开发优势

    • 原生 API 直接调用鸿蒙系统能力,无需中间层适配
    • 多端部署能力更原生,无需额外插件支持
  2. UI 设计技巧

    • 使用主题常量统一管理颜色和字体,保证多端风格一致
    • 链式调用语法让样式配置更简洁直观
  3. 开发效率

    • ArkTS 支持热重载,开发调试效率高
    • 原生组件性能更优,尤其在鸿蒙设备上表现更佳

在这里插入图片描述

总结

本文介绍了基于 HarmonyOS 6.0 原生开发DormMate 新生宿舍管理系统欢迎区域模块实现思路。通过 ArkTS + ArkUI 构建的原生界面,充分利用了鸿蒙系统的分布式能力和原生渲染优势,为新生提供了一个简洁、易读、现代化的入口界面。

HarmonyOS 原生开发在系统集成度、性能表现和多端适配方面更具优势,尤其适合深度适配鸿蒙生态的应用。该欢迎区域组件具备良好的可扩展性,可快速添加公告、快捷入口等功能,并天然支持在手机、平板、桌面端等多设备上统一呈现。

DormMate 的设计理念是:原生、高效、跨端统一,为学校宿舍管理系统提供了一套深度适配 HarmonyOS 生态的前端解决方案。

关键点回顾

  1. 核心实现:使用 ArkTS 声明式语法,通过 Row/Column/Stack 布局组合 + LinearGradient 渐变背景实现欢迎区域 UI
  2. 主题管理:通过主题常量统一管理颜色和字体样式,支持深色/浅色模式适配
  3. 多端适配:利用 flex 布局、百分比宽度和媒体查询,实现手机/平板/桌面端的自适应展示

HarmonyOS 6.0 图书馆管理系统(ArkTS)开发实战

一、项目概述

你想要开发的是基于HarmonyOS 6.0、使用ArkTS语言构建的图书馆管理系统,该系统面向图书馆管理员和读者,核心实现图书查询、借阅/归还、图书管理等基础功能,采用HarmonyOS 6.0的最新特性(如Stage模型、ArkUI组件化)开发,适配多设备形态,兼顾易用性和性能。
在这里插入图片描述

二、技术栈与环境准备

1. 核心技术

  • 开发语言:ArkTS(TypeScript超集,HarmonyOS原生开发语言)
  • 应用模型:Stage模型(HarmonyOS 6.0推荐的主流应用模型)
  • UI框架:ArkUI(基于TSX的声明式UI)
  • 数据存储:Preferences(轻量级键值存储)+ RelationalStore(关系型数据库)
  • 设备适配:自适应布局(Flex/Grid)

2. 环境要求

  • DevEco Studio:4.2及以上版本
  • HarmonyOS SDK:6.0(API Version 11)
  • 模拟器/真机:HarmonyOS 6.0及以上设备

三、核心功能设计

本系统聚焦3个核心模块,满足基础图书馆管理需求:

  1. 图书查询(读者端):按书名/作者/分类检索图书,查看图书状态(可借/已借出)
  2. 借阅/归还(管理员端):扫描/输入图书编号,完成借阅、归还操作
  3. 图书管理(管理员端):新增、编辑、删除图书信息

四、代码实现

1. 项目结构(Stage模型)

library-system/
├── entry/
│   ├── src/main/ets/
│   │   ├── entryability/       # 应用入口
│   │   ├── pages/              # 页面(图书列表、借阅页、管理页)
│   │   ├── model/              # 数据模型
│   │   ├── util/               # 工具类(数据库、存储)
│   │   └── resources/          # 资源(字符串、样式)

在这里插入图片描述

2. 数据模型定义(model/BookModel.ets)

定义图书和借阅记录的数据结构,作为全局数据模型:

/**
 * 图书数据模型
 */
export interface Book {
  id: string;        // 图书编号(唯一标识)
  name: string;      // 书名
  author: string;    // 作者
  category: string;  // 分类(如计算机、文学)
  status: boolean;   // 状态:true-可借,false-已借出
  borrowTime?: string;// 借阅时间(可选)
  borrower?: string;  // 借阅人(可选)
}

/**
 * 全局状态管理(简化版)
 */
export class BookManager {
  private static instance: BookManager;
  private books: Book[] = [];

  private constructor() {
    // 初始化测试数据
    this.books = [
      { id: "001", name: "ArkTS开发实战", author: "鸿蒙开发者", category: "计算机", status: true },
      { id: "002", name: "HarmonyOS 6.0进阶", author: "华为技术团队", category: "计算机", status: false, borrowTime: "2026-02-01", borrower: "张三" },
      { id: "003", name: "百年孤独", author: "加西亚·马尔克斯", category: "文学", status: true }
    ];
  }

  // 单例模式,保证全局唯一实例
  public static getInstance(): BookManager {
    if (!BookManager.instance) {
      BookManager.instance = new BookManager();
    }
    return BookManager.instance;
  }

  // 获取所有图书
  getBooks(): Book[] {
    return this.books;
  }

  // 按关键词查询图书
  searchBooks(keyword: string): Book[] {
    return this.books.filter(book => 
      book.name.includes(keyword) || 
      book.author.includes(keyword) || 
      book.category.includes(keyword)
    );
  }

  // 借阅图书
  borrowBook(bookId: string, borrower: string): boolean {
    const book = this.books.find(b => b.id === bookId);
    if (book && book.status) {
      book.status = false;
      book.borrowTime = new Date().toLocaleDateString();
      book.borrower = borrower;
      return true;
    }
    return false;
  }

  // 归还图书
  returnBook(bookId: string): boolean {
    const book = this.books.find(b => b.id === bookId);
    if (book && !book.status) {
      book.status = true;
      book.borrowTime = undefined;
      book.borrower = undefined;
      return true;
    }
    return false;
  }

  // 新增图书
  addBook(book: Book): void {
    this.books.push(book);
  }

  // 删除图书
  deleteBook(bookId: string): boolean {
    const index = this.books.findIndex(b => b.id === bookId);
    if (index !== -1) {
      this.books.splice(index, 1);
      return true;
    }
    return false;
  }
}

3. 图书列表/查询页面(pages/BookListPage.ets)

实现图书列表展示和关键词查询功能,采用ArkUI声明式UI:

@Entry
@Component
struct BookListPage {
  // 状态变量:搜索关键词、图书列表
  @State searchKeyword: string = "";
  @State bookList: Book[] = [];
  private bookManager = BookManager.getInstance();

  // 页面初始化时加载数据
  aboutToAppear() {
    this.bookList = this.bookManager.getBooks();
  }

  // 搜索图书
  onSearch() {
    this.bookList = this.bookManager.searchBooks(this.searchKeyword);
  }

  build() {
    Column() {
      // 搜索栏
      Row({ space: 10 }) {
        TextField({ placeholder: "输入书名/作者/分类查询" })
          .width("70%")
          .height(40)
          .border({ width: 1, radius: 8 })
          .padding(8)
          .onChange((value) => {
            this.searchKeyword = value;
          })
        Button("搜索")
          .width("20%")
          .height(40)
          .backgroundColor("#007DFF")
          .onClick(() => this.onSearch())
      }
      .padding(10)
      .width("100%")

      // 图书列表
      List() {
        ForEach(this.bookList, (book: Book) => {
          ListItem() {
            Column() {
              Row({ space: 15 }) {
                Text(`编号:${book.id}`)
                  .fontSize(14)
                  .fontColor("#666")
                Text(`书名:${book.name}`)
                  .fontSize(16)
                  .fontWeight(FontWeight.Bold)
                Text(book.status ? "可借" : "已借出")
                  .fontSize(14)
                  .fontColor(book.status ? "#00C800" : "#FF4D4F")
              }
              .width("100%")
              .padding(5)

              Row({ space: 15 }) {
                Text(`作者:${book.author}`)
                  .fontSize(14)
                Text(`分类:${book.category}`)
                  .fontSize(14)
              }
              .width("100%")
              .padding(5)

              // 已借出图书显示借阅信息
              if (!book.status) {
                Row() {
                  Text(`借阅人:${book.borrower}`)
                    .fontSize(12)
                    .fontColor("#999")
                  Text(`借阅时间:${book.borrowTime}`)
                    .fontSize(12)
                    .fontColor("#999")
                }
                .width("100%")
                .padding(5)
              }
            }
            .width("100%")
            .padding(10)
            .borderBottom({ width: 0.5, color: "#EEEEEE" })
          }
        })
      }
      .width("100%")
      .flexGrow(1)
    }
    .width("100%")
    .height("100%")
    .padding(5)
  }
}

4. 借阅/归还页面(pages/BorrowReturnPage.ets)

实现图书借阅和归还的核心操作:

@Entry
@Component
struct BorrowReturnPage {
  @State bookId: string = "";
  @State borrower: string = "";
  @State tipText: string = "";
  @State tipColor: string = "#333";
  private bookManager = BookManager.getInstance();

  // 借阅操作
  borrowBook() {
    if (!this.bookId || !this.borrower) {
      this.tipText = "图书编号和借阅人不能为空!";
      this.tipColor = "#FF4D4F";
      return;
    }
    const result = this.bookManager.borrowBook(this.bookId, this.borrower);
    if (result) {
      this.tipText = `借阅成功!图书${this.bookId}已借出`;
      this.tipColor = "#00C800";
    } else {
      this.tipText = "借阅失败!图书不存在或已借出";
      this.tipColor = "#FF4D4F";
    }
    // 清空输入框
    this.bookId = "";
    this.borrower = "";
  }

  // 归还操作
  returnBook() {
    if (!this.bookId) {
      this.tipText = "图书编号不能为空!";
      this.tipColor = "#FF4D4F";
      return;
    }
    const result = this.bookManager.returnBook(this.bookId);
    if (result) {
      this.tipText = `归还成功!图书${this.bookId}已入库`;
      this.tipColor = "#00C800";
    } else {
      this.tipText = "归还失败!图书不存在或未借出";
      this.tipColor = "#FF4D4F";
    }
    // 清空输入框
    this.bookId = "";
  }

  build() {
    Column({ space: 20 }) {
      // 借阅模块
      Column({ space: 10 }) {
        Text("图书借阅")
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .alignSelf(ItemAlign.Start)
        TextField({ placeholder: "输入图书编号" })
          .width("100%")
          .height(40)
          .border({ width: 1, radius: 8 })
          .padding(8)
          .onChange((value) => this.bookId = value)
        TextField({ placeholder: "输入借阅人姓名" })
          .width("100%")
          .height(40)
          .border({ width: 1, radius: 8 })
          .padding(8)
          .onChange((value) => this.borrower = value)
        Button("确认借阅")
          .width("100%")
          .height(40)
          .backgroundColor("#007DFF")
          .onClick(() => this.borrowBook())
      }
      .width("90%")
      .padding(15)
      .backgroundColor("#F5F7FA")
      .borderRadius(10)

      // 归还模块
      Column({ space: 10 }) {
        Text("图书归还")
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .alignSelf(ItemAlign.Start)
        TextField({ placeholder: "输入图书编号" })
          .width("100%")
          .height(40)
          .border({ width: 1, radius: 8 })
          .padding(8)
          .onChange((value) => this.bookId = value)
        Button("确认归还")
          .width("100%")
          .height(40)
          .backgroundColor("#00C800")
          .onClick(() => this.returnBook())
      }
      .width("90%")
      .padding(15)
      .backgroundColor("#F5F7FA")
      .borderRadius(10)

      // 提示信息
      Text(this.tipText)
        .fontSize(14)
        .fontColor(this.tipColor)
    }
    .width("100%")
    .height("100%")
    .padding(20)
    .justifyContent(FlexAlign.Center)
  }
}

五、功能扩展与优化建议

  1. 持久化存储:当前数据仅存在于内存中,可集成RelationalStore将图书数据存入本地数据库,保证应用重启后数据不丢失;
  2. 权限管理:新增登录模块,区分管理员/读者权限(管理员可操作借阅/归还,读者仅可查询);
  3. 扫码功能:集成HarmonyOS的扫码API,通过扫描图书条形码/二维码快速获取图书编号;
  4. 多设备适配:使用MediaQuery适配手机、平板、智慧屏等不同尺寸设备,优化大屏布局;
  5. 网络同步:对接后端接口(如SpringBoot),实现多设备数据同步、远程图书管理。

六、运行效果

  1. 图书列表页:可输入关键词搜索图书,列表展示图书基本信息和状态;
  2. 借阅/归还页:输入图书编号和借阅人信息,完成借阅/归还操作,实时提示操作结果;
  3. 所有操作实时同步到内存中的图书数据,刷新列表可看到状态变化。

总结

  1. 本图书馆管理系统基于HarmonyOS 6.0 + ArkTS开发,采用Stage模型和声明式UI,核心实现了图书查询、借阅/归还、图书管理等基础功能;
  2. 代码采用单例模式管理图书数据,保证全局数据一致性,同时通过ArkUI组件实现了简洁易用的交互界面;
  3. 可基于本基础版本扩展持久化存储、权限管理、扫码、网络同步等功能,适配更复杂的图书馆业务场景。

该系统充分利用了HarmonyOS 6.0的ArkTS特性,代码结构清晰、易扩展,适合作为HarmonyOS应用开发的入门实战项目。

引言
在Web开发中,我们习惯了RESTful API。但在金融量化(FinTech)领域,RESTful往往是性能瓶颈的代名词。
本文将从后端工程的角度,详细拆解如何使用Python的websocket-client库,对接第三方行情服务商(以AllTick为例),实现一个高可用、低延迟的港股行情接入模块。

技术栈选择

Language: Python 3.9+

Protocol: WebSocket (RFC 6455)

Library: websocket-client (同步阻塞模式,适合独立进程)

模块实现细节

  1. 连接管理类(Connection Manager)
    为了保持代码的整洁,建议将WebSocket操作封装在一个类中。我们需要处理Socket生命周期的四个关键事件:Open, Message, Error, Close。
    在on_open回调中,我们执行订阅操作。这是一种典型的异步编程思想——连接建立是事件,订阅是响应。
import websocket
import json

def on_message(ws, message):
    data = json.loads(message)
    print(data)  # 输出实时行情数据

def on_open(ws):
    # 订阅港股代码为HK.0005(汇丰控股)的实时数据
    ws.send(json.dumps({
        "event": "subscribe",
        "symbol": "HK.0005",  # 港股代码
        "channel": "market_data"
    }))

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("wss://api.alltick.co/market_data",  # 使用AllTick的WebSocket URL
                                on_message=on_message,
                                on_open=on_open)
    ws.run_forever()
  1. 消息反序列化与路由(Deserialization & Routing)
    服务端推送的数据是Byte流或String。我们需要做两件事:

JSON反序列化:将字符串转为Dict。

业务路由:根据symbol字段,将数据分发给不同的策略回调函数。
注意:这里的异常处理至关重要,格式错误的包不应导致进程崩溃。

response = '{"symbol": "HK.0005", "price": 123.45, "volume": 10000}'
data = json.loads(response)

price = data['price']
volume = data['volume']

print(f"汇丰控股当前价格: {price}, 成交量: {volume}")
  1. 订阅协议的构造
    根据API文档,订阅请求通常是一个包含Event Type和Channel的JSON对象。这里演示了如何构造一个标准的订阅Payload。
  2. 弹性设计(Resilience Engineering)
    在分布式系统中,"Design for Failure"是核心准则。我们利用while True循环配合try...except块,实现了一个简易但有效的守护进程(Daemon)。如果Socket意外断开,程序会休眠数秒后尝试重连,实现无人值守运行。
import time

def fetch_data_with_retry():
    retries = 3
    for _ in range(retries):
        try:
            data = fetch_data_from_api()
            return data
        except Exception as e:
            print(f"请求失败: {e}, 正在重试...")
            time.sleep(2)  # 等待2秒后重试
    print("重试次数已用完,无法获取数据")

总结
通过WebSocket,我们成功将网络开销分摊到了连接建立的一次性成本上,后续的数据传输几乎没有额外Header开销。这对于高频数据处理是非常必要的优化。

博睿大使|推荐Bonree ONE 有礼活动正式启幕!原创 一体化智能可观测 博睿宏远 2026年2月12日 16:00 北京
图片
博睿大使【推荐Bonree ONE有礼】活动正式启幕!即日起至2026年12月31日诚邀各位伙伴成为 Bonree ONE 的引荐者向博睿数据推荐新客户、新商机,解锁丰厚奖励!即刻点击下方海报或扫描海报二维码参与活动吧!具体活动规则详见下方海报👇
图片
— 精彩资料推荐 —
图片

图片

图片

图片

图片

图片

图片
往期推荐_● 博睿数据持续领跑中国APMO市场!► 点击阅读_● 扬帆奋楫 再攀高峰!博睿数据2025年度精彩回顾!► 点击阅读_● 新起点·新视觉|博睿数据全球品牌VI系统全新升级!► 点击阅读_● 《智能体协同矩阵重塑自主运维新范式》白皮书重磅发布!► 点击阅读

编者按: 你是否曾好奇过,那些声称能将长文本输入成本降低90%、延迟减少85%的"Prompt Caching"技术,背后究竟缓存了什么?是简单的文本复用,还是某种更深层的计算优化?

我们今天为大家带来的文章,作者的核心观点是:Prompt Caching的本质并非简单的文本字符串缓存,而是对Transformer注意力机制中Key-Value(KV)矩阵计算结果的复用,通过避免重复计算注意力权重来实现成本削减与性能提升。

文章的重点内容包括:第一,从Tokenizer到Embedding再到Transformer的完整技术拆解,帮助读者建立对LLM内部数据流的直觉认知;第二,对注意力机制(Attention)的数学原理进行深入浅出的阐释,详细展示了Query、Key、Value矩阵的计算过程以及Softmax权重分配机制;第三,揭示了"KV Caching"的核心实现逻辑 —— 通过缓存历史token的K、V投影矩阵,使模型在增量生成时只需计算最新token,而非重新处理整个上下文;第四,对OpenAI与Anthropic两种缓存策略的对比分析,指出自动路由与显式控制之间的权衡,以及Temperature等采样参数对缓存机制的零影响。

作者 | Sam Rose

编译 | 岳扬

撰写本文时,OpenAI 和 Anthropic 的 API 中,缓存的 input token 单价仅为普通 input token 的十分之一。

Anthropic 甚至声称[1],prompt caching 能将长 prompt 的延迟“最高降低 85%”。而在实际测试中,我发现对于足够长的 prompt,这一说法确实成立。我向 Anthropic 和 OpenAI 各发送了数百次请求,注意到在所有 input token 均被缓存的情况下,首 token 延迟(time-to-first-token latency)出现了明显下降。

缓存 token(cached token)到底是什么玩意儿? 

这背后究竟发生了什么,让服务商能给 input token 打出 1 折的超低折扣?他们在各次请求之间到底保存了什么?这可不是简单地把响应结果存下来,等收到相同 prompt 时再复用 —— 通过 API 就能很容易地验证这一点并未发生。随便写个 prompt,连续发送十几次,你会发现即使使用情况栏(usage 部分)显示 input token 已被缓存,每次得到的回复仍然各不相同。

我对大模型厂商文档中的解释[2-3]并不满意 —— 它们虽能很好地说明如何使用 prompt caching,却巧妙地避开了“究竟缓存了什么”这个核心问题。于是我决定深入探究,一头扎进 LLM 工作原理的“兔子洞”,直到彻底搞明白服务商究竟缓存了哪些精确的数据、这些数据的用途,以及它们如何让每个人的 LLM 请求都变得更快速、更便宜。

读完本文,你将……

  • 在更深层次上理解 LLM 的工作原理
  • 对“LLM 的运作方式”建立新的直觉认知
  • 弄明白究竟哪些二进制数据被缓存了,以及它们如何降低你的 LLM 请求成本

01 LLM 架构

本质上,LLM 就是一个巨大的数学函数:输入一串数字,并输出一个数字。在 LLM 内部,存在着一个由数十亿个精心设计的运算构成的巨型图结构,负责将这些输入数字转化为输出数字。

这个由海量数学运算构成的巨型图结构大致可分为 4 个部分。

图中的每个节点都可以看作一个函数,接收输入并产生输出。输入会以循环方式不断馈入 LLM,直到遇到某个特殊的输出值指示其停止。 用伪代码表示大致如下:

prompt ="What is the meaning of life?";

tokens = tokenizer(prompt);
while(true){
 embeddings = embed(tokens);
for([attention, feedforward] of transformers){
 embeddings = attention(embeddings);
 embeddings = feedforward(embeddings);
}
 output_token = output(embeddings);
if(output_token === END_TOKEN){
break;
}
 tokens.push(output_token);
}

print(decode(tokens));

尽管以上描述已大幅简化,但现代 LLM 的核心代码行数之少仍让我感到意外。

Sebastian Raschka[4] 用 PyTorch 从零实现了多个开源模型,还产出了大量高质量的教学材料 —— 如果你喜欢本文,大概率也会喜欢他的内容。以当前领先的开源模型之一 Olmo 3 为例,其核心代码仅数百行[5]。

Prompt caching 发生在 Transformer 的“attention(注意力机制)”中。接下来我们将按顺序逐步拆解 LLM 的工作原理,直到抵达这一环节。这意味着,我们的旅程得从 tokens 说起。

02 Tokenizer(分词器)

在 LLM 处理你的 prompt(提示词)之前,必须先将其转换为它能理解的表示形式。这个过程分为两步,由 tokenizer 和 embedding 共同完成。为什么要这么做,要到讲 embedding 时才能完全明晰,现在请先耐心了解 tokenizer 的作用。

Tokenizer 会将你的 prompt 拆成多个小片段,并为每个唯一的片段分配一个整数 ID,称为"token"。例如,GPT-5 对 prompt "Check out ngrok.ai" 的分词结果如下:

该 prompt 已被拆分为数组 [“Check”, " out", " ng", “rok”, “.ai”],并转换为 tokens [4383, 842, 1657, 17690, 75584]。相同的 prompt 始终生成相同的 tokens。tokens 也是区分大小写的 —— 因为大小写能传递语义信息。例如,首字母大写的 "Will" 更可能是人名,而小写的 "will" 则更可能是助动词。

为什么不直接按空格或字符分割?

这其实是个相当深刻的问题,细讲起来足以让本文篇幅翻倍。简短而不尽兴的答案是:这是一种权衡。若想深入理解,Andrej Karpathy 有一期从零实现 tokenizer 的精彩视频(https://www.youtube.com/watch?v=zduSFxRajkE) 。对于 prompt caching 而言,只需知道:tokenization 的作用就是把文本变成数字。

Tokens 是 LLM 输入与输出的基本单位。当你向 ChatGPT 提问时,回复会随着每次 LLM 迭代完成而逐个 token 流式返回。服务商这么做,是因为生成完整回复可能需要数十秒,而一旦 token 生成就立即返回,能让交互体验更流畅自然。

我们来问一个 LLM 领域的经典问题,亲眼看看这个过程:

Prompt tokens 输入,✨ AI 魔法发生 ✨,输出一个 token,循环往复。这个过程称为“inference(推理)”。注意:每个输出 token 都会在下一轮迭代前被追加到 input prompt 中。LLM 需要全部上下文才能给出高质量回答 —— 如果只输入原始 prompt,它会反复尝试生成答案的第一个 token。如果只输入已生成的回答部分,它会立刻忘记问题本身。因此,每一轮迭代都必须将完整的 prompt 加上已生成的回答内容重新输入 LLM。

那个 199999 <END> token 是什么?

这个推理过程总得有个终点。LLM 拥有多种“特殊”token,其中之一就是标志着响应结束的 token。 在 GPT-5 的分词器中,这就是 token 199999。这只是 LLM 终止生成过程的多种方式之一:你也可以通过 API 指定最大生成 token 数,服务商还可能基于安全策略设定其他终止规则。

此外还有用于标记对话消息起止的特殊 token —— 正是这些 token 让 ChatGPT、Claude 等聊天模型能分辨一条消息何时结束、下一条何时开始。

关于 tokenizer(分词器)的最后一点:它们种类繁多!ChatGPT 使用的 tokenizer 与 Claude 不同,甚至 OpenAI 自家的不同模型也使用不同的 tokenizer。每种 tokenizer 都有自己独特的文本切分规则。如果你想直观比较不同 tokenizer 的分词效果,可以试试 tiktokenizer[6]。

认识了 tokens 之后,接下来我们聊聊 embeddings。

03 Embedding

经过 tokenizer 处理后的 tokens,现在进入 embedding 阶段。要理解 embedding,不妨先思考模型的目标是什么。

人类用代码解决问题时,会编写接收输入、产生输出的函数,比如华氏转摄氏:

function fahrenheitToCelsius(fahrenheit){
return((fahrenheit -32)*5)/9;
}

我们可以把任意数字传入 fahrenheitToCelsius,并能获得正确结果。但假如我们面对一个问题,却不知道背后的公式呢?假如我们只有下面这张神秘的输入-输出对照表:

(我并不指望你能认出这个函数 —— 不过,如果你把截图贴进 ChatGPT,它能立刻识别出来。)

当我们知道每个输入对应的正确输出,却不知道产生这种对应关系的函数时,就可以“训练”一个模型来学习这个函数。做法是:给模型提供一块“画布” —— 那个由海量数学运算构成的巨型图结构,然后不断调整这个图结构,直到模型收敛到正确的函数。每次更新图结构后,我们都将输入数据喂进去,观察输出数据与目标的差距。反复迭代,直到结果足够接近目标。这就是训练的本质。

事实证明,在训练文本生成模型时,能够识别两个句子是否“相似”会很有帮助。但“相似”具体指什么?它们可能同样悲伤、幽默或发人深省;也可能在长度、节奏、语气、语言、词汇或结构上相近。描述句子相似性的方式有无数维度,而两个句子可能在某些维度上相似,在另一些维度上则不然。

Tokens 本身只是简单的整数编号,没有任何“维度”信息;而 embeddings 则是高维向量,承载了丰富的语义和结构信息。

Embedding 是一个长度为 n 的数组,代表 n 维空间中的一个位置。如果 n=3,embedding 可能是 [10, 4, 2],表示三维空间中 x=10、y=4、z=2 的坐标点。在 LLM 训练过程中,每个 token 会被随机分配一个起始位置,随后训练过程会不断微调所有 token 的位置,直到找到能产生最佳输出的排列方式。

Embedding 阶段的第一步,就是查表获取每个 token 对应的 embedding。用伪代码表示大概是这样:

// Created during training, never changes during inference.
const EMBEDDINGS = [...];
 
function embed(tokens) {
 return tokens.map(token => {
 return EMBEDDINGS[token];
 });
}

于是,我们把 tokens(整数数组)转换成了 embeddings(数组的数组,即“矩阵”)。

tokens [75, 305, 284, 887] 被转换为一个由 3 维 embeddings 构成的矩阵。

Embedding 的维度越多,模型可用于比较句子的“角度”就越多。 我们刚才一直在用 3 维 embeddings 举例,但当前主流模型的 embedding 维度通常是几千维,最大的甚至超过 10,000 维。

为了说明更高维度的价值,下面我展示了 8 组彩色形状,它们最初位于一维空间中 —— 挤在一条直线上,杂乱无章,难以理解。但随着维度增加,你就能清楚地看到存在 8 个不同的、相关的组别。

三维是我这里能提供的视觉示例的极限,至于几千维的空间能表达什么,就得靠你发挥想象力了。

Embedding 阶段还有最后一件事要做。在获取 token 的 embedding 后,会将该 token 在 prompt 中的位置信息编码进 embedding 中。 我没有深入研究这一机制的具体实现方式,只知道它对 prompt caching 的工作方式影响不大,但如果没有这一步,LLM 就无法判断 prompt 中 tokens 的先后顺序。

更新一下前面的伪代码,假设存在一个叫 encodePosition 的函数,它接收 embeddings 和位置信息,并返回嵌入了位置编码的新 embeddings。

const EMBEDDINGS =[...];
 
// Input: array of tokens (integers)
function embed(tokens){
// Output: array of n-dimensional embedding arrays
return tokens.map((token, i)=>{
 const embeddings = EMBEDDINGS[token];
return encodePosition(embeddings, i);
});
}

总而言之,embeddings 是 n 维空间中的点,你可以将其视为它们所代表文本的语义含义。在训练过程中,每个 token 都会在该空间中移动,靠近其他语义相似的 token。维度越多,LLM 对每个 token 的表示就越复杂、越细腻。

至此,tokenizer 和 embedding 阶段所做的全部工作,都是为了把原始文本转换成 LLM 能处理的形式。接下来,我们来看看这些数据进入 transformer 阶段后会发生什么。

04 Transformer

Transformer 阶段的核心任务,就是接收 embeddings 作为输入,并在 n 维空间中对它们进行调整。它通过两种方式实现这一点,而我们只关注第一种:attention(注意力机制)。我们暂不讨论 “Feedforward” 层或输出阶段(至少在这篇文章中👀)。

Attention 机制的作用,是帮助 LLM 理解 prompt 中各个 token 之间的关系 —— 具体做法是让每个 token 能够影响其他 token 在 n 维空间中的位置。 它通过加权组合 prompt 中所有 token 的 embeddings 来实现这一点。输入是整个 prompt 的 embeddings,输出则是一个新的 embedding,它是所有输入 embeddings 的加权组合。

举个例子,如果 prompt 是 “Mary had a little”,被分词为四个 token:Mary、had、a、little,那么 attention 机制可能会决定,在生成下一个 token 时,模型会认为:

  • “Mary” 最重要(63%)(译者注:因为整个句子的主语是 Mary,后续内容很可能围绕她展开)
  • “had” 和 “a” 次之(16% 和 12%)(译者注:它们是语法结构的一部分,但语义信息较弱)
  • “little” 也有一定作用(9%)(译者注:它修饰后面的名词)

然后,它会把所有 token 的 embeddings 分别乘以对应的权重,然后把结果加在一起,得到一个融合后的向量。这正是 LLM 判断“在当前上下文中,每个 token 应该被关注多少”的方式。

这是目前为止整个流程中最复杂、最抽象的部分。我会先用伪代码展示它,然后再看看 embeddings 在经过这一过程时是如何被变换的。我本想让这一部分的数学内容少一些,但这里很难避免一些数学运算。别担心,你能行的,我相信你。

Attention 中的大部分计算都是矩阵乘法。对于本文而言,你只需知道:输出矩阵的形状由两个输入矩阵的形状决定,输出的行数等于第一个输入矩阵的行数,列数等于第二个输入矩阵的列数。

理解了这一点,我们来看一个简化版的注意力机制如何计算分配给每个 token 的权重。在以下代码中,我用 * 表示矩阵乘法。

// Similar to EMBEDDINGS from the pseudocode
// earlier, WQ and WK are learned during 
// training and do not change during inference.
//
// These are both n*n matrices, where n is the
// number of embedding dimensions. In our example
// above, n =3.
const WQ =[[...],[...],[...]];
const WK =[[...],[...],[...]];

// The input embeddings look like this:
//[
//[-0.1,0.1,-0.3],// Mary
//[1.0,-0.5,-0.6],// had
//[0.0,0.8,0.6],// a
//[0.5,-0.7,1.0]// little
//]
function attentionWeights(embeddings){
 const Q = embeddings * WQ;
 const K = embeddings * WK;
 const scores = Q * transpose(K);
 const masked = mask(scores);
return softmax(masked);
}

接下来,让我们看看 embedding 在流经这个函数时是如何变化的。

等等,WQ 和 WK 变量到底是什么?

还记得我之前说过,每个 token 的 embedding 最初都被随机分配了一个位置,然后在训练过程中不断微调,直到模型找到一个良好的排列状态吗?

WQ 和 WK 也是类似的。它们是 n×n 的矩阵(n 即 embedding 维度),在训练开始时被赋予随机值,随后也在训练中被不断调整,以帮助模型收敛到一个更优的解。

任何在训练过程中被调整的数,都被称为“模型参数”。embedding 向量中的每个浮点数,以及 WQ、WK 矩阵中的每个数值,都是一个参数。当你听说某个模型有“1750 亿参数”时,指的就是这些数字。

至于 WQ 和 WK 到底代表什么,我们其实并不完全清楚。随着模型训练收敛,它们最终会变成某种对 embedding 的变换方式,有助于模型生成更好的输出。 它们内部可能在做任何事情 —— 而如何解释这些矩阵的含义,目前仍是一个开放且活跃的研究方向。

要得到 Q 和 K,我们分别将 embeddings 与 WQ 和 WK 相乘。WQ 和 WK 的行数和列数始终等于 embedding 的维度(本例中为 3)。这里我为 WQ 和 WK 选取了随机值,并将结果四舍五入到小数点后两位以便阅读。

得到的 Q 矩阵有 4 行 3 列。4 行是因为 embeddings 矩阵有 4 行(每个 token 一行),3 列是因为 WQ 有 3 列(每个 embedding 维度一列)。

K 的计算完全相同,只是将 WQ 换成 WK。

Q 和 K 都是输入 embedding 到新的 n 维空间的"投影"。它们不是原始的 embedding,但由原始 embeddings 推导而来。

然后,我们将 Q 和 K 相乘。我们对 K 进行“转置”,也就是沿对角线翻转,使得得到的矩阵是一个方阵,其行数和列数都等于输入提示词中的 token 数量。

这些 scores 表示每个 token 对下一个生成 token 的重要程度。 左上角的数值 -0.08,代表 “Mary” 对 “had” 的重要性。再往下一行的 -0.10,则代表 “Mary” 对 “a” 的重要性。在展示完矩阵运算后,我会用图示更直观地说明这一点。接下来的所有操作,都是为了将这些 scores 转换为可用于混合 embeddings 的权重。

这个 score 矩阵的第一个问题是:它允许未来的 token 影响过去的 token。在第一行,我们唯一知道的词是"Mary",所以它应该是唯一对生成"had"有贡献的词。第二行也是如此,我们知道"Mary"和"had",所以只有这两个词应该对生成"a"有贡献,依此类推。

为了解决这个问题,我们对矩阵应用一个三角形掩码(triangular mask),将未来 token 对应的位置置零。不过,我们并不是真的设为 0,而是设为负无穷(negative infinity) —— 原因稍后解释。

第二个问题是,这些 scores 是任意的数值。如果它们能变成一个每行之和等于 1 的概率分布,对我们来说会更有用。这正是 softmax 函数的作用。softmax 具体如何运作的细节并不重要 —— 它比简单的“将每个数字除以该行总和”稍复杂一点,但结果是一样的:每行之和为 1,且每个数字都在 0 和 1 之间。

为了解释为什么用负无穷,下面是一个 softmax 的代码实现:

function softmax(matrix){
return matrix.map(row =>{
 const exps = row.map(x => Math.exp(x));
 const sumExps = exps.reduce((a, b)=> a + b,0);
return exps.map(exp => exp / sumExps);
});
}

它并不是简单地把每个数加起来再除以总和,而是先对每个数值取 Math.exp,也就是计算 e^x。如果我们用 0 代替负无穷,Math.exp(0) === 1,这些被屏蔽的位置仍然会产生非零权重。而 Math.exp(-Infinity) 是 0,这正是我们想要的。

下面的图片展示了提示词"Mary had a little"的 attention 权重示例。

这些权重与上面的计算结果不匹配,因为我是从 Transformer Explained 网站[7]上运行的 GPT-2 模型中提取的。所以这些是一个真实模型(尽管是老模型)的真实权重。

第一行只有"Mary",因此Mary对"had"的生成的贡献是100%。然后在第二行,"Mary"贡献了79%,而"had"贡献了21%用于生成"a",以此类推。LLM 认为这个句子中最重要的词是 “Mary”,这一点并不意外——从每一行中 “Mary” 都拥有最高权重就能看出。如果我让你补全"Jessica had a little"这个句子,你不太可能选择"lamb"。

接下来就只剩下对 token embeddings 进行加权混合了,谢天谢地,这一步比计算权重要简单得多。

// Learned during training, doesn't change 
// during inference. This is also an n*n matrix,
// where n is the number of embedding dimensions.
const WV =[[...],[...],...];
 
function attention(embeddings){
 const V = embeddings * WV;
// This is the `attentionWeights` function from
// the section above. We're wrapping it in
// this `attention` function.
 const weights = attentionWeights(embeddings);
return weights * V;
}

为什么不直接混合原始 embeddings?

当我们通过 Q 和 K 相乘得到 attention 权重时,我们完全是在衡量 token 之间的相关性。Embeddings 编码了 token 的各种语义信息 —— 某一维可能表示“颜色”,另一维表示“大小”,再一维表示“礼貌/粗鲁程度”,等等。而权重是通过相似度来判断哪些 token 更相关。

WV 的作用,则是让模型决定在混合时保留哪些维度的信息。

以句子 “Mary had a little” 为例,这里关于 “Mary” 最重要的信息是“人名”。模型在训练中可能也学到了很多关于 “Bloody Mary(血腥玛丽鸡尾酒)” 或 “Mary Queen of Scots(苏格兰女王玛丽)” 的知识,但这些与这首童谣无关,如果带入后续计算反而会引入噪声。因此,WV 允许模型在混合 embeddings 之前,先过滤掉不相关的特征。

接着,我们将生成的权重与 V 相乘,输出一组新的 embeddings:

Attention 机制的最终输出,就是这个输出矩阵的最后一行。 通过 attention 过程,前面所有 token 的上下文信息都被融合进了这一行。但要注意:为了得到最后一行,前面所有行都必须被计算出来。

总而言之,输入是一组 embeddings,输出是一个新的 embedding。Attention 机制通过大量精细的数学运算,按照训练中学到的 WQ、WK 和 WV 矩阵所决定的重要性比例,将各个 token 的信息进行了加权融合。正是这一机制,让 LLM 能够理解在其上下文窗口中“什么内容重要,以及为什么重要”。

现在,我们终于掌握了讨论 caching 所需的一切知识。

当然,Attention 还有更多技术细节

我在本文展示的是一个简化版的 attention,目的是突出与 prompt caching 最相关的核心部分。实际中的 attention 机制更为复杂。如果你希望深入了解更多技术细节,我推荐 3blue1brown 关于 attention 的视频[8]。

05 Prompt caching

我们再来看一遍上面的网格,但这次会展示在推理循环中每生成一个新 token 时,它是如何逐步填充的。

每次生成新 token 时,都会将其追加到输入中,并重新完整处理整个 prompt。但仔细观察:之前计算出的权重从未改变。第二行始终是 0.79 和 0.21,第三行始终是 0.81、0.13、0.06。我们其实在不断重复大量不必要的计算。如果你刚刚才处理完 “Mary had a”,那么在生成下一个 token 时,对 “Mary had a little” 中前三个 token 的大部分矩阵运算其实是冗余的 —— 而这正是 LLM 推理循环的默认行为。

通过以下两个改动,就能避免这些重复计算:

  • 在每次迭代中缓存 K 和 V 矩阵。
  • 只将最新 token 的 embeddings 输入模型,而不是整个 prompt。

现在我们再次走一遍矩阵运算过程,但这一次:前 4 个 token 的 K 和 V 矩阵已被缓存,我们只传入一个新 token 的 embeddings。

是的,又要面对矩阵运算了,抱歉!不过内容和之前基本一致,我们会快速过一遍。

计算新的 Q 时,输出只有一行。WQ 和之前一样,没有变化。

接着,计算新的 K 也同样只输出一行,而 WK 也和之前一样保持不变。

但随后我们将这一新行追加到前一次迭代缓存的 4 行 K 矩阵之后:

于是现在我们拥有了提示词中所有 token 的 K 矩阵,但我们只需要计算它的最后一行。

我们继续以这种方式来获取新的 score:

以及新的的 weights:

全程我们只计算必需的部分,完全不需要对旧值进行任何重新计算。获取 V 的新一行时也是同样的做法:

然后将其追加到我们缓存的 V 中:

最后,我们将新的权重与新的 V 相乘,得到最终的新 embeddings:

我们只需要这单独一行新的 embedding。得益于缓存的 K 和 V,先前所有 token 的上下文信息都已被融入其中。

被缓存的数据是 embeddings WK 和 embeddings WV 的结果,也就是 K 和 V。 因此,提示词缓存通常被称为"KV caching"。

就是这样,上面那些 K 和 V 矩阵,就是服务提供商保存在他们巨大数据中心里的 1 和 0,用来给我们提供一折的 token 成本和更快的响应。

服务提供商在请求发出后,会将每个提示词的这些矩阵保留 5-10 分钟,如果你发送一个以相同提示词开头的新请求,他们就会复用缓存的 K 和 V,而不是重新计算它们。缓存匹配不需要完全一致 —— 即使新 prompt 只和缓存中的某一部分开头相同,也可以复用那部分已缓存的计算结果,而不必整个 prompt 完全匹配。

OpenAI 和 Anthropic 的缓存机制截然不同。OpenAI 完全自动处理,会尽可能尝试将请求路由到缓存条目。 在我的实验中,通过发送请求然后立即重发,缓存命中率约为 50%。考虑到长上下文窗口的首字节延迟(time-to-first-byte)可能很长,这种自动缓存可能导致性能表现不稳定。

Anthropic 则赋予你更多控制权,让你决定何时缓存以及缓存多久。 你需要为这项特权付费,但在我进行的实验中,当我们要求 Anthropic 缓存某个提示词时,他们会 100% 地将请求路由到缓存条目。因此,如果你的应用涉及长上下文窗口,并且需要可预测的延迟,Anthropic 可能是更合适的选择。

等等,那 temperature 这些参数会影响提示词缓存吗?

LLM 提供商提供了多种参数来控制模型输出的随机性,常见的有 temperature、top_p 和 top_k。这些参数都作用于推理循环的最后一步,即模型根据它为词表中每个 token 分配的概率来选取 token。这发生在 attention 机制产生最终 embedding 之后,因此提示词缓存不受这些参数影响。你可以随意调整它们,而不用担心导致缓存的提示词失效。

致谢

为了学习撰写本文所需的全部知识,我如饥似渴地阅读了大量优质内容,以下是我认为对我最有帮助的:

  • Build a Large Language Model (From Scratch)[9] by Sebastian Raschka[10].
  • Neural Networks: Zero to Hero[11] by Andrej Karpathy[12].
  • Neural Networks video course[13] by 3blue1brown[14].
  • Transformer Explainer[15] by Aeree Cho[16] et al.

如果你喜欢这篇文章,你一定会喜欢这些资源。

END

本期互动内容 🍻

❓按照文中逻辑,缓存本质是拿内存换计算。当你处理10万Token以上的超长上下文时,有没有估算过KV Cache的内存占用成本 vs 重新计算的API成本?在什么临界点你会选择放弃缓存?

文中链接

[1]https://claude.com/blog/prompt-caching

[2]https://docs.claude.com/en/docs/build-with-claude/prompt-caching

[3]https://platform.openai.com/docs/guides/prompt-caching

[4]https://magazine.sebastianraschka.com/

[5]https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/13_olmo3/standalone-olmo3.ipynb

[6]https://tiktokenizer.vercel.app/

[7]https://poloclub.github.io/transformer-explainer/

[8]https://www.youtube.com/watch?v=eMlx5fFNoYc

[9]https://www.oreilly.com/library/view/build-a-large/9781633437...

[10]https://sebastianraschka.com/

[11]https://www.youtube.com/watch?v=VMj-3S1tku0&list=PLAqhIrjkxbu...

[12]https://karpathy.ai/

[13]https://www.youtube.com/playlist?list=PLZHQObOWTQDNU6R1_67000...

[14]https://www.youtube.com/@3blue1brown

[15]https://poloclub.github.io/transformer-explainer/

[16]https://aereeeee.github.io/

本文经原作者授权,由 Baihai IDP 编译。如需转载译文,请联系获取授权。

原文链接:

https://ngrok.com/blog/prompt-caching/

在使用OpenClaw的过程中,很多用户都会面临Token消耗过快的问题,目前各大厂商纷纷推出相关Code Plan计划以优化Token使用体验,但多数需要付费购买。对于追求低成本、高实用性的用户而言,一款免费且可用的Token供应方案就显得尤为重要。本文将详细介绍讯飞星辰推出的春节免费Token计划,以及如何将其配置到OpenClaw中,帮助大家零成本畅享大模型应用。

一、讯飞星辰免费Token计划核心优势

讯飞星辰MaaS平台推出的春节免费Token计划,是目前验证可行的免费Token解决方案,核心优势集中在以下几点,适配OpenClaw用户的实际需求:

  • 完全免费:经实际测试,该计划真正实现0元获取Token,无需支付任何费用即可使用,有效解决OpenClaw Token消耗过快、成本过高的痛点。
  • 适配性强:明确支持OpenClaw运行,无需额外修改工具核心设置,配置流程简单,新手也能快速上手。
  • 官方合规:Token供应来自讯飞星辰官方平台,稳定性有保障,无需担心非正规渠道Token带来的账号安全或使用异常问题。

补充说明:官方宣传该Token使用无速度限制,但实际使用过程中会存在轻微卡顿,整体流畅度可满足日常使用需求,属于可接受范围。

二、前期准备:获取讯飞星辰Token及相关授权

在进行OpenClaw配置前,需先前往讯飞星辰MaaS平台获取模型API授权、API Key等关键信息,具体步骤如下:

  1. 访问讯飞星辰MaaS平台官方地址:https://maas.xfyun.cn/,完成平台注册及登录(若已有账号可直接登录)。

  1. 进入平台模型集市:访问https://maas.xfyun.cn/modelSquare,找到对应模型卡片,点击“API调用”按钮,即可获取模型API授权及现金礼品卡(礼品卡可用于后续相关服务拓展,非配置必需)。

  1. 获取核心配置信息:登录后进入推理服务控制台(地址:https://maas.xfyun.cn/modelService),该页面将展示所有模型服务配置所需的关键信息,其中开发者专属API Key是后续配置的核心,需重点记录。

三、OpenClaw详细配置步骤(附可直接复制模板)

讯飞星辰MaaS平台提供OpenAI兼容的接口形态,因此在OpenClaw中可直接按“OpenAI / OpenAI-Compatible”模式配置,具体操作如下,全程无需修改复杂参数:

步骤1:找到OpenClaw配置文件

打开OpenClaw工具,定位到配置文件(通常可在工具设置中找到“配置文件”入口,或按工具指引找到对应文件路径),将以下模板复制粘贴到配置文件中,替换原有相关配置(若配置文件为空,可直接粘贴)。

步骤2:填充核心配置信息

将前期在讯飞星辰推理服务控制台获取的API Key,替换模板中“YOUR_API_KEY”位置,其余参数无需修改(模板已包含常用模型及最优基础配置)。

可直接复制的配置模板

{
"meta": {

"lastTouchedVersion": "2026.2.1",
"lastTouchedAt": "2026-02-04T12:14:10.945Z"

},
"models": {

"mode": "merge",
"providers": {
  "ds": {
    "baseUrl": "https://maas-api.cn-huabei-1.xf-yun.com/v2",
    "apiKey": "YOUR_API_KEY",
    "api": "openai-completions",
    "models": [
      {
        "id": "xopdeepseekv32",
        "name": "DeepSeek-V3.2",
        "reasoning": false,
        "input": [
          "text"
        ],
        "cost": {
          "input": 0.0025,
          "output": 0.01,
          "cacheRead": 0,
          "cacheWrite": 0
        },
        "contextWindow": 32768,
        "maxTokens": 32768
      }
    ]
  }
}

},
"agents": {

"defaults": {
  "model": {
    "primary": "ds/xopdeepseekv32"
  },
  "models": {
    "ds/xopdeepseekv32": {
      "alias": "xopdeepseekv32"
    }
  },
  "compaction": {
    "mode": "safeguard"
  },
  "maxConcurrent": 4,
  "subagents": {
    "maxConcurrent": 8
  }
}

},
"messages": {

"ackReactionScope": "group-mentions"

},
"commands": {

"native": "auto",
"nativeSkills": "auto"

},
"channels": {
},
"gateway": {

"mode": "local",
"tailscale": {
  "mode": "off"
}

},
"plugins": {

"entries": {
},
"installs": {
}

}
}

步骤3:验证配置是否成功

配置完成后,保存配置文件并重启OpenClaw(部分版本无需重启,直接生效)。在工具的聊天窗口中发送一条简单的测试消息(如“你好”),若能正常收到返回结果,即表示已成功调用讯飞星辰MaaS平台的模型服务,Token可正常使用。

四、常见问题及补充说明

  1. 配置后无法正常使用?

优先检查API Key是否填写正确(注意大小写、空格),若API Key无误,可重新登录讯飞星辰平台确认API授权是否有效,或刷新推理服务控制台后重新获取API Key再次配置。

  1. 使用过程中速度过慢?

如前文所述,实际使用中会存在轻微卡顿,属于正常现象,不影响日常使用;若卡顿严重,可检查网络连接,或重启OpenClaw及网络设备尝试优化。

  1. 免费Token有使用期限或额度限制吗?

该计划为讯飞星辰春节专属免费活动,具体使用期限及额度以平台官方通知为准,建议获取Token后及时配置使用,避免过期。

五、后续支持

若在配置过程中遇到其他问题,无法独立解决,可通过以下方式获取协助:点赞、关注本文,转发一次并在评论区留言“666”,即可获取手把手配置指导,全程协助完成所有操作,确保顺利使用免费Token畅享OpenClaw服务。

综上,讯飞星辰春节免费Token计划是OpenClaw用户的高性价比选择,零成本、易配置、可实用,无需额外付费即可解决Token消耗过快的问题,适合各类OpenClaw用户尝试使用。按照本文步骤操作,即可快速完成配置,开启流畅的大模型应用体验。

本公众号提供有偿搭建 openclaw 和 opencode 等服务,并提供免费AI 模型 token方案,让大家可以畅快使用,免费续杯。

有需要的加V mapleCx330

本文由mdnice多平台发布

基于 Flutter × Harmony6.0 的入侵检测系统:构建检测规则模块

前言

随着网络安全形势的日益严峻,入侵检测系统(IDS)成为了防御恶意攻击、保障网络安全的重要工具。在移动互联网和物联网的时代背景下,如何设计一个高效的入侵检测系统,并通过跨平台技术在多个设备上进行部署和管理,成为了开发者面临的一个重要问题。本篇技术博客将通过 Flutter × Harmony6.0 跨端开发技术,深入解析如何构建一个入侵检测系统,并重点介绍如何实现“检测规则”模块。
在这里插入图片描述

背景

入侵检测系统主要用于监控计算机系统和网络流量,及时发现潜在的安全威胁并进行报警。随着技术的发展,越来越多的企业和组织开始依赖智能手机和其他移动设备来管理入侵检测系统。因此,如何实现跨平台、统一的用户体验,成为了关键。FlutterHarmony6.0 技术结合,能够满足这一需求,通过一次开发即可覆盖多个平台(包括安卓、iOS、HarmonyOS等)。

Flutter × Harmony6.0 跨端开发介绍

在这里插入图片描述

Flutter 是 Google 推出的 UI 框架,它使用 Dart 语言,可以编写原生应用,支持 iOS、Android、Web 以及桌面应用开发。它的一大优势是可以通过一个代码库编译生成多个平台的应用,极大地提高了开发效率。

HarmonyOS(鸿蒙操作系统)是华为开发的一款分布式操作系统,支持多设备互联、资源共享。Harmony6.0 是其最新版本,具有更高效的多屏协同和跨平台能力。将 FlutterHarmony6.0 结合使用,可以实现跨平台的无缝连接和高度一致的用户体验。

开发核心代码

在这里插入图片描述

1. 构建监控项

在入侵检测系统中,监控项用于显示具体的监控数据,例如网络流量、进程监控等。以下是 Flutter 中构建监控项的代码:

/// 构建监控项
/// @param label 标签文本
/// @param value 数值文本
/// @param icon 图标
/// @param color 主题颜色
/// @param theme 主题数据
Widget _buildMonitoringItem(String label, String value, IconData icon, Color color, ThemeData theme) {
  return Row(
    children: [
      Container(
        width: 40,
        height: 40,
        decoration: BoxDecoration(
          color: color.withOpacity(0.1),
          borderRadius: BorderRadius.circular(8),
        ),
        child: Icon(icon, color: color, size: 20),
      ),
      const SizedBox(width: 12),
      Expanded(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              label,
              style: theme.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.bold),
            ),
            Text(
              '今日检测',
              style: theme.textTheme.bodySmall?.copyWith(
                color: theme.colorScheme.onSurfaceVariant,
              ),
            ),
          ],
        ),
      ),
      Text(
        value,
        style: TextStyle(
          fontSize: 18,
          fontWeight: FontWeight.bold,
          color: color,
        ),
      ),
    ],
  );
}

解析:

  • Row 组件用于排列监控项的各个部分,包括图标、标签文本、数值文本等。
  • Container 用来显示图标,图标颜色使用传入的 color 参数,并通过 withOpacity(0.1) 添加透明度效果。
  • 使用 Expanded 让标签文本和数值文本能够自适应布局。
  • 最后,数值文本通过 Text 组件展示,并根据传入的 color 进行样式设置。
2. 构建检测规则模块

检测规则模块是入侵检测系统的核心部分之一,用于展示各种检测规则及其启用状态。以下是代码实现:

/// 构建检测规则模块
/// 显示各种入侵检测规则及其启用状态
Widget _buildDetectionRules(ThemeData theme) {
  return Card(
    elevation: 2,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
    child: Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                '检测规则',
                style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
              ),
              TextButton.icon(
                onPressed: () {},
                icon: const Icon(Icons.add, size: 16),
                label: const Text('添加', style: TextStyle(fontSize: 12)),
              ),
            ],
          ),
          const SizedBox(height: 16),
          _buildRuleItem('端口扫描检测', '已启用', Colors.green, theme),
          const SizedBox(height: 8),
          _buildRuleItem('暴力破解检测', '已启用', Colors.green, theme),
          const SizedBox(height: 8),
          _buildRuleItem('DDoS攻击检测', '已启用', Colors.green, theme),
          const SizedBox(height: 8),
          _buildRuleItem('SQL注入检测', '已启用', Colors.green, theme),
          const SizedBox(height: 8),
          _buildRuleItem('XSS攻击检测', '已启用', Colors.green, theme),
        ],
      ),
    ),
  );
}

解析:

  • Card 组件用于显示规则卡片,卡片设置了圆角和阴影效果。
  • Row 中的 TextButton.icon 按钮用于添加新的检测规则。
  • 每条规则通过 _buildRuleItem 展示,传入规则名称、状态以及颜色。使用 SizedBox 来控制每条规则之间的间距。
3. 构建检测规则项

每个检测规则项通过 _buildRuleItem 方法实现,显示规则的名称、启用状态以及颜色:

/// 构建检测规则项
/// 显示单条检测规则
Widget _buildRuleItem(String ruleName, String status, Color color, ThemeData theme) {
  return Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    children: [
      Text(
        ruleName,
        style: theme.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold),
      ),
      Text(
        status,
        style: TextStyle(color: color, fontWeight: FontWeight.bold),
      ),
    ],
  );
}

解析:

  • 使用 Row 展示每个规则的名称和状态。
  • 状态文本的颜色使用传入的 color 参数,确保与规则的启用状态一致。

在这里插入图片描述

心得

在构建这一入侵检测系统时,借助 FlutterHarmony6.0 的强大跨平台能力,能够大大简化开发流程。特别是在设计 UI 时,Flutter 提供的组件库能够灵活地适配不同平台的界面要求,同时与 Harmony6.0 的分布式特性结合,保证了系统在多设备上的一致性体验。

总结

通过本次项目的开发实践,我们实现了一个基于 Flutter × Harmony6.0 的入侵检测系统,并重点完成了“检测规则”模块的构建。通过详细的代码解析,可以看到 Flutter 在跨平台开发中的优势,以及如何通过简洁的代码实现丰富的功能和界面。这个系统的核心目标是提高网络安全的检测效率,同时通过统一的用户界面让用户能够方便地查看和管理入侵检测规则。

Xcode 的最新版本 26.3 扩展了对编程代理的支持,可接入 Anthropic 的 Claude Agent 和 OpenAI 的 Codex,助力开发者处理复杂任务并提升生产力。

借助智能体编程,Xcode 能够以更高的自主性朝着开发者的目标推进工作——从任务分解、基于项目架构做出决策,到使用内置工具执行操作。

新版本在 Xcode 26 已引入的智能体编程能力基础上,进一步为编程智能体开放了更多 Xcode 功能的访问权限。苹果公司表示,智能体现在可以进行协作、搜索文档、浏览文件结构以及修改项目设置。此外,智能体还能通过捕获 Xcode 预览来验证代码,查看正在构建的界面效果,识别问题并在此基础上迭代优化。Anthropic 表示,这“在构建 SwiftUI 视图时尤为实用,因为视觉输出是重中之重”。

Anthropic 还强调,Xcode 26.3 集成了 Claude Agent SDK,该 SDK 为 Claude Code 提供支持,让开发者能够使用“Claude Code 的全部功能,包括子智能体、后台任务和插件”。

Xcode 26.3 另一项重要新功能是支持模型上下文协议(Model Context Protocol,MCP),允许开发者将任意兼容 MCP 的智能体或工具与 Xcode 配合使用,也使得在 IDE 直接使用 Claude、Codex 之外的其他智能体成为可能。MCP 集成可通过 xcrun mpcbridge 来启用,示例如下:

codex mcp add xcode -- xcrun mcpbridge

iOS 开发者 Akhlaq Ahmad 在 LinkedIn 上指出,该公告似乎标志着从 AI 编程助手向更多 AI 协作伙伴变,因为智能体现在可以与 Xcode 交互,“分解目标、规划、实施、运行构建/测试,并持续优化直到代码编译成功并按预期运行”。

虽然对 MCP 的支持有望让 Xcode 以以往无法实现的方式与外部工具交互,但 Reddit 用户 TrajansRow 提醒,其权限模型“会带来一些阻碍”

如果你将 Xcode MCP 服务器添加到外部智能体系统,每次有新的智能体 PID 发出请求时,都必须手动关闭“允许‘agent’访问 Xcode?”的弹窗。

不过,Hacker News 的读者 drak0n1c4 反馈,Xcode 26.3 中的 MCP 支持“目前存在缺陷”,它返回的格式与其声明的 Schema 不一致,导致无法与 OpenCode 配合使用。

Xcode 26.3 虽可安装在旧版 macOS 上,但 AI 编程相关功能仅在运行代号为 Tahoe 的 macOS 26 时可用。Xcode 26.3 目前已通过苹果开发者网站向 Apple Developer Program 会员提供,不久后将通过 App Store 向所有开发者开放。

原文链接:

https://www.infoq.com/news/2026/02/xcode-26-3-agentic-coding/

做外汇量化策略开发这么久,我一直被一个问题困扰:为什么回测很稳的策略,一上实盘就容易跟不上行情?
后来慢慢发现,问题往往不是算法不行,而是数据链路太弱
外汇市场 24 小时不间断波动,尤其做短线、波段、震荡策略时,对数据实时性要求极高。以前我也用过爬虫、手动刷新网页、Excel 同步等方式,不仅效率极低,还经常漏数据、错格式,真正想抓住关键波动时,机会早就没了。

一、传统数据方式,到底坑在哪?
只要你做过外汇程序开发,基本都会遇到这三个共性痛点:

  • 延迟不可控
    网页刷新、第三方数据更新都有固定间隔,几分钟的延迟,在快节奏行情里就是致命差距。
  • 格式乱七八糟
    不同平台数据结构不统一,解析、清洗、对齐要花大量时间,还容易出 Bug。
  • 很难接入自动化系统
    没有标准接口,数据没法直接喂给策略、回测框架、监控面板,自动化基本是空谈。
    这些问题不解决,策略再漂亮也跑不起来。

二、稳定的外汇 API,究竟解决了什么?
对量化开发者来说,实时汇率 API 不是锦上添花,而是底层基建
它能提供低延迟、连续推送、格式统一的行情数据,拿来就能直接在代码里使用,不用再做多余处理。
我在实际项目里会用 AllTick API 来订阅主流货币对实时行情,接入简单、稳定性也够用。

import websocket
import json

url = "wss://realtime.alltick.co/forex?symbols=USDCNY"

def on_message(ws, message):
    data = json.loads(message)
    print(f"USD/CNY 当前汇率: {data['price']} 时间: {data['time']}")

def on_error(ws, error):
    print(f"连接错误: {error}")

def on_close(ws):
    print("连接已关闭")

def on_open(ws):
    print("实时数据连接成功,开始接收数据...")

ws = websocket.WebSocketApp(url,
                            on_message=on_message,
                            on_error=on_error,
                            on_close=on_close)
ws.on_open = on_open
ws.run_forever()

像这样通过 WebSocket 订阅后,数据可以直接进入你的逻辑:分析、计算、可视化、触发信号都没问题。
对比手动爬取、表格整理,效率提升是数量级的。

三、实战开发中,这些细节直接影响效果
在长期写策略、接数据的过程中,我总结了几个非常实用的关键点:

  • 合理设置波动阈值
    网络抖动、微小波动很常见,不要对每一次价格变动都响应,设置阈值能大幅减少无效计算。
  • 按需订阅货币对
    一次性订阅太多品种会增加程序压力,只选策略真正用到的就行。
  • 精简数据存储
    实时数据量巨大,只存价格、时间戳、货币对这些关键字段,能显著降低数据库压力。
    把实时数据 + 历史数据结合,还能做回测、预警、自定义看板,比单纯看 K 线更贴近真实市场。

四、对量化策略与自动化的真实价值
API 解决的不只是 “看行情”,而是让策略从理论变成可运行的系统。
有了稳定实时数据,你可以:

  • 实时监控汇率,突破 / 跌破自动报警
  • 记录关键支撑阻力位
  • 快速验证轻量级量化策略
  • 实现半自动甚至全自动交易逻辑
    没有实时数据入口,再好的策略也只能停留在回测阶段。
    把数据层交给稳定接口,我们才能真正专注在策略逻辑、风控和算法优化上。

五、给开发者的实用建议
如果你也在做外汇相关开发,这几点可以直接参考:

  • 先明确自己的策略覆盖范围,不要盲目全量订阅
  • 实时数据最好配合持久化或可视化,分析效率更高
  • 优先选择低延迟、格式标准、长期稳定的接口,减少后期重构

一句话总结:
在外汇量化开发里,稳定的实时 API = 策略落地的基础效率底座,它能让你在快速波动的市场里,更稳、更准地抓住真正有价值的机会。

时序更替,华章日新。回顾2025年,是AI技术从“云端狂欢”走向“落地深耕”的一年,也是1024Foundation践行初心的关键之年。自2024年10月发起 AI for AII Initiative (AI4AI) 普及公益以来,我们始终致力于推动AI从“精英专属”走向“大众普及”。 

过去的一年,我们以「AI雏鹰计划 」、「AI转型计划 」、「AI领袖计划 」为三大支柱,通过教育培训、OPC项目实操实训、智能体AI主题沙龙等多样化活动,在AI4AI普及公益之路留下了坚实而有力的足迹。 

值此马年新春,让我们回顾过往、启新未来,共享成果,同贺新春! 

AI4AI 雏鹰计划 

AI4AI 雏鹰计划致力于帮助零基础青少年发展AI能力,开启全新思维模式。2025年,我们通过传授零代码智能体、大模型等前沿技术,打破地域限制与认知门槛,让 AI 学习真正无门槛、有温度,为未来的科技创新播下希望的种子。 

西藏中小学生AI英语教学平台 

我们用“结对智能体”的形式,手把手助力8名上海文绮汇点美高学子,通过“零代码智能体”方法论,成功打造了面向西藏中小学生的 AI 英语教学平台。科技打破了地域的界限,让高原上的孩子们也能享受到个性化的智能教育资源。 

图片

零代码AI智能体开发高中课程 

我们与闵行中学/文绮汇点合作开展《零代码 AI 智能体开发课程》,采用从AI理论到实践操作的教学模式,赋能学生们通过零代码智能体方法论,结合πDataCS、OpenClaw、OpenCode等AI产品,将智能体开发创意转化为实践成果,在项目实操中零距离学习人工智能技术。 

图片

出席TED X NAIS活动 

5月,1024Foundation发起人冯雷(Ray Von)出席 TED X NAIS 等多个活动。他结合丰富的人生阅历与科技创业经验,向青年学子传达了 “The Sense of Mission”(使命感)的理念,并提出了 “Meta thinking”(元思维)核心思想,为学子们打开了通往未来科技与创新思维的大门。
 
图片

AI4AI 转型计划 

AI4AI 转型计划帮助有职业转型需求的人群,开拓职业机会,提升个人竞争力,顺利适应并融入人工智能行业需求。2025年,我们以沙龙、研讨会、讲座为载体,打破技术壁垒,将前沿理论转化为实战演练,帮助各行各业的奋斗者掌握驾驭 AI 的工具,完成职业生涯的华丽转身。 

智能体新纪元沙龙 

6月,我们成功举办【智能新纪元:AI Agent如何赋能实体产业】主题沙龙。行业专家齐聚一堂,共同探讨AI Agent如何深入业务场景,构建垂类智能体的可持续发展范式,为产业升级寻找具体的解题思路。 

图片

助力商科教育“数智化”转型 

在AI重构商业逻辑的当下,商科教育正面临从“传统管理”向“数智管理”转型的关键挑战。冯雷先后走进浙大EMBA、上海大学MBA、吉林大学等知名高校课堂,用理论实践为高校学子讲解数智化转型的关键要素。 

图片

图片

图片

 传递AI颠覆式创新理念 冯雷受邀参与中国MBA创业大赛等重要活动,面向未来的创业者,全方位传递AI转型理念,深度剖析智能体时代的商业重构逻辑,激发颠覆式创新思维。
 
图片

AI4AI 领袖计划 

AI4AI 领袖计划汇聚行业顶尖思想,赋能管理者构建AI时代的战略决策力。2025年,冯雷出席多场行业领袖交流座谈,与多方伙伴,共建生态引领时代航向。 

出席数安港企业座谈会 

7月,冯雷出席中国(温州)数安港企业座谈会并分享“如何赋能数字经济发展”。
 
图片

出席之江创投政协委员会 

9月,冯雷作为科技企业代表,出席之江创投政协委员会客厅主题活动,分享可信大模型数据计算系统的实践经验,推动AI领域资源联动与人才培育,为行业领袖搭建交流与成长的平台。 

图片

技术的意义不在于多么先进,而在于能为人类带来多少便利。
回望2025,每一步实践都凝聚着初心,每一项成果都彰显着担当。 
展望2026,我们将继续推进AI4AI普及公益。
坚守“技术有温度”的初心,让技术创新真正扎根生活惠及大众!

今天我对象突然给我发信息,说我淘宝给她发莫名其妙的话。

我说我没有啊,今天淘宝都没打开呢。

结果我登上账号一看,我看到“我”发的信息

我在微信说不是我,我对象也是直接开骂,然而他竟然还能登录,还在发信息

当我看登录日志,手机卡一键登录的时候我明白了,

我手机还是绑定的之前不用的手机号,我也是立马换绑了

异地登录,一点消息提醒也没有,还显示我非常安全,这个非本人登录还是我手动标记的

而客服也是非常“专业”的让我去修改密码,更让我震惊那个人还想给我打电话

我想问,发生这样的事情有渠道投诉吗,

更重要的是,我想知道我还有没有其他软件有这样的情况,之前的手机号不在我手里我能查到吗,怎么查?

这属于很严重的安全问题吗?

也提醒各位,看看自己账号,把自己之前不用的手机号换绑了,

之前是懒得弄,而且也比较相信淘宝的安全性。






作者:王博涵 小步外勤产品总监,外勤管理数字化专家

很多企业在销售团队规模扩大之后,都会遇到一个共性问题:人越来越多,管理却越来越难。

销售人员长期在外跑客户,拜访过程看不见,拜访真实性难核查,客户跟进节奏无法统一,差旅报销管理也经常出现争议。时间一长,销售执行力下降,管理层却很难找到真正的原因。

这也是为什么越来越多企业开始关注销售团队拜访系统。它的价值不在于增加一套工具,而在于把销售过程变得可追溯、可分析、可复盘

但在真正选型时,企业往往又会犹豫。销售团队拜访系统到底怎么选,价格是不是很高,功能是否真的实用,推行会不会遇到阻力,这些问题如果没有想清楚,很容易踩坑。

销售团队拜访系统解决的到底是什么问题

在没有系统之前,大多数企业依赖微信群汇报、Excel登记或人工抽查来做客户拜访管理。这种方式在团队规模较小时还能维持,但一旦人员扩张,问题会逐渐显现。

例如,拜访签到可能存在虚拟定位,拜访记录写得完整却难以核实真实性,业务员在客户处停留时间过短却难以及时发现,销售过程管理缺少连续数据支撑。差旅报销管理尤其明显,里程核算和油补发放常常缺乏客观依据。

销售团队拜访系统的核心作用,是让这些分散的外勤行为形成结构化数据。从拜访签到、拜访轨迹,到客户停留时长和拜访记录沉淀,都能够形成连续链路。管理者看到的不再只是结果,而是完整过程。

销售团队拜访系统真正解决的是销售过程不可视的问题。

选销售团队拜访系统,关键看三点

第一是数据是否真实可靠

销售拜访管理系统如果无法识别虚拟定位或异常定位行为,所有数据都可能失去意义。外勤打卡和拜访签到必须建立在真实定位基础之上,否则销售过程管理只是形式化。

第二是流程是否可以标准化

优秀的销售团队拜访系统不仅记录拜访,更会推动拜访动作的规范化。例如门店拜访中,是否需要陈列检查、库存采集、竞品上报、拍照取证,这些都应该被配置成固定流程。流程一旦固化,销售执行力才会趋于稳定。

第三是是否具备轨迹与分析能力

拜访轨迹还原和客户停留时长分析,是判断拜访质量的重要依据。通过轨迹数据,企业可以优化拜访路线,减少空跑,提高人均产出。这也是外勤管理系统区别于简单签到工具的关键所在。

销售团队拜访系统是否有效,往往就取决于这三项能力是否扎实。

关于销售团队拜访系统价格

很多企业在搜索销售团队拜访系统时,第一反应是价格。但真正成熟的管理决策,往往不会只看采购成本。

目前主流销售拜访管理系统多采用SaaS模式,按账号数量订阅,不需要额外部署服务器。从投入形式来看,相对轻量。

更重要的是系统带来的管理回报。通过规范客户拜访管理、减少无效拜访、提升销售执行力,以及结合拜访轨迹进行差旅报销管理核算,企业往往能够在效率和费用控制上获得长期收益。

判断销售团队拜访系统是否值得投入,关键不在单价,而在能否持续降低管理成本。

推行难度是否可控

系统是否推得动,是很多企业最现实的顾虑。事实上,销售团队拜访系统的推行效果,与制度设计和执行绑定程度密切相关。

如果企业明确拜访频次要求,将拜访记录纳入绩效考核,并配合上线培训和实施支持,推行难度并不会过高。反之,如果只上线工具而不调整管理规则,再好的系统也难以发挥价值。

在实际落地过程中,例如小步外勤等优秀管理系统,都强调陪跑式支持,帮助企业从流程配置到制度导入逐步推进,使销售团队拜访系统真正融入日常管理。

销售团队拜访系统适合哪些企业

一般来说,当企业拥有一定规模的外勤销售团队,客户分布较广,终端巡店频繁,差旅报销管理复杂时,就已经具备部署销售团队拜访系统的必要条件。

团队越大,销售过程越复杂,系统带来的管理价值就越明显。

结语

销售团队拜访系统不是为了强化监管,而是为了让销售过程更清晰,让团队执行更稳定。

当拜访轨迹可追溯,客户拜访记录可沉淀,差旅报销管理有依据,销售执行力可以量化时,企业管理将不再依赖经验判断,而是建立在真实数据之上

摘要:
在AI辅助编程时代,开发者使用 Cursor 进行开发时,可能会因AI助手不熟悉OceanBase推出的AI原生搜索数据库 seekdb,无法获取精准的技术解答。seekdb Cursor Extension 可向 Cursor AI 注入官方文档知识,使其理解 seekdb 核心概念、提供合规代码建议并精准解答技术问题。该扩展支持一键安装与双模式文档检索,操作简便,能有效减少开发者查阅文档的时间,提升 seekdb 相关 AI 应用开发效率。

本文将为大家介绍如何通过 seekdb Cursor Extension,让 Cursor AI 助手拥有 seekdb 专业知识,从而在大家基于 seekdb 进行 AI 应用开发的过程中获得精准的技术指导。

什么是 seekdb?

seekdb 是由 OceanBase 推出的一款 AI 原生搜索数据库。它在单一引擎中统一了关系型数据、向量、文本、JSON 和 GIS 等多种数据模型,支持混合搜索和数据库内的 AI 工作流。

seekdb 的典型应用场景包括:

RAG 与知识检索:为大语言模型引入实时可信的外部知识,提升回答质量
AI 辅助编程:为代码仓库构建向量和全文索引,实现基于语义的代码搜索
语义搜索引擎:捕捉用户搜索意图,实现跨模态精准检索
智能体(Agent)应用:为 AI Agent 提供记忆、规划、感知和推理的统一基础

什么是 seekdb Cursor Extension?

seekdb Cursor Extension 是一款 Cursor 扩展,它通过在 .cursor/rules 目录下添加规则,使 Cursor AI 助手能够检索 seekdb 官方文档,从而理解 seekdb 数据库知识,使其能够:

理解 seekdb 数据库概念:向量搜索、混合搜索、AI 函数等
提供准确的代码建议:基于官方文档生成符合最佳实践的代码
回答 seekdb 相关问题:直接在编辑器中获取技术支持
加速开发流程:减少查阅文档的时间,专注于业务逻辑

核心特性

一键安装:通过 Cursor 扩展市场或命令面板快速安装
完整文档:检索 seekdb 官方文档知识库,涵盖向量搜索、混合搜索、AI 函数等全面技术文档
双模式支持:优先从 GitHub 获取最新文档,本地文档作为备份

快速开始

第一步:安装扩展

  1. 在 Cursor 中打开扩展市场(Ctrl+Shift+X 或 Cmd+Shift+X)
  2. 搜索 "seekdb"
  3. 点击 Install 安装扩展

第二步:添加 seekdb 文档

  1. 使用 Cursor 打开一个项目目录(文档将添加到该目录下)
  2. 打开命令面板:
    Windows/Linux: 按 Ctrl+Shift+P
    macOS: 按 Cmd+Shift+P
  3. 输入并选择命令:
    输入 "seekdb" 或 "Add seekdb Docs"
    选择 Add seekdb Docs 命令
  4. 文档将自动添加:
    .cursor/rules/seekdb-docs 目录(官方文档)
    .cursor/rules/seekdb.mdc 文件(规则文件)
  5. 重新加载窗口使规则生效

安装完成!现在你可以直接向 Cursor AI 助手询问任何 seekdb 相关问题了。

实际效果演示

让我们通过一个实际示例,看看 seekdb Cursor Extension 如何帮助你进行开发。

示例:使用 AI 助手创建一个 seekdb 混合搜索应用

安装扩展并添加文档后,在 Cursor 中开始一个新对话,输入以下问题:

例如:我想用 Python 创建一个简单的 seekdb 应用,实现文档的混合搜索功能,请帮我写代码。

Cursor AI 助手此时就会给出准确的回答:

运行示例

  1. 安装 pyseekdb

  1. 运行代码

  1. 查看结果

混合搜索结合了关键词匹配(包含 "机器学习" 的文档)和语义搜索(与 "AI 技术" 语义相近的文档),通过 RRF(Reciprocal Rank Fusion)算法融合两路检索结果,返回最相关的文档。

特别说明:seekdb 的嵌入式模式暂时只支持 Linux 服务器,如果是在 Mac 或者 Windows 本地测试,需要把 Python 代码里的 client = pyseekdb.Client() 改成服务器模式的连接地址(推荐在 Mac 或者 Windows 上使用 seekdb 桌面版)。

更多使用场景

安装 seekdb Cursor Extension 后,你可以向 AI 助手询问各种 seekdb 相关问题:

基础查询

如何开始使用 seekdb?
seekdb 支持哪些部署模式?

技术问题

如何在 seekdb 中创建向量索引?
seekdb 的 AI 函数有哪些?如何使用 AI_EMBED 函数?

代码示例

展示一个使用 seekdb SQL 实现向量相似度搜索的示例。
如何将 seekdb 与 LangChain 集成?

集成相关

seekdb 如何配置 OpenAI 模型进行向量嵌入?

工作原理

seekdb Cursor Extension 的工作原理非常简单:

  1. 规则文件注入:扩展将 seekdb 官方文档和 .mdc 规则文件添加到 .cursor/rules 目录
  2. AI 上下文增强:Cursor 会自动读取 .cursor/rules 目录中的内容,作为 AI 助手的上下文知识
  3. 智能检索:当你询问 seekdb 相关问题时,AI 助手会基于这些文档提供准确的回答

移除文档

如果你不再需要 seekdb 文档,可以轻松移除:

  1. 打开命令面板(Ctrl+Shift+P 或 Cmd+Shift+P)
  2. 输入 "Remove seekdb Docs"
  3. 选择该命令执行

文档将从 .cursor/rules 目录中移除。

总结

通过 seekdb Cursor Extension,你可以在使用 Cursor 进行开发时,随时获取 seekdb 的官方文档支持。无论是学习 seekdb 的新功能,还是解决开发中遇到的技术问题,AI 助手都能基于最新的官方文档提供准确的指导。

欢迎访问 OceanBase 官网获取更多信息:https://www.oceanbase.com/

演讲者:一锤(周克勇)| EMR Serverless Spark 技术负责人

2025年9月,阿里云EMR Serverless Spark 以QphDS超6568万分的性能结果成功登顶TPC-DS 100T榜单,这是全球大数据领域最具权威性和挑战性的性能测试基准。

阿里云EMR Serverless Spark TPC-DS 100T 性能测试结果

TPC-DS Benchmark是数据仓库领域最新和最复杂的权威测试标准,被工业界和学术界广泛认可,也是数据仓库选型的重要参考指标。TPC-DS包含99个查询,从简单的全局聚合到复杂的20以上多表连接,体现了真实分析场景日益增长的复杂度。其中,100T是TPC-DS提供的最大测试数据集,最大表有288,017,344,252(2880亿)条数据,迄今为止只有阿里云EMR和Databricks成功通过了该榜单的官方评审。

阿里云 EMR Serverless Spark实现了 性能提升100%性价比提升500% 的突破,证明了EMR Serverless Spark 在 OpenLake湖仓底座架构下,超大规模、超高复杂度的数据分析、数据更新、数据处理的市场领先能力。

本文将深入剖析支撑这一成绩背后的技术内核,从产品定位、架构设计到核心优化策略,全面解读 EMR Serverless Spark 如何实现“高性能、低成本、高弹性、强兼容”的统一。

产品定位与核心场景

EMR Serverless Spark 定位为新一代 Lakehouse(湖仓一体)平台,旨在融合传统数据仓库的极致查询性能与数据湖的低成本、开放性优势。

其核心聚焦三大场景:

  1. 湖仓分析场景:以高度优化的 Spark 替代 Hive 执行 ETL/ELT 任务,替代 Trino/Presto 提供高性价比交互式分析。支持 SQL、DataFrame、Pandas、RDD 等多种接口,并全面兼容 Paimon、Iceberg、Delta、Hudi 等主流湖表格式。
  2. 机器学习场景:作为成熟的分布式 ML 框架,Spark 支持从数据清洗、特征工程到模型训练与批量推理的全流程。内置 MLlib,集成 XGBoost、LightGBM、scikit-learn 等生态工具,并提供 GPU 加速能力,实现 Data + AI 一体化。
  3. 多模态数据处理场景:随着大模型兴起,PySpark 成为处理文本、图像、视频等非结构化数据的理想选择。产品推出的 AI Function 功能,允许用户在 Spark 作业中直接调用大模型。针对基模训练数据预处理做了专门优化,在文本去重任务中实现 5倍性能提升

产品架构与极致弹性

EMR Serverless Spark 采用标准 Lakehouse 架构:

  • 存储层:基于阿里云 OSS 对象存储与 OSS-HDFS 接口,提供高吞吐、低成本的持久化能力;
  • 元数据层:兼容 Hive Metastore(HMS)与 Data Lake Formation(DLF),支持 ACID 事务;
  • 资源层:依托阿里云全 Region 的 ECS 资源池,实现近乎无限的弹性供给;
  • 引擎层:核心创新包括 Fusion 向量化执行引擎 与 Celeborn Remote Shuffle Service
  • 产品层:提供认证鉴权、开发 IDE、资源监控、智能诊断等企业级功能。

极致弹性 是其关键竞争力:

  • 支持 进程级弹性,最小资源单位低至 1 Core;
  • 容器启动时间 <15 秒,会话模式、Standalone模式下实现“零冷启”;
  • 采用 Workspace + 队列的双层 Quota 机制,满足多租户资源隔离需求;
  • 实际客户案例显示,资源使用波动可从数万核骤降至零,Serverless 架构帮助客户 节省40%资源成本

此外,系统默认提供 跨可用区高可用 能力,Spark 控制面与 Celeborn 服务均多 AZ 部署,作业自动故障迁移,SLA 达 99.9%,且无额外费用。

EMR Serverless Spark 产品架构

全方位生态兼容

EMR Serverless Spark 坚持 开放生态优先 的设计理念:

  • 接口兼容:完整支持 spark-submit、spark-sql、beeline、JDBC 等经典方式,也集成 Kyuubi(含 HA)、Livy 等交互式查询服务;
  • 工具链集成:无缝对接 Jupyter、Zeppelin、Superset、DBT 等主流开发分析工具;
  • 调度系统:深度适配 Airflow、DolphinScheduler,并在阿里云生态内与 DataWorks 原生集成——作为DataWorks“一等公民”,支持 SQL 节点、Notebook、工作流编排、统一权限与数据血缘等;
  • 安全与元数据:支持 Kerberos、LDAP、Ranger;
  • 湖格式:湖格式覆盖 Paimon/Delta/Hudi/Iceberg;
  • 外部数据源:连接 StarRocks、Doris、Hologres、HBase、Elasticsearch、MongoDB、MaxCompute、MySQL 、Postgres等数十种系统。

这种广泛的兼容性极大降低了用户迁移和集成成本,真正实现“开箱即用”。

TPC-DS 100T 背后的四大核心技术

官方TPC-DS 100T 测试包含数据生成、导入、Power Test(单并发99查询)、Throughput Test(4并发396查询)、Maintenance Test(Upsert 操作)等环节,最终通过 QphDS 分数衡量综合性能。

阿里云的突破源于以下四大技术创新:

1. Fusion 向量化执行引擎

自2019年起研发,Fusion 将 Spark 从行式计算升级为 列式向量化执行

  • 利用 SIMD 指令并行处理多列数据;
  • 连续内存布局显著提升 CPU Cache 命中率;
  • 异步 IO 与 IO 合并优化读取效率;
  • 关键算子(Sort/Window/Join)优化,性能提升达 300%

在 TPC-DS 场景中,Fusion 还引入 Subplan Reuse、Broadcast Join Reuse、Semi Join 哈希表去重等优化,大幅减少重复计算与内存占用。

2. 与 Paimon 深度协同

Fusion 与阿里自研湖表格式 Paimon 深度整合:

  • 向量化读写使读性能提升 70%,写性能提升 30%
  • Variant 类型相比原始 JSON 提升 178%
  • Shredding 技术进一步加速JSON解析,性能再提升 364%

3. Celeborn Remote Shuffle Service

作为 Apache 顶级项目,Celeborn 采用 推送式 Shuffle 架构

  • 在大规模作业中提供更高吞吐与更低延迟;
  • 支持副本容错与Stage重算,保障作业稳定性;
  • 大规模生产验证,成为业界事实标准。

4. DLF 3.0 与优化器增强

基于 Paimon 的 DLF 3.0 提供高性能 ACID 能力,满足 TPC-DS Maintenance 测试要求;同时优化器在 Join 顺序选择、代价模型等方面持续迭代,提升复杂查询效率。

最终成果:在仅使用一半内存的情况下,QphDS 性能翻倍,性价比提升5倍,所有结果均通过 TPC 官方严格审计。

AI 时代的新功能:让 Spark 成为 AI 基础设施

面对 AI 浪潮,EMR Serverless Spark 推出多项创新功能:

  • AI Function:内置 ai_queryai_sentimentai_classifyai_embedding 等函数,用户可在 SQL 中直接调用大模型,如同使用内置 UDF。支持接入百炼、OpenAI、PAI EAS 或本地 GPU 模型。
  • Spark on GPU:提供弹性 GPU 实例,按需配置 CPU/GPU 混合机型,避免固定集群成本。支持:

    • AI Function 本地 GPU 推理;
    • Spark ML(XGBoost/LightGBM)GPU 加速;
    • Spark SQL 向量化 GPU 计算。

  • 即将上线功能

    • Spark + Ray 双引擎融合:满足 Python 分布式与异构计算需求;
    • DuckDB 集成:针对中小数据分析,在 Notebook 中已内置,未来支持直连 DLF 3.0;
    • 文本去重加速:在 FineWeb-edu(8TB、30亿文档)上,800 核仅需 72 分钟,提速 5 倍。

携手客户共同成长

EMR Serverless Spark 已在多家金融、互联网、智能硬件及零售企业的生产环境中稳定运行,广泛应用于数据仓库加速、实时风控、向量检索、机器学习等核心场景。

同时,Celeborn 社区也在多个头部互联网平台和科技企业中落地,支撑高并发、大规模的数据计算需求。

阿里云 EMR Serverless Spark 的 TPC-DS 登顶,不仅体现了优异性能,更体现了架构理念、工程能力和生态战略。在 Data + AI 融合的新时代,它正成为企业构建下一代智能数据基础设施的核心引擎。

大家好,我是老刘

昨天有个老朋友找我喝酒,哭丧着脸。

这哥们是个五年的Android开发,技术那是没得说。

能独立完成复杂的系统模块,优化过千万级日活的App。

最近去面试一家大厂挂了。

面试官就问了一句:

“如果让你带队把这个模块迁移到鸿蒙和 iOS,除了重写三遍,你有什么低成本方案?”

他愣住了,他引以为傲的原生深度,在降本增效的大潮面前,就变成了一颗昂贵的螺丝钉。

2026 年,不知道有多少朋友又开始了或者将要开始投简历、面试的过程。

老刘借着这篇文章聊聊原生开发、跨平台开发在AI时代的客户端开发领域的生态位,以及作为开发者,我们该如何优化我们的技能树,以适应这个快速变化的行业。


原生不是死了,而是下沉了

别误会,我不是说原生技术要消失。

它只是下沉了,沉成了基础设施,就有点像汇编语言一样。

你看现在的 App 架构是啥样的?

Mermaid Diagram

如果你只守着那 10% 的原生胶水层,你的职业道路只会越走越窄。

最后变成公司里那个维护遗留代码的守门人,每天对着一堆陈年老代码叹气。

新人都在用新架构写新业务,你在角落里修改哪些该死的兼容性 Bug。


Flutter vs 原生

这里老刘不想讨论技术参数的差异,而是想站在开发者的角度,谈谈聚焦不同的技术栈对我们有什么影响。

很多坚持“纯原生”的兄弟,理由往往只有一个:

“原生体验更好,性能更强,跨平台总觉得有点卡。”

听着挺有道理,但在 2026 年,这更像是一个给自己寻找舒适区的借口。

现在的跨平台技术,早已经不是当年的“缝合怪”了。

以 Flutter 为例, Impeller 引擎的成熟,让它的渲染性能在大多数业务场景下,已经做到了和原生毫无差异的程度。

你还在纠结那个滚动条的阻尼感是不是差了 0.01 秒,老板在纠结为什么隔壁组用一套代码已经上线了三个平台,而你还在为 iOS 的一个布局错位调了一下午。

未来的核心竞争力,是端抽象的能力

当你站在跨平台的角度进行开发,你思考问题的维度就变了:

  • 你不再是“那个写 Android 的”,你是“那个能搞定全端交付的”。
  • 你不再关心某个系统的私有 API,你开始关心如何设计一套通用的组件库,让业务逻辑在各个端之间无缝流动。

这种“端抽象”的能力,才是跨平台开发者真正的护城河。


跨平台(Flutter)不是加分项,而是及格线

以前面试,你会 Flutter是加分项。

现在那是及格线。

看看现在的终端环境,乱成一锅粥了:Android、iOS、鸿蒙、web、桌面端...

在这里插入图片描述

你指望老板招 5 拨人去维护 5 套代码?

老板根本不在乎你用原生还是Flutter,老板只在乎能不能把团队裁到只有一个人,然后你把所有的工作搞定。

只会纯原生等于把自己锁死在一个平台上。

一旦那个平台没落了,或者公司业务调整不做了。

想想当年的塞班开发,甚至前几年的 Windows Phone 开发,是不是背脊发凉?


你是不是那个解决问题的人?

其实不管是团队的技术方案选择,还是开发者个人技能树的升级,技术栈的选择从来都不仅仅是技术参数的对比。

站在团队的角度,老刘当年选择Flutter的原因有以下几个方面:

Mermaid Diagram

站在开发者个人的角度,你需要保证自己的技能树中点出来当前主流的技术方向。

我不是说一定要学Flutter或者某一个技术,而是你要知道现在在行业内大家都在用哪些技术。

而被大量企业和团队选择的技术栈大概率是当前最能解决实战问题的,或者是最具备性价比的选项。

为啥要做从众的选择?

因为商业社会你不能做那个掌握屠龙技的人,而要与做那个解决问题的人。

那什么样的技术路线能解决问题呢?

并不是你钻研最深入的,看起来参数最好的,而是你的同事都会用的。

不仅仅是你的同事,还是那些LLM比如Claude、ChatGPT甚至Kimi都能玩得转的。


换个活法,路宽得很

我建议你放弃纯原生,不是让你忘掉基础。

而是让你放弃那种画地为牢的心态。

不要做一个只会砌墙的泥瓦匠,要做一个懂结构的建筑师。

未来属于那些站在原生肩膀上,用跨平台和 AI 俯视业务的开发者。

别让技术栈限制了你的想象力。

更别让纯原生限制了你的身价。

共勉。

🤝 如果看到这里的同学对客户端开发或者Flutter开发感兴趣,欢迎联系老刘,我们互相学习。

🎁 点击免费领老刘整理的《Flutter开发手册》,覆盖90%应用开发场景。

🚀 覆盖90%开发场景的《Flutter开发手册》

📂 老刘也把自己历史文章整理在GitHub仓库里,方便大家查阅。
🔗 https://github.com/lzt-code/blog

2 站的各位大家好!

小弟來自台北,目前定居上海已經 11 年了。
很高興能透過這個平台,在新的一年認識大家!

📝 關於我

座標:上海(已待了 11 年的偽在地人)

職業:醫療 IT 相關行業

興趣:露營 、戶外活動

專長:羽毛球 (歡迎球友交流!)

PS:雖然來了很久,但小弟還是沒學會拼音輸入法,所以習慣使用繁體字發文。(工作需要會先轉換)
不知道各位同胞閱讀上會不會有困擾?如果有造成不便,先說聲不好意思啦!XDD

最後,送上自家主子的肥貓照一張當作見面禮 👇

祝大家"豬"事如意、萬事圓滿~

image

马年到了,做了个红包封面送给大家。
封面是个娃娃机。抓到马,又主动松手。马落地那一瞬,变成金币。

做久了工作才发现:
不是每个机会都必须抓住。
不是每次松手都是失败。
有时候不是不努力,是太用力了。新的一年,允许自己放自己一“马”。

咱们 25 年辛苦了。努力工作,努力生活,照顾家人。所以在 26 年春节假期,别委屈自己,好好放松,吃点好的。

预览图


领取方式

  • 直接扫图里的二维码就行。

祝大家在新的一年:少一点内耗,多一点冗余!
春节快乐!给大家拜个早年!