包含关键字 typecho 的文章

写文案、发平台内容、准备作业或提交表单时,很多人都会遇到“字数不确定”的问题:担心超出限制,或者内容太短不达标。为了解决这个高频小需求,我用 Vue 3(Nuxt 3) 做了一个文本字符数统计在线工具,打开网页就能直接用。

在线工具网址:https://see-tool.com/text-character-count
工具截图:

这个工具适合普通用户的日常场景:

  • 写公众号、小红书、知乎等平台内容,先看字数是否在要求范围内
  • 准备简历、自我介绍、申请说明时,控制内容长度
  • 学生写作文、读后感、课程作业时快速核对字数
  • 运营同学编辑活动文案、商品描述时避免超限

使用方法很简单:

  1. 把需要统计的文本粘贴到输入框。
  2. 页面会实时显示总字符数、中文字符数、英文字符数、数字和空格数量。
  3. 根据目标平台限制,继续增删内容直到合适。
  4. 完成后一键复制,直接去发布或提交。

我在设计这个工具时,重点做了“即时反馈”和“零学习成本”:你不需要安装软件,也不用登录账号;手机和电脑都能使用。文本统计过程在浏览器本地完成,不需要把内容上传到服务器,隐私更安心。

如果你经常写内容、填表单或做文字整理,这个小工具可以帮你省下反复试错的时间,让字数控制更准确、更轻松。

花了几天时间搭建了一套高度可定制化的 AI 信息推送系统——AI Daily

  • 不想错过重要信息? 它可以及时推送,比如重大模型发布
  • 想要定时汇总?它可以设定定时任务
  • 信息渠道太少了?可以随意按照自己的喜好增加信息源
  • 想要手机端通知?它支持 webhook 推送(目前已加入飞书和 Discord 的支持)

这是我自己的飞书截图:

3.4 夜里(即时推送):

ai-daily-03-04-notify

3.4 晨报:

ai-daily-push-03-04

如果你对这个项目感兴趣,欢迎 Star

基于 YOLOv8 的面向文档智能处理的表格区域检测系统 [目标检测完整源码]

一、技术背景:表格检测为何成为文档智能化的关键环节?

在政务档案、金融报表、医疗记录、学术论文等典型文档中,表格承载着高度结构化且信息密集的数据。然而,对于计算机而言,表格并非天然可解析对象,其行列结构、边框形式、排版风格差异极大,这使得自动识别难度远高于普通文本区域。

在实际工程中,表格检测往往是以下任务的前置步骤:

  • OCR 前的版面结构分析
  • 表格内容结构化与数据库入库
  • 文档自动审核与信息抽取
  • 智能档案与知识管理系统

传统基于图像规则的方法(如边缘检测、连通域分析)在扫描件模糊、背景复杂或无明显边框的情况下稳定性较差。因此,引入深度学习目标检测模型成为更具可行性的技术路径。

基于此,本文介绍一套以 YOLOv8 为核心的文档表格检测系统,并结合 PyQt5 构建完整的可视化应用,实现从模型训练到实际使用的闭环。
在这里插入图片描述

源码下载与效果演示

哔哩哔哩视频下方观看:
https://www.bilibili.com/video/BV1WM8qzqEJe/

在这里插入图片描述

包含:

📦完整项目源码

📦 预训练模型权重

