一加和 OPPO 的 ColorOS 会有微信消息延迟的问题吗?怎么才能解决这个问题?
别的程序也容易出现后台被杀,或权限被杀。比如 Tasker 的访问通知权限和无障碍权限经常隔一两天就没了,虽然在设置中看到这些权限的状态依然是开启的,但实际权限已经没了,需要重新开启才行。
能设置的都设置了:
1 、已完全允许后台
2 、已允许微信自启
3 、关闭了睡眠待机优化
4 、已在任务视图锁定微信
xiaohack博客专注前沿科技动态与实用技术干货分享,涵盖 AI 代理、大模型应用、编程工具、文档解析、SEO 实战、自动化部署等内容,提供开源项目教程、科技资讯日报、工具使用指南,助力开发者、AI 爱好者获取前沿技术与实战经验。
前段时间在某大厂做安全研究时,针对SDLC的重复性审计工作结合大模型Agent思索了一些可行的思路,便在不断摸索中构建了一个Multi-Agent的协同漏洞挖掘框架系统,目前个人使用来看对于开源的web应用的实战效果相比传统的SAST、DAST以及纯LLM的漏洞挖掘工具来说还是很不错的,便记录此篇框架实现过程和当今Agent赋能漏挖的可行性与优势供师傅们交流指点....
当前针对Web应用后端的自动化漏洞挖掘技术主要受困于“覆盖率”与“准确性”难以两全的矛盾:
在探索新的框架实现时,我们可以思考是否能将黑白盒的现有技术互补结合来引导漏洞挖掘?以及我们可以看到几年LLM与Agent相关技术如MCP、RAG的工程化落地,能否用LLM赋于框架更好的语义理解和丰富的上下文能力,再通过Agent做一套自动化流程?
为突破上述技术瓶颈,我在探索新的漏洞挖掘框架时也看了一些目前学术界的相关LLM赋能的研究与github开源的技术实现,总体的探索方法还是在论文与现实实践中思考各个方面的优势与缺陷,最终确定做一个基于Muti-Agent协同的智能化漏洞挖掘框架:构建一个从静态分析到动态验证的闭环生态。技术上引入MCP 来作为连接LLM推理能力与静态分析工具的桥梁,利用RAG 技术通过构建高质量漏洞专家知识库来校准模型判定,深度缓解LLM的“幻觉”与知识盲区;同时,结合运行时自动化的流量Fuzz模糊测试技术,将白盒的逻辑推演与黑盒的攻击验证深度融合,减少漏洞的误报和漏报。
这里放一个当时挖到的有CNVD证书的水洞,通过项目上传与聊天,自动化分析审计出多处SQL注入漏洞,并且能够给出攻击POC,以及后续完整的修复方案

该框架核心架构旨在重构传统安全检测的边界,提出了一种 “白盒语义指引黑盒,黑盒动态验证白盒”的深度融合范式。框架并非单一工具的线性叠加,而是一个基于Multi-Agent编排(Agent Orchestration)的异构系统。
我认为将静态视角的逻辑推演与动态视角的攻击验证相结合的机制,能极大地提升了漏洞检测的置信度,实现了真正意义上的全链路攻防评估,刚开始时候画的大致架构草图,仅贴示了主要功能,一些细节实现并未展示:

在传统的LLM应用中,模型往往被禁锢在文本交互的孤岛中,难以触及本地庞大的代码仓库,且面临着Context Window对海量代码理解的限制。本框架设计的漏洞定位Agent,本质上是一个 静态分析增强型智能体(Static Orchestration Agen) ,通过引入MCP与构建Prompt定义角色任务将LLM从被动的文本生成者转变为主动的工具使用者,通过静态分析获取代码结构中的丰富语义上下文
不同于简单的API调用,MCP协议使得Agent能够理解工具的输入输出Schema,实现复杂的推理链条:
针对LLM处理大规模代码时的“大海捞针”难题,高效定位漏洞利用链

作为本框架保障检测精度的核心组件,校验 Contextual Reasoning Agent承担着“校验”的角色。针对通用大语言模型在特定安全领域存在的专业知识匮乏与逻辑幻觉 问题,本模块引入RAG 技术,人为构建了一个可随时扩展的领域专家知识文档库,通过实时注入精确的先验知识来约束和校准模型的推理过程。
为了让非结构化的安全知识能够被机器高效理解,摒弃粗暴的文本截断,采用基于Markdown语法树的结构化清洗策略。系统依据标题层级对海量的漏洞PoC、修复方案及原理分析文档进行逻辑切分,确保每个Chunk都包含完整的语义单元
例如一个简易的MARKDOWN文档:

在知识切片过程中,为了规避硬切分导致的语义断层,切片策略采用基于重叠策略(Overlapping Strategy)的动态滑窗机制:

采用all-MiniLM-L6-v2模型作为Embedding引擎。该模型在保持低延迟推理的同时,在多语言的语义相似度任务上有更好的泛化能力;数据库采用集成Qdrant向量数据库,支撑大规模向量的高并发检索
我从以往的安全研究触发,针对通用型漏洞的工具做了大量的调研,并基于BurpSuite原生API开发了自动化Fuzz工具如:反射性和存储型XSS、SSRF、CORS、敏感信息泄露等(同时也是在锻炼开发能力,也让日常重复性漏洞渗透工作能够做的更高效),再结合MCP集成给Agent。该模块并非简单的随机测试,而是作为一个流式检测组件,实时拦截、解析并重放业务流量,对潜在漏洞动态扫描。而对于敏感信息泄露则是比较容易 ,针对Spring Boot Actuator、Swagger UI、Druid Monitor等常见中间件的指纹来做识别。同时,结合模式匹配,对响应包中的JWT Token、阿里云AK/SK、AWS凭证等高熵字符串进行实时监测,有效发现硬编码或调试信息泄露。
下面挑了几个通用型漏洞的Fuzz来做简单做下原理解释
比如针对XSS反射型和存储型漏洞,开发时采用了全量参数解析+动态污点标记的检测策略,确保对异构http包结构中参数的全面覆盖。
唯一性污点标记:
为了解决并发扫描时的结果混淆问题,引擎摒弃了静态Payload,转而采用动态生成的唯一性测试标记。
(当时研究设计思路时绘制的草图)

自动化Fuzz HTTP请求头中的Origin字段,构造包括恶意第三方域名、特殊字符(如null)及子域名在内的多种变异Payload