🗂️ 数据集地址(含标注脚本

二、系统整体设计思路

本系统在设计之初,重点考虑三个工程目标:

  1. 检测精度可靠:适应多类型文档与复杂排版
  2. 部署使用便捷:支持非算法人员直接操作
  3. 具备扩展能力:可衔接 OCR、结构解析等后续模块

基于上述目标,系统整体架构可划分为四个核心模块:

  • 数据与标注模块:统一 YOLO 数据格式,支持快速扩展
  • 模型训练模块:基于 YOLOv8 Detection 分支进行优化训练
  • 推理与接口模块:封装模型推理逻辑,支持多输入源
  • 图形化交互模块:通过 PyQt5 提供完整桌面端操作界面

这种模块化设计,使系统既适合作为研究验证平台,也能够直接服务于实际业务场景。


在这里插入图片描述
在这里插入图片描述

三、表格检测数据集构建与类别设计

3.1 检测目标定义

在本系统中,检测目标聚焦于文档图像中的表格区域。不同于表格结构识别(行、列、单元格级别),该阶段的核心目标是:

准确定位表格在整张文档中的空间位置

这一步的准确性,直接决定后续 OCR 与结构解析的效果。

3.2 数据组织与标注规范

数据集采用 YOLO 标准格式进行组织,图像与标签一一对应,所有标注均以归一化坐标形式存储,保证模型对不同分辨率文档的适应能力。

通过引入多样化文档来源(扫描件、电子文档截图、拍照文档等),模型在训练阶段即可学习到不同表格形态下的视觉特征,从而提升泛化性能。


在这里插入图片描述

四、YOLOv8 在文档表格检测中的优势分析

相较于早期目标检测模型,YOLOv8 在文档类任务中具备明显优势:

  • Anchor-Free 机制
    避免复杂先验框设计,更适合表格尺寸差异大的场景。
  • 端到端训练流程
    模型结构简洁,训练与推理逻辑清晰,便于工程维护。
  • 轻量化模型配置
    在保证精度的同时,推理速度快,适合批量文档处理。

在实际训练过程中,模型能够有效区分表格与正文文本、图片区域,即使在边框模糊或背景干扰较多的情况下,依然能保持较高的检测置信度。


在这里插入图片描述

五、模型训练与性能评估策略

5.1 训练流程概述

模型训练主要包括以下步骤:

  1. 数据加载与随机增强
  2. 特征提取与多尺度检测
  3. 分类与定位损失联合优化
  4. 自动保存最优权重模型

整个过程可通过 YOLOv8 官方训练接口完成,训练日志与结果图表自动生成,便于分析模型收敛情况。

5.2 评估指标说明

模型性能主要从以下几个维度进行评估:

  • mAP@0.5:整体检测精度
  • Precision / Recall:误检与漏检分析
  • 混淆矩阵:类别区分能力验证

在文档处理场景中,稳定性与一致性往往比极限精度更重要,因此评估过程中也会重点关注不同文档类型下的检测表现。


在这里插入图片描述

六、PyQt5 可视化应用的工程实现

6.1 引入图形界面的必要性

在实际业务中,文档处理系统的使用者往往并非算法工程师。命令行方式虽然灵活,但学习成本较高。基于此,本项目通过 PyQt5 构建桌面端应用,实现以下目标:

  • 降低系统使用门槛
  • 提供直观的检测结果展示
  • 方便教学、演示与部署

6.2 核心功能模块

图形界面集成了完整的检测流程,包括:

  • 单张文档图像检测
  • 文件夹批量处理
  • 视频与实时摄像头检测
  • 模型权重灵活切换
  • 检测结果自动保存与导出

通过可视化操作,用户无需关心底层模型细节,即可完成表格区域检测任务。


在这里插入图片描述

七、典型应用场景与扩展方向

该系统可广泛应用于以下领域:

  • 财务报表与票据自动处理
  • 医疗与保险文档数字化
  • 学术文献与档案管理
  • 智能 OCR 系统前处理模块

在此基础上,系统还可进一步扩展:

  • 表格结构识别(行、列、单元格)
  • OCR 文本识别与语义解析
  • 与数据库或业务系统对接

从工程角度看,该方案具备良好的可扩展性与长期应用价值。


八、总结

本文围绕文档图像中的表格检测任务,介绍了一套基于 YOLOv8 的完整工程化解决方案。从数据集构建、模型训练到 PyQt5 可视化部署,系统性展示了深度学习目标检测在文档智能处理领域的实际落地路径。

实践结果表明,YOLOv8 在表格区域检测任务中具备良好的精度与鲁棒性,而图形化界面的引入显著提升了系统的可用性与推广价值。该方案不仅可作为文档表格检测的独立工具,也可作为更大规模文档智能处理系统中的核心模块,为文档数字化与自动化处理提供坚实的技术基础。

本文围绕文档图像中表格区域自动检测这一实际工程需求,系统介绍了一套基于 YOLOv8 的表格检测与应用落地方案。从数据集构建、模型训练与评估,到多输入源推理及 PyQt5 图形化界面集成,完整展示了文档视觉任务从算法到产品化的实现路径。实践表明,YOLOv8 在复杂文档版式与多样表格形态下具备良好的检测精度与稳定性,而可视化界面的引入显著降低了系统使用与部署门槛。该方案可作为 OCR 与文档结构化处理的前置模块,为金融、医疗、政务等场景中的文档智能化应用提供可靠的技术支撑。

image

前段时间由于刚开通汇丰账户不久,不太清楚汇丰银行的规则,在操作过程中触发了风控导致账户被锁。虽然过程比较波折,但最终成功解封。

为了避免大家重蹈覆辙,我花时间复盘了这次从“账户被锁”到“成功解封”的全过程。如果你在使用汇丰( HSBC )账户时遇到了 SAS301 报错,这篇笔记应该能帮你节省几天的时间。

核心教训:新账户磨合期不要随意点击未开通的理财功能,资金不要大笔汇入快进快出,而且如果有香港电话必检查手机拦截设置。

如果有想开通香港账户的朋友,可以参考:肉身实测:香港一日极限开 4 张卡(汇丰/ZA/天星/中银)全流程复盘与避坑指南

image


第一部分:触发风控的背景与原因

复盘整个过程,这次风控并非因为资金问题,而是因为操作行为被系统判定为异常。

1. 账户背景 账户刚开通不到半个月。在此期间,我向账户内转了三笔资金,全部为同名账户互转,资金链路非常干净,没有任何问题。

2. 触发操作(关键点) 出于好奇,我在 APP 内点击了 “理财账户” 功能。 由于该功能需要实时拍摄身份证进行验证,而当时我并未随身携带证件,因此我中途直接关闭了两次开通流程,只简单浏览了一下股票界面。

3. 爆发节点 第二天尝试进行转账交易时,系统直接拦截,弹出 “SAS301” 报错代码,显示无法转账。至此,账户资金被实质性冻结。

推测结论:汇丰的风控机制非常敏感。在新号磨合期,连续发起“开通投资账户”请求又中途放弃,极大概率被系统判定为风险操作或非本人操作。


第二部分:解决经过与弯路

发现报错后,我经历了线上咨询和电话沟通两个环节。这里有一个巨大的坑,导致我白白浪费了两天时间。

1. 线上咨询 第一时间联系了 APP 内的线上客服。对方询问了交易详情和时间,但最终回复:线上客服无权解封,必须通过电话处理,并提供了一个联系号码。

image

image

2. 电话沟通(踩坑经历) 拨打客服电话后,工作人员记录了我的情况,表示会有专员进行核实,让我留意接听回电。

3. 严重避坑:国内手机的默认设置 在这里我吃了一个大亏。在等待回电的两天里,我一直没有接到任何电话。后来检查才发现:

  • 问题所在:国内运营商或手机系统默认开启了 “拦截海外/境外来电” 的功能(海外接听电话还是蛮贵的,我显示的是 0.84/每分钟,而且接听也需要收费,如果有香港的手机号码注册的时候建议使用香港的手机号码,因为注册的时候填写的是大陆的+86 手机号码,如果你的是香港的手机号码就这个问题。)

  • 后果:银行的核实电话被系统自动拦截,导致我错过了审核时效,耽误了两天。

image

还有一个点就是给我接待的那个经理只有工作日上班,在周末的时间是不上班的,这也导致我只非工作日时间没有办法完成账户认证。

4. 最终解封 解决拦截问题后,我终于接到了工作人员的电话。流程非常简单:

  • 对方询问了基本情况。

  • 核实身份信息。

  • 当场解除账户封锁,账户恢复正常使用。

确认当天我就尝试了转账,成功转账


第三部分:给新户的经验总结

经过这次折腾,总结出以下三条规则都是我自己踩得坑,希望给刚开好汇丰卡的朋友有参考意义:

1. 磨合期少操作生僻功能 在账户刚开通的“磨合期”(通常为前 1-3 个月),尽量只进行基础的存取款操作。不要出于好奇去点击借贷、理财、投资等不常用的功能,尤其是不要在流程中反复进入和退出,这极其容易触发系统的自动风控。

2. 留意手机拦截设置 在预留给银行的手机号上,务必检查是否开启了拦截境外来电功能。汇丰的风控核实电话通常来自香港,一旦被拦截,会导致解封流程无限期延长。

3. 大额资金需循序渐进 涉及大额资金进出时要格外谨慎,建议资金量由小到大逐步增加,建立良好的使用记录。

希望这篇记录能帮大家避开 SAS301 这个坑。账户解封不难,难的是找不到原因和接不到电话。

图片

2026年全国两会正在热烈进行中,这也是“十五五”规划开局的关键节点。人工智能与实体经济的深度融合以及“人工智能+”行动,正在央企的生产一线加速推进——从电力调度到装备制造,从日常办公到核心生产,大模型和智能体开始真正“上岗干活”。截至目前,已有80%的央企用百度智能云,一起探索产业智能化的落地路径。从今天起,看看“国家队”如何用AI跑出加速度。今天分享的是:百度智能云和国家电网的实践故事。国务院在去年印发了《关于深入实施“人工智能+”行动的意见》,作为新型基础设施建设的重要力量,百度智能云与国家电网基于国家发改委“人工智能+”应用场景建设任务,针对输电、配网、变电三大核心场景,建设设备专业智能体。不仅让电网巡视效率提升超50%,更探索出一条大模型时代下,AI深入电网核心生产环节的可行路径。
图片
破解“误报多、迭代慢”难题:大小模型融合下的“新质生产力”

电力巡检是保障能源安全的第一道防线。传统AI小模型在复杂的野外和变电站环境中,常常面临“误报满天飞”的困境,一线人员不得不从海量无效告警中筛查真实缺陷,费时费力。此外,各地地理环境差异大导致的模型泛化能力不足、罕见缺陷样本的稀缺,一直是行业痛点。针对上述挑战,百度智能云基于“一见· 多模态专业视觉管理平台积累的多模态大模型+专业视觉小模型”,通过“大小模型融合”的创新架构,实现了从“人工看”到“AI看”的跨越式升级:降误报、减负担:在网省与地市两级架构中,引入多模态大模型对小模型的初判结果进行二次复判。结合人工复核数据形成高质量负样本数据集,驱动模型精准优化,显著降低误报率,将一线人员从繁重的无效筛查中解放出来。零代码、自迭代:针对各地市场景差异大的问题,系统将人工复核的高质量数据接入“零代码AI产线”。业务人员无需算法背景,即可通过可视化操作快速完成模型迭代,实现“越用越准,分钟级调优”。补短板、强长尾:针对引下线锈蚀、U型螺栓等罕见缺陷(小概率+大影响),创新引入AIGC技术。基于少量真实样本合成高质量训练数据,有效补齐了长尾缺陷样本稀缺的短板,大幅提升了对罕见风险的预控能力。设备专业智能体如何运转?一线巡检的“云边协同”一天

想象一下。清晨,无人机按规划航线自动起飞,对山区输电线路开展巡检作业,实时采集高清影像。在边端,部署在现场的专业视觉小模型对图像进行实时初判,快速标记出可能存在缺陷的图像,并上传至省侧平台。在云端,多模态大模型对初判结果进行二次复判——它能够理解图像上下文,区分是真正的设备缺陷还是光影变化、鸟类飞过造成的误报。二次复判后的疑似缺陷推送至地市公司监控中心,由业务人员在可视化界面上进行最终人工复核确认。当一线人员复核发现AI漏判或误判时,系统自动记录这些高质量样本数据。这些数据一方面回流至省侧数据中心,持续积累属地化特色样本集;另一方面直接接入零代码AI产线,地市公司的业务人员无需算法背景就可在界面中勾选新样本、点击“开始训练”,系统自动完成模型优化。优化后的新模型在数小时内即可下发至现场边端设备,投入下一轮巡检作业。而对于引下线锈蚀、U型螺栓松动等罕见缺陷场景,当某地市公司首次发现并确认这类样本后,省侧平台可利用AIGC技术基于该样本生成多样化的合成数据,补充至全省模型训练集中,快速提升各地市对此类风险的识别能力。这一业务流形成了完整的设备专业智能体全流程自主闭环:边端采集与初判→云端复判与复核→数据回流与沉淀→模型自动迭代→新模型下发边端。在这一流程中,AI不仅完成识别任务,更在每一次人机交互中持续学习进化,真正实现了“在运营中越用越准”。数智化转型见实效:三大场景全面落地护航能源安全

目前,设备专业智能体已在国家电网全国范围内规模化应用,取得了显著的降本增效成果:输电线路“空中慧眼”:覆盖9大类225小类缺陷,巡检时间较传统人力巡检减少50%以上。目前,该成果已服务于27家省(市)公司超300个地市公司,覆盖上万名一线飞巡人员,智能识别应用覆盖率达80%。配网线路“精准诊断”:针对17大类典型缺陷,综合识别准确率稳定在86%以上。未来将惠及超200个地市公司的3000余个班组,让配网运维更智能、更可靠。变电站“无人巡视”:针对变电站14大类缺陷实现智能复判,结合Prompt调优技术降低实际误报。单站巡视时间由人工所需的2.5小时锐减至45分钟,极大减轻了1.8万余名运维人员的工作压力,覆盖全国500千伏及以上变电站800余座。百度智能云与国家电网的合作,是“人工智能+”行动在能源领域的一次深度实践。我们验证了“大模型的通解能力”与“小模型的专解能力”结合的巨大潜力,智能体的完美自动迭代,让AI成为真正成为懂地域、懂设备、懂业务的“资深专家”。

当 token 计数“看不见”时

本次请求的 token 计数会在请求末尾时返回,但正如漏算的 Token:AI 网关限额机制的攻防博弈提到的,有些时候请求并不能正常结束,导致中间件无法获知 token 的计数。我们之前遇到过一个场景,高达 10% 左右的请求是提前中断的。即使不考虑这种异常请求,有些时候我们也需要提早知道 token 计费请求,比如基于 token 数的限流。如果需要等到上个请求结束时才更新计数器,有可能已经是明日黄花。

这个时候,就需要有不依赖于推理引擎的 token 计数方式。调用服务提供商或推理引擎的 count token 接口是一个办法,但考虑到请求放大等原因,这个方法并不实用。我们需要在本地完成计算。于是,网关里通常需要集成 tokenizer。

tiktoken 还是 hf tokenizer,这是个问题

主流的 tokenizer 库有两个流派:来自于 OpenAI 的 tiktoken,或来自于 HuggingFace 的 tokenizer(以下简称 hf tokenizer)。许多 tokenizer 库,要么是它们的移植版本(如 tiktoken-go),要么是对它们的 binding。这两个项目都是用 Rust 实现的(tiktoken 没有对外提供对应的 crate,只有 Python package,但它是 Rust 核 Python 皮的项目)。

有趣的是,这两个项目各自在 README 的头顶显眼位置贴上和对家比较的 benchmark,简直是相爱相杀。

谁的陈述更贴近真相?我自己也写了个 benchmark(代码见文后)。和 hf tokenizer 不同的是,我测试的模型是 gpt-4o,因为这个模型用户更为广泛,而且作为 tiktoken 官方支持的模型,相对更能体现 tiktoken 的真实实力。注意由于 tiktoken 没有提供直接的 crate,而我又想做尽可能贴近核心的 benchmark,所以 benchmark 是在 tiktoken 项目里实现的,需要在 tiktoken 代码库目录下运行。

这里直接贴性能结论:在全部四个用例里(短、中、长、多语言),tiktoken 的吞吐量都是 hf tokenizer 的 1.5 倍以上。

tiktoken 为什么更快

现代 tokenizer 大致分成几类:

  • WordPiece:合并规则不只看频率,还看“合并后能提升训练数据概率多少”。
  • Unigram:反过来,从大词表开始,逐步删掉对模型贡献最小的 token。
  • BPE:从字符(或字节)开始,反复合并最常见的相邻对。它是 Byte-Pair Encoding 的缩写。通常使用的是它的一个变种:Byte-Level BPE。

Tiktoken 的实现即是 Byte-Level BPE。编码操作如下:

Full text → [Regex split] → Chunks → [BPE merge per chunk] → Tokens

在 Regex split 时,针对 gpt-4o 它会采用下列的正则:

pat_str = "|".join(
          [
              r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]*[\p{Ll}
  \p{Lm}\p{Lo}\p{M}]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?""",
              r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]+[\p{Ll}
  \p{Lm}\p{Lo}\p{M}]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?""",
              r"""\p{N}{1,3}""",
              r""" ?[^\s\p{L}\p{N}]+[\r\n/]*""",
              r"""\s*[\r\n]+""",
              r"""\s+(?!\S)""",
              r"""\s+""",
          ]

解释一下:

Pattern 1:小写结尾的单词

[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]*[\p{Ll}\p{Lm}\p{Lo}\p{M}]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?

[^\r\n\p{L}\p{N}]?:可选,单个“非换行/字母/数字”的字符(空格、标点)

[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]*:0 次或多次:大写/标题大小写/修饰/其他字母 + 组合符号

[\p{Ll}\p{Lm}\p{Lo}\p{M}]+:1 次或多次:小写/修饰/其他字母 + 组合符号

(?i:'s|'t|...):可选的英语缩写(大小写不敏感)

匹配:以小写结尾的单词
"hello" → "hello"
"Hello" → "Hello"
"HELLo" → "HELLo"
" hello" → " hello" (带前导空格)
".Hello's" → ".Hello's" (带前导标点 + 英语缩写)
"中文" → "中文" (Lo 字符)

Pattern 2:大写开头的单词

[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]+[\p{Ll}\p{Lm}\p{Lo}\p{M}]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?

[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]+:1 次或多次:大写字母(必需)
[\p{Ll}\p{Lm}\p{Lo}\p{M}]*:0 次或多次:小写字母

匹配:以大写开头的单词,末尾可继续大写
"HELLO" → "HELLO" (全大写)
"USA" → "USA"
"NASA's" → "NASA's"
" HTTP" → " HTTP"

Pattern 1 vs 2 的区分:
"Hello" → Pattern 1(upper* lower+)✓
"HELLO" → Pattern 2(upper+ lower*)✓
"hello" → Pattern 1(upper*=∅, lower+)✓

Pattern 3:数字(1-3 位)

\p{N}{1,3}

匹配:每次 1 到 3 位数字
"123456" → ["123", "456"]
"42" → ["42"]
"1000" → ["100", "0"]

避免长数字被当作单个 token,有利于算术相关的处理。

Pattern 4:标点/符号

?[^\s\p{L}\p{N}]+[\r\n/]*

?:可选空格

[^\s\p{L}\p{N}]+:1 次或多次:非空白/字母/数字(标点、符号)

[\r\n/]*:0 次或多次:换行或斜杠

匹配:连续的标点序列
"..." → "..."
" !!!" → " !!!"
"===" → "==="
"---\n" → "---\n"

Pattern 5:行尾换行

\s*[\r\n]+

匹配:带可选前导空白的换行
"\n" → "\n"
" \n\n" → " \n\n"
"\t\r\n" → "\t\r\n"

Pattern 6:末尾空白

\s+(?!\S)

\s+: 1 次或多次空白
(?!\S): 负向前瞻:后面不是非空白字符

匹配:字符串末尾(或换行前)的空白
"hello " → the " " at the end

Pattern 7:其他空白

\s+

匹配:其余所有空白(单词之间的空格)
"a b" → the " " between a and b

最后需要强调的是,顺序很重要。

正则的 alternation(|)从左到右尝试,先匹配到的胜出:

1. 单词(小写结尾)  ─┐
2. 单词(大写占优)  ─┼── 先匹配字母
3. 数字(1-3 位)   ─── 再匹配数字
4. 标点符号         ─── 再匹配符号
5. 换行            ─┐
6. 末尾空白         ─┼── 最后处理空白
7. 其他空白         ─┘

完整示例

输入:"Hello WORLD! 12345\n "

Token 列表:
"Hello" → Pattern 1
" WORLD" → Pattern 2
"!" → Pattern 4(这里没有前导空格,因为空格已经被消耗掉了)
" " → Pattern 7(数字前的空格)
"123" → Pattern 3
"45" → Pattern 3
"\n" → Pattern 5
" " → Pattern 6(末尾空白)

在切割成多个 chunks 后,tiktoken 会在每个 chunk 内执行 _byte_pair_merge_byte_pair_merge 包含以下步骤:

  • O(mn),n 为 parts 数量,m 为 merges 次数
  • 每次迭代线性扫描,找最小 rank 的 pair
  • 使用 Vec::remove(),会触发元素移动

Parts 的数量(n):
初始值:输入片段的长度。
例如,输入 "hello" → 初始 parts 为 5 个:['h', 'e', 'l', 'l', 'o']
合并之后:每次合并让 parts -1,合并 m 次后剩下 n - m 个 parts(也就是最终 tokens)。

合并次数(m):
上界:m ≤ n - 1(n 个 parts 最多合并成 1 个 token)

实际值:取决于有多少相邻 pair 在词表里有合并规则。由下列因素决定:

  1. 训练词表 - BPE 训练阶段学到哪些 byte-pair merges
  2. 具体输入 - 哪些 pair 实际上相邻出现
  3. 合并优先级 - rank 越小越先合并,影响后续相邻关系

示例:
输入:"hello"(n=5)
Vocab 有:"ll"→rank 1, "he"→rank 2, "lo"→rank 3

步骤 0:[h, e, l, l, o](5 parts)
步骤 1:[h, e, ll, o](合并 "ll",4 parts)
步骤 2:[he, ll, o](合并 "he",3 parts)
步骤 3:[he, llo](如果 vocab 里有 "llo" 就合并,否则停止)

m = 2 或 3,取决于 vocab

有趣的是,由于每个 chunk 都很小,导致性能的瓶颈实际上不在 BPE 算法上,而在正则表达式切割的部分。以下是 tiktoken encode 时的火焰图:

tiktoken火焰图

在 tiktoken 代码里也提到:

// Most of the time is spent in regex. The easiest way to speed this up is by using less fancy
// regex features. For instance, using a regex parse-able by `regex` crate is 3x faster than
// the usual regex we use

它之所以没法用更快的 regex crate,是因为 split 用到的正则表达式里需要 \s+(?!\S) 这样的 lookahead 操作。在注释里,tiktoken 的作者还抱怨了下因为 tokenizer 的这种设计导致性能无法进一步优化。所以模型推理是一个系统工程,在训练时处理数据的 token pattern 会给推理时的 tokenizer 设立一个上界。

hf tokenizer 真的慢吗?

hf tokenizer 作为一个更加通用的 tokenizer 库,即使实现了 Byte-Level BPE,也不能像 tiktoken 那样直接操作在 byte 序列的层次。虽然 hf tokenizer 的架构和 tiktoken 类似:

Text → Normalizer → PreTokenizer → BPE → PostProcessor → Tokens

其中 PreTokenizer 相当于 tiktoken 的 Regex split。但是为了更好地适应不同的模型,hf tokenizer 必须实现其他的流程。

以 hf tokenizer 的火焰图为例,我们可以看到两张火焰图都花了很多时间在 <fancy_regex::Matches as core::iter::traits::iterator::Iterator>::next 上。

hf tokenizer 的火焰图

但 tiktoken 的这个函数直接就是 tiktoken::CoreBPE::encode_ordinary 的下级函数,而 hf tokenizer 还需要执行同样占用大量时间的 normalization。这里面额外的内存分配,也许解释了那多出来的 50% 时间花在了哪里。

作为代价,tiktoken 只适合用来解析 GPT 系列模型,如果有模型需要不一样的 PreTokenizer 或其他操作,就没办法直接用 tiktoken 这种简单的流程。

所以 hf tokenizer 尽管在支持 GPT tokenizer 上不如 tiktoken 快,但是有些时候你不得不用它。

举一个实际的例子。Qwen3-235B-A22B-Instruct-2507 的 tokenizer 和 GPT 的基本上差不多。我们曾经测试过,即使在差异较大的中文场景,Qwen 和 GPT 在输出长度的偏差上仍然只有 10% 左右。所以如果只关心 token 长度,tiktoken 可以当作 Qwen tokenizer 使用。

但是 DeepSeek 和 GPT 的差异较大。在中文场景的实验中,DeepSeek 和 GPT 的结果相差一倍。我估计是因为 DeepSeek 的词表和分割时用的正则表达式较 GPT 的差别很多。

这也是为什么不能用 tiktoken 来模拟 DeepSeek 的 tokenizer。

眼尖的读者在看完 benchmark 代码后可能会发现,benchmark 时我是直接指定词表和分割的正则表达式。虽然 Python 层次上的 API 没有暴露指定正则和词表的接口,但是底层的 Rust 核心是有的。那是不是可以给 DeepSeek 的词表做一些预处理,让它和 GPT 差不多,这样就能基于 tiktoken 的底层接口来解析呢?也许可以,也许不行。注意 DeepSeek 除了 pretokenizer 不一样外,还定义了额外的 PostProcessor:https://huggingface.co/deepseek-ai/DeepSeek-V3.2/resolve/main...。我不确定这种额外的 PostProcessor 是否已经在 tiktoken 里面对齐。

注意 Qwen3.5 的 pretokenize 较 Qwen3-235B-A22B-Instruct-2507 有所变更,优化了中文编码,预计在中文场景下和 tiktoken 的结果会有更大的不同。

结语

当 token 计数不能依赖推理引擎回传时,网关侧的本地 tokenizer 是绕不开的基础设施。tiktoken 和 hf tokenizer 的差别不只在速度,还在“能否复现模型的预处理语义”:前者以极简流程换高吞吐,后者以更强的可配置性换更广泛的模型适配。实践中更关键的是知道自己在模拟哪一种模型行为,以及吞吐量和准确度之间的取舍。

benchmark 代码

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use sha2::Digest;
use std::collections::HashMap;
use std::io::Read;
use std::time::Duration;
use tiktoken::CoreBPE;
use tokenizers::Tokenizer;

const O200K_BASE_URL: &str =
    "https://openaipublic.blob.core.windows.net/encodings/o200k_base.tiktoken";
const O200K_BASE_HASH: &str = "446a9538cb6c348e3516120d7c08b09f57c36495e2acfffe59a5bf8b0cfb1a2d";

// Pattern from openai_public.py o200k_base()
const O200K_PAT_STR: &str = r#"[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]*[\p{Ll}\p{Lm}\p{Lo}\p{M}]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?|[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]+[\p{Ll}\p{Lm}\p{Lo}\p{M}]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?|\p{N}{1,3}| ?[^\s\p{L}\p{N}]+[\r\n/]*|\s*[\r\n]+|\s+(?!\S)|\s+"#;

const ENDOFTEXT: &str = "<|endoftext|>";
const ENDOFPROMPT: &str = "<|endofprompt|>";

/// Number of times to encode each text in a single benchmark iteration
/// This amortizes the measurement overhead and focuses on encoding performance
const ITERATIONS_PER_SAMPLE: usize = 100;

/// Downloads and caches the tiktoken file
fn download_tiktoken_bpe(
    url: &str,
    expected_hash: &str,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let cache_dir = std::env::var("TIKTOKEN_CACHE_DIR").unwrap_or_else(|_| {
        let tmp = std::env::temp_dir();
        tmp.join("data-gym-cache").to_string_lossy().to_string()
    });

    std::fs::create_dir_all(&cache_dir)?;

    let cache_key = format!("{:x}", md5::compute(url.as_bytes()));
    let cache_path = std::path::Path::new(&cache_dir).join(&cache_key);

    // Check cache
    if cache_path.exists() {
        let data = std::fs::read(&cache_path)?;
        let hash = format!("{:x}", sha2::Sha256::digest(&data));
        if hash == expected_hash {
            return Ok(data);
        }
        // Hash mismatch, remove cached file
        let _ = std::fs::remove_file(&cache_path);
    }

    // Download
    let resp = ureq::get(url).call()?;
    let mut data = Vec::new();
    resp.into_reader().read_to_end(&mut data)?;

    // Verify hash
    let hash = format!("{:x}", sha2::Sha256::digest(&data));
    if hash != expected_hash {
        return Err(format!("Hash mismatch: expected {}, got {}", expected_hash, hash).into());
    }

    // Cache
    std::fs::write(&cache_path, &data)?;

    Ok(data)
}

/// Parses tiktoken BPE format (base64-encoded token + rank)
fn parse_tiktoken_bpe(data: &[u8]) -> Result<HashMap<Vec<u8>, u32>, Box<dyn std::error::Error>> {
    use data_encoding::BASE64;

    let mut ranks = HashMap::new();
    for line in data.split(|&b| b == b'\n') {
        if line.is_empty() {
            continue;
        }
        let parts: Vec<&[u8]> = line.splitn(2, |&b| b == b' ').collect();
        if parts.len() != 2 {
            continue;
        }
        let token = BASE64.decode(parts[0])?;
        let rank: u32 = std::str::from_utf8(parts[1])?.parse()?;
        ranks.insert(token, rank);
    }
    Ok(ranks)
}

/// Loads o200k_base encoding
fn load_o200k_base() -> Result<CoreBPE, Box<dyn std::error::Error>> {
    let data = download_tiktoken_bpe(O200K_BASE_URL, O200K_BASE_HASH)?;
    let mergeable_ranks = parse_tiktoken_bpe(&data)?;

    let mut special_tokens = HashMap::new();
    special_tokens.insert(ENDOFTEXT.to_string(), 199999);
    special_tokens.insert(ENDOFPROMPT.to_string(), 200018);

    CoreBPE::new::<_, _, Vec<(String, (u32, u32))>>(mergeable_ranks, special_tokens, O200K_PAT_STR)
        .map_err(|e| format!("Failed to create CoreBPE: {}", e).into())
}

/// Loads HuggingFace tokenizer
fn load_hf_tokenizer() -> Result<Tokenizer, Box<dyn std::error::Error>> {
    // Try to load from HuggingFace Hub using the http feature
    // This requires network access on first run
    match Tokenizer::from_pretrained("Xenova/gpt-4o", None) {
        Ok(tokenizer) => Ok(tokenizer),
        Err(e) => {
            // Fallback: try to load from local cache
            let cache_path = dirs::home_dir()
                .ok_or("Could not determine home directory")?
                .join(".cache/huggingface/hub/models--Xenova--gpt-4o/snapshots")
                .join("main/tokenizer.json");

            if cache_path.exists() {
                Tokenizer::from_file(cache_path)
                    .map_err(|e| format!("Failed to load from cache: {}", e).into())
            } else {
                Err(format!("Failed to load HF tokenizer: {}. Try running: python -c 'from tokenizers import Tokenizer; Tokenizer.from_pretrained(\"Xenova/gpt-4o\")'", e).into())
            }
        }
    }
}

/// Sample texts for benchmarking
fn get_sample_texts() -> Vec<(&'static str, String)> {
    vec![
        (
            "short",
            "Hello, world! This is a test.".to_string(),
        ),
        (
            "medium",
            "The quick brown fox jumps over the lazy dog. ".repeat(50),
        ),
        (
            "long",
            "Artificial intelligence (AI) is intelligence demonstrated by machines, in contrast to the natural intelligence displayed by humans and animals. Leading AI textbooks define the field as the study of \"intelligent agents\": any device that perceives its environment and takes actions that maximize its chance of successfully achieving its goals. ".repeat(100),
        ),
        (
            "multilingual",
            "Hello 世界 مرحبا Привет こんにちは 안녕하세요 ".repeat(50),
        ),
    ]
}

fn bench_tiktoken(c: &mut Criterion) {
    let enc = load_o200k_base().expect("Failed to load o200k_base");
    let texts = get_sample_texts();

    let mut group = c.benchmark_group("tiktoken_encode");
    for (name, text) in &texts {
        let text_bytes = text.len();
        // Throughput is total bytes processed (iterations * text size)
        group.throughput(Throughput::Bytes(
            (text_bytes * ITERATIONS_PER_SAMPLE) as u64,
        ));
        group.bench_with_input(BenchmarkId::from_parameter(name), text, |b, text| {
            b.iter(|| {
                // Encode multiple times per iteration to amortize measurement overhead
                for _ in 0..ITERATIONS_PER_SAMPLE {
                    let tokens = enc.encode_ordinary(black_box(text));
                    black_box(tokens);
                }
            })
        });
    }
    group.finish();
}

fn bench_hf_tokenizer(c: &mut Criterion) {
    let tokenizer = load_hf_tokenizer().expect("Failed to load HF tokenizer");
    let texts = get_sample_texts();

    let mut group = c.benchmark_group("hf_tokenizer_encode");
    for (name, text) in &texts {
        let text_bytes = text.len();
        // Throughput is total bytes processed (iterations * text size)
        group.throughput(Throughput::Bytes(
            (text_bytes * ITERATIONS_PER_SAMPLE) as u64,
        ));
        group.bench_with_input(BenchmarkId::from_parameter(name), text, |b, text| {
            b.iter(|| {
                // Encode multiple times per iteration to amortize measurement overhead
                for _ in 0..ITERATIONS_PER_SAMPLE {
                    let encoding = tokenizer.encode(black_box(text.clone()), true).unwrap();
                    let token_ids: Vec<u32> = encoding.get_ids().to_vec();
                    black_box(token_ids);
                }
            })
        });
    }
    group.finish();
}

fn bench_comparison(c: &mut Criterion) {
    let tiktoken_enc = load_o200k_base().expect("Failed to load o200k_base");
    let hf_tokenizer = load_hf_tokenizer().expect("Failed to load HF tokenizer");

    let texts = get_sample_texts();

    let mut group = c.benchmark_group("comparison");
    group.measurement_time(Duration::from_secs(10));

    for (name, text) in &texts {
        let text_bytes = text.len();
        // Throughput is total bytes processed (iterations * text size)
        group.throughput(Throughput::Bytes(
            (text_bytes * ITERATIONS_PER_SAMPLE) as u64,
        ));

        group.bench_with_input(BenchmarkId::new("tiktoken", name), text, |b, text| {
            b.iter(|| {
                // Encode multiple times per iteration to amortize measurement overhead
                for _ in 0..ITERATIONS_PER_SAMPLE {
                    let tokens = tiktoken_enc.encode_ordinary(black_box(text));
                    black_box(tokens);
                }
            })
        });

        group.bench_with_input(BenchmarkId::new("hf_tokenizer", name), text, |b, text| {
            b.iter(|| {
                // Encode multiple times per iteration to amortize measurement overhead
                for _ in 0..ITERATIONS_PER_SAMPLE {
                    let encoding = hf_tokenizer.encode(black_box(text.clone()), true).unwrap();
                    let token_ids: Vec<u32> = encoding.get_ids().to_vec();
                    black_box(token_ids);
                }
            })
        });
    }
    group.finish();
}

criterion_group!(
    benches,
    bench_tiktoken,
    bench_hf_tokenizer,
    bench_comparison,
);
criterion_main!(benches);

本文从简单概率的概念出发,逐步过渡到条件概率,最后介绍贝叶斯定理。整个过程会尽量保持直观,不涉及复杂的数学形式。

假设有两个盒子:盒子 A 和盒子 B。盒子 A 装了 4 个球,3 红 1 绿;盒子 B 同样装了 4 个球,1 红 3 绿。

一个蒙着眼的人站在两个盒子前面,随机选中任一盒子的概率是 1/2。选定了某个盒子,比如盒子 A,从中摸到红球的概率是 3/4,摸到绿球的概率是 1/4。

树形图清楚地展示了盒子选择和球选择的概率分布也引出了几个基本概念:蒙眼的人选定盒子 A 后,取到红球的概率是 3/4,取到绿球的概率是 1/4。

选中任一盒子的概率是 1/2,写成数学语言:P(A) = 1/2,P(B) = 1/2。这属于简单概率。

在盒子 A 已被选中的前提下,从中取出红球的概率是 P(R | A) = 3/4。这就是条件概率,它以"盒子 A 已被选中"为条件,说法是"在盒子 A 已被选中的条件下,取出红球的概率"。

  P(R | A) = 3/4 = count of Red balls in box A / total balls in box A

同理,P(G | A) = 1/4 表示在盒子 A 已被选中的条件下取出绿球的概率。

条件概率有一个关键特征:它缩小了"世界"的范围。计算条件概率时,参考系限定在条件所界定的子集之内。选择盒子时,"世界"是包含两个盒子的全集;选择球时,"世界"缩小到了那个特定的盒子,概率以该盒子中球的总数为分母。换言之,就是用盒子 A 中红球的数量除以盒子 A 中球的总数。

P(R ∩ A) 和 P(G ∩ A) 呢?它们也是概率但不附加任何条件。它们代表的是从树的根节点出发、从最开始起算的概率,不假设已经选好了盒子正在取球而是把选盒子和取球两步合在一起,得出一个从起点到终点的总概率。

P(R ∩ A) = P(A) . P(R | A) = 1/2 . 3/4 = 3/8

想想这个公式为什么成立呢?从盒子 A 中取到红球的条件概率是 3/4,但现在还要考虑选中盒子 A 本身的概率是1/2。两者相乘3/4 被缩减为 3/8。蒙眼人选中盒子 A 的概率 P(A) = 1/2,继而在盒子 A 中摸到红球的概率 P(R | A) = 3/4,两步合起来 P(R ∩ A) = 3/8。由于两个盒子各含 4 个球且等概率被选中,3/8 实际上等于盒子 A 中的红球数除以全集中球的总数。

这个结果符合理论:所有条件概率都会因为前置的盒子选择概率而按比例缩小,即按选中该盒子的概率做缩放。出发点也从树的盒子节点退回到了根节点。

同理:

 P(G ∩ A) = 1/8 = Green balls A contains / total number of balls in whole universe  
 P(R ∩ B) = 1/8, P(G ∩ B) = 3/8

到这里,整个全集已经被切分成了四个不重叠的概率块:

P(R ∩ A) + P(G ∩ A) + P(R ∩ B) + P(G ∩ B) = 3/8 + 1/8 + 1/8 + 3/8 = 8/8 = 1

四个块加起来刚好等于 1,说明全集中所有盒子与球的组合都已穷尽,不存在遗漏。图示如下:

Universe-1

P(R | A) 描述的是红球在盒子 A 内部占多大比例;P(R ∩ A) 描述的是盒子 A 中的红球在整个全集中占多大比例。二者的区别至关重要。

现在换一个方向提问:随机拿起一个红球,它来自盒子 A 的概率是多少?即 P(A | R) = ?

这个问题的方向和树形图恰好相反。原先的逻辑是先选盒子再选球,"世界"从全集缩小到特定盒子,在盒子层面计算条件概率。现在的逻辑则是先假定取到的球是红色的—— "世界"缩小到只有红球——然后再看其中多大比例来自盒子 A。

一种理解方式是先构造一个"红球星球",把全集中所有红球聚在一起,再看盒子 A 贡献了其中多少。

P(R) = P(R ∩ A) + P(R ∩ B) = 1/2

为什么这个值合理?全集被切成四个块,其中两个包含红球 P(R ∩ A) 和 P(R ∩ B)。将它们合并就得到红球的总概率。两个值都是以全集为参考系的,所以 P(R) = 1/2 的含义是全集中一半的球是红色的。

新的参考系如下:

Universe-2

同理:

 P(G) = P(G ∩ A) + P(G ∩ B) = P(G | A) . P(A) + P(G | B) . P(B)  
 P(G) = 1/4 . 1/2 + 3/4 . 1/2 = 1/2

到这一步,"世界"的组织方式变了,从"盒子包含球"变成了"球携带来源盒子的标签"。

为什么要做这个转换?原来的概率链条是"先选盒子、再取球",但目标问题是反过来的:已知取到了红球,想知道它来自哪个盒子,方向一反转就需要从 P(R | A) 转向计算 P(A | R):

 P(A | R) = P(A ∩ R) / P(R) = (3/8) / (1/2) = 3/4

为什么不直接用 P(A ∩ R) = 3/8 来回答?因为 3/8 是站在全集视角看的——全集中盒子 A 红球所占的比例。但问题要求站在"红球星球"的视角,而不是全集的视角。红球星球的总量比全集小,所以 3/8 按比例放大——除以 P(R) = 1/2,等价于乘以 2,得到 3/4。P(A ∩ R) 和 P(R) 的分母都是全集,度量单位一致,相除后结果就落在了红球星球的尺度上。

换个角度看也行:红球星球上共 4 个红球,其中 3 个来自盒子 A。

 P(A | R) = count of red balls from planet A / total red balls = 3/4

还可以这样理解:红球星球由两个块组成——P(A ∩ R) 和 P(B ∩ R),两者之和即 P(R)。要求 P(A ∩ R) 在 P(R) 中的占比,直接做除法即可。

绿球方向的计算完全对称:条件是绿球已被选中,求它来自盒子 B 的概率。

 P(B | G) = P(B ∩ G) / P(G) = P(B ∩ G) / (P(A ∩ G) + P(B ∩ G))  
 P(B | G) = (3/8) / ((1/8) + (3/8)) = 3/4

小结一下整个过程:在全集 1 中,星球是盒子 A 和盒子 B,各自包含红球和绿球的分区。经过重组后,全集 2 中的星球变成了红球和绿球,各自包含盒子 A 和盒子 B 的分区。从一种划分到另一种划分的转换——这就是贝叶斯定理的本质。

直接代入公式验证:

 P(A | R)  = P(A ∩ R) / P(R)  
           = P(A ∩ R) / (P(A ∩ R) + P(B ∩ R))  
           = P(R | A) . P(A) / (P(R | A) . P(A) + P(R | B) . P(B))  
           = 3/4 (you can put values to confirm)

不过还需注意一点:

 P(A ∩ R) = P(R | A) . P(A) [This is given in our problem. So we use this in our formula]  
 P(A ∩ R) = P(A | R) . P(R) [This is what we would find eventually. So we didn't use it in formula for calculation]

为什么这套全集转换的逻辑能走通?为什么原本以盒子为视角的概率可以翻转成以球为视角?根本原因在于全集能够通过交集运算被拆解成互不重叠的概率块。

"全集可以被分割成小的、带标签的块(联合概率)。"

这些小块各自携带一个条件标签,可以按需重新组合成新的"星球",从而以不同的视角审视同一个全集。P(R ∩ A)、P(R ∩ B)、P(G ∩ A)、P(G ∩ B)——这四个联合概率就是构建一切的基本单元。

贝叶斯公式:

P(A | R) = P(A ∩ R) / P(R) = P(R | A) . P(A) / P(R)

从盒子出发提问"给定盒子颜色是什么"——答案是条件概率

P(R | A)

P(G | B)

等。将条件概率乘以降落在该盒子上的概率

P(A)

P(B)

,得到联合概率

P(R ∩ A)

等。按颜色对联合概率分组,得到边缘概率

P(R)

P(G)

,进而就可以反转提问方式:

P(A | R)

P(B | G)

P(R | A)

P(A | R)

的反转:这正是贝叶斯定理所形式化的运算。

贝叶斯的思想之所以自然到几乎不需要解释,因为全集天然地可以被切分成带标签的小块(联合概率),这些小块按盒子分组就得到盒子级别的概率,按颜色分组就得到颜色级别的概率。贝叶斯定理不过是一套以一致、归一化的方式将"给定"方向从盒子→颜色翻转为颜色→盒子的算术规则。

https://avoid.overfit.cn/post/491104cf4f374349bf12850ac618242d

by Syed Abdullah

项目介绍

本项目是一个面向水产识别场景的鱼类图像识别系统,采用“前后端分离 + 深度学习推理服务”的实现方式。系统前端基于 Vue3 与 Element Plus,提供用户登录注册、图片上传识别、历史结果查询、公告查看等交互页面;后端基于 Flask,按认证、用户、识别、公告四个蓝图组织业务接口,并通过 JWT 完成访问鉴权。识别流程中,用户上传图片后,系统先进行文件大小与格式校验,再保存到本地 media 目录,随后调用 TensorFlow 模型执行推理,返回最高置信度类别及全部类别置信度排序结果。系统当前支持 30 种常见鱼类(含墨鱼、石斑鱼、鲈鱼、鲫鱼等)识别,并将结果、置信度、图片地址与时间信息持久化到 SQLite,形成可追溯的识别历史。

图片

图片

图片

选题背景与意义

在水产流通、餐饮供应链、养殖管理与科普教育等场景中,鱼类品种识别长期依赖人工经验,存在主观性强、效率不稳定、培训成本高等问题。尤其在多品类混杂、图像质量不一致或人员经验不足的情况下,传统人工判断容易出现误判,进而影响采购定级、价格评估与质量追踪。基于计算机视觉的自动识别技术能够将“经验判断”转化为“数据驱动决策”,在标准化与规模化应用方面具有明显优势。本课题以深度学习模型为核心,构建从“图像采集-模型推理-结果存储-历史追溯”的完整闭环.

关键技术栈:ResNet50

ResNet50 是一种经典深层卷积神经网络,通过残差连接(Residual Connection)有效缓解深层网络训练中的梯度消失与网络退化问题,使模型在保证表达能力的同时维持稳定收敛。其核心思想是让网络学习“残差映射”而非直接学习复杂目标映射,从而提升训练效率与泛化性能。本系统中,训练后的 ResNet50 模型以 resnet50_model.h5 形式部署在后端,推理阶段将输入图片统一预处理为 224×224 RGB 张量,并做归一化后送入模型预测。系统根据输出向量计算 Top1 识别结果与对应置信度,同时返回全部 30 类别置信度列表,便于前端做可视化展示与结果解释。

技术架构图(Mermaid)

图片

系统功能模块图(Mindmap)

图片

演示视频 and 完整代码 and 安装

地址:https://www.yuque.com/ziwu/qkqzd2/venxy6gagq2ngfl9s

在国内科技圈被阿里 Qwen 技术负责人林俊旸离职的消息刷屏时,硅谷的一则人事变动却显得异常安静。

OpenAI 后训练负责人、GPT-5 系列的核心推手 Max Schwarzer 宣布离职,转身加入 Anthropic,回归一线研究员身份。

这一离职时间点尤为微妙。此前 Open AI 与 Anthropic 两家公司正处在对垒态势中,OpenAI 接下了 Anthropic 曾明确回避的五角大楼订单。此消息一出,引起民众强烈反弹,ChatGPT 短期卸载量随之激增 295%。

在商业版图扩张与伦理争议并存的十字路口,作为掌控模型效果的关键人物,Max Schwarzer 在升任研究副总裁仅 7 个月后便选择离开。这本身就是一个强烈的信号:OpenAI 正在全速驶向商业化的未来,或许已不再是纯粹研究者心中的理想之地。

Max Schwarzer 的离开并非孤例。她在离职声明中坦言,许多她欣赏的同僚已身在 Anthropic。

比如 OpenAI 联合创始人、后训练负责人、ChatGPT “对话能力”的缔造者 John Schulman,超级对齐团队负责人、曾试图为超越人类的 AI 建立安全围栏的 Jan Leike,OpenAI 联合创始人、极客型算法科学家 Durk Kingma......

这揭示了一条正在成形的“人才迁徙线”,这些技术领袖正陆续从 OpenAI 位于旧金山 Mission 区的总部撤离,向着更强调“宪法级 AI”与安全研究的 Anthropic 汇聚。

而 Max Schwarzer 在 Anthropic 的新方向是强化学习(RL),这正是她曾主导 GPT-o1 时的核心领域,重在逻辑推理,旨在突破模型的思考上限。这种对“能力边界”的执着,与 OpenAI 当下的产品策略形成了鲜明反差。

回顾近期 GPT-5 系列的迭代路径,一个明显的变化是,OpenAI 的研发重心正在从单纯拓展模型的参数边界转向解决商业落地的“最后一公里”问题。

无论是推理优化、幻觉降低,还是嵌入 Agent 能力与企业级部署,GPT-5 系列的目标都指向了“可控、可靠、可规模化”。这一趋势在最新推出的模型 GPT-5.3 Instant 上尤为明显,该模型致力于体验的优化与情商的提升。显然,一场关乎“用户体验”新一轮竞赛已经悄然打响。

如果将视野拉大,看 OpenAI 近期的一系列动作,从签下五角大楼的政府订单,到将推出取代 GitHub 的代码托管平台,从模型提供商向开发者工具生态扩张,这其实都在讲述同一个故事:

OpenAI 正处于战略转型的关键时期,它要变成一个具有全球影响力、深度嵌入商业与政府体系的 AI 平台型巨头。

产品战略的转向:从“参数军备赛”到“体验护城河”

从 GPT-4 系列到 GPT-5 系列的迭代,可以清晰看到,Open AI 正在从“让 AI 更聪明”,转向 “让 AI 更值得信任”。

这种转变并非偶然,而是在经历了长达两年的“参数军备竞赛”后,OpenAI 意识到,单纯的模型规模增长正面临边际效应递减的困境。

正如 OpenAI 前首席科学家 Ilya Sutskever 所言:“只靠 Scaling Law 的时代已经结束,我们重新回到了探索与发现的阶段。”

过去模型大一倍,能力上一个台阶;如今模型大十倍,效果提升可能不足 10%。盲目扩参性价比变得极低,而在后训练与推理环节做优化,反而能带来极高的投入回报。

然而,这里存在一个巨大的悖论,既然“后训练”已成为 OpenAI 的新战略高地,为何 Max Schwarzer 等后训练领域的顶级掌舵人,却纷纷选择出走?

这恰恰揭示了 OpenAI 内部对于“后训练”定义的根本性分歧,是“为了真理”,还是“为了产品”。在科学家眼中,后训练本应是通往 AGI 的最后一道安全闸。但在商业化加速的 OpenAI 管理层看来,后训练正在被重新定义为一种“高级客服培训”。

此前,GPT-5.2 版本那种“爹味十足”的说教语气或过度易被触发的安全防御,已经激怒了大量用户,在社交媒体上掀起实质性退订潮。这让 OpenAI 痛切地意识到,糟糕的交互体验正在抵消模型智力的优势。

于是,在最新推出的 GPT-5.3 Instant 中,巨大的算力资源被从“逻辑推理”倾斜到了更务实的“工程修补”上:如何让语气更圆滑?情商更高?对话更流畅?它不再试图成为一个“全知全能的神”,而是努力成为一个“甚至懂你潜台词的人”。

至此,Open AI 后训练的目标,从“防止 AI 毁灭世界”,降维成了“防止 AI 惹上官司”。

OpenAI 的这种商业化转向,也正对应着整个行业评价尺度的重写。

年初,吴恩达提出的“图灵-AGI 测试”不再关心 AI “会不会解题”,而是看它能否在路径不可控的条件下把一件事真正做完;斯坦福大学的《2026 AI 预测报告》与谷歌云的 ROI 报告,也都直指同一个风向标:别谈模型智力上限,谈企业中的落地效益。

对于企业客户而言,账算得很清楚:一个能考满分但偶尔胡言乱语的天才,远不如一个考 90 分但情绪稳定、逻辑自洽的助手有价值。降低企业的合规风险,这才是企业级落地的关键门槛。

正如苹果从不堆砌硬件参数却能赢得用户一样,OpenAI 也试图通过极致的工程化打磨证明,在商业世界里,“体验决胜”远比“参数堆叠”更能留住客户,这也是其商业化转向的核心逻辑

商业与政治布局加速:从科研机构向“基础设施级平台”演化

OpenAI 的技术转向只是冰山一角,其水面之下潜藏的,是 Sam Altman 在政治与商业的双重布局。

这家诞生于“开源、公益、推动 AGI 造福人类”理想的公司,正在疯狂吞噬算力、数据与资本,其所有动作的核心,都围绕一个关键词“控制权”。

据 The Information 爆料,OpenAI 正秘密研发一款代码托管平台,意图直接取代微软旗下的 GitHub,成为全球新一代代码托管与生成中心。

尽管 OpenAI 的工程师对外宣称,是对 GitHub 近期频繁宕机的不满。但从结果来看,此举更像是争夺软件工业的“底层定义权”。

目前,OpenAI 的 Copilot 不过是挂在 IDE 上的“外挂”,始终依附于微软的开发者生态。而它真正想要的,是让 AI 成为编程的“原生环境”。

开发者在其平台上完成代码托管、生成、调试、部署的全流程,OpenAI 则通过掌控这一闭环,收割最鲜活、最核心的工程数据,构建“数据-模型-应用”的自循环体系。

Open AI 也完成从 AI 模型提供者向开发者工具生态的扩张。

而更具争议的一步,则是 OpenAI 拥抱五角大楼的政治“投名状”。

2024 年初,OpenAI 更新了其使用政策,删除了此前明确禁止“军事和战争用途”的条款。这一调整并未高调宣布,但被多家媒体注意到。

随后,OpenAI 任命前美国国家安全局(NSA)局长 Paul Nakasone 进入董事会,并成立“安全与保障委员会”。

这些动作被普遍解读为 OpenAI 加深与美国国家安全体系合作的信号。

而在最新“五角大楼订单”事件中,Anthropic 选择坚守“宪法级 AI”的边界,拒绝让其 AI 模型用于大模型国内监控或全自动武器,最终被列为“国家安全供应链风险。而 OpenAI 却很快便与五角大楼达成协议,接受了五角大楼“符合适用法律即可使用”的核心框架。虽然增设三条安全红线,并争取到“云端部署、自主掌控安全栈”的技术防护权,但协议措辞仍为潜在监控留下解释空间。

OpenAI 的接手,不仅是一次商业截胡,更是一次政治站位。这表明 OpenAI 已准备好承担作为“国家级 AI 基础设施”的复杂性。

毕竟,在 2 亿美元的国防预算面前,企业级 SaaS 的收入显得微不足道。能成为美军的供应商,则获得了“大而不能倒”的政治豁免权。

而在最新一轮融资中,OpenAI 融资规模高达 1100 亿美元,堪称 AI 史上最大的一笔融资,OpenAI 投后估值直逼 8400 亿美元,直逼万亿俱乐部。

Open AI 计划拿这笔钱扩大人工智能基础设施建设,堆出一道竞争对手无法翻越的算力高墙。这正是平台经济“网络效应”的极致运用:通过垄断算力、数据等核心资源,形成“用户聚集—资源强化—更多用户涌入”的自我强化循环,最终实现市场垄断,这也是 OpenAI 追求“控制权”的核心逻辑之一。

但在这场看似风光的狂奔背后,悬着一把致命的达摩克利斯之剑,极度疯狂的烧钱速度,与尚未闭环的商业模式,构成了 OpenAI 最大的隐患,

如今的 OpenAI 正深陷政府安全需求、伦理风险、商业利益三方博弈之中。它正在进行一场人类商业史上前所未有的豪赌,在资金链断裂之前,强行跑通商业闭环。

人才流动与文化摩擦:战略重心转变下的必然分化

当企业的基因突变,必然引发细胞的代谢。Open AI 的高层震荡,是战略重心转移后的必然分化

随着 ChatGPT 成为拥有数亿用户的超级应用,OpenAI 内部的引力场发生了根本性反转:工程化与产品化开始主导决策,纯粹的研究探索被迫后退。

多位 OpenAI 高管和研究负责人在近几年离职,包括 CTO、后训练负责人、研究负责人。对于像 John Schulman 或 Max Schwarzer 这样的技术原教旨主义者来说,当算力资源开始优先向产品部署而非前沿探索倾斜,当安全团队的权限被商业交付节点挤压,离开便成了唯一选择。

Anthropic 成为了这些“流亡者”的庇护所。那里更像 2019 年之前的 OpenAI:更慢的发布节奏、更严苛的安全审查、对 Scaling Laws 理论更为痴迷。

在风险投资公司 SignalFire 最新发布的 2025 人才趋势报告中,Anthropic 顶尖 AI 人才的留存率达到 80%,而且工程师从 OpenAI 跳槽到 Anthropic 的可能性是从 Anthropic 转投 OpenAI 的 8 倍。

而 Anthropic 的首席执行官 Dario Amodei 更是夸耀,自家公司有能力抵御竞争对手“挖墙脚”,他们不会理会竞争对手,如 Meta 花十倍薪水挖角的伎俩,因为大部分员工都会因为“使命”自愿留下。据相关人士透露,在此诱惑下,只有两名员工辞职去了 Meta,其留存率远高于 Open AI。

这种人才迁徙也表明了 OpenAI 正在筛选掉“纯粹研究者”,留下“产品经理”和“工程师”。 它聚集了最优秀的产品化人才,他们擅长将技术变现,打造像 ChatGPT 这样改变世界、体验极致的产品。OpenAI 正在变成 AI 时代的微软。

而 Anthropic 正在吸纳最纯粹的“科学家”和“安全专家”。 它聚集了致力于探索 AGI 理论边界和安全底座的头脑,似乎成为 AI 时代的贝尔实验室。

这不仅仅是两家公司的竞争,更是两条技术路线的赌局。OpenAI 选择了“广度与渗透”,先成为不可或缺的基础设施,赢下市场份额。Anthropic 选择了“深度与边界,赌在未来,赌在安全底座。

结尾

OpenAI 近期的“小动作”不断,一边用更少幻觉、更少拒答、更不冒犯的交互把 ChatGPT 打磨成可规模化的“企业级默认入口”,一边又把触角伸向代码托管、政府合同与安全治理,把自己嵌入更硬的生产体系与国家机器。这正在从“模型公司”跨进“基础设施公司”的转变。

而这条路的代价,是信任与文化的再分配:研究者会用脚投票,用户会用卸载表达态度,竞争者会用“更道德”的叙事抢夺心智。

参考链接:

https://x.com/max_a_schwarzer

https://www.theinformation.com/articles/openai-developing-alternative-microsofts-github

https://www.reuters.com/business/openai-is-developing-alternative-microsofts-github-information-reports-2026-03-03/

https://openai.com/zh-Hant/index/gpt-5-3-instant/

想象一下,你和一个外国朋友语言不通,需要找翻译。
这个翻译有个特别的规则:不按“字”算钱,而是按“词块”算钱

你说:“今天天气真好”,
翻译会拆成:

今天 / 天气 / 真 / 好

这些一个个“小块”,在 AI 里就叫 token

PixPin_2026-03-04_18-17-15.png


token 到底是啥?

很多人以为 token 就是“字数”,其实并不是。

最简单的理解:

token 是 AI 阅读和理解文字时使用的最小单位
可以把它理解成“词块”。

几个直观例子:

  • 苹果 → 1 个 token
  • 人工智能 → 常被拆成「人工 / 智能」→ 2 个 token
  • hello → 1 个 token
  • hello world → 2 个 token

中英文的拆分规则也不一样:

  • 英文:大致按单词算

    • 100 个单词 ≈ 130 个 token
  • 中文:按“字”或常见词组算

    • 100 个汉字 ≈ 150 个 token

为什么 AI 要用 token?

因为 AI 并不直接理解“字”“词”或“句子”。
在它眼里,所有文字最终都要变成数字

token 的作用就是:

把文字切成小块 → 给每块编号 → 交给模型计算

例如:

“我喜欢 AI”
→ [我, 喜欢, AI]
→ [1234, 5678, 9012]

模型真正处理的,其实是这些数字。


token 为什么这么重要?

1. 决定你能聊多长

每个模型都有 token 上限,比如:

  • 8K
  • 32K
  • 128K

注意:

输入 + 输出 的 token 总数,加在一起算

不是字数限制,也不是消息条数限制。

如果你遇到过:

  • 聊着聊着 AI “失忆”
  • 前面说过的话突然不记得了

大概率就是:上下文 token 用完,被截断了


2. 决定你花多少钱

大多数 AI 服务都是按 token 计费的:

  • 输入:$X / 百万 tokens
  • 输出:$Y / 百万 tokens(通常更贵)

也就是说:

同样一句话,说得越啰嗦,用的 token 越多,越贵

3. 决定响应速度

模型是逐 token 生成内容的:

  • token 越多
  • 计算步骤越多
  • 响应就越慢

所以通常:

  • 提示词越精简,回复越快
  • 长上下文模型,更慢也更贵

一个更生活化的比喻

把 AI 当成快递站

  • 你的文字 = 包裹
  • token = 重量单位(不是按件,是按重量)
  • 最大承重 = token 上限
  • 运费 = 按 token 计费

同一个意思,表达越精简:

更便宜、更快,也更不容易超限

怎么快速估算 token?

日常使用,记住一个粗略公式就够了:

  • 英文:1 token ≈ 4 个字母 ≈ 0.75 个单词
  • 中文:1 token ≈ 1.5 个汉字

所以可以简单估:

  • 1000 字中文 ≈ 600~700 token
  • 1000 个英文单词 ≈ 1300 token

⚠️ 注意:
标点、数字、代码、URL、生僻词,都会影响 token 数,不存在绝对精确的换算公式


超简小结

问题一句话答案
token 是什么?AI 处理文字的最小单位,可理解为“词块”
和字数关系?中文约 1.5 字 = 1 token英文约 0.75 单词 = 1 token
为什么重要?影响对话长度、费用、响应速度
怎么省 token?表达精简,少废话、少重复

一句话总结:
token 是 AI 的量尺 + 计价器
用它来“读文字”,也用它来“算成本”。

下次再看到:

  • 8K context:约 6000~8000 汉字
  • 128K context:可一次性读几万字
  • 1M context:整本书、超大代码库都能一次吃下

你就知道,它真正指的是什么了。

286e130df2a023a5fd6e298dba030309

小时候屁股痒、脸上长白斑,去诊所给开了肠虫清,吃了就好

问了身边的人似乎很少有人有定期驱虫的习惯

常见的寄生虫感染有:蛲虫蛔虫肝吸虫
感染途径有吃生食(生腌)、不勤洗手、食物污染等

image.png

当全球AI巨头还在比拼谁家的模型参数更多、规模更大时,阿里突然亮出了一手“反向操作”。3月2日晚,阿里巴巴旗下千问团队一口气开源了四款Qwen3.5小尺寸模型,从最小的0.8B到最大的9B,全部“巴掌大小”,却个个身怀绝技。发布后不久,一向毒舌的埃隆·马斯克火速在社交媒体上点赞,称其拥有“令人印象深刻的智能密度”。

这四款模型可谓是“术业有专攻”。Qwen3.5-0.8B和2B主打“极致轻量”,推理速度极快,专为手机、智能眼镜、IoT设备等端侧场景设计,让AI无需联网也能跑在用户口袋里。Qwen3.5-4B则被定位为“轻量级智能体的强劲基座”,在视觉智能体评测(ScreenSpot Pro)中,它的表现与尺寸大近8倍的Qwen3-VL-30B-A3B持平,这意味着它有望像真人一样自主操作手机和电脑。而Qwen3.5-9B虽是紧凑尺寸,却上演了“越级打怪”的戏码,在涵盖博士级别推理(GPQA)的权威评测中,其得分超越了参数规模大10倍以上的OpenAI开源模型gpt-oss-120B。

为什么“小”反而成了亮点?这背后是技术路线的深刻变革。传统的模型扩大规模追求“蛮力”,而千问3.5小模型通过混合架构创新,在极小参数下实现了原生多模态能力。有开发者实测后发现,仅凭一台配备了M4芯片的普通笔记本电脑,甚至是在浏览器里,就能流畅运行这些模型。正如一位开发者所言:“这简直是凭一己之力,把顶级模型的智能装进了每个人的电脑,而且免费。”

此次开源填补了千问3.5家族在端侧部署的空白,形成了从0.8B到397B的完整产品矩阵。所有模型均采用Apache 2.0协议,这意味着企业可以免费商用、随意修改,无需担心“卡脖子”或高昂的API调用费。

从“大炼参数”到“端侧觉醒”

千问此次连发四款小模型,看似是在追逐“迷你”潮流,实则掐准了AI落地的下一个命门——端侧智能。过去两年,业界沉迷于参数竞赛,但万亿大模型因成本高、延迟长、数据隐私等问题,难以真正走进日常生活。而千问3.5小模型的出现,证明了“智能”不等于“重量”。当0.8B模型能在手机上离线运行,当4B模型能像大脑一样控制手机操作,AI才算真正从云端飞入了寻常百姓家。银河证券研报指出,硬件是AI应用落地最确定的受益方向。阿里这步棋,正是将千问打造成“一脑多端”的AI助手,为即将爆发的AI硬件浪潮备好了最核心的“芯”。与其在红海里比拼谁更大,不如在蓝海里证明谁更“灵”。这一次,阿里显然选对了赛道。

图片1.png
随着智能体技术正从单点工具升级为协同系统,医药行业正迎来AI赋能的黄金窗口期。一方面,Agentic AI以智能规划与执行能力构建新型"数据基建",将科研人员从繁重的数据搬运中解放,突破了医药数据分散、质量低、处理慢等困局。另一方面,智能体正在重构从靶点发现到临床试验的研发闭环,并驱动科研文献分析、合规文档、销售决策等关键场景的效率革命。

从实施的成果来看,枫清科技的智能体平台已走在行业前列。枫清科技已深度服务华润医药、华润三九、东阿阿胶、罗氏诊断等医药产业标杆企业,以"研产供销服管"一体化智能中枢,覆盖医药企业超9个主要职能领域,落地了40多个高价值场景,彰显智能化渗透的广度与深度。

智能研发:专利洞察与文献分析双引擎

在医药研发这一核心领域,枫清科技构建了覆盖科研全链路的智能体生态。在AI技术的驱动下,大分子药物及新药物形态的开拓、药企对疾病机制的理解、mRNA及疫苗设计与优化、抗体药物设计和优化、试验设计等领域都迎来了较多新兴机遇。而枫清科技通过将分散的科研信息转化为可执行的研发洞察,助力药企在日渐激烈的竞争中更快找到研发路线、获得高质量研究结果。

值得一提的是,枫清科技的专利智能体作为医药研发人员的“知识产权智囊”,帮助研发人员快速定位技术发展方向。例如,根据不同场景,用户以自然语言输入需求后,该智能体就可自动输出要素词、同义词、上位词等信息;可生成精准检索表达式,辅助专利搜索;还可查找指定内容的相关专利;智能体还能根据用户输入json数据,用模型能力对数据进行解读。

此外,枫清科技的“医药科研文献总结助手”,进一步为研发人员带来信息筛选与知识整合上的重要突破。这款智能体不仅能精准获取核心知识,使用户的阅读效率提升80%,还能快速穿透不同领域文献中复杂的专业术语、分子结构及实验数据,将其整合为对研发项目有指导意义的统一知识体系。

生产合规:质量文件审核的系统级变革

在医药生产与供应链环节,质量合规是企业的生命线。枫清科技的“质量文件审核助手”,直击传统人工审核效率低、易遗漏、标准不一的痛点,可自动完成格式、内容、标准符合性审查,并实现审核结论的可追溯与归档。

该智能体内置国标/行标/GMP规范、企业SOP模板库及历史审核知识库,通过NLP技术实现智能解析与规则内嵌。系统采用三层审核路径,可"秒级"完成格式校对与标准比对,自动识别超过95%的文字差异,覆盖率达100%,并自动生成结构化的审核报告。企业由此可节约70%的基础审核时间,并将法规、内部模板转化为可持续复用的系统规则。

营销管理:一问即得——数据决策的闭环重构

而在医药营销场景,针对数据分散、查询慢、指标维度庞杂等销售管理的痛点,枫清科技推出“销售智能问数系统”,帮助用户快速获取业务洞察,敏捷应对业务变化。该系统支持销售人员用自然语言提问,AI可调取相关指标并以多模态即时呈现关键数据,真正实现"一问即得"。该系统覆盖了销售相关目标、客户与区域、产品组合、覆盖与拜访、合同与仪器、运营与风控等六大类维度,帮助企业缩短发现问题和决策的时间。

无论是实验室、生产线,还是市场前线,枫清科技在医药行业关键价值领域落地的众多智能体,不仅实现了单点效率的跃升,更通过知识图谱构建、知识库沉淀及多维数据洞察,将个体经验转化为组织资产,推动了医药行业从"人力密集型"向"智能密集型"的深度转型,在前沿赛道的创新突围中,为企业锻造差异化领先的智能化利器。


在“HarmonyOS NEXT+AI大模型打造智能助手APP(仓颉版)”课程里面,有学员在开发国产中遇到了这么一个问题:

the error occurs after the macro is expanded

这里就这位学员的问题,统一做下回复,以方便其他同学参考。往期问答,可以在我主页查到。

图片

问题定位

hvigor Finished :entry:default@GenerateCangjieResource. . . after 7 ms> hvigor ERROR: Failed :entry : default@compileCangjie. . .

hvigor ERROR: Tools execution failed.

error: undeclared identifier 'percent'

==> D:\CangJie\AIAssistant[entny\src\main\cangjie \util\chatline.cj:81:80:

81 l

}.padding(4) .backgroundColor(OXFFFFFF).constraintSize(maxWidth: 90.percent) .borderRadius(10)

l

note: which is expanded as followsl

l/ 73.13 /

Row() { thisView.observeComponentCreation({ elmtId,isInitialRendep => Row() { thisView.obser

l

note: the error occurs after the macro is expanded

==> D:\CanqJie\AIAssistant \entny\snc\main\cangjie\util\chatline.cj:73:1:

图片

图片

从错误信息可以看到the error occurs after the macro is expanded是指当前环境没有提供宏。

那么什么是宏呢?

在仓颉中,宏可以理解为一种特殊的函数。一般的函数在输入的值上进行计算,然后输出一个新的值,而宏的输入和输出都是程序本身。在输入一段程序后,输出一段新的程序,这段输出的程序随后用于编译和执行。为了把宏的调用和函数调用区分开来,在调用宏时需使用 @ 加上宏的名称。

从报错的位置可以知道,报错代码是这个:

Column() {

Text(title).fontSize(24).fontWeight(FontWeight.W400)

.width(100.percent).height(100.percent)

.textAlign(TextAlign.Center)

}.width(100.percent).height(48)

.alignItems(HorizontalAlign.Start).backgroundColor(0x5DE2E7)

图片

这里的100.percent就是使用了宏。那么要宏就要导入宏。

解决方法

图片

在代码中导入这个即可

import ohos.base.LengthProp

图片

参考引用

更多仓颉学习资料,详见:


《跟老卫学HarmonyOS开发》:https://github.com/waylau/harmonyos-tutorial


《跟老卫学仓颉编程语言开发》:https://github.com/waylau/cangjie-programming-language-tutorial


“HarmonyOS NEXT+AI大模型打造智能助手APP(仓颉版)”:https://coding.imooc.com/class/927.html


《仓颉编程从入门到实践》(北京大学出版社):https://waylau.com/about-cangjie-programming-language-tutorial-book/



图片

编者按

在医药零售行业,传统的“进销存”模式正面临严峻挑战,数字化转型已成必答题。对于拥有8600多家门店、自营会员超 3500 万的上市企业漱玉平民而言,如何支撑海量数据的实时分析?如何应对大促期间数倍于平时的流量洪峰?

本文梳理了漱玉平民移动互联研发负责人张新新的实战分享。他详细复盘了漱玉平民如何从传统 MySQL 架构向平凯数据库(TiDB企业版) + Kubernetes (K8s) 云原生架构演进,成功构建起支撑亿级会员 CRM 及药店数字助理(PDA)的核心系统,实现了从“T+1”报表到实时精准营销的跨越。

漱玉平民移动互联研发负责人 张新新

一、 转型背景:当传统架构遇上“万店规模”

作为长江以北医药零售领域的领军企业,漱玉平民大药房于 2021 年上市后,迅速按下了数字化转型的快进键。截至目前,漱玉平民拥有 8600 多家门店(含直营、加盟及托管),自营会员数超过 3,500 万,全生态用户日活超 2 万。

随着业务版图从山东向东北、福建、河南等地扩张,传统的商业模式和技术架构遭遇了瓶颈:

  • 业务模式转变: 竞争日益激烈,单纯靠赚取差价的模式难以为继,企业急需通过数字化手段挖掘客户价值,从简单的药品订单转向全生命周期的健康服务。
  • 数据规模爆发: 会员中心(CRM)不仅要管理千万级会员,还涉及积分(作为负债管理,要求强一致性)、订单明细、优惠券等数据。单表数据量迅速突破 10 亿级
  • 实时性要求极高: 业务部门需要基于 1,700 多个标签进行复杂的“漏斗查询”和精准营销,传统数据库无法满足实时响应需求。

二、 痛点分析:MySQL 分库分表的“不可承受之重”

在数字化中心成立初期,核心系统底层使用的是自建 MySQL。面对海量数据,团队曾尝试通过中间件(如 MyCat、Sharding)进行分库分表优化,但很快触及了天花板:

  1. 查询性能瓶颈: 当进行复杂的会员分析(如流失模型、慢病复购、组合标签筛选)时,MySQL 难以支撑跨库的复杂关联查询,往往只能做到“T+1”的数据分析,无法支持实时营销。
  2. 运维研发成本高昂: 分库分表方案将大量的数据路由和运维复杂性抛给了研发和运维团队。作为甲方企业,维护一套复杂的中间件逻辑,投入产出比极低。
  3. 扩展性受限: 企业的扩张往往通过并购进行,数据增长是非线性的。每并购一家连锁药店(可能涉及千万级会员),就需要重新规划分片,传统架构难以应对这种垂直式的数据激增。

三、 选型决策:拥抱分布式与云原生 (平凯数据库 + K8s)

基于 CAP 理论和业务需求,漱玉平民决定引入分布式数据库以降低运维复杂度,并最终锁定了平凯数据库,结合 Kubernetes (K8s) 构建混合云架构。

核心选型逻辑:

  • 水平扩展能力: 必须具备弹性伸缩能力,以应对“5·18”大促等活动期间高达 7,000-8,000 万 的日销售额峰值(平时约 2,000 万)。
  • MySQL 高兼容性: 降低迁移成本。实战中,核心 CRM 系统仅用不到 1 小时便完成了从 MySQL 到 TiDB 的切换,且无需回滚。
  • HTAP 混合负载: 同时满足交易(TP)的高并发写入和分析(AP)的实时查询需求。

四、 核心场景实践与收益

亿级会员 CRM:从 T+1 到“实时精准营销”

CRM 系统是本次重构的重中之重。通过平凯数据库的列存分析能力(TiFlash),漱玉平民实现了真正的实时标签检索。

  • 场景: 业务人员需要拼接几千个条件,筛选出“即将流失”、“慢病需复购”或“高价值”会员。
  • 改变: 以前只能依赖 T-1 的离线数据;现在可以秒级响应,实时触发优惠券推送、用药提醒和复购激励,极大地激活了存量会员价值。

药店数字助理(PDA):赋能一线店员

PDA 是店员工作的统一入口,承载了进销存、收货强监管(电子签章)、业绩查询等核心功能。

  • 技术支撑: 基于 K8s 的微服务架构与平凯数据库的结合,保证了应用的高可用。
  • 业务价值: 在应对医保双面账、药品监管码全链路追踪等复杂业务逻辑时,系统保持了极高的稳定性,支撑了日均万级的 QPS。

运维体系:AI 赋能与全链路追踪

研发团队并不止步于数据库的替换,还构建了完善的运维监控体系:

  • 可观测性: 利用 Dashboard 和全链路追踪(Trace ID),将数据库视为“白盒”,精准定位慢 SQL 和 API 响应延迟。
  • AIOps 探索: 引入 AI 运维机器人,基于指标预测潜在故障;在开发阶段引入 CodeReview AI 工具,提前规避性能低下的 SQL 代码上线。

五、 未来展望

目前,漱玉平民已将 CRM、数据资源中心等核心业务平滑迁移至平凯数据库,并构建了基于 K8s 的混合云底座。

张新新表示,未来的技术规划将聚焦于三个方向:

  1. 深化智能化营销 (MA):医药零售具有极强的季节性和突发性(如流感季)。未来,漱玉平民计划进一步挖掘平凯数据库的实时分析能力,结合 AI 算法,从“看库存”向“预测库存”进阶。系统将能够根据区域销售趋势,自动进行智能补货和调拨,在降低周转天数的同时,确保急需药品的现货率。 这套更智能的一体化营销系统,基于 K8s 和平凯数据库进一步隔离资源,确保高并发下的营销计算不影响核心交易。
  2. 扩大分布式应用范围: 将更多历史遗留系统逐步迁移至平凯数据库,统一技术栈,降低维护成本。
  3. 强化业务连续性 (BCP): 持续优化双机房集群架构,确保在单机房故障时核心业务不受影响,守护每一笔交易。

对于零售企业而言,技术的价值在于业务连续性和数据准确性。平凯数据库+ K8s 的组合,不仅解决了海量数据的存储与计算瓶颈,更重要的是,它将研发团队从繁琐的分库分表维护中解放出来,更专注于业务创新和客户价值的实现。


早上突然收到短信说中签,有点不敢相信,之前就听说现在中签率挺低的。
重点是我刚开通可转债权限,第一次打新债,群友都说我是天选之子哈哈哈!

付一个可转债权限开通条件:
1.账户要求:需开立正常的证券账户并完成风险测评(等级为 C2 及以上)。
2.资产门槛:申请开通权限前 20 个交易日日均资产不低于 10 万元。
3.交易经验:参与证券交易满 2 年以上。

第 2 点我是在 2 月初买了 10 万块 28 天期的逆回购,前几天到期就可以开通可转债交易权限了。

全球数字化浪潮愈演愈烈,大有席卷全球之势。对此,为网站部署数字证书,启用HTTPS安全加密技术,依然成为企业建立线上安全营销环境与信任体系的基础动作,其醒目的安全锁标识与https前缀,是网站安全的象征,也是获取用户信任的基石。然而,拥有证书并非完全等同于拥有相应的安全防护。从SSL证书部署配置,到日常管理,任何一个环节出现纰漏,都可能导致证书的安全加密形同虚设,成为网络黑客攻击的突破口。JoySSL市场调研专家指出,综合目前国内绝大多数网络安全事故发生的诱因来看,绝大多数事件的根源并非源于数字证书的加密技术,而是企业在部署和配置环节存在严重失误,导致未能正确利用SSL证书加密功能,从而给网络威胁提供了有利条件。唯有正确、规范的配置管理数字证书,才能彻底发挥其作为网络安全防护产品应有的作用,将潜在的网络风险转化为信任资产。

弱加密与私钥管理不当 为攻击者创造便利条件

证书部署后若采用默认TLS配置,会导致仍支持存在漏洞的旧版协议,加密连接可被强制降级,从而信息被轻松窃取与解密。私钥管理不当导致泄露,会被攻击者利用,解密来往通信数据,伪造服务器身份进行中间人攻击,危害将持续至证书到期。

启用TLS1.2以上版本,选择支持前向保密的强加密套件,是确保信息加密的技术前提,是封堵漏洞的关键;而采用硬件安全模块等方式正确管理私钥,可确保核心资产不受影响。

证书过期阻断业务 内容混合让安全锁形同虚设

一旦SSL证书因续费不及时,或缺乏有效监测机制导致过期,会直接中断所有安全服务,导致排名下降,用户信任崩塌,运维压力与成本上升,品牌信誉受损。混合内容加载则进一步降低安全评级,警告用户甚至直接拦截,HTTP加载的资源也可能遭到中间人随意篡改。

自动化证书监控与续期机制,对证书过期危机至关重要,可有效杜绝人为遗忘引发的一系列严重后果,保障业务与服务的连贯性;而实现全站HTTPS则可有效修复混合内容问题,构建纯净的加密环境。

配置SSL证书出现匹配与完整问题 引发信任危机

证书与域名不匹配,会导致浏览器弹出证书错误警示,使用户访问受阻,导致潜在客户对网站的专业性产生质疑。同时,在部署SSL证书时一旦不能完整拼接,用户将无法正常访问网站,潜在客户极速流失。

正确选择数字证书类型,可有效匹配域名,避免因覆盖不完整导致信任链条中断;而严格按照服务器类型配置证书链,可确保证书在所有客户端都能被正确验证,维系客户信任。

正确管理与配置数字证书 高效发挥安全锁功用

SSL证书如同一把精密的安全门锁,仅仅拥有它,并不能体现安全价值。唯有通过正确的安装,合理的配置与持续的监控维护,才能让门锁的功能得到全面发挥,创造便利的同时也享受安全保障。JoySSL市场经理认为,投资数字证书不应局限于购买行为,更需要覆盖至证书整个生命周期内,通过规范化管理,充分展现SSL证书的安全能力,让每一道加密都坚不可摧,让每一次访问都始于信任。

“市值几千亿美元的公司,竟然直接来蹭我的代码,真不要脸。”

 

近日,美团旗下的光年之外团队宣布,其全新产品 Tabbit AI 浏览器已进入公测。据介绍,Tabbit 的核心突破在于通过“智能代理”“妙招”“脚本”等自动化执行能力,在浏览器上实现了“人机并行”的高效协作。为了让 AI 能力无缝融入工作流,光年之外团队还对 Tabbit 浏览器的核心交互组件进行了全面重构。

 

昨日凌晨,独立开发者 @梦溪睡了吗突然在 X 上喊话称:“美团这么大一家公司,居然把我的代码抄去做他们自家的 AI 浏览器,而且连赞助都不给我?”

 

据悉,该开发者做了一款 AI 驱动的浏览器语言学习扩展插件,支持沉浸式翻译、文章解析、多 AI 模型切换等功能,名为陪读蛙,并且在 Github 上开源已久。从代码提交记录来看,该项目最早的代码提交可追溯至 9 个月前。截止目前,该项目已获了 3.9k stars。

 

陪读蛙开源项目链接:https://github.com/mengxi-ream/read-frog

这位开发者放出了多张 Tabbit AI 浏览器和陪读蛙的产品对比图,两者的界面和布局极为相似,图标文件名也完全没改,“居然还是 read-frog”。并且其称,“我的代码中有充分的证据链。”虽然目前还未放出,但该开发者表示“稍后会做个视频”。

有网友说,“开源不就是为了让别人复制吗?”该开发者则表示,根据其用的 GPL 开源协议,任何使用其代码的产品都必须开源。GPL 协议的全称是 GNU General Public License,即 GNU 通用公共许可协议,目的是强制代码开源和免费使用。虽然该协议给予了终端用户运行、学习、共享和修改软件的自由,但其最大的特点是“开源的传染性”。也就是说,假设某公司使用了具有 GPL 协议的代码库,那么理论上也必须把自己的代码库开源。

 

因此,他还隔空喊话道,“所以美团,你们这款新 AI 产品开源了吗?”值得一提的是,这位独立开发者的个人主页信息显示,其曾担任字节跳动高级软件工程师。

 

昨天下午,Tabbit 官方在小红书平台发布声明称,第一时间对项目的开源和合规情况进行了深度自查,2025 年 12 月 30 日团队在开发翻译功能时关注到该项目仓库中并未包含任何开源协议声明,后经评估进行了项目 fork,项目原作者是在 1 月 2 日为其添加了 GPLv3 协议,并称“由于未继续合并原项目的后续代码,未能及时关注到此次协议变更”。当时,这名开发者回应道:“建议您先删除这份声明,和我沟通完毕之后再发。不然我可能要给您在小红书科普为什么您这个说法站不住脚了。”

 

随后,Tabbit 官方再次发布声明称,他们将从 Tabbit 浏览器新版中移除此翻译项目代码,并已将此项目完整开源;已跟 read frog 的作者沟通项目 License 的正式授权,授权后再更新代码并恢复此功能。这次该开发者表示,“对方态度积极,处理及时,可以确认本次问题并非出于主观恶意,而是对开源协议理解和合规流程不够严谨所致。”

本文围绕 Jira 替代软件 的核心诉求,测评对比 ONES、Tower、Azure DevOps、GitLab、YouTrack、GitHub Projects、Linear、monday dev,从流程、进度、协作、效能、开放拓展、端到端闭环、知识沉淀与质量测试治理等维度给出可执行的选型建议,帮助管理者降低替换风险、提升落地成功率。

在我参与过的替换项目里,真正推动组织寻找 Jira 替代软件 的,往往是三类结构性变化:

  • 规模变化:团队数量上升、产品线增多、外包/合作团队加入后,“靠自觉更新状态”的方式会迅速失效。
  • 复杂度变化:从单一 Scrum 团队走向“多团队并行 + 平台/业务耦合”,依赖与资源冲突成为常态,靠看板很难提前预警。
  • 治理诉求变化:合规审计、质量体系、交付可靠性要求提升,组织需要“证据链”和“追溯链”,而不是“看起来很忙的状态流转”。

选型框架:评估 Jira 替代软件的 8 个维度

我建议把评估框架拆成三层:先看约束,再看闭环,后看体验。这样更接近管理决策逻辑,也更利于 PMO 组织评审会落地。

1. 三层选型法:先约束、再闭环、后体验

  • 第一层:组织约束(先排除):部署形态(SaaS/私有化)、数据安全与审计、账号体系、权限模型、采购合规。这是“能不能用”的底线。
  • 第二层:端到端闭环(决定上限):需求→开发→测试→发布→反馈能否形成追溯链?如果交付可靠性是竞争力,这一层权重大于“界面是否好看”。
  • 第三层:协作体验与效率(决定落地速度):易用性、通知、跨职能协作、自动化与开放接口,决定团队“愿不愿意用、能不能坚持用”。

结论:治理诉求越强(合规/质量/审计),越要优先选择“闭环与治理能力强”的 Jira 替代软件;团队越小越敏捷,越要优先选择“体验与效率强”的替代工具。

2. 八个维度:让“评估可复制”,避免平均用力

  1. 工作项模型与流程:Issue/需求/缺陷/任务层级是否清晰?工作流可配置且可治理?
  2. 规划与路线图:是否支持 Epic/Feature/里程碑、跨项目视图、依赖与时间线(roadmap/甘特)?
  3. 进度与交付治理:冲刺、燃尽、WIP、关键路径、风险与资源冲突能否提前暴露?
  4. 协作体验:评论、@、通知、跨部门协作、移动端与沟通工具集成是否顺畅?
  5. 端到端闭环:需求→代码→测试→发布→反馈的追溯链是否可建立?
  6. 质量与测试治理:测试计划/用例/执行/缺陷/回归/覆盖的治理能力如何?
  7. 效能度量与改进:是否支持稳定口径的仪表盘、趋势、分层改进闭环?
  8. 开放拓展与合规:API/Webhook、生态集成、权限审计、数据安全与部署弹性。

3. Jira 常见能力映射:替代时你到底在替代什么

为了更贴近检索意图,这里把 Jira 常见能力拆成“可被替代的能力块”:

  • Issue/工作项管理:任务、缺陷、需求、子任务层级
  • Workflow/流程与权限:状态流、审批、字段口径、权限边界
  • Agile/敏捷节奏:Backlog、Sprint、看板、燃尽、WIP
  • Roadmap/跨团队计划:里程碑、依赖、时间线、组合视图
  • Reporting/度量:报表、仪表盘、趋势与口径稳定性
  • Integration/开放生态:代码仓库、CI/CD、IM、知识库、工单

结论:如果你的 Jira 主要被当成“协作推进中心”,替代重点在体验与跨职能;如果 Jira 被当成“研发治理中心”,替代重点在闭环、测试治理与口径稳定。

本章要点

  • 选型优先级:约束(能用)→闭环(上限)→体验(落地速度)。
  • 用“Jira 能力块映射”做对比,评审会更容易达成共识。
  • 真正的替代不是 UI 相似,而是 治理能力与追溯链能力 能否承接。

工具速览:8 款 Jira 替代软件的定位与替代强度

这里的“替代强度”不是绝对好坏,而是对 Jira 能力块(流程、计划、协作、治理、闭环)的覆盖程度,以及对组织治理的支撑上限。

本章要点

  • ONES / Azure DevOps / GitLab 更偏“治理与闭环型”Jira 替代软件。
  • Tower / monday dev 更偏“协作推进与跨职能对齐型”。
  • Linear / GitHub Projects 更偏“轻量高效率、贴近研发日常型”。
  • YouTrack 介于两者之间,优势在工作流弹性与工程团队适配。

四、分层深评:8 款 Jira 替代软件测评对比

1)ONES:组织级研发治理底座的 Jira 替代软件

很多组织在替换 Jira 时,会忽略一个事实:Jira 解决的是“记录与流转”,而组织需要的往往是“治理与闭环”。ONES 的价值更接近后者——它更像一个可承接组织方法论、支持规模化治理的研发管理底座。

ONES 核心信息

定位:企业级端到端研发管理平台(研发项目管理 + 质量测试治理 + 知识沉淀 + 度量改进)

Jira 替代能力映射:

  • Issue/工作项:可承接(需求/任务/缺陷层级)
  • Workflow/权限:可承接(流程、字段口径、权限边界)
  • Agile/敏捷:可承接(Backlog、迭代节奏、看板/燃尽)
  • Roadmap/组合:偏强(多项目视角、里程碑与治理)
  • Reporting/度量:偏强(口径治理与持续改进导向)
  • Integration/生态:可承接(开放拓展与平台化)

适用场景:多团队并行、跨项目依赖明显、希望端到端闭环与统一治理的组织

一句话结论:把 Jira 从“项目工具”升级为“组织能力底座”的团队,通常更容易发挥 ONES 的价值。

优势亮点:

ONES 的优势在于更容易把“端到端闭环”设计成一个系统工程:

  • 知识沉淀:需求背景、决策记录、技术方案、复盘结论与工作项关联,降低新人上手成本与重复决策。
  • 质量测试治理:测试活动与需求、缺陷、版本形成追溯链,质量不再只是测试团队责任,而是组织交付约束。
  • 效能改进:当你拥有稳定口径的数据,改进就不会变成观点之争,而能回到事实与趋势。

这正是我判断某个工具是否是合格的 Jira 替代软件 的分水岭:它能否支撑组织把“流程—交付—质量—度量”连成闭环,而不是做成局部最优。

局限与使用体验

  • 前期设计成本不可回避:越强调闭环与治理,越需要把流程与口径讲清楚,否则会出现“工具很强,但用法各异”的二次混乱。
  • 需要组织配套机制:例行评审、里程碑检查、质量门禁、复盘机制。没有这些,任何平台最终都会退化为“更贵的任务列表”。

2)Tower:更轻量的 Jira 替代软件

Tower 的典型优势是:让“计划—执行—推进—对齐”变得更轻、更直观。很多组织实际把 Jira 用成跨职能协作中枢,而不是纯研发工具;此时 Tower 往往能更快提升体验与协作效率。

Tower 核心信息

定位:团队协作与项目推进工具(多视图进度管理)

Jira 替代能力映射:

  • Issue/工作项:可承接(任务协作为主)
  • Workflow/权限:部分承接(适度流程)
  • Agile/敏捷:部分承接(节奏管理可做)
  • Roadmap/组合:中等(更偏推进视图)
  • Reporting/度量:中等(更多依赖机制)
  • Integration/生态:需按场景评估

适用场景:协作密集、跨部门推进频繁、希望降低工具学习与维护成本

一句话结论:更像“推进中枢”的 Jira 替代方案,而不是“研发治理中枢”。

优势亮点:

替换 Jira 往往不是技术难,而是变更管理难。工具如果让成员感觉“更复杂、更痛苦”,迁移就会失败。Tower 的优势在于上手快、推广快、跨职能参与门槛低——这对很多组织是关键胜负手。

局限与使用体验:

  • 数据一致性更依赖团队纪律:需要例行机制(周计划/里程碑检查/风险登记)。
  • 工程闭环与质量治理要配套:否则会出现“推进很顺,但质量与证据链缺失”。

3)Azure DevOps:工程治理型 Jira 替代软件

如果你的组织对工程化治理要求高——例如标准化流程、审计追溯、测试体系、发布证据链——Azure DevOps 往往更贴近“体系化替代”。

Azure DevOps 核心信息

定位:DevOps 套件(计划/开发/测试/交付协同)

Jira 替代能力映射:

  • Issue/工作项:强
  • Workflow/权限:强
  • Agile/敏捷:强
  • Roadmap/组合:中强(取决于组织用法)
  • Reporting/度量:中强(需要口径治理)
  • Integration/生态:偏强(尤其微软生态)

适用场景:微软技术栈、中大型研发组织、质量体系要求高

一句话结论:工程治理型 Jira 替代软件,适合“要证据链、要可审计”的组织。

局限与使用体验:

  • 需要明确平台与流程负责人,否则容易出现“功能很多但团队只用浅层”。
  • 对非研发角色不一定最友好,需要模板化与培训支持。

4)GitLab:计划与交付型 Jira 替代软件

GitLab 的典型优势是把计划、代码、流水线、交付与度量拉到同一平台,使“状态更新”更可能从手工变成自动联动。对追求交付效率与可靠性的组织,这种整合价值往往高于“单点功能更强”。

GitLab 核心信息

定位:一体化 DevSecOps 平台(计划到交付同平台)

Jira 替代能力映射:

  • Issue/工作项:强
  • Workflow/权限:中强(看组织需求)
  • Agile/敏捷:中强
  • Roadmap/组合:强
  • Reporting/度量:中强(需治理)
  • Integration/生态:强(平台内整合优势明显)

适用场景:希望减少系统割裂、强调从需求到发布追溯的组织

一句话结论:适合把“计划—开发—交付”做强整合的 Jira 替代方案。

局限与使用体验:

  • 更偏工程团队主导,产品与业务参与时可能需要更友好的协作入口与模板化流程。
  • 需要明确边界:哪些工作留在 GitLab,哪些留在知识/产品系统,避免“所有东西都塞进一个平台”。

5)YouTrack:流程与工作流自动化 Jira 替代软件

YouTrack 的特点在于:既支持 Scrum、Kanban 与混合方法,也强调工作流自动化与可定制性。对流程有明确“组织个性”的团队,这是一个折中选择。

YouTrack 核心信息

定位:Issue 跟踪 + 敏捷面板 + 工作流自动化(工程团队适配)

Jira 替代能力映射:

  • Issue/工作项:中强
  • Workflow/权限:中强(弹性大)
  • Agile/敏捷:强
  • Roadmap/组合:中等(看治理与配置)
  • Reporting/度量:中等(更依赖口径)
  • Integration/生态:中等(按场景)

适用场景:工程团队主导、需要灵活流程、希望“流程可编排”

一句话结论:灵活是优势,也是治理挑战;成功高度依赖 Owner 机制。

6)GitHub Projects:开发者适用的 Jira 替代软件之一