在对Multi-Agent探索自动化漏洞挖掘实践的探索过程中,其实我们一直在试图回答一个核心问题:如何在安全攻防领域,构建一个具备“感知-推理-决策-行动”完整闭环的智能系统。目前的Agent主要还停留在“检测与验证”阶段,之后更完备的阶段是自动化环境的感知探索与白盒源码的结合,以及能够基于当前的Shell环境或数据库权限,自主规划后续的横向移动与权限提升路径。另一个重要的方面是自适应Payload生成:比如利用强化学习反馈机制,让Agent在面对WAF拦截时,能够动态调整Payload的混淆策略,实现智能化的WAF绕过
希望本文的实践能为各位师傅提供一种新的视角供师傅们交流指点~
struct类型的定义以关键字struct开头,后跟struct的名字,接着是定义在一对花括号中的struct定义体。struct定义体中可以定义一系列的成员变量、成员属性、静态初始化器、构造函数和成员函数。 以下是定义struct类型的一个示例: 上例中定义了名为Rectangle的struct类型,它有两个Int64类型的成员变量width和height,一个有两个Int64类型参数的构造函数init,以及一个成员函数area,用于返回width和height的乘积。 struct成员变量分为实例成员变量和静态成员变量(使用static修饰符修饰,且必须有初值),二者访问上的区别在于实例成员变量只能通过struct实例访问,静态成员变量只能通过struct类型名访问。 实例成员变量定义时可以不设置初值(但必须标注类型),如上例中的width和height。也可以设置初值,例如: struct支持定义静态初始化器,并在静态初始化器中通过赋值表达式来对静态成员变量进行初始化。 静态初始化器以关键字组合static init开头,后跟无参参数列表和函数体,且不能被访问修饰符修饰。函数体中必须完成对所有未初始化的静态成员变量的初始化,否则编译报错。 一个struct中最多允许定义一个静态初始化器,否则报重定义错误。 struct支持两类构造函数:普通构造函数和主构造函数。 普通构造函数以关键字init开头,后跟参数列表和函数体,函数体中必须完成对所有未初始化的实例成员变量的初始化,否则编译报错。 一个struct中可以定义多个普通构造函数,但它们必须构成重载,否则报重定义错误。 除了可以定义若干普通的以init为名字的构造函数外,struct内还可以定义(最多)一个主构造函数。主构造函数的名字和struct类型名相同,它的参数列表中可以有两种形式的形参:普通形参和成员变量形参(需要在参数名前加上let或var),成员变量形参同时扮演定义成员变量和构造函数参数的功能。 使用主构造函数通常可以简化struct的定义,例如,上述包含一个init构造函数的Rectangle可以简化为如下定义: 主构造函数的参数列表中也可以定义普通形参,例如: 如果struct定义中不存在自定义构造函数(包括主构造函数),并且所有实例成员变量都有初始值,则会自动为其生成一个无参构造函数(调用此无参构造函数会创建一个所有实例成员变量的值均等于其初值的对象);否则,不会自动生成此无参构造函数。例如,对于如下struct定义,注释中给出了自动生成的无参构造函数: struct成员函数分为实例成员函数和静态成员函数(使用static修饰符修饰),二者的区别在于:实例成员函数只能通过struct实例访问,静态成员函数只能通过struct类型名访问;静态成员函数中不能访问实例成员变量,也不能调用实例成员函数,但在实例成员函数中可以访问静态成员变量以及静态成员函数。 下例中,area是实例成员函数,typeName是静态成员函数。 实例成员函数中可以通过this访问实例成员变量,例如: struct的成员,包括成员变量、成员属性、构造函数、成员函数、操作符函数,可以用4种访问修饰符修饰:private、internal、protected和public,缺省的修饰符是internal。 下面的例子中,width是public修饰的成员,在类外可以访问,height是缺省访问修饰符的成员,仅在当前包及子包可见,其他包无法访问。 递归和互递归定义的struct均是非法的。例如: 定义了struct类型后,即可通过调用struct的构造函数来创建struct实例。在struct定义之外,通过struct类型名调用构造函数。例如,下例中定义了一个Rectangle类型的变量r。 创建了struct实例之后,可以通过实例访问它的(public修饰的)实例成员变量和实例成员函数。例如,下例中通过r.width和r.height可分别访问r中width和height的值,通过r.area()可以调用r的成员函数area。 如果希望通过struct实例去修改成员变量的值,需要将struct类型的变量定义为可变变量,并且被修改的成员变量也必须是可变成员变量(使用var定义)。举例如下: 在赋值或传参时,会对struct实例进行复制,生成新的实例,对其中一个实例的修改并不会影响另外一个实例。以赋值为例,下面的例子中,将r1赋值给r2之后,修改r1的width和height的值,并不会影响r2的width和height值。 struct类型是值类型,其实例成员函数无法修改实例本身。例如,下例中,成员函数g中不能修改成员变量i的值。 mut函数是一种可以修改struct实例本身的特殊的实例成员函数。在mut函数内部,this的语义是特殊的,这种this拥有原地修改字段的能力。 注:只允许在interface、struct和struct的扩展内定义mut函数,禁止在class中定义mut函数。 mut函数与普通的实例成员函数相比,多一个mut关键字来修饰。 例如,下例中在函数g之前增加mut修饰符之后,即可在函数体内修改成员变量i的值。定义struct类型
struct Rectangle {
let width: Int64
let height: Int64
public init(width: Int64, height: Int64) {
this.width = width
this.height = height
}
public func area() {
width * height
}
}1. struct成员变量
struct Rectangle {
let width = 10
let height = 20
}2. struct静态初始化器
struct Rectangle {
static let degree: Int64
static init() {
degree = 180
}
}struct Rectangle {
static let degree: Int64
static init() {
degree = 180
}
static init() { // 错误!用前面的静态init函数重新定义
degree = 180
}
}3. struct构造函数
struct Rectangle {
let width: Int64
let height: Int64
public init(width: Int64, height: Int64) { // 错误! 'height'未在构造函数中初始化
this.width = width
}
}struct Rectangle {
let width: Int64
let height: Int64
public init(width: Int64) {
this.width = width
this.height = width
}
public init(width: Int64, height: Int64) { // 正确!用第一个init函数重载
this.width = width
this.height = height
}
public init(height: Int64) { // 错误!使用第一个init函数重新定义
this.width = height
this.height = height
}
}struct Rectangle {
public Rectangle(let width: Int64, let height: Int64) {}
}struct Rectangle {
public Rectangle(name: String, let width: Int64, let height: Int64) {}
}struct Rectangle {
let width: Int64 = 10
let height: Int64 = 10
/* Auto-generated memberwise constructor:
public init() {
}
*/
}4. struct成员函数
struct Rectangle {
let width: Int64 = 10
let height: Int64 = 20
public func area() {
this.width * this.height
}
public static func typeName(): String {
"Rectangle"
}
}struct Rectangle {
let width: Int64 = 1
let height: Int64 = 1
public func area() {
this.width * this.height
}
}5. struct成员的访问修饰符
package a
publicstructRectangle {
public var width: Int64
var height: Int64
private var area: Int64
...
}
func samePkgFunc() {
var r = Rectangle(10, 20)
r.width = 8 // Ok: public 'width' can be accessed here
r.height = 24 // Ok: 'height' has no modifier and can be accessed here
r.area = 30 // 错误!, private 'area' can't be accessed here
}
package b
import a.*
main() {
var r = Rectangle(10, 20)
r.width = 8 // Ok: public 'width' can be accessed here
r.height = 24 // 错误!, no modifier 'height' can't be accessed here
r.area = 30 // 错误!, private 'area' can't be accessed here
}6. 禁止递归struct
struct R1 { // 错误!'R1' 递归引用自身
let other: R1
}
struct R2 { // 错误!'R2' 和 'R3' 递归引用自身
let other: R3
}
struct R3 { // 错误!'R2' 和 'R3' 递归引用自身
let other: R2
}
创建struct实例
let r = Rectangle(10, 20)let r = Rectangle(10, 20)
let width = r.width // width = 10
let height = r.height // height = 20
let a = r.area() // a = 200struct Rectangle {
public var width: Int64
public var height: Int64
public init(width: Int64, height: Int64) {
this.width = width
this.height = height
}
public func area() {
width * height
}
}
main() {
var r = Rectangle(10, 20) // r.width = 10, r.height = 20
r.width = 8 // r.width = 8
r.height = 24 // r.height = 24
let a = r.area() // a = 192
}struct Rectangle {
public var width: Int64
public var height: Int64
public init(width: Int64, height: Int64) {
this.width = width
this.height = height
}
public func area() {
width * height
}
}
main() {
var r1 = Rectangle(10, 20) // r1.width = 10, r1.height = 20
var r2 = r1 // r2.width = 10, r2.height = 20
r1.width = 8 // r1.width = 8
r1.height = 24 // r1.height = 24
let a1 = r1.area() // a1 = 192
let a2 = r2.area() // a2 = 200
}mut函数
struct Foo {
var i = 0
public func g() {
i += 1 // 错误!无法在实例成员函数中修改实例成员变量的值
}
}struct Foo {
var i = 0
public mut func g() {
i += 1 // 正确
}
}参考引用
在开发“智能带办”应用时涉及到用户体系,开发阶段使用固定验证码形式跑通,在上线前准备接入短信服务时却遇到了难题,短信服务目前只对企业开发者开放了,个人开发者没办法再使用短信服务。为了顺利上架,退后求其次,改为了使用邮箱验证码等了。 邮箱验证码登录有两个弊端,一是不方便,很多用户进来发现是邮箱验证码登录不方便直接就退出应用了;二是合规风险,在申请安全评估报告时如果涉及到用户体系要求实名,邮箱没办法保证实名,还得再加入额外的实名体系,不仅麻烦而且很多都限制个人开发者没法使用。 其实最开始也考虑过要接入华为登录,看了一键登录文档发现也是只针对企业开发者,以为也是只有企业开发者可以使用,后面看了“华为账号登录”后发现个人开发者也可以使用,只是取不到手机号,正好不使用手机号可以规避合规方面的风险。 Account Kit(华为账号服务)提供简单、快速、安全的登录功能,让用户快捷地使用华为账号登录应用。用户授权后,Account Kit可提供头像、昵称、手机号码等信息,帮助应用更了解用户。华为账号服务提供了登录、获取华为账号用户信息、未成年模式等。在开发过程中涉及下面几个概念: 在我们接口华为用户服务后,可以使用OpenId和UnionID绑定我们自己的账号体系。 由于个人开发者无法使用“一键登录”,本文主要介绍 “华为账号登录”按钮登录。使用按钮登录我们可以使用Account Kit提供的华为账号登录按钮及服务端交互获取华为账号用户身份标识UnionID、OpenID,通过UnionID、OpenID完成用户登录;或者与应用账号完成绑定,绑定后用于登录或者验证。 华为账号登录按钮包含文本、标志和文本、标志三种样式,以满足应用对界面风格一致性和灵活性的要求。 账号服务开发者与华为能力交互流程如下图所示: 交互流程说明如下: 调用登录按钮展示登录页阶段(序号1-3): 用户点击登录阶段(序号4-6): 用户关联应用账号阶段(序号7-16): 华为账号服务提供了LoginWithHuaweiIDButton组件,构造中需要传入LoginWithHuaweiIDButtonParams类型和 LoginWithHuaweiIDButtonController类型的参数,LoginWithHuaweiIDButtonParams属性如下: 目前应用只支持华为登录,页面UI如下: 在页面中配置红色的LoginWithHuaweiIDButton: 控制器controller定义如下: 在controller中获取回调,如果登录成功则通过authorizationCode继续申请用户华为头像和昵称授权: 用户授权成功后请求服务端接口,服务端通过authorizationCode调用华为服务获取accessToken,接着获取用户信息,绑定自己的账号体系返回自己账号体系的token即可。通过下面接口获取用户级凭证: 接着通过下面示例获取用户昵称和头像: 必须在手机上调起授权获取用户授权后这里才可以请求到用户头像和昵称。 本次“智能带办”应用的登录体系接入实践,源于上线前短信服务仅对企业开发者开放的限制,迫使我们从固定验证码、邮箱验证码转向华为账号登录方案。初期因误判“一键登录”仅限企业开发者而忽略“华为账号登录”,后发现个人开发者虽无法获取手机号,但恰好规避了邮箱登录的用户体验差(用户因不便退出)与实名合规风险(需额外实名体系),成为关键破局点。 华为账号服务(Account Kit)通过OpenID(应用唯一标识)、UnionID(开发者唯一标识)等核心概念,为个人开发者提供了安全高效的登录能力:既支持自定义样式的登录按钮(如本文配置的红色 此次实践的核心启示在于:面对企业级服务限制时,需深度挖掘平台对个人开发者的差异化能力——华为账号登录虽不提供手机号,却以“去实名化”特性解决了合规痛点,同时依托成熟的OAuth 2.0/OIDC协议与丰富组件(如支持深色模式、自定义圆角的按钮),兼顾了开发效率与用户体验。未来,可进一步探索背景

华为登录能力介绍
华为账号服务简介
华为账号服务交互流程


流程说明:名称 类型 只读 可选 说明 style Style 否 否 LoginWithHuaweiIDButton组件的样式。支持样式包括:BUTTON_RED、BUTTON_WHITE、BUTTON_WHITE_OUTLINE、BUTTON_BLACK、ICON_RED、ICON_WHITE、ICON_WHITE_OUTLINE、ICON_BLACK、ICON_GRAY、BUTTON_GRAY、BUTTON_CUSTOM。 borderRadius number 否 是 按钮边框圆角半径。取值范围:[0,+∞),值小于0时,按0处理。默认值:height属性取值的一半。单位:vp。 iconRadius number 否 是 Icon类型按钮的半径。取值范围:[0,+∞),值小于0时,按0处理。默认值:24。单位:vp。 supportDarkMode boolean 否 是 表示按钮的样式是否随系统深浅色模式变化。true:按钮的样式会随着系统深浅色模式变化。false:按钮的样式不会随着系统深浅色模式变化。默认值:true。 loginType LoginType 否 是 华为账号登录类型。默认值:LoginType.ID。一键登录请使用LoginType.QUICK_LOGIN。 textAndIconStyle boolean 否 是 是否展示图文混合样式的华为账号登录按钮。true:按钮支持Icon和文字混合样式。false:按钮仅支持文本样式。默认值:false。当loginType不等于LoginType.QUICK_LOGIN且style等于BUTTON_RED、BUTTON_WHITE、BUTTON_WHITE_OUTLINE、BUTTON_BLACK、BUTTON_GRAY时该参数生效。起始版本:5.0.0(12) customButtonParams CustomButtonParams 否 是 BUTTON_CUSTOM按钮样式参数。起始版本:5.0.0(12) verifyPhoneNumber boolean 否 是 华为账号用户在过去90天内未进行短信验证,是否拉起Account Kit提供的短信验证码页面。true:拉起Account Kit提供的短信验证码页面。false:不拉起Account Kit提供的短信验证码页面。需要应用验证手机号时效性。默认值:true。起始版本:5.0.0(12) extraStyle ExtraStyle 否 是 如果应用想使用华为账号提供的固定样式之外的效果,可使用此接口自定义按钮样式。起始版本:5.0.0(12) loginButtonTextType LoginButtonTextType 否 是 当loginType为LoginType.QUICK_LOGIN时,可传入此参数,控制按钮文本内容显示。默认值:LoginButtonTextType.QUICK_LOGIN。当该参数为LoginButtonTextType.QUICK_LOGIN时,按钮文本内容显示“华为账号一键登录”。当该参数为LoginButtonTextType.QUICK_REGISTRATION时,按钮文本内容显示“华为账号一键注册”。起始版本:5.0.0(12) riskLevel boolean 否 是 是否需要获取华为账号用户风险等级。仅登录类型为LoginType.QUICK_LOGIN时需要设置该参数。true:需要获取用户风险等级。false:不获取用户风险等级。默认值:false。起始版本:5.1.0(18) securityVerification boolean 否 是 用户开启华为账号一键登录增强身份验证后,应用会在登录过程中通过华为账号使用生物识别或短信进行身份验证。如果需要获取用户一键登录增强身份验证的开关状态,需设置该字段为false。仅登录类型为LoginType.QUICK_LOGIN时需要设置该参数。true:响应结果HuaweiIDCredential将不会返回 enableSecurityVerification。false:响应结果HuaweiIDCredential将返回 enableSecurityVerification。默认值:true。起始版本:6.0.0(20) 智能带办接入过程

LoginWithHuaweiIDButton({
params: {
// LoginWithHuaweiIDButton支持的样式
style: loginComponentManager.Style.BUTTON_RED,
// 账号登录按钮在登录过程中展示加载态
extraStyle: {
buttonStyle: new loginComponentManager.ButtonStyle().loadingStyle({
show: true
})
},
// LoginWithHuaweiIDButton的边框圆角半径
borderRadius: 24,
// LoginWithHuaweiIDButton支持的登录类型
loginType: loginComponentManager.LoginType.ID,
// LoginWithHuaweiIDButton支持按钮的样式跟随系统深浅色模式切换
supportDarkMode: true
},
controller: this.controller
})
}
.height(40)
.width('100%')
.margin({top:50})
.padding({left:25, right:25})controller: loginComponentManager.LoginWithHuaweiIDButtonController =
new loginComponentManager.LoginWithHuaweiIDButtonController()
.setAgreementStatus(loginComponentManager.AgreementStatus.NOT_ACCEPTED)
.onClickLoginWithHuaweiIDButton((error: BusinessError, response: loginComponentManager.HuaweiIDCredential) => {
if (error) {
this.dealAllError(error);
return;
}
if (response) {
Logger.i(TAG, 'Succeeded in getting response.');
const authCode = response.authorizationCode;
// 开发者处理authCode
this.getUserInfoPermission(authCode)
}
});getUserInfoPermission(authCode:string){
// 创建授权请求,并设置参数
const authRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();
// 获取头像昵称需要传如下scope
authRequest.scopes = ['profile'];
// 若开发者需要进行服务端开发以获取头像昵称,则需传如下permission获取authorizationCode
authRequest.permissions = ['serviceauthcode'];
// 用户是否需要登录授权,该值为true且用户未登录或未授权时,会拉起用户登录或授权页面
authRequest.forceAuthorization = true;
// 用于防跨站点请求伪造
authRequest.state = util.generateRandomUUID();
// 执行授权请求
try {
const controller = new authentication.AuthenticationController(this.getUIContext().getHostContext());
controller.executeRequest(authRequest).then((data) => {
const authorizationWithHuaweiIDResponse = data as authentication.AuthorizationWithHuaweiIDResponse;
const state = authorizationWithHuaweiIDResponse.state;
if (state && authRequest.state !== state) {
Logger.i(TAG, `Failed to authorize. The state is different, response state: ${state}`);
return;
}
Logger.i(TAG,'Succeeded in authentication.');
const authorizationWithHuaweiIDCredential = authorizationWithHuaweiIDResponse?.data;
const avatarUri = authorizationWithHuaweiIDCredential?.avatarUri;
const nickName = authorizationWithHuaweiIDCredential?.nickName;
// 开发者处理avatarUri, nickName
const authorizationCode = authorizationWithHuaweiIDCredential?.authorizationCode;
Logger.i(TAG, 'getUserInfoPermission:' + JsonUtils.toJSONString(authorizationWithHuaweiIDCredential))
this.sendLoginRequest(authorizationCode??authCode)
// 涉及服务端开发以获取头像昵称场景,开发者处理authorizationCode
}).catch((err: BusinessError) => {
this.dealAllError(err);
});
} catch (error) {
this.dealAllError(error);
}
}POST /oauth2/v3/token HTTP/1.1
Host: oauth-login.cloud.huawei.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=<code>&client_id=<client_id>&client_secret=<client_secret>POST /rest.php?nsp_svc=GOpen.User.getInfo HTTP/1.1
Host: account.cloud.huawei.com
Content-Type: application/x-www-form-urlencoded
access_token=<Access Token>总结
BUTTON_RED按钮),又通过Authorization Code→Access Token→用户信息的流程保障安全,避免身份伪造等风险。接入过程中,我们通过LoginWithHuaweiIDButton组件实现前端交互,结合服务端解析凭证绑定自有账号体系,最终完成用户登录闭环。UnionID在多应用间的用户打通能力,或结合GroupUnionID拓展关联主体场景,持续完善登录体系的灵活性与扩展性。
优化智能体解决方案需要软件工程确保组件协调、并行运行并与系统高效交互。例如预测执行,会尝试处理可预测查询以降低时延,或者进行冗余执行,即对同一智能体重复执行多次以防单点故障。其他增强现代智能体系统可靠性的模式包括: 还有很多其他模式。 本系列将实现最常用智能体模式背后的基础概念,以直观方式逐一介绍每个概念,拆解其目的,然后实现简单可行的版本,演示其如何融入现实世界的智能体系统。 所有理论和代码都在 GitHub 仓库里:🤖 Agentic Parallelism: A Practical Guide 🚀 代码库组织如下: 许多复杂的用户查询并非单一问题,而是比较性的、多步骤的调研任务,需要从多个不同来源的文档中综合信息。 解决方案是 并行多跳检索(Parallel Multi-Hop Retrieval) 架构,这种模式将 RAG 系统提升为真正的调研代理,工作流模拟人类研究员如何处理复杂问题的过程: 我们将以一个无法通过单一检索回答的比较性问题为例,构建并比较简单 RAG 系统与多跳 RAG 系统,证明只有多跳系统才能成功收集必要的证据,以提供准确且富有洞察力的最终答案。 首先为初始分解步骤定义 Pydantic 模型,从而结构化元代理规划阶段输出的内容。 这个 然后构建高级多跳系统作为 下一个节点将并行为每个子问题协调执行标准的 RAG 流程。 最后,“合成器”节点作为元代理的最终步骤,将并行发现整合为一个连贯的答案。 最后按线性顺序组装图:分解 -> 并行检索 -> 综合。 给两个系统一个复杂且需要比较的问题,这个问题无法通过单次检索调用正确回答,从而对比分析两种查询方式。 输出为…… 最终分析得出明确结论,性能差异并非渐进式,而是一次能力上的飞跃。 本文由mdnice多平台发布本系列介绍增强现代智能体系统可靠性的设计模式,以直观方式逐一介绍每个概念,拆解其目的,然后实现简单可行的版本,演示其如何融入现实世界的智能体系统。本系列一共 14 篇文章,这是第 14 篇。原文:Building the 14 Key Pillars of Agentic AI