对以 GitHub 为研发中心的团队,GitHub Projects 的优势非常现实:减少工具切换,计划与执行靠近代码与 PR。

GitHub Projects 核心信息

定位:围绕 GitHub Issues/PR 的轻量计划与跟踪

Jira 替代能力映射:

  • Issue/工作项:中强(围绕 Issues)
  • Workflow/权限:中等(偏轻)
  • Agile/敏捷:中等(够用但不重)
  • Roadmap/组合:中等
  • Reporting/度量:偏弱(需外部补齐)
  • Integration/生态:强(在 GitHub 生态内)

适用场景:中小团队、开源/内源、研发协作为中心

一句话结论:替代“研发任务协同”容易,替代“组织治理”较难。

7)Linear:高节奏团队适用的 Jira 替代软件

Linear 的价值在于做减法:减少配置噪音,让注意力回到交付与节奏上。对迭代快、团队小而精的组织,常有明显体感提升。

Linear 核心信息

定位:现代产品研发协作系统(轻量高效、节奏清晰)

Jira 替代能力映射:

  • Issue/工作项:中强
  • Workflow/权限:中等(偏简)
  • Agile/敏捷:强(节奏优势)
  • Roadmap/组合:中等(看规模)
  • Reporting/度量:中等(需治理)
  • Integration/生态:中强(自动化友好)