agentic-parallelism/
├── 01_parallel_tool_use.ipynb
├── 02_parallel_hypothesis.ipynb
...
├── 06_competitive_agent_ensembles.ipynb
├── 07_agent_assembly_line.ipynb
├── 08_decentralized_blackboard.ipynb
...
├── 13_parallel_context_preprocessing.ipynb
└── 14_parallel_multi_hop_retrieval.ipynb深度推理的多跳检索

from langchain_core.pydantic_v1 import BaseModel, Field
from typing import List
class SubQuestions(BaseModel):
"""分解代理输出的Pydantic模型,包含一组独立的子问题"""
questions: List[str] = Field(description="A list of 2-3 simple, self-contained questions that, when answered together, will fully address the original complex query.")SubQuestions 模型是元代理首次行动的合约,迫使 LLM 将复杂查询分解为一系列简单、可回答的问题,是并行"分而治之"策略的基础步骤。LangGraph 图。第一个节点将是"分解器",即元代理的规划角色。from typing import TypedDict, List, Dict, Annotated
import operator
class MultiHopRAGState(TypedDict):
original_question: str
sub_questions: List[str]
# 字典以问题作为键,存储每个子问题的答案
sub_question_answers: Annotated[Dict[str, str], operator.update]
final_answer: str
# 节点 1:分解器(元代理的第一步)
decomposer_prompt = ChatPromptTemplate.from_template(
"You are a query decomposition expert. Your job is to break down a complex question into simple, independent sub-questions that can be answered by a retrieval system. "
"Do not try to answer the questions yourself.\n\n"
"Question: {question}"
)
decomposer_chain = decomposer_prompt | llm.with_structured_output(SubQuestions)
def decomposer_node(state: MultiHopRAGState):
"""获取原始复杂问题并将其分解为子问题列表"""
print("--- [Meta-Agent] Decomposing complex question... ---")
result = decomposer_chain.invoke({"question": state['original_question']})
print(f"--- [Meta-Agent] Generated {len(result.questions)} sub-questions. ---")
return {"sub_questions": result.questions}decomposer_node 是研究代理的战略大脑,它不会尝试回答查询,其唯一且关键的任务是分析用户意图并将其分解为一组独立、可并行化的研究任务。from concurrent.futures import ThreadPoolExecutor, as_completed
# 标准、自包含的RAG链,是并行检索代理的“引擎”
sub_question_rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| generator_prompt
| llm
| StrOutputParser()
)
def retrieval_agent_node(state: MultiHopRAGState):
"""节点 2:为每个子问题并行运行完整 RAG 进程"""
print(f"--- [Retrieval Agents] Answering {len(state['sub_questions'])} sub-questions in parallel... ---")
answers = {}
# 用 ThreadPoolExecutor 对每个子问题并发运行‘sub_question_rag_chain’
with ThreadPoolExecutor(max_workers=len(state['sub_questions'])) as executor:
# 为每个待回答子问题构建一个 future
future_to_question = {executor.submit(sub_question_rag_chain.invoke, q): q for q in state['sub_questions']}
for future in as_completed(future_to_question):
question = future_to_question[future]
try:
answer = future.result()
answers[question] = answer
print(f" - Answer found for sub-question: '{question}'")
except Exception as e:
answers[question] = f"Error answering question: {e}"
# 将结果收集到“sub_question_answers”字典中
return {"sub_question_answers": answers}retrieval_agent_node 是系统中的分散-聚合核心,接收 sub_questions 列表,并用 ThreadPoolExecutor 将每个条目分配到各自独立的 RAG 链。这是一种强大的并行形式,同时运行多个完整 RAG 流程。在所有并行代理找到答案后,该节点将所有发现汇总到 sub_question_answers 字典中。# 节点 3:合成器(元代理的最后一步)
synthesizer_prompt = ChatPromptTemplate.from_template(
"You are a synthesis expert. Your job is to combine the answers to several sub-questions into a single, cohesive, and comprehensive answer to the user's original complex question.\n\n"
"Original Question: {original_question}\n\n"
"Sub-Question Answers:\n{sub_question_answers}"
)
synthesizer_chain = synthesizer_prompt | llm | StrOutputParser()
def synthesizer_node(state: MultiHopRAGState):
"""获取子问题的答案,并合成最终的全面答案"""
print("--- [Meta-Agent] Synthesizing final answer... ---")
# 将收集的子问题答案格式化为最终提示
sub_answers_str = "\n".join([f"- Q: {q}\n- A: {a}" for q, a in state['sub_question_answers'].items()])
final_answer = synthesizer_chain.invoke({
"original_question": state['original_question'],
"sub_question_answers": sub_answers_str
})
return {"final_answer": final_answer}synthesizer_node 是至关重要的最终推理步骤,它本身不执行任何检索,任务是接收 sub_question_answers 中的预处理事实,并将其构造为能直接回应用户原始复杂查询的连贯叙述。from langgraph.graph import StateGraph, END
workflow = StateGraph(MultiHopRAGState)
workflow.add_node("decompose", decomposer_node)
workflow.add_node("retrieve_in_parallel", retrieval_agent_node)
workflow.add_node("synthesize", synthesizer_node)
workflow.set_entry_point("decompose")
workflow.add_edge("decompose", "retrieve_in_parallel")
workflow.add_edge("retrieve_in_parallel", "synthesize")
workflow.add_edge("synthesize", END)
multi_hop_rag_app = workflow.compile()
# 查询需要比较两个产品,信息在独立、不重叠的文档中
user_query = "Compare the QLeap-V4 and the Eco-AI-M2, focusing on their target use case and power consumption."
# --- 执行简单 RAG ---
print("="*60)
print(" SIMPLE RAG SYSTEM OUTPUT")
print("="*60 + "\n")
print(f"Final Answer:\n{simple_answer}")
# --- 执行多跳 RAG ---
print("\n" + "="*60)
print(" MULTI-HOP RAG SYSTEM OUTPUT")
print("="*60 + "\n")
print("--- Sub-Question Answers ---")
for i, (q, a) in enumerate(multi_hop_result['sub_question_answers'].items()):
print(f"{i+1}. Q: {q}\n A: {a}")
print("\n--- Final Synthesized Answer ---")
print(multi_hop_result['final_answer'])
# --- 最终分析 ---
print("\n" + "="*60)
print(" ACCURACY & QUALITY ANALYSIS")
print("="*60 + "\n")
print("**Simple RAG Performance:**")
print("- Result: COMPLETE FAILURE.")
print("- Reason: The user's query contained terms for both products. Vector search found the documents that were, on average, most semantically similar to the entire query, retrieving only documents about the Eco-AI-M2. It completely failed to retrieve any information about the QLeap-V4. Without the necessary context for both products, a comparison was impossible.\n")
print("**Multi-Hop RAG Performance:**")
print("- Result: COMPLETE SUCCESS.")
print("- Reason: The system's intelligence was in the initial decomposition step. The Meta-Agent broke the complex comparative query into two simple, focused sub-questions: 1. Get info on Product A. and 2. Get info on Product B. The parallel Retrieval Agents had no trouble answering these simple questions, each retrieving the correct, focused context. The final Synthesizer agent then received a perfect, complete set of facts about both products, making the final comparison trivial.")#### 输出 ####
============================================================
SIMPLE RAG SYSTEM OUTPUT
============================================================
Final Answer:
Based on the provided context, the Eco-AI-M2 chip is designed for edge computing and mobile devices, with a primary feature of low power consumption at only 15W under full load. The context does not contain information about the QLeap-V4, so I cannot provide a comparison.
============================================================
MULTI-HOP RAG SYSTEM OUTPUT
============================================================
--- Sub-Question Answers ---
1. Q: What is the target use case and power consumption of the QLeap-V4?
A: The QLeap-V4 processor is designed for maximum performance in data centers, with a primary use case of large-scale AI model training. It consumes 1200W of power under full load.
2. Q: What is the target use case and power consumption of the Eco-AI-M2?
A: The Eco-AI-M2 chip is designed for edge computing and mobile devices like drones and smart cameras. Its key feature is low power consumption, drawing only 15W under full load.
--- Final Synthesized Answer ---
The QLeap-V4 and the Eco-AI-M2 are designed for very different purposes, primarily distinguished by their target use case and power consumption.
- **QLeap-V4**: This is a high-performance processor intended for data centers. Its main use case is large-scale AI model training, and it has a high power consumption of 1200W.
- **Eco-AI-M2**: This is a low-power chip designed for edge computing and mobile devices. Its focus is on energy efficiency, consuming only 15W, making it suitable for applications like drones and smart cameras.Hi,我是俞凡,一名兼具技术深度与管理视野的技术管理者。曾就职于 Motorola,现任职于 Mavenir,多年带领技术团队,聚焦后端架构与云原生,持续关注 AI 等前沿方向,也关注人的成长,笃信持续学习的力量。在这里,我会分享技术实践与思考。欢迎关注公众号「DeepNoMind」,星标不迷路。也欢迎访问独立站 www.DeepNoMind.com,一起交流成长。
最近看到一个职场社区帖子,吐槽了一个关于面试和 offer 的相关话题,参与讨论的同学非常多。 问题描述差不多是这样: 那关于这个问题,不知道大家有没有类似的体验或者经历? 你信心满满地去一家公司,面试官是个看起来技术大拿模样的人,一上来就给你整了个高并发场景下的分布式锁实现,问你 JVM 调优的十八般武艺,甚至还要跟你探讨一下 Linux 内核的源码细节。 你虽然答得满头大汗,但自我感觉还不错,仿佛自己把毕生所学都展示出来了。 但是最后结果呢?客气地送你一句等通知,然后便石沉大海。或者回去等了个三五天、一个星期,最后等来的是一句冰冷的不合适。 而反观另外一些面试经历,你可能就是抱着去溜达一圈的心态去转转的,面试让你感觉像在聊天,聊聊项目,聊聊过往经历,聊聊技术。 你心里还在犯嘀咕,没了?就这? 结果第二天,HR 就打电话过来找你谈薪,然后询问入职时间,速度快得让你怀疑人生。 看到这里,你是不是也挺疑惑,这到底是为什么? 难道某些公司就爱玩反向筛选?还是说问技术细节本身就是一种送客的委婉方式?这背后到底有没有什么可以遵循的逻辑原理可以分析分析。 所以今天咱们也用一篇文章的篇幅来聊一聊这个话题,也欢迎大家分享交流自己的观点和看法。 对于那些问得又细又深,最后却没给 offer 的,往往有这么几种情况。 第一种,也是最现实、最常见的大环境筛选。 什么意思呢? 现在的求职大环境大家也知道,岗位有限,候选人太多。HR 和面试官手里攥着一堆 985、211 甚至大厂背景的简历。 简单点说,他们不缺候选人,所以他们有资格挑。 对于中间段位的候选人,也就是我们大多数普通人,他们不需要看你有多优秀,只需要找出你简历里的一个瑕疵,一个技术细节没答上来,或许就有可能会把你刷掉。毕竟对于他们来说,能选择的太多。 其次,还有一个比较现实的问题是,对于那些问得细的公司,不代表真的招人。 当一个团队实际并不缺人,或者只是抱着宁缺毋滥的心态在招人时,他们就有资本去挑刺。 这时候面试官常常带着一种找漏洞的心态。他们的问题像一张细密的筛网,目的似乎不是看你有多合适,而是为了证明你哪里不合适。 说实话,这种还是挺恶心的。 第三种,也是最最扎心的一种情况:你只是他们的「免费咨询顾问」。 更直白一点说就是在套方案。 现在的行情下,很多公司业务停滞,不怎么招人,但又面临一些棘手的技术难题。 他们打着招聘的旗号,实际上是把市场上优秀的工程师请过来,所谓的面试其实也就是一场免费的头脑风暴。他们会故意引导你去讲你上一家公司的架构设计、服务拆分方案、甚至是具体的排错思路。 整个面试过程你自认为胸有成竹,方案和思路也讲得滔滔不绝,殊不知,人家还另有企图呢。 有一说一,这种是最最恶心的一种情况。 而对于那些问得不多、但 offer 倒是给的挺痛快的公司,通常又是怎么回事呢? 首先,这往往意味着这个公司是「真·缺人」呐。 这种公司通常处于一种“生死存亡”或者业务极速扩张的阶段。老板或者团队负责人可能已经被缺人折磨得寝食难安了。 他们的核心诉求非常明确:找个能立刻干活、能立刻上手的人。 这时候,他们不会跟你去扯什么虚头巴脑的设计模式,更不会去考你那些冷门的技术知识。 他们关心的是:你能不能明天就来上班,你能不能把这个烂摊子代码接过去维护,你能不能抗住连续一个月的强度。 在这种极度的需求面前,所谓的技术细节反倒成了次要的。 但是说实话,这种 offer 虽然来得容易,但兄弟记住,这往往也是把双刃剑。 因为“真·缺人”的背后,往往意味着技术债巨多、管理混乱,或者是一个谁都不愿意接的坑。 拿到这种 offer,你既可能是一飞冲天的救世主,也有可能是一头扎进泥潭的接盘侠。 当然,还有一种情况,虽然不那么好听,但也必须提一嘴。 那就是,有些公司其实是在广撒网。他们可能并没有确切的 HC,或者他们需要的只是一个廉价的劳动力。 对于这种公司,问太多技术细节反而会吓跑你,他们更希望用更轻松的面试体验和更高薪的承诺来把你招进去,至于技术匹配度嘛,额……那是入职以后的事情了。 文章的最后我想说的是,面试是一个双向选择的过程,也是一个互相试探的过程。 当你遇到那个问得特别细的面试官时,别急着心里骂娘,也别急着觉得自己没戏了。你可以试着把这场技术拷问变成一场技术交流。 如果对方是在套方案,你可以适当保留,点到为止;如果对方是真的在考察技术深度,那正好展示你的技术功底。 而当你遇到那个聊两句就给 offer 的公司时,也别急着狂喜。 可以多问问团队现状,问问业务体量,问问技术栈,这时候,一定要记住,你该反问的要反问,该考察的要考察。 因为虽说大环境寒冷,但是我觉得找到一个不坑的公司有时候比拿到一个所谓的 offer 更加重要,大家觉得呢? 好了,今天就先聊这么多吧,希望能对大家有所启发,我们下篇见。“我发现凡是给 offer 的公司,面试时基本不问技术细节,那些问得又多又细的公司,后面基本就没下文了……”

注:本文在GitHub开源仓库「编程之路」 https://github.com/rd2coding/Road2Coding 中已经收录,里面有我整理的6大编程方向(岗位)的自学路线+知识点大梳理、面试考点、我的简历、几本硬核pdf笔记,以及程序员生活和感悟,欢迎star。
InheritableThreadLocal相比ThreadLocal多一个能力:在创建子线程Thread时,子线程Thread会自动继承父线程的InheritableThreadLocal信息到子线程中,进而实现在在子线程获取父线程的InheritableThreadLocal值的目的。 关于ThreadLocal详细内容,可以看这篇文章:史上最全ThreadLocal 详解 举个简单的栗子对比下InheritableThreadLocal和ThreadLocal: 执行结果: 可以看到子线程中可以获取到父线程设置的inheritableThreadLocal值,但不能获取到父线程设置的threadLocal值 InheritableThreadLocal 的实现原理相当精妙,它通过在创建子线程的瞬间,“复制”父线程的线程局部变量,从而实现了数据从父线程到子线程的一次性、创建时的传递 。 其核心工作原理可以清晰地通过以下序列图展示,它描绘了当父线程创建一个子线程时,数据是如何被传递的: 下面我们来详细拆解图中的关键环节。 ### 核心实现机制 **数据结构基础: 复制过程的核心: 一般来说,在真实的业务场景下,没人会直接 new Thread,而都是使用线程池的,因此 首先,我们先理解 再看线程池创建新线程的条件,对于标准的 不会继承的场景 不只是线程池污染,线程池使用 执行结果: 当然了,解决这个问题可以考虑使用阿里开源的 TransmittableThreadLocal (TTL),或者在提交异步任务前,先获取线程数据,再传入。例如:和 ThreadLocal 的区别
public class InheritableThreadLocalTest {
private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
testThreadLocal();
testInheritableThreadLocal();
}
/** * threadLocal测试 */
public static void testThreadLocal() {
// 在主线程中设置值到threadLocal
threadLocal.set("我是父线程threadLocal的值");
// 创建一个新线程并启动
new Thread(() -> {
// 在子线程里面无法获取到父线程设置的threadLocal,结果为null
System.out.println("从子线程获取到threadLocal的值: " + threadLocal.get()); }
).start();
}
/** * inheritableThreadLocal测试 */
public static void testInheritableThreadLocal() {
// 在主线程中设置一个值到inheritableThreadLocal
inheritableThreadLocal.set("我是父线程inheritableThreadLocal的值");
// 创建一个新线程并启动
new Thread(() -> {
// 在子线程里面可以自动获取到父线程设置的inheritableThreadLocal
System.out.println("从子线程获取到inheritableThreadLocal的值: " + inheritableThreadLocal.get());
}).start();
}
}从子线程获取到threadLocal的值:null
从子线程获取到inheritableThreadLocal的值:我是父线程inheritableThreadLocal的值实现原理
Thread类内部维护了两个 ThreadLocalMap类型的变量 :threadLocals:用于存储普通 ThreadLocal设置的变量副本。inheritableThreadLocals:专门用于存储 InheritableThreadLocal设置的变量副本 。InheritableThreadLocal通过重写 getMap和 createMap方法,使其所有操作都针对 inheritableThreadLocals字段,从而与普通 ThreadLocal分离开 。new Thread())时。在 Thread类的 init方法中,如果判断需要继承(inheritThreadLocals参数为 true)且父线程(当前线程)的 inheritableThreadLocals不为 null,则会执行复制逻辑 。createInheritedMap。这是实现复制的核心方法 。它会创建一个新的 ThreadLocalMap,并将父线程 inheritableThreadLocals中的所有条目遍历拷贝到新 Map 中。InheritableThreadLocal对象本身)是直接复制的引用。InheritableThreadLocal的 childValue(T parentValue)方法来生成子线程中的初始值。默认实现是直接返回父值(return parentValue;),这意味着对于对象类型,父子线程将共享同一个对象引用 。关键特性与注意事项
InheritableThreadLocal变量的修改互不影响 。InheritableThreadLocal最需要警惕的问题。线程池中的线程是复用的,这些线程在首次创建时可能已经从某个父线程继承了值。但当它们被用于执行新的任务时,新的任务提交线程(逻辑上的“父线程”)与工作线程已无直接的创建关系,因此之前继承的值不会更新,这会导致数据错乱(如用户A的任务拿到了用户B的信息)或内存泄漏 。对于线程池场景,应考虑使用阿里开源的 TransmittableThreadLocal (TTL) 。childValue方法默认是浅拷贝,如果存入的是可变对象(如 Map、List),父子线程实际持有的是同一个对象的引用。在一个线程中修改该对象的内部状态,会直接影响另一个线程 。若需隔离,可以重写 childValue方法实现深拷贝 。ThreadLocal类似,如果线程长时间运行(如线程池中的核心线程),并且未及时调用 remove方法清理,那么该线程的 inheritableThreadLocals会一直持有值的强引用,导致无法被GC回收。良好的实践是在任务执行完毕后主动调用 remove()线程池中局限性
InheritableThreadLocal在线程池中的使用局限性要额外注意InheritableThreadLocal的继承前提InheritableThreadLocal的继承只发生在 新线程被创建时(即 new Thread()并启动时)。在创建过程中,子线程会复制父线程的 InheritableThreadLocal值。ThreadPoolExecutor,新线程的创建遵循以下规则:InheritableThreadLocal。InheritableThreadLocal。InheritableThreadLocal值保持不变(可能是之前任务设置的值),这可能导致数据错乱(如用户A的任务看到用户B的数据)。InheritableThreadLocal 还可能存在获取不到值的情况。例如,在执行异步任务的时候,复用了某个已有的线程A,并且当时创建该线程A的时候,没有继承InheritableThreadLocal,进而导致后面复用该线程的时候,从InheritableThreadLocal获取到的值为null:public class InheritableThreadLocalWithThreadPoolTest {
private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
// 这里线程池core/max数量都只有2
private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
2,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(3000),
new ThreadPoolExecutor.CallerRunsPolicy()
);
public static void main(String[] args) {
// 先执行了不涉及InheritableThreadLocal的子任务初始化线程池线程
testAnotherFunction();
testAnotherFunction();
// 后执行了涉及InheritableThreadLocal
testInheritableThreadLocalWithThreadPool("张三");
testInheritableThreadLocalWithThreadPool("李四");
threadPoolExecutor.shutdown();
}
/** * inheritableThreadLocal+线程池测试 */
public static void testInheritableThreadLocalWithThreadPool(String param) {
// 1. 在主线程中设置一个值到inheritableThreadLocal
inheritableThreadLocal.set(param);
// 2. 提交异步任务到线程池
threadPoolExecutor.execute(() -> {
// 3. 在线程池-子线程里面可以获取到父线程设置的inheritableThreadLocal吗?
System.out.println("线程名: " + Thread.currentThread().getName() + ", 父线程设置的inheritableThreadLocal值: " + param + ", 子线程获取到inheritableThreadLocal的值: " + inheritableThreadLocal.get());
});
// 4. 清除inheritableThreadLocal
inheritableThreadLocal.remove();
}
/** * 模拟另一个独立的功能 */
public static void testAnotherFunction() {
// 提交异步任务到线程池
threadPoolExecutor.execute(() -> {
// 在线程池-子线程里面可以获取到父线程设置的inheritableThreadLocal吗?
System.out.println("线程名: " + Thread.currentThread().getName() + ", 线程池-子线程摸个鱼");
});
}
}线程名:pool-1-thread-2,线程池-子线程摸个鱼
线程名:pool-1-thread-1,线程池-子线程摸个鱼
线程名:pool-1-thread-1,父线程设置的inheritableThreadLocal值:李四,子线程获取到inheritableThreadLocal的值:null
线程名:pool-1-thread-2,父线程设置的inheritableThreadLocal值:张三,子线程获取到inheritableThreadLocal的值:null// 1. 在主线程中先获取inheritableThreadLocal的值
String name = inheritableThreadLocal.get();
// 2. 提交异步任务到线程池
threadPoolExecutor.execute(() -> {
// 3. 在线程池-子线程里面直接传入数据
System.out.println("线程名: " + Thread.currentThread().getName() + ", 父线程设置的inheritableThreadLocal值: " + param + ", 子线程获取到inheritableThreadLocal的值: " + name);
}); 与 ThreadLocal 的对比
特性 ThreadLocal InheritableThreadLocal 数据隔离 线程绝对隔离 线程绝对隔离 子线程继承 不支持 支持(创建时) 底层存储字段 Thread.threadLocalsThread.inheritableThreadLocals适用场景 线程内全局变量,避免传参 父子线程间需要传递上下文数据
大家好,我是R哥。 话说我昨天不是发了《IDEA 出现重大 Bug!不要升级!不要升级!》这篇文章吗? 今天上午就收到了某同学的反馈: 今天确实也收到 IDEA 2025.3.1.1 版本的更新了: 难道 IntelliJ IDEA 连夜就修复了我这个 BUG?? 这也太巧了吧?! 抱着预期的心情更新了 2025.3.1.1,结局让我有点失望,还是那样。。 删除各种缓存,试了各种方法都没有用,就差重装了(估计也没用),社区一堆的 BUG 贴都还是 于是我去查了 2025.3.1.1 的更新说明: 确实修复了几个大 BUG,包括 IDEA 2025.3.1 打开大 Maven 项目时会卡死的问题也修复了,但弹窗空白这个 BUG 并没有涵盖其中。。 似乎官方是解决不了这个 BUG? 这个问题在 24.2.5 版本后就开始出现了,一直都没有解决,一个这么重大的 BUG 拖了这么久不修复,着实难以理解! 先勉强用着吧,后面如果官方修了,或者有绕过方案,我也会第一时间再跟大家同步。 好了,今天的分享就到这里了,后面我也会分享更多好玩的 Java 技术和最新的技术资讯,关注Java技术栈第一时间推送。


OPEN 状态呢。https://youtrack.jetbrains.com/articles/IDEA-A-2100662602/Int...