适用场景:中小团队、高迭代节奏、追求效率与低负担

一句话结论:适合“速度优先”的团队;强治理诉求组织需谨慎评估边界。

8)monday dev:跨职能可视化 Jira 替代软件

monday dev 的优势是“让不同角色都看得懂”。当产品、设计、运营、交付等角色参与度高时,沟通成本往往成为主要瓶颈。

monday dev 核心信息

定位:面向产品开发的协作与节奏管理套件(跨职能可视化强)

Jira 替代能力映射:

  • Issue/工作项:中
  • Workflow/权限:中等(需评估复杂度)
  • Agile/敏捷:中强(节奏与可视化)
  • Roadmap/组合:中等(偏对齐)
  • Reporting/度量:中等
  • Integration/生态:中等(按场景)

适用场景:跨职能协作密集、希望提升计划透明与沟通效率

一句话结论:更像“对齐与推进型”Jira 替代方案,工程治理深度需验证。

趋势预测:从“换 Jira”到“建设组织数字化能力”

未来两年我更确认的方向是:研发管理工具正在从“记工系统”走向“治理系统”。组织要的不是把工作记录下来,而是把“怎么做、做到什么程度、如何持续改进”沉淀为稳定能力。

替换 Jira 的项目,如果只完成“数据迁移 + 界面切换”,最终一定会回到老问题:流程各自为政、口径漂移、协作依旧靠人盯人。真正能把替换做成能力建设的,是你是否同步完成三件事(这也是最容易被忽略的“成功条件”):

  • 明确最小标准:统一核心对象与口径(需求、缺陷、版本、验收标准、关键状态)。
  • 建立角色分工:流程 Owner(定义做法)、数据 Owner(定义口径)、平台 Owner(配置与集成)。
  • 让机制驱动数据:例会、检查点、门禁与自动化,把更新状态变成流程的一部分,而不是额外负担。

我建议的迁移路线图通常是:小范围试点(跑通闭环)→ 样板复制(固化模板与口径)→ 数据度量(建立可信指标)→ 持续改进(用数据对话)。这样,你得到的不只是一个 Jira 替代软件,而是一套可复制的协作与治理能力。

结尾总结

选择 Jira 替代软件,本质上是在选择一种“组织协作操作系统”。轻量工具能快速降低沟通成本、提升推进效率,但上限取决于团队自律;工程型平台能建立更强的闭环与治理,但前提是组织愿意投入流程与口径建设。更稳妥的做法是:先用框架讲清楚目标能力,再用试点验证真实体验与迁移成本——把替换 Jira 从一次工具项目,升级为一次组织数字化能力建设。