版权声明: 本文系公众号 "Java技术栈" 原创,转载、引用本文内容请注明出处,抄袭、洗稿一律投诉侵权,后果自负,并保留追究其法律责任的权利。
本周AI领域迎来密集进展,大模型在动漫生图(Niji V7)、端侧智能(AgentCPM-Explore)、医疗(Baichuan-M3)、多模态生图(GLM-Image)、视频生成(Veo 3.1、PixVerse R1)及机器人(1X World Model、LimX COSA)等垂直场景实现性能突破与场景适配;AI工具则聚焦电商、办公、音频处理等高频需求推出,Google UCP、Claude Cowork、Voice-Pro等高效解决方案,技术则在药物研发(DrugCLIP)、大模型部署(Engram模块)、生物研究(Stack模型)等领域实现跨学科赋能,一起来回顾本周的AI新鲜事儿吧! Midjourney联合推出动漫风格AI生图模型「Niji V7」 1月9日,Midjourney联合推出动漫风格AI生图模型「Niji V7」,核心更新包括图像质量提升(连贯性增强、细节如眼睛反射、花瓣更清晰,实现“高清升级”)、提示词遵循能力强化(精准理解位置/数量等具体请求,sref风格参考功能向前兼容,cref角色参考暂不支持)、设计美学突破(线条可传达更多形体质感信息,支持简约风格留白,线条与空间结合呈现更平面化效果),且个性化与情绪板功能即将上线。实测线条流畅、细节优化,但复杂场景和中式风格仍有不足。 OpenBMB开源社区联合发布4B「AgentCPM-Explore」端侧智能体模型 1月13日,OpenBMB开源社区联合清华大学自然语言处理实验室、中国人民大学及面壁智能发布4B参数的「AgentCPM-Explore」端侧智能体模型,是首个支持GAIA、Xbench等8个长难智能体任务的4B模型,可实现100+轮稳定环境交互,在主流评测基准上取得同尺寸SOTA表现,越级赶超8B级模型、比肩部分30B级以上及「Claude-4.5-Sonnet」等闭源大模型,还展现出“质疑”“求真”等类人思考逻辑,通过模型融合、信号去噪、信息精炼三大技术破解小模型训练难题,全流程开源AgentDock工具沙盒平台、AgentRL强化学习框架与AgentToLeaP一键测评平台支持复现与扩展。 Arc研究所开源单细胞基础模型「Stack」与「Perturb Sapiens」图谱 1月13日,Arc研究所宣布开源首个无需重新训练即可学习新任务的单细胞基础模型「Stack」及预测性细胞反应全景图谱「Perturb Sapiens」,「Stack」基于1.49亿个人类单细胞数据预训练、5500万个细胞后训练,通过表格化Transformer模块、基因模块表征符的架构创新及上下文学习的训练策略创新,能以细胞为“提示”预测目标细胞群在全新环境中的反应,在零样本下游任务中表现优于基线模型和现有方案;「Perturb Sapiens」则依托其能力生成约20000个“细胞类型-组织-扰动”预测组合,填补了相关实验空白,模型及图谱均已开源。 百川智能发布开源新一代医疗大模型「Baichuan-M3」,医疗幻觉率降至3.5 1月13日,百川智能正式开源新一代医疗大模型「Baichuan-M3」,在全球权威医疗AI评测HealthBench等多项权威评测中全面超越「GPT-5.2」,且以3.5的全球最低幻觉率刷新行业底线(通过将医学事实一致性融入训练实现)。该模型创新提出“严肃问诊范式”与SCAN原则,借助SPAR算法和SCAN-bench全流程动态评测体系,具备原生端到端严肃问诊能力,在安全分层、信息澄清等四大维度显著高于真人医生平均水平,同时其医疗应用「百小应」已同步接入该模型向医生与患者开放。 美团龙猫LongCat升级全新稀疏注意力机制「LoZA」,解码快10倍 1月13日消息,美团龙猫LongCat系列升级全新稀疏注意力机制「LoZA」(LongCat ZigZag Attention),通过给MLA模块配可学习权重α筛选50%低重要性模块替换为线性复杂度的SSA,形成ZigZag交错结构并设计1024 Token稀疏窗口,在中期训练阶段即可完成改造,使模型上下文窗口从256K扩展至1M,128K文本解码速度快10倍、256K预加载提速50%且解码省30%算力,日常任务性能持平原版,长文本任务表现更优,还计划支持动态稀疏比例及多模态长内容处理。 1X公司为家用人形机器人NEO推出全新世界模型「1X World Model」 1月13日,1X公司为家用人形机器人NEO推出全新世界模型「1X World Model」,相关内容浏览量超500万次。该模型基于视频预训练技术,通过“世界模型主干(文本条件扩散模型,经互联网视频预训练、人类第一视角中期训练、NEO专属微调)+逆动力学模型IDM”两阶段对齐,无需大规模机器人数据即可泛化到全新物体、动作与任务,能通过生成“成功完成任务”的视频倒推动作轨迹,支持抓取、双手协调、人机交互等任务且保持稳定成功率。 智谱与华为联合开源首个基于国产芯片训练的SOTA生图模型「GLM-Image」 1月14日,智谱与华为联合发布中国首个全程基于国产华为Ascend A2芯片及昇思MindSpore框架训练的SOTA多模态生图模型「GLM-Image」,采用“9B自回归模型+7B DiT扩散解码器”混合架构,擅长文字精准渲染,拿下CVTG-2K和LongText-Bench双榜单开源第一,原生支持1024x1024至2048x2048任意尺寸,API调用仅0.1元/张,可适配小红书封面、商业海报等多场景,已开源并提供多个平台接入地址,印证了国产算力底座支撑前沿模型训练的能力。 Google升级视频模型「Veo 3.1」,首次原生支持9:16竖屏视频 1月14日,Google升级视频模型「Veo 3.1」,首次原生支持9:16竖屏视频(适配YouTube Shorts等移动端平台,无需裁剪)并新增4K分辨率,同时提升创意能力(简单提示词可生成小剧场)、强化角色与背景物体一致性(跨场景保持元素完整)、改善元素融合能力(无缝组合多图元素),普通用户可通过YouTube Shorts、Gemini等体验,企业用户可借助Flow、Gemini API等使用;Google依托YouTube的平台、流量与生态优势,形成“创作-分发-反馈-优化”正向循环,而AI视频竖屏化已成趋势,OpenAI、迪士尼及国内可灵AI等均有相关布局。 爱诗科技发布全球首个通用实时世界模型「PixVerse R1」 1月14日,爱诗科技发布全球首个支持最高1080P分辨率实时生成的世界模型「PixVerse R1」,区别于传统AI视频的高延迟、固定时长与单向生成,凭借Omni原生多模态模型(统一多模态为连续Token流)、自回归流式生成机制(支持无限时长与长时序一致性)、瞬时响应引擎IRE(采样步骤1-4步,效率提升数百倍)三大技术创新,实现瞬时响应、实时共创,支持多模态交互与最高1080P输出,开启视频即交互、世界可共创的新范式,适用于游戏、电影、直播等场景。 生数科技Vidu AI开放平台发布「一键生成AI MV」功能 1月14日,生数科技Vidu AI开放平台发布「一键生成AI MV」功能,依托深度协同的多智能体系统,用户仅需提交音乐、1-7张参考图及文本指令,即可全自动实现分钟级输出(适配10-300 S主流流媒体时长),通过攻克角色与风格一致性、歌词驱动叙事、帧级音画融合等行业痛点,解决了传统“手工作坊”模式的效率与质量瓶颈,大幅降低创作门槛、压缩成本(刊例价为同行业50%),推动音乐视觉内容叙事权从主流机构向个体创作者转移,定义了AI原生MV的质量基线,重塑音乐产业生产与消费范式。 Google发布专为AI智能体设计的通用商业协议「UCP」及「Gemini CX」 1月12日,Google官宣发布Agentic电商解决方案,包括专为AI智能体设计的通用商业协议「UCP」(Universal Commerce Protocol)及企业端的「Gemini CX」(Gemini Enterprise for Customer Experience)。「UCP」接入Shopify、沃尔玛等伙伴,贯穿商品发现到售后全流程;「Gemini CX」具备复杂推理、多模态交互、执行授权操作能力,可覆盖客户服务全生命周期,已落地麦当劳等企业;国内阿里、1688、京东、抖音也纷纷推出电商相关AI工具与功能。 Anthropic基于Claude Code底层架构推出智能协作工具「Claude Cowork」 1月13日,Anthropic基于Claude Code底层架构推出智能协作工具「Claude Cowork」,核心定位是从“对话助手”转变为能理解任务、制定计划并持续执行的“数字同事”,支持用户授权访问指定本地文件进行分类、信息提取、报告整理等非编码工作,还具备内置虚拟机隔离、浏览器自动化支持等创新体验与安全功能。目前以研究预览版形式面向macOS平台的Claude Max订阅用户开放,后续计划加入跨设备同步、Windows版本及强化安全机制。 夸克AI浏览器上线千问划词「快捷指令」功能,划选即调用告别复制粘贴 1月13日,夸克AI浏览器上线千问划词「快捷指令」功能,用户只需三步(开启划词工具栏、添加自定义指令并命名保存)即可完成设置,浏览网页或文档时划选内容便能一键调用AI指令,无需复制粘贴,该功能提供了学术润色、种草文案撰写、情侣聊天支招、内容创作润色、代码优化、外语翻译、职场黑话解读等多场景指令模板,助力提升各类场景下的使用效率。 5.6K Star开源神器「Voice-Pro」,免费本地实现视频翻译+声音克隆 1月13日消息,GitHub上5.6K Star的开源工具「Voice-Pro」原是韩国创业团队的付费软件,现因新项目开发停止维护并完全开源,它整合WhisperX、F5-TTS等先进语音模型,在Windows等主流PC平台实现“视频下载-人声分离-字幕识别-文本翻译-声音克隆配音-视频合成”一站式本地运行,支持100多种语言处理、零样本语音克隆,无需代码,通过脚本即可轻松安装,免费无字符限制且不上传云端,是ElevenLabs等商业工具的优质替代方案,适配视频创作者和出海玩家需求。 Vercel Labs开源AI Agents浏览器自动化CLI工具「Agent-browser」 1月14日,Vercel Labs发布开源AI Agents浏览器自动化CLI工具「Agent-browser」,发布两天即获3.4k GitHub星,相比传统Playwright MCP可节省93%上下文,其中外层基于Rust编写,通过返回清洗后的可访问性树并为可交互元素打标签(Ref),让AI以简单指令精准操控浏览器,零配置且支持无头/有头模式,兼容多款AI工具,能降低Token消耗、提升AI注意力与稳定性,安装仅需两步命令。 清华团队研发的AI药物虚拟筛选平台「DrugCLIP」登上Science 1月9日,清华大学联合团队研发的AI药物虚拟筛选平台「DrugCLIP」相关成果发表于《Science》,其通过语义检索技术实现筛选速度较传统方法提升百万倍,首次完成人类基因组规模虚拟筛选,实验验证对NET、TRIP12等靶点的筛选有效性,构建全球最大蛋白-配体筛选数据库并免费开放,配套服务平台已服务千余名用户,未来将助力抗癌、罕见病等领域新药研发。 逐际动力发布全球首个具身智能体系统「LimX COSA」 1月12日,逐际动力在深圳正式发布具身智能体系统「LimX COSA」,这是面向物理世界原生、深度融合高阶认知与全身运控的Agentic OS,采用自底向上的小脑基础模型、大小脑融合高阶技能层、自主认知决策层三层结构,赋予全尺寸人形机器人Oli高阶认知推理、语义记忆与主动感知、实时感知全身移动操作三大核心能力,实现“能想能动、知行合一”,标志着具身智能从Demo迈向产品落地,推动多领域的广泛应用。 DeepSeek V4核心技术「Engram」曝光:CPU替GPU存参,性能与降本双突破 1月13日,DeepSeek联合北京大学发布新论文,曝光「DeepSeek-V4」核心技术「Engram模块」,该模块基于N-gram改造,通过哈希函数映射与门控机制快速检索静态知识,以CPU内存替代GPU显存存储大规模参数(推理损耗<3%),相关模型在知识、推理、代码、长文本任务上显著优于现有模型,印证V4性能突破,降低超大规模模型部署成本。AI 大模型
AI 工具
技术突破
在企业级表格应用场景中,排版规整度直接影响文档的专业质感与可读性——无论是财务报表、项目方案还是正式汇报材料,文本在单元格内的分布均匀性往往成为细节加分项。此前,面对“文本两端对齐”这一高频排版需求,开发者常需通过复杂自定义实现,且难以保证与Excel的兼容性。 SpreadJS V19.0 正式推出单元格两端对齐(Justify Alignment) 功能,完美复刻Excel排版逻辑,兼顾美学呈现与实用体验,为纯前端表格应用带来排版升级,让专业文档制作更高效、更精准。 两端对齐功能提供水平与垂直两个维度的精准排版能力,适配不同文本展示需求,实现“边界对齐、内部均匀”的视觉效果: 支持同时启用水平与垂直两端对齐,让文本在单元格内实现“上下左右全边界对齐、内部均匀分布”,适用于对排版精度要求极高的正式文档(如财务报表附注、合同条款)。 启用两端对齐时,系统将自动开启“自动换行”功能,文本将根据单元格宽度智能拆分换行,避免因手动设置遗漏导致的排版错乱,降低操作门槛。 针对合并后的大尺寸单元格,两端对齐功能可根据合并后的实际宽高自适应调整文本分布,无需额外设置适配规则,完美支持复杂表格布局(如报表标题、分类汇总区域)。 无论是基础纯文本,还是包含字体样式、颜色、链接的富文本,均可正常使用两端对齐功能。仅需注意:富文本在旋转文本场景下需遵循特殊适配逻辑,确保排版一致性。 针对不同语言文本的排版特性,两端对齐功能内置智能分词策略: 部分功能兼容限制: SpreadJS V19.0 两端对齐功能的推出,不仅填补了纯前端表格在专业排版领域的空白,更通过“Excel兼容、智能适配、低操作门槛”的设计,让开发者无需编写复杂自定义代码,即可快速实现高质量排版效果。 无论是企业级报表制作、正式文档导出,还是复杂表格布局设计,这一功能都能有效提升文档质感与可读性,同时降低开发与维护成本。SpreadJS 始终以“复刻Excel体验、赋能前端开发”为核心,持续优化细节功能,让纯前端表格应用更贴合企业实际业务需求。 SpreadJS V19.0 即将正式发布,更多实用特性等待解锁,敬请期待!如需提前体验两端对齐功能,可访问 SpreadJS 官方Demo 或联系技术支持获取试用版本。
一、核心功能:双向对齐,文本分布更均匀
1. 水平两端对齐(Horizontal Justify)
2. 垂直两端对齐(Vertical Justify)
3. 组合对齐:水平+垂直双向优化
二、特性亮点:适配多元场景,兼顾兼容性与灵活性
1. 自动换行强制启用,无需手动配置
2. 无缝适配合并单元格
3. 普通文本与富文本全面支持
4. 智能分词规则,适配多语言场景
CultureManager 配置分词规则,满足特殊业务场景需求三、使用场景:覆盖企业级文档核心需求

四、注意事项:这些细节让排版更精准
五、总结:排版升级,效率与专业度双提升
适合读者:DBA / 后端架构师 / 运维工程师 关键词:MySQL 复制、binlog_do_db、replicate_do_db、数据不一致 在许多 MySQL 体系的数据库环境中,为了降低 binlog / relay log 日志量、缓解从库复制压力或减少同步延迟,往往会引入 主库 binlog 过滤 或 从库复制过滤 的配置方案。 这些手段在一定程度上能够缓解资源消耗,但如果对其工作机制理解不充分,使用了不合理的过滤策略,极易引入隐蔽且不可逆的数据不一致风险。更为危险的是,这类问题在系统运行过程中通常不会立刻暴露,当业务侧发现数据异常时,往往已经无法通过常规手段进行补救。 本文将从 主库与从库两种过滤方式的实现机制入手,分析它们各自的优缺点及潜在风险。 明确主库和从库在处理 SQL 和 row event 时的判断逻辑存在差异。 判断发生在 SQL 执行完成之后。 判断发生在 SQL Thread 回放阶段 判断依据包括: 结论:当主库和从库判断条件不一致时,即使 binlog 已记录,从库也可能未执行对应 row event,从而导致数据不一致。 执行结果: 从库复制过滤前提条件就是主库的binlog必须完整。 说明: Replicate_Do_DB/Replicate_Ignore_DB 这两个参数一个是只同步某些库,另一个是只忽略某些库,判断依据是relay log中记录use的数据库,并不是SQL语句实际操作的库。 测试: 主库查看数据: 从库查看数据: 结论: 从库报错表不存在,所以这样会导致从库同步数据失败,因为use的是test库。 风险: 多库写入(跨库SQL)、存储过程、触发器、应用层不指定USE库都会导致数据不同步的风险。 说明: Replicate_Do_Table/Replicate_Ignore_Table 这两个参数一个是只同步指定表,另一个是只忽略指定表,两个参数都不支持通配符,可以精确到表但使用要确保库名表名正确。 测试: 主库查看数据: 从库查看数据: 结论: 由于主库执行rename操作将t1表更为t1_bak,t1_tmp更为t1,而从库忽略了t1_tmp导致sql同步失败,如果业务往新t1表插入数据从库就会因表不存在而断开复制链路,这是典型的“表级过滤被 DDL 绕过”事故。 风险: 说明: Replicate_Wild_Do_Table/Replicate_Wild_Ignore_Table 这两个参数一个是同步指定表,另一个是忽略指定表,两个参数都支持通配符,使用要确保库名表名没有通配符的隐患存在。 匹配方式: 测试: 忽略日志类表,不需要同步到从库。 一年后业务上线新业务test1.log_important 主库查看数据: 从库查看数据: 结论: 风险:聊聊复制过滤的那些隐藏陷阱
一、背景
二、复制过滤的判断逻辑
2.1 主库:是否写 binlog
binlog_do_db / binlog_ignore_db 仅根据当前会话的 USE db 判断,而不关注 SQL 实际操作的目标表。2.2 从库:是否执行 relay log
三、主库过滤参数及风险
3.1 binlog_do_db / binlog_ignore_db 的行为示例
主库参数设置:
binlog_do_db = db1
主库执行SQL:
USE db1;
INSERT INTO db2.t2 VALUES (1);db2.t2,与 USE db1 不一致。3.2 相关风险
四、从库复制过滤参数及风险
4.1 常用复制过滤参数
4.2 复制或忽略库参数
STOP SLAVE;
CHANGE REPLICATION FILTER Replicate_Do_DB = test1;
START SLAVE;USE test;
CREATE TABLE TEST1.T1 LIKE TEST.T1;
INSERT INTO TEST1.T1 VALUES(1,'A');greatsql> SELECT * FROM TEST1.T1;
+----+-------+
| id | cname |
+----+-------+
| 1 | A |
+----+-------+
1 row in set (0.00 sec)greatsql> SELECT * FROM TEST1.T1;
ERROR 1146 (42S02): Table 'test1.t1' doesn't exist4.3 复制或忽略表参数
STOP SLAVE;
CHANGE REPLICATION FILTER Replicate_Ignore_Table= (test1.t1_tmp);
START SLAVE;RENAME TABLE test1.t1 TO test1.t1_bak;
RENAME TABLE test1.t1_tmp TO test1.t1;greatsql> use test1
Database changed
greatsql> show tables;
+-----------------+
| Tables_in_test1 |
+-----------------+
| t1 |
| t1_bak |
+-----------------+
2 rows in set (0.01 sec)greatsql> USE test1
Database changed
greatsql> SHOW tables;
+-----------------+
| Tables_in_test1 |
+-----------------+
| t1_bak |
| t1_tmp |
+-----------------+
2 rows in set (0.01 sec)4.4 指定复制或忽略库参数
%、_(LIKE 语义)STOP SLAVE;
CHANGE REPLICATION FILTER Replicate_Wild_Ignore_Table = (test1.log%);
START SLAVE;greatsql> USE test1
Database changed
greatsql> SHOW tables;
+-----------------+
| Tables_in_test1 |
+-----------------+
| log_important |
+-----------------+
1 row in set (0.00 sec)greatsql> USE test1
Database changed
greatsql> SHOW tables;
Empty set (0.00 sec)log_important 被 log_% 命中新业务数据未同步到从库,主从复制正常但是从库数据丢失,如果主库故障切换到从库才发现数据不一致就会导致故障,这是典型的“通配规则忽略业务表”事故。五、最常见的踩坑配置
主库 从库 风险 是否推荐 binlog_do_db Replicate_Do_DB/Replicate_Ignore_DB 跨库静默丢数据 不推荐 binlog_do_db replicate_wild_ignore 从库失效 不推荐 binlog_ignore_db 无过滤 永久不可补 不推荐 无过滤 Replicate_Do_DB/Replicate_Ignore_DB 跨库静默丢数据 不推荐 无过滤 Replicate_Do_Table/Replicate_Ignore_Table 与DDL操作存在冲突,人工维护成本高 可用,前提是过滤表数量少 无过滤 Replicate_Wild_Do_Table/Replicate_Wild_Ignore_Table 匹配范围过宽,通配符需要转义 可用,前提是确保通配符不会影响其他表 六、最终建议(可直接当规范)
大家好,我是良许。 在嵌入式开发中,串口通信是我们最常用的通信方式之一。 但很多初学者经常会被 UART、RS232、RS485 这几个概念搞混,不清楚它们之间到底有什么区别和联系。 今天我就来详细聊聊这三者的区别,帮助大家彻底理解这些概念。 UART(Universal Asynchronous Receiver/Transmitter)的中文名称是通用异步收发器,它本质上是一种通信协议和硬件电路。 UART 定义了数据如何在设备之间进行串行传输,包括数据格式、传输速率、起始位、停止位等。 简单来说,UART 是一种逻辑层面的协议标准。 它规定了数据帧的格式,比如一个标准的 UART 数据帧通常包含:1 个起始位(低电平)、5 到 8 个数据位、可选的校验位、1 到 2 个停止位(高电平)。 在我们的 STM32 单片机中,UART 就是芯片内部集成的一个硬件模块,负责将并行数据转换为串行数据发送出去,或者将接收到的串行数据转换为并行数据。 UART 通信只需要两根线:TX(发送)和 RX(接收),再加上一根地线 GND。 RS232 是由美国电子工业协会(EIA)制定的一种物理层标准,全称是 EIA-RS-232。 它定义了数据终端设备(DTE)与数据通信设备(DCE)之间的物理接口标准,包括电气特性、机械特性、功能特性等。 RS232 最重要的特点是它的电平标准:逻辑 1(MARK)的电压范围是-15V 到-3V,逻辑 0(SPACE)的电压范围是 +3V 到 +15V。 注意,这个电平标准和我们单片机的 TTL 电平(0V 和 3.3V 或 5V)是完全不同的。 RS232 通常使用 DB9 或 DB25 接口,最大传输距离约为 15 米,最大传输速率一般不超过 20kbps(理论上可以更高,但实际应用中受限于线缆长度和质量)。 RS485 同样是一种物理层标准,它是 RS232 的改进版本。 RS485 采用差分信号传输方式,使用两根线(A 和 B)来传输数据,通过两根线之间的电压差来表示逻辑 0 和 1。 RS485 的主要优势包括:传输距离可达 1200 米,传输速率可达 10Mbps(短距离下),支持多点通信(最多可以连接 128 个设备),抗干扰能力强。由于采用差分信号,RS485 在工业环境中的应用非常广泛。 理解了基本概念后,我们来看看它们之间的关系。 简单来说: UART 是协议层,RS232 和 RS485 是物理层。 这就好比我们说话时,UART 定义了"说什么"(语言规则),而 RS232 和 RS485 定义了"怎么说"(声音的大小、传播方式)。 一个完整的串口通信系统,既需要 UART 协议来组织数据,也需要 RS232 或 RS485 这样的物理层标准来实际传输数据。 在实际应用中,我们的单片机 UART 输出的是 TTL 电平信号(比如 0V 和 3.3V),如果要通过 RS232 接口通信,就需要使用电平转换芯片(如 MAX232)将 TTL 电平转换为 RS232 电平;如果要通过 RS485 通信,就需要使用 RS485 收发器芯片(如 MAX485)进行转换。 从电气特性来看,三者有明显的区别: UART(TTL 电平):逻辑 1 通常是 3.3V 或 5V,逻辑 0 是 0V。这是单片机内部直接使用的电平标准,驱动能力弱,抗干扰能力差,只适合板级通信。 RS232:采用负逻辑,逻辑 1 是-3V 到-15V,逻辑 0 是 +3V 到 +15V。这种较大的电压摆幅提供了一定的抗干扰能力,但功耗相对较高。RS232 是单端信号传输,容易受到共模干扰的影响。 RS485:采用差分信号传输,两根线之间的电压差大于 +200mV 表示逻辑 1,小于-200mV 表示逻辑 0。差分传输的最大优势是抗共模干扰能力强,即使两根线同时受到相同的干扰,只要它们之间的电压差保持不变,就不会影响数据传输。 在实际应用中,传输距离和速率是我们选择通信方式的重要考虑因素: UART(TTL 电平):传输距离非常有限,一般不超过 1 米,速率可以很高,但受限于线缆和驱动能力。在 PCB 板上的芯片间通信非常合适。 RS232:标准规定最大传输距离为 15 米,但在实际应用中,如果降低波特率,可以达到更远的距离。比如在 9600bps 的速率下,可以传输 30 米甚至更远。但随着距离增加,信号衰减和干扰会导致通信质量下降。 RS485:这是三者中传输距离最远的,标准距离可达 1200 米。而且 RS485 的传输速率和距离是可以权衡的:短距离下可以达到 10Mbps,而在最大距离 1200 米时,速率通常限制在 100kbps 左右。 从通信拓扑结构来看: UART/RS232:只支持点对点通信,即一个发送端对应一个接收端。如果需要连接多个设备,就需要多个串口,或者使用串口服务器等设备。 RS485:支持多点通信(也叫总线型通信),可以在同一条总线上连接多达 128 个设备(理论值,实际应用中需要考虑负载能力)。这使得 RS485 在工业控制系统中非常受欢迎,可以大大减少布线成本。 另外,RS485 支持半双工和全双工两种模式。半双工模式只需要两根线(A 和 B),但同一时刻只能有一个设备发送数据;全双工模式需要四根线,可以同时收发数据。 在嵌入式开发中,UART 最常见的应用场景包括: 下面是一个 STM32 使用 HAL 库进行 UART 通信的简单示例: RS232 虽然是比较老的标准,但在很多场合仍然在使用: 在使用 RS232 时,我们需要在单片机的 UART 和 RS232 接口之间加入电平转换芯片。 以 MAX232 为例,它可以将 TTL 电平转换为 RS232 电平,反之亦然。 电路连接非常简单,只需要几个外围电容即可。 RS485 在工业自动化领域应用极为广泛: 使用 RS485 时,需要注意以下几点: 下面是一个使用 MAX485 进行 RS485 通信的示例代码: 在实际项目中,我们应该如何选择呢?可以参考以下原则: 通过以上的详细分析,我们可以清楚地看到 UART、RS232、RS485 之间的区别和联系: UART 是一种通信协议和硬件模块,定义了数据的组织方式;RS232 和 RS485 则是物理层标准,定义了信号的电气特性和传输方式。 它们不是互相替代的关系,而是协同工作的关系。 在实际应用中,我们通常是在单片机的 UART 基础上,根据具体需求选择合适的物理层标准。 如果是短距离通信,直接使用 UART 的 TTL 电平;如果需要更远的传输距离或更强的抗干扰能力,就通过电平转换芯片将 TTL 电平转换为 RS232 或 RS485 电平。 理解这些概念对于我们进行嵌入式系统设计非常重要,可以帮助我们在不同的应用场景中选择最合适的通信方式,设计出稳定可靠的系统。 希望这篇文章能够帮助大家彻底搞清楚这三者的区别,在以后的项目中能够灵活运用。1. 基本概念解析
1.1 UART 是什么
1.2 RS232 是什么
1.3 RS485 是什么
2. 三者之间的关系
3. 详细对比分析
3.1 电气特性对比
3.2 传输距离和速率对比
3.3 通信方式对比
4. 实际应用场景
4.1 UART 的典型应用
// UART初始化
UART_HandleTypeDef huart1;
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
// 发送数据
uint8_t txData[] = "Hello UART!\r\n";
HAL_UART_Transmit(&huart1, txData, sizeof(txData)-1, 1000);
// 接收数据
uint8_t rxData[100];
HAL_UART_Receive(&huart1, rxData, 10, 1000);4.2 RS232 的典型应用
4.3 RS485 的典型应用
// 定义RS485方向控制引脚
#define RS485_DE_GPIO_Port GPIOA
#define RS485_DE_Pin GPIO_PIN_8
// 设置为发送模式
void RS485_TX_Mode(void)
{
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET);
HAL_Delay(1); // 等待芯片切换
}
// 设置为接收模式
void RS485_RX_Mode(void)
{
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET);
HAL_Delay(1);
}
// 发送数据
void RS485_SendData(uint8_t *data, uint16_t len)
{
RS485_TX_Mode();
HAL_UART_Transmit(&huart1, data, len, 1000);
RS485_RX_Mode();
}
// 接收数据
void RS485_ReceiveData(uint8_t *data, uint16_t len)
{
RS485_RX_Mode();
HAL_UART_Receive(&huart1, data, len, 1000);
}5. 如何选择合适的通信方式
6. 总结
连续第二周,JDK 26 的早期访问版本仍为Build 29。更多详情请参阅其发布说明。 同样,JDK 27 的早期访问版本当前仍为Build 3。详细信息可查阅其发布说明。 对于JDK 26和JDK 27,鼓励开发者通过Java Bug数据库报告缺陷。 Spring Shell 4.0.0 正式发布GA版本,包含缺陷修复、文档改进、依赖项升级以及多项新特性,包括,命令编程模型重构,在使用 Spring Boot 时,不再需要 JReleaser 1.22.0发布,这是一个用于简化 Java 项目发布流程的工具,本次更新包括缺陷修复、文档改进、依赖项升级以及新功能,包括,Signing模块全面重构,支持同时使用多种方法对构件(artifacts)进行签名;新增对Minisign(一个用于文件签名和验证的工具)的支持;支持在部署构件到 Maven Central 时跳过等待期。更多详细信息请见发布说明。 TornadoVM团队宣布,其开源 IntelliJ 插件TornadoInsight(旨在提升 TornadoVM 的开发体验)现已兼容最新发布的TornadoVM 2.0。相关配置指南也已同步更新。关于 TornadoInsight 的更多信息,可参考 InfoQ 的新闻报道。 Apache Camel 4.14.3发布,包含缺陷修复、依赖项升级及功能改进,包括,在使用Camel JBang时,可通过 原文链接: Java News Roundup: Spring Shell, JReleaser, TornadoInsight, Apache CamelJDK 26
JDK 27
Spring Framework
@EnableCommand或@CommandScan注解,并修复了@Command注解的意外行为;全新升级的 DSL,解决了CommandRegistration.Builder实例与 Spring Security 的SecurityFilterChain接口在新构建器格式下的匹配问题;与 Spring Framework 7.0 和 Spring Boot 4.0 对齐;新增对JSpecify的空安全(null safety)支持。更多细节请参见发布说明。JReleaser
TornadoVM
Apache Camel
--repos命令为Camel Kamelet相关操作指定 Maven 仓库;Camel Neo4j组件改进了消息体的检测逻辑,避免内部错误;修复了Camel Netty中 SSL 客户端证书主题名称(subject name)从可读字符串表述被错误转换为晦涩的LDAP格式的问题。更多详情请查阅发布说明。
2026 年 1 月 16 日,OpenAI 通过官方 X(原 Twitter)账号正式宣布,将在未来数周内开始在 ChatGPT 的免费版和新推出的 ChatGPT Go($8/月)中测试广告投放。与此同时,Plus($20/月)、Pro($200/月)及企业版将继续保持无广告体验。这一决策迅速引发了科技圈的广泛关注和激烈讨论。 OpenAI 官方推文宣布广告计划,并发布广告原则说明 | 来源:X @OpenAI 值得注意的是,OpenAI 同时发布了一份详尽的"广告原则"(Our Ad Principles),试图向用户保证广告不会影响 ChatGPT 的回答质量和隐私保护。然而,这份承诺并未能平息用户的担忧——社交媒体上的反应呈现出高度两极分化的态势。 OpenAI 发布的广告原则框架:强调使命对齐、答案独立、对话隐私、用户控制与长期价值 📋 OpenAI 官方承诺清单 ✓答案独立性:ChatGPT 的回答始终基于客观有用性,广告不会影响答案内容 ✓对话隐私:不向广告商出售用户数据,对话内容保持私密 ✓用户控制:用户可随时关闭个性化广告,清除广告相关数据 ✓付费保护:Plus、Pro、Business、Enterprise 等高价层级永不显示广告 ✓未成年保护:18 岁以下用户不会看到广告 ✓敏感话题禁区:政治、健康、心理健康等敏感话题禁止广告投放 从华尔街日报、彭博社等主流财经媒体的报道来看,OpenAI 此举并非心血来潮,而是面对真实财务压力的"不得已之举"。据公开数据显示: 在如此悬殊的付费转化率面前,广告变现被多家媒体评价为"不可避免"的选择。富国银行预测,ChatGPT 在搜索市场的占比将从 2025 年底的 17%增长到 2030 年的三分之一,这为广告业务提供了巨大的潜在市场空间。 公告发布后,X 平台上的评论区迅速沦陷。从截图可见,用户反应从嘲讽、愤怒到直接引用 Sam Altman 此前的反广告言论,形成了鲜明的对比和讽刺效果。 X 平台用户对 OpenAI 广告公告的部分反应 | Grok 引用了 Altman 2024 年称广告是"反乌托邦"的言论 Sam Altman 在 2024 年曾称将广告嵌入 ChatGPT 回复是一种"反乌托邦"的想法:"很容易想象那种未来的反乌托邦场景——你问 ChatGPT 一个问题,它回答说'你应该考虑买这个产品'或者'你应该去这里度假'之类的。" —— 来源:Grok @grok 引用 Altman 2024 年采访 这种前后矛盾的表态成为用户攻击的焦点。有用户直言:"直接说你们需要更多钱不就得了"(Just say you guys need more money),简洁而犀利地戳破了官方话术的包装。 用户普遍担忧:一旦 AI 提供的建议与商业利益相关联,就很难保证答案仍然是纯粹基于"客观有用性"的判断。有用户形象地比喻:"感觉就像在心理咨询师办公室里竖起了广告牌。" 尽管 OpenAI 承诺"不会出售用户数据给广告商",但用户对此类承诺持谨慎态度。有 Reddit 用户反映,在 ChatGPT 中讨论特定话题后,很快在其他平台看到相关广告,这加深了数据被滥用的担忧。 2025 年 12 月,ChatGPT Plus 付费用户在对话中看到来自 Target、Peloton 等品牌的"推荐"。OpenAI 最初辩称这不是广告,只是应用发现功能,但最终在用户强烈反对下关闭了该功能。首席研究官 Mark Chen 道歉承认公司"做得不够好"。这一事件严重损害了用户对 OpenAI 承诺的信任。 CEO Sam Altman 提到欣赏 Instagram 的广告模式,但用户尖锐地指出:Instagram 之所以成功,正是因为 Meta 大规模收集和出售了用户的个人数据——这与 OpenAI 声称的"隐私优先"立场本质矛盾,形成了无法调和的逻辑悖论。 根据 OpenAI 展示的概念图,广告将以"Sponsored"标签的形式出现在 ChatGPT 回复的底部,与回答内容明确分离。在下图的示例中,当用户询问墨西哥晚宴菜谱时,系统在给出食谱建议后,底部会显示相关食材的赞助商购买链接。 ChatGPT 广告投放概念设计:广告以"Sponsored"标签形式出现在回复底部,与答案内容分离 这种设计理论上可以降低用户对答案被"污染"的担忧,但批评者指出,长期来看广告逻辑一旦被引入系统,算法污染可能是微妙且难以察觉的——即使不是故意为之。 2024 年 Altman 公开反对广告 Sam Altman 在采访中称将广告嵌入 ChatGPT 回复是"反乌托邦"的想法,表示更倾向于订阅模式以避免用户成为产品。 2025 年 12 月 App Recommendations 争议 ChatGPT Plus 付费用户发现对话中出现 Target、Peloton 等品牌推荐。OpenAI 先是否认为广告,后在舆论压力下关闭该功能并道歉。 2026 年 1 月 16 日正式宣布广告测试 OpenAI 官宣在免费版和 Go 版本中测试广告,同时发布"广告原则"框架,承诺付费用户永远不会看到广告。 这一系列事件的累积效应是:用户现在不再轻易相信 OpenAI 关于"高价订阅永远不会有广告"的承诺。Reddit 社区中大量评论指出,这正是流媒体巨头采用过的老套路——"先在免费端试水,再慢慢侵入付费端"。 💡部分理性用户的接受条件 并非所有反应都是负面的。部分用户认为,如果 OpenAI 能够做到以下几点,免费用户看广告是一种合理的交换: 1. 透明性:广告必须明确标注为"Sponsored",不能伪装成自然回答 2. 相关性:广告应与当前对话相关,而非完全无关的干扰 3. 可控性:用户可以关闭个性化广告设置,或清除用于投放广告的对话记录 4. 底线:高价订阅(Plus/Pro)必须永远保持无广告体验 这类"有条件宽容"的声音提醒我们,用户并非完全不能接受商业化,关键在于执行的边界和信任的维护。 从更宏观的行业视角来看,OpenAI 的这一决策也反映了 AI 领域日益激烈的商业化竞争。谷歌的 Gemini 和 Meta 的 AI 产品已经内置广告机制,OpenAI 不想在市场份额争夺中落于下风。 Marketing AI Institute 的分析尤其指出,OpenAI 内部正面临巨大的商业化压力。公司聘请前 Facebook 和 Instacart 高管 Fidji Simo 担任应用业务 CEO,这一人事任命本身就暗示了公司的战略方向——从技术研究机构向消费级商业平台的全面转型。 OpenAI 的创新尝试在于"对话语境驱动的广告"(contextual ads triggered by current conversation),理论上这种做法可以降低隐私风险。但实践中,用户很难确信系统不会进行隐蔽的数据关联。 社交媒体反应以担忧和怀疑为主,核心议题围绕信任、隐私和"前科"问题。用户普遍采取了"show me"的态度——可以测试,但任何迹象表明承诺被破坏就会转向竞品。 主流媒体的评价则务实与批判并存:认可这一决策的商业必然性,但广泛质疑其能否在不伤害信任的前提下成功。最尖锐的评论来自社区用户的讽刺——AGI 实际上是"Ads Generating Income"(广告创造收入)。这反映了一个更深层的焦虑:开放人工智能的使命(AGI 造福全人类)与商业化压力之间可能存在根本性冲突。 OpenAI 正在走钢丝——既要维持无广告体验的付费用户的付费意愿,又要通过免费/低价层的广告收入覆盖高昂的运营成本。这个平衡能维持多久,将决定 ChatGPT 是否会重蹈社交媒体平台从纯净到被商业完全入侵的老路。事件背景

OpenAI 的广告原则解读

商业化背后的财务压力

社交媒体的激烈反应

四大核心担忧
1. 答案中立性与商业影响的矛盾
2. 数据隐私与"监听"恐惧
3. 前科问题:App Recommendations 事件
4. Instagram 模式类比的逻辑悖论
广告形态预览

前科回顾:信任的裂痕
有条件的宽容声音
行业视角:竞争压力与战略转向
结论:信任与商业化的钢丝行走
1 月 16 日,OpenAI 宣布计划在未来几周内,开始在 ChatGPT 中测试广告功能。首批测试将针对美国市场的用户展开,随后逐步推广至全球。广告将主要出现在免费版以及每月订阅费用为 8 美元的低价方案 Go(在印度测试数月后推广至更多市场)中,而 Plus、Pro 及企业版的高级订阅用户目前将不会看到广告。
OpenAI 承诺,广告不会干预或改变 ChatGPT 生成的答案内容。广告将以带有清晰标识的独立方框形式,出现在聊天机器人回复内容的下方。例如,当用户询问纽约的旅行建议时,ChatGPT 会先提供标准的行程规划,随后可能在下方展示当地酒店的广告。OpenAI 应用负责人表示,还将探索更具交互性的广告形式,例如未来用户可以直接向广告内容提问以辅助购买决策。

针对用户普遍关心的隐私问题,OpenAI 明确表示不会将用户数据出售给广告商,也不会向广告商泄露具体的对话内容。广告商仅能获得如展示次数、点击量等汇总后的效果数据。虽然系统会根据对话主题和部分个性化数据来匹配广告,但用户可以随时在设置中关闭用于广告投放的数据权限。此外,OpenAI 设定了严格的屏蔽机制,涉及健康、政治等敏感话题的对话,以及未成年用户的对话中,将不会展示广告。
OpenAI 面临着巨大的商业化压力。尽管 ChatGPT 目前已拥有超过 8 亿的周活跃用户,但其中绝大多数为不产生直接收益的免费用户。作为一家成立十年、累计融资约 640 亿美元的科技巨头,OpenAI 亟需在现有的订阅模式之外开辟新的收入来源,以支撑高昂的模型训练与运营成本,并应对来自 Google Gemini 等竞争对手日益激烈的挑战。
1 月 16 日,国家医保局宣布,已在全国完成「定点药店医保药品比价」小程序上线工作。该小程序整合了全国医保定点零售药店的实时数据资源,能够提供及时更新的药品价格、库存状态及生产企业等信息。
据介绍,参保人可通过微信、支付宝搜索进入本地医保服务平台,或直接使用国家医保服务平台 app,找到比价功能模块。输入药品名称后,系统便会显示所在区域内各定点药店的该药品价格区间、具体库存情况,并支持按照价格从低到高、距离由近及远等多种方式进行排序筛选。选定意向药店后,用户还可直接使用内置的导航功能前往,或一键拨打药店电话进行咨询,从而减少盲目奔波与不必要的支出。
在基础比价功能全面落地的基础上,各地医保部门结合群众在使用过程中的反馈,正持续拓展小程序的服务场景与应用功能。例如,针对老年群体可能存在的打字输入困难,多个省份的小程序已新增「拍照识药」功能,只需对准药盒拍照即可自动识别药品并查询比价信息;部分区域还对接了线上购药与配送上门服务,为行动不便或有紧急需求的居民提供便利。
此外,一些地区进一步探索了医用耗材价格查询、处方匹配寻药、药品价格波动趋势分析等特色服务,并同步展示药店基于医保药品「量价比较」的指数评级,辅助群众更全面地做出购药选择。
据新华社报道,市场监管总局于近日发布《直播电商经营者落实食品安全主体责任监督管理规定》,将于 2026 年 3 月 20 日起施行,其中明确了 13 类不得直播经营的食品,包括:用非食品原料生产、添加有毒有害物质的食品;致病性微生物、重金属超标食品;过期、腐败变质、霉变生虫食品;病死毒死或检疫不合格的畜禽水产肉类及其制品;无标签预包装食品;国家明令禁止生产经营的食品等。
《规定》将直播电商平台经营者、直播间运营者、直播营销人员、直播营销人员服务机构全部纳入监管范畴。这些主体都必须按照《规定》要求,履行相应的食品安全主体责任。其中,食品生产经营者开设直播间需要公示许可信息、查验供货资质,非食品生产经营者需要建立严格的选品制度;直播营销人员要加强选品把关;平台要建立审查登记、培训、风险管控等制度措施,配备食品安全管理人员,制定食品安全风险管控清单,建立「智能监测、排查调度、快速处置」的工作机制。
在强化监管措施、完善消费者权益保护方面,《规定》要求市场监管部门将直播经营食品纳入到日常监管和年度抽检计划中,明确技术监测记录可以作为电子数据证据;平台必须开通便捷的投诉举报功能,及时处置消费者诉求。
近日,乌克兰知名软件开发商 MacPaw 宣布,将于 2 月 16 日正式关闭其面向欧盟用户的第三方 iOS 应用商店 Setapp Mobile。MacPaw 在官方支持页面中解释称,做出这一决定是因为应用市场「仍在不断演变且复杂的商业条款」已不再契合公司当前的商业模式,暗示盈利困难。
Setapp Mobile 于 2024 年 9 月在欧盟地区启动公开测试,通过订阅制为用户提供 iOS 应用分发服务。服务正式关停后,用户通过该平台获取的所有应用将被移除。官方建议用户在截止日期前备份重要数据,以免服务终止后无法访问。Setapp 基于订阅制的 Mac 版将保持正常运营,不受此次移动端业务调整的影响。
Setapp Mobile 的诞生得益于欧盟《数字市场法案》(DMA)的生效,该法案强制 Apple 在欧盟地区开放第三方应用侧载。然而,这一新兴的分发渠道面临着严峻挑战。尤其是 Apple 引入的「核心技术费」(Core Technology Fee)规则,要求应用在超过一定安装阈值后,必须为每次年度首次安装向 Apple 支付费用。这极大地增加了第三方商店及其开发者的运营成本。
目前,欧盟市场仍有包括 Epic Games Store 在内的另外五家第三方商店在运营。Epic 曾多次抨击 Apple 的收费政策阻碍了竞争对手立足,但目前仍坚持运营并等待欧盟监管机构对 Apple 规则的进一步审查。
据财新报道,近日,英伟达于 2025 年 5 月发布的一篇博客文章重获市场关注。该文中,英伟达称 1 兆瓦(MW)的机架需要 200 公斤铜母线,1 吉瓦(GW)数据中心的机架母线需要 50 万吨(half a million tons)铜。
由于 1 吉瓦相当于 1000 兆瓦,按比例换算,1 吉瓦数据中心需要的铜应为 200 公斤的 1000 倍,即 20 万公斤。因此,英伟达原文所谓的「50 万吨」应为笔误。英伟达后来修订了该错误。
然而,这一数据已经被许多市场调研报告引用,而英伟达的勘误直接使国际铜价短线大跌:伦铜(LME)在 1 月 14 日创造每吨 13407 美元的历史新高后,连续两日回调,累计回撤约3.4%,暂时跌破了 10 天移动平均线(MA10)。
事实上,受美国关税套利、铜矿供应受限、AI 等新需求因素影响,2025 下半年铜价屡创新高,伦铜从 2025 年 9 月初的 9900 美元/吨,一路上涨,于 2026 年 1 月历史首次突破每吨 1.3 万美元。花旗预计铜价将在未来三个月升至每吨 1.4 万美元。
据日经新闻援引 IDC 发布的数据报道,从 2025 年中国手机出货量来看,华为时隔 5 年后再次重回首位。从绝对数字上看,华为 2025 年的手机出货量比 2024 年减少 1.9%,为 4670 万部。由于 2024 年位居首位的 vivo 大幅下降 6.6%,华为逆转升至首位。
此前,华为采购高性能半导体受限,无法实现 5G,导致消费者远离。而近年,华为借助麒麟芯片,重振了销售。2025 年 11 月发售最新机型 Mate80 提高了性能,具备 AI 自动处理各种工作的功能,同时相比前代产品降价。
出货量排在第 2 位的是美国苹果,增长 4%,达到 4620 万部。2025 年 9 月上市的 iPhone17 系列销售强劲。2025 年底苹果通过官方销售渠道对高端机型 Pro 和 Pro Max 提供 300 元优惠刺激了需求。
2025 年,中国的整体手机出货量减少 0.6%,为 2 亿 8460 万部,2 年来首次低于上年。虽然鼓励以旧换新的政府补贴起到了推动作用,但有些地区提前用完补贴额度,显得后继乏力。鉴于消费低迷,IDC 预测 2026 年的出货量为 2 亿 7800 万部,持续低于上年。
如果你从浏览器访问少数派,可能已经注意到我们于近日优化了首页样式。

如你所见,我们此次主要优化了——
通过此次更新,我们希望以更清晰的层级关系,让不同类型的信息在同一视野中有序呈现,同时反映少数派立足内容而多元发展的面向。我们还将在此次更新建立的基础上,持续改进并增加更多功能,敬请期待。
我们鼓励各位读者积极探索、尝试新的版式、组件和交互功能,并提出问题和建议。如你有任何反馈,可以通过下方表单告知我们。
疯狂投入了十几个周末进行优化,在一个 issue 里和用户共讨论了 150 多楼,每周末都开发出一个新的 RC 版本,从 RC1 干到 RC11 ,终于发布了一个稳定版。
作为一个新项目,知道的人还不多,现在才 100 多的 star ,却比同类老项目( mosh: 13k 多的 star )的功能强大很多,详见 README 里的功能对比。支持 多平台、低延迟、保持连接、切换网络、SSH X11 转发、SSH Agent 转发、SSH 端口转发、输出上下滚动、OSC52 复制粘贴、多级 ProxyJump、tmux -CC 集成等。
欢迎大家来试用,与同类老项目 mosh 比较比较。在服务器安装 tsshd,在本地安装 tssh,然后使用下面的命令登录服务器即可:
tssh --udp <服务器 IP 或别名>
注意 tsshd 默认随机监听 61001 - 61999 的 UDP 端口,防火墙要放开相应策略。支持在命令行指定 UDP 端口和 tsshd 安装路径,如下:
tssh --udp --tsshd-path /path/on/server/tsshd --tsshd-port 10000-10010 xxx
( 和 ssh xxx 一样的效果,配置可写在 ~/.ssh/config 中,然后直接 tssh xxx 登录 )
tssh --kcp xxx 启用,也支持配置指定 KCP 协议(默认使用传输吞吐量更高一些的 QUIC 协议)。作为 ssh 工具,tssh 和 tsshd 都很注重安全的,详见 README 里的安全介绍。欢迎各位大佬来分析评估其安全性。(说到安全,我周末基本都献给开源 了,CTF 界少了个 PWN 选手,开源界多了几个项目: trzsz、tssh 和 tsshd 等)
服务端开源(对标 mosh ,并提供相应 Client 库):tsshd https://github.com/trzsz/tsshd
客户端开源(支持标准 ssh ,扩展了很多实用功能):tssh https://github.com/trzsz/trzsz-ssh
本月初,腾讯法务向超过 30 个 GitHub 项目发出了 DMCA 通知,导致这些项目被迫下架。腾讯法务指控开发者们违反了 DMCA 绕过技术保护措施的条款、违反了微信禁止逆向工程条款、威胁用户隐私和安全,以及侵犯知识产权。
TG 上的评论:让用户访问自己的微信数据是非法的
https://www.solidot.org/story?sid=83334
https://github.com/github/dmca/blob/master/2026/01/2026-01-08-tencent.md
https://github.com/ellermister/wechat-clean
前贴: https://www.v2ex.com/t/1186284
看到贴文内容之后,我针对各个关键节点版本做了部署测试,得出如下结论:
从 3.77.0 版本开始,组件数上限 10w ,日请求上限 20w 。从 3.87.0 版本开始,组件数上限降为 4w ,日请求数降为 10w 。
版本详细介绍,以及上限验证结果,详见博文: https://wiki.eryajf.net/pages/b6b711/
除了记录了对应版本,我还针对历史镜像做了归档,有需要的同学可以自取。
如果你跟我一样,同时在用 Claude Code, Cursor, Windsurf, Copilot 等好几个工具,肯定被这件事折磨过:
每改一次 Custom Instruction 或 Rule ,都要去四五个地方手动同步一遍。 只要漏掉一个,AI 的表现就打折扣。
我撸了个小工具 AI Global ,核心逻辑只有一句话:一个中心化配置,全平台指令同步。
🚀 核心功能:
一键分发:支持自动识别并同步 30+ 种 AI 工具(几乎涵盖目前市面所有主流工具)。
共享大脑:所有工具的指令自动合并到 global.md ,改一处,处处生效。
模块化资源:你可以沉淀一套自己的 .md 技能库、规则库,所有 AI 助手瞬间共享你的“武器库”。
极简且美观:全 256 色系着色的 CLI 输出,状态一目了然。
安全第一:自带备份逻辑,支持一键 unlink 无痕还原。
🛠️ 安装使用:
npm install -g ai-global
然后只需运行
ai-global
它会自动帮你把家里的活儿全干了。
GitHub:
https://github.com/nanxiaobei/ai-global
欢迎大家体验,有喜欢的工具没适配的,欢迎提 Issue 或 PR !

题主人在加拿大,混迹已有 5 年。我刚来的时候,对“免费医疗”这四个字是有滤镜的。心想:发达国家嘛,医疗一定又好又人性。后来真正用上了,才发现这事儿得慢慢听我说。
先说个大前提:在加拿大,不管你看啥病,第一步几乎永远是“预约”。家庭医生要预约,专科要转诊再预约,时间单位通常是“周”起步。真要是紧急情况?那只有一个地方——急诊。
听起来很清晰,对吧?但现实往往很骨感。
我儿子有一次出水痘,症状挺明显的,我们也不敢拖,直接去了急诊。结果你猜怎么着?等了 11 个小时。那种感觉你应该能想象:孩子难受,大人焦虑,夜里灯光惨白,塑料椅子坐到怀疑人生。好不容易轮到医生了,医生看了两眼,说: “不是水痘。” 然后就让我们回去了。
后来事实证明真的是水痘。就这么一句“不是”,硬生生耽误了病情。那一刻,我对加拿大医疗的第一层幻想,算是碎了。
但话也不能只说一半。加拿大的医疗,也有让我挺感动的时候。
有一次我太太在申请医保卡的过程中,医保还没正式生效(超过了官方说的处理时间但依然没处理完)。偏偏那天做饭切到了手,口子不小,只能去急诊。我们心里其实是有点慌的,因为没有医保,急诊费用至少一千多加币起。果不其然,医院也明确说了:先记账,账单随后会寄给你们。
第二天我抱着试试看的心态,给医保部门打了个电话,把情况说明了一下。没想到对方效率极高,直接把我的申请加急处理,两个工作日后就确认生效,随后那次急诊费用全免。
那一刻你会觉得:
这个系统慢是慢,但它有它的规则感和人情味——只要你真的符合条件,它不会为难你。
再说回整体感受。发达国家的“免费医疗”,本质上是很容易被挤兑的。大家都不用掏钱,结果就是:能拖就拖,能等就等,系统的优先级极其严格。你不“濒死”,在系统里就不算急。听说有个在加拿大得了肾结石的,排队排了几天看不上病,马上买票回国把石头打掉了。
反过头看国内。
国内的问题大家也都知道:人多、医院挤、医生累。但有个特别现实的优势——病人多,医生见得多,经验是真的被“堆”出来的。很多病,国内医生一眼就知道怎么回事。而且说句大实话:看病是真的方便。不舒服?挂号。想做检查?当天安排。想找专家?多花点钱,但路径清晰。
这点在我自己身上体现得特别明显。
我有一次痔疮发作,是真的惨。疼得坐立不安,还发烧,马应龙都已经压不住了。放在国内,基本就是一句话:“来,检查一下,该处理处理。”
但在加拿大不一样。医生很冷静地告诉我:“不算紧急。”于是——不处理。
给我开了个带抗生素的痔疮栓,说观察。就这么过去了。
那一刻我内心其实已经做了决定:等哪天回国,彻底处理掉。
加拿大医生的逻辑是:标准化、风险控制、避免过度医疗;
国内医生的逻辑是:经验驱动、效率优先、先把你治舒服了再说。
你说哪种好?真不好一刀切。
说到底,这两套系统就像两种性格的人:
至于我这个痔疮患者,目前的状态是:人在加拿大,心在国内肛肠科。
医疗这事儿,真不是“免费”和“不免费”那么简单,只有真正用过,才知道自己更适合哪一套。
对了,加拿大的免费医疗是:看病免费,药不免费,全免是我以前的误解。
GistLedger 是一个基于 GitHub Gist 的极简个人记账应用。它利用 GitHub Gist 作为免费、私有的云端数据库,实现数据的安全存储与多端同步。
体验地址: https://gist-ledger.knowsky404.com
🌐 核心理念: Own your data (数据隐私) | Serverless (无后端) | Lightweight (轻量化)



gist 权限。# 1. 克隆项目
git clone https://github.com/KnowSky404/gist-ledger.git
cd gist-ledger
# 2. 安装依赖 (推荐使用 pnpm)
pnpm install
# 3. 启动开发服务器
pnpm dev
GistLedger-Data 的私有 Gist 和 ledger_data.json 文件。GNU General Public License v3.0 (GPL-3.0)