腾讯云文本审核工具类封装实践:从SDK调用到一行接入
做后端这几年涉及用户输入的场景就没断过——评论、弹幕、私信、AI对话、论坛发帖几乎每个能提交文字的地方都得过一道内容安全检查。一开始我也图省事想着审核就审核直接在业务代码里new一个请求对象调一下腾讯云SDK完事。结果第一个月就让我反应过来这样不行代码里到处散落着请求构建、密钥读取、异常处理和响应解析的逻辑改个配置得翻遍全项目出问题也想不起来哪些调用点没有兜底。后来我花了一个下午把腾讯云文本审核TMS的SDK调用封装成了一个独立的工具类所有接入方只需要一行代码就能完成文本审核。这篇文章就是把这套工具类的设计思路、完整实现和踩坑记录整理出来给正在做内容安全接入的后端同学一个能直接参考的样板。不管你是刚接触腾讯云SDK的新手还是已经接入但想优化封装结构的开发者应该都能从中看到一些可落地的细节。1. 为什么需要这样一个工具类1.1 文本审核不只是“调一个接口”那么简单很多人以为文本审核就是用户发一段文字你把它丢给云服务返回一个 pass 就放行、返回 block 就拦截。实际进入生产环境之后你会发现这背后的动作远不止一个接口。你需要判断一个请求究竟属于哪条业务线是评论、昵称还是聊天消息你需要给每条请求带上关联信息比如用户ID、消息来源这样后续在控制台里才能追查你需要约定好在什么阈值之下直接放行、什么阈值之下必须人工复审接口超时了怎么办SDK内部报错了怎么办并发上来了Client实例要不要复用。这些逻辑如果每个调用方都自己写一遍一旦业务从1个场景变成5个场景同一套代码会被复制粘贴四五次。更麻烦的是每个复制版本都可能走样——有的人忘记传UserDataId有的人把Review当Pass放行有的人异常处理直接吞掉。这已经不只是代码冗余的问题而是内容安全策略一致性被破坏的问题。1.2 工具类到底解决了哪几件事我设计的这个工具类目标非常明确把腾讯云文本审核从“操作SDK”变成“描述意图”。调用方不需要关心Credential怎么创建、TmsClient该不该复用、Request里的字段怎么补齐也不需要关心返回的Suggest和Label怎么映射成业务动作。工具类会把这些全部下沉收敛对外暴露一个简单的入口比如boolean isContentAllowed(String text)返回结果直接告诉你内容能不能用。具体来说工具类承担这几类工作密钥与客户端管理从环境变量读取密钥初始化并缓存TmsClient避免每个请求都重复创建。请求批量参数装配自动填充业务类型、内容、用户标识等公共字段调用方只需传文本。响应结果标准化把SDK返回的结构化数据转换成业务可读的枚举或布尔值屏蔽细节。异常兜底策略网络抖动、超时、接口频率限制时统一处理并降级。可观测性输出每次调用输出日志方便定位问题。这几个点听起来简单但每个都是生产环境里的真实痛点。下面我把每一步展开来讲。2. 接入前的环境准备2.1 引入SDK依赖与版本选择我用的是Java技术栈所以工具类基于腾讯云Java SDK来实现。在pom.xml中引入文本审核专用SDK模块而不是全量SDK这样可以减少依赖体积也让项目结构更清晰。dependency groupIdcom.tencentcloudapi/groupId artifactIdtencentcloud-sdk-java-tms/artifactId version3.1.700/version /dependency版本号我建议不要盲目跟最新先看腾讯云官方文档里该模块的推荐版本。SDK更新频率很高个别大版本升级可能会出现方法签名变化所以我通常锁定一个稳定版本用起来没问题就不轻易升级。如果你的项目已经有tencentcloud-sdk-java基础依赖则需要注意与tms模块的版本兼容性问题最稳妥的做法是直接引入统一版本下的专用模块由Maven仲裁。2.2 密钥管理与客户端初始化密钥是调用腾讯云接口的凭证最常见的错误是把SecretId和SecretKey直接硬编码在工具类里甚至提交进Git仓库。这类密钥泄露事故在行业内时有发生代价很大。我的做法是从环境变量读private static Credential buildCredential() { String secretId System.getenv(TENCENTCLOUD_SECRET_ID); String secretKey System.getenv(TENCENTCLOUD_SECRET_KEY); return new Credential(secretId, secretKey); }在本地开发时在IDE的运行配置里设置环境变量在服务端部署时通过运维平台或容器环境注入。如果你的团队已经接入了CAM角色授权更推荐使用临时密钥方案这里先不展开核心原则是让密钥不出现在代码和配置文件里。接下来是客户端的初始化。TmsClient是线程安全的创建成本也高所以工具类中一定要复用实例不能每次都new一个。private static volatile TmsClient client; private static TmsClient getClient() { if (client null) { synchronized (TextModerationUtil.class) { if (client null) { client new TmsClient(buildCredential(), ap-guangzhou); } } } return client; }地域建议选择离业务最近、且控制台里开通服务的地域例如上海选ap-shanghai广州选ap-guangzhou。地域选错会出现无法调用或者延迟偏高的现象。3. 工具类设计原理与思路3.1 调用方视角决定接口设计写工具类之前我先站在使用者的角度想了一个问题业务开发到底想用这个工具类做什么他们不关心Label具体是Porn、Illegal还是Ad也不关心Score是87还是92。他们在意的是这段文本要不要放行如果复核是哪一类问题需要人工怎么介入。所以我对外的接口设计了两个层次结果型接口TextModerationResult moderate(String content)返回完整审核结果适合需要做入库记录、人工复核、数据统计的场景。判断型接口boolean isContentAllowed(String content)内部调用结果型接口按照策略把Review和Block统一映射为是否放行适合快速过滤场景。这样的设计让不同诉求的调用方各取所需。底层方法保持通用上层判断策略集中在工具类内部不会散落到业务代码里。3.2 核心流程的六个环节工具类内部请求流程可以拆成六个环节每个环节对应一段清晰的职能入参校验检查文本是否为空是否超过SDK限制长度避免无效请求浪费QPS。客户端获取从单例缓存中拿TmsClient。请求装配创建TextModerationRequest设置Content、BizType、DataId、User等信息。接口调用执行client.TextModeration(req)。响应处理对TextModerationResponse做字段提取封装成自定义结果对象。兜底逻辑捕获异常、记录日志、执行降级策略。这六个环节顺序明确每个环节的错误都能定位到具体原因。比如某天线上发现一堆请求超时我第一反应不是去查业务代码而是先看工具类的调用日志确认是否所有请求都在第四步卡住了还是只有部分业务线的BizType配置不对。问题边界一清晰处理速度就快很多。3.3 响应结果如何映射成业务信号腾讯云文本审核返回的核心字段有以下几个字段含义业务处理建议Suggestion建议动作Pass/Review/BlockPass放行Review复核Block拦截Label命中的分类标签用于记录和展示具体违规类型Score置信度分数0到100可用来做自定义阈值调整Keywords命中的自定义关键词列表补充业务判断依据Detail详细审核结果列表人工复核时扩展查看工具类中我定义了一个POJO来承载这些信息public class TextModerationResult { private boolean pass; private String suggestion; private String label; private Integer score; private ListString keywords; private String requestId; // getter / setter 省略 }然后提供一个解析方法把SDK返回的字段填充进去。这里有个关键点需要特别说明suggestion的Review经常被忽视很多业务只拦截了Block对Review直接放行结果一些疑似违规内容就这么漏出去了。我在工具类里默认把Review的最终结果设置为false也就是不直接放行由调用方决定是走人工还是二次审核而不是默认放过去。4. 完整实现代码逐段拆解4.1 核心工具类代码下面给出完整工具类实现。这些代码是我在生产环境里验证过的简化版去掉了部分配置项但核心逻辑都在。import com.tencentcloudapi.common.Credential; import com.tencentcloudapi.common.exception.TencentCloudSDKException; import com.tencentcloudapi.tms.v20201229.TmsClient; import com.tencentcloudapi.tms.v20201229.models.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * 腾讯云文本审核工具类 * 统一封装TMS调用对外提供简洁的审核入口 */ public class TextModerationUtil { private static final Logger log LoggerFactory.getLogger(TextModerationUtil.class); private static final String DEFAULT_REGION ap-guangzhou; private static final String DEFAULT_BIZ_TYPE ; private static final int MAX_TEXT_LENGTH 5000; // 根据官方限制对超长文本分段 private static volatile TmsClient client; private TextModerationUtil() { } private static TmsClient getClient() { if (client null) { synchronized (TextModerationUtil.class) { if (client null) { client new TmsClient(buildCredential(), DEFAULT_REGION); } } } return client; } private static Credential buildCredential() { String secretId System.getenv(TENCENTCLOUD_SECRET_ID); String secretKey System.getenv(TENCENTCLOUD_SECRET_KEY); return new Credential(secretId, secretKey); } /** * 审核文本返回结构化结果 */ public static TextModerationResult moderate(String content) { return moderate(content, DEFAULT_BIZ_TYPE, null, null); } /** * 审核文本支持业务类型与用户关联信息 */ public static TextModerationResult moderate(String content, String bizType, String dataId, String userIp) { TextModerationResult result new TextModerationResult(); if (content null || content.trim().isEmpty()) { result.setPass(true); result.setSuggestion(Pass); result.setLabel(Normal); result.setScore(0); return result; } // 超长文本分段处理 if (content.getBytes(java.nio.charset.StandardCharsets.UTF_8).length MAX_TEXT_LENGTH) { return moderateBySegments(content, bizType, dataId, userIp); } TmsClient tmsClient getClient(); TextModerationRequest request new TextModerationRequest(); request.setContent(content); request.setBizType(bizType); request.setDataId(dataId); request.setUser(new User {{ setUserId(dataId); if (userIp ! null) { setUserIp(userIp); } }}); try { TextModerationResponse response tmsClient.TextModeration(request); fillResult(result, response); } catch (TencentCloudSDKException e) { log.error(腾讯云文本审核调用失败, contentLength{}, errorCode{}, message{}, content.length(), e.getErrorCode(), e.getMessage()); // 降级策略调用失败默认放行但需要打上标记 // 生产环境建议结合业务风险决定是否放行 result.setPass(true); result.setSuggestion(FailOpen); result.setLabel(Unknown); result.setScore(0); result.setRequestId(e.getRequestId()); } return result; } /** * 分段审核任一分段不通过则整体不通过 */ private static TextModerationResult moderateBySegments(String content, String bizType, String dataId, String userIp) { TextModerationResult finalResult new TextModerationResult(); finalResult.setPass(true); finalResult.setSuggestion(Pass); finalResult.setLabel(Normal); finalResult.setScore(0); int segmentSize 800; // 按800个字符一段 int length content.length(); for (int start 0; start length; start segmentSize) { int end Math.min(start segmentSize, length); String segment content.substring(start, end); TextModerationResult segmentResult moderate(segment, bizType, dataId, userIp); if (!segmentResult.isPass()) { // 记录分段信息便于定位 log.warn(文本审核分段拦截, segmentIndex{}, suggestion{}, label{}, start / segmentSize, segmentResult.getSuggestion(), segmentResult.getLabel()); return segmentResult; } } return finalResult; } /** * 直接判断内容是否允许放行 */ public static boolean isContentAllowed(String content) { return moderate(content).isPass(); } private static void fillResult(TextModerationResult result, TextModerationResponse response) { if (response null) { result.setPass(true); result.setSuggestion(FailOpen); return; } // 只有建议为Pass才放行Review和Block都视为不允许直接放行 String suggestion response.getSuggestion(); result.setSuggestion(suggestion); result.setLabel(response.getLabel()); result.setScore(response.getScore()); result.setRequestId(response.getRequestId()); String[] keywords response.getKeywords(); result.setKeywords(keywords null ? Collections.emptyList() : Arrays.asList(keywords)); result.setPass(Pass.equalsIgnoreCase(suggestion)); } }这里特别提醒一下有的SDK版本里setUser参数是User对象有的版本则直接支持setUserId、setUserIp这些快捷方法。上面代码里用了双花括号初始化如果你项目里禁用了这种写法可以改成先创建User对象再set。本质上不复杂按实际SDK版本调整即可。4.2 业务侧一行调用封装完之后业务代码变得非常清爽。比如在Spring Boot的Controller里PostMapping(/comment) public Result postComment(RequestBody CommentRequest request) { String content request.getContent(); if (!TextModerationUtil.isContentAllowed(content)) { return Result.error(内容包含违规信息请修改后提交); } // 正常业务逻辑 commentService.save(request); return Result.success(); }这里只展示了一种最简调用。如果你想在后台记录每一次审核内容可以调用moderate()拿到完整结果连同业务ID一起存库。后面做误判申诉、人工复核、安全报表时这些数据都会很有价值。4.3 关键参数的可配置化工具类里我在接口上暴露了bizType、dataId、userIp几个参数但业务调用方多数时候只传一个content。我的建议是把bizType从方法参数改成可配置项因为不同业务场景评论、昵称、私信在腾讯云控制台可以配置不同的审核策略一个工具类往往要对接多个BizType。更合理的做法是定义一个枚举public enum BizType { COMMENT(comment), NICKNAME(nickname), CHAT(chat); private final String value; BizType(String value) { this.value value; } public String getValue() { return value; } }调用方需要指定审核策略时传入枚举值即可。工具类内部会把bizType原样透传给SDK。这样一来同一个工具类可以服务多个业务线而策略控制权仍然保留在云端配置中。5. 踩坑记录与排查思路5.1 放行策略没想到Review这层坑这是我最开始踩的坑。当时只判断了SDK返回的BlockReview直接放行了觉得“没被Block就是安全的”。结果运营同事反馈某些明显可疑的广告词都通过了。一查日志发现返回的Suggestion一直是Review也就是系统建议人工复核而我把它当成了Pass。后来工具类改了规则只有Pass才直接放行Review和Block都走拦截或人工流程。这个策略不能省。5.2 长文本触顶请求失败用户输入一长段文章直接调用接口报参数错误或请求被拒原因是单次请求文本长度超了官方限制。我第一版工具类没有做长度检查上线第一天就收到告警。后来在入口处加上了UTF-8字节数判断超长时按800字符一段切分逐段审核任一段不过就整体拦截。这里有个细节切分时尽量不要把一个词从中间劈开否则可能影响审核效果。我当前实现是简单按字符切如果你们有更高要求可以按中文标点或空格切分。5.3 中文乱码与编码不一致有段时间我们内部网关对请求体做了重新编码导致传入工具类的字符串出现乱码审核结果完全不可用。排查时发现问题的根源不在腾讯云SDK而是下游服务把GBK字节流错误解码成了UTF-8字符串。这个属于业务链路的编码问题工具类本身无能为力但可以在入参校验时做一个启发式检查如果文本中这类替换字符占比过高直接打警告日志方便快速定位链路问题。5.4 高并发下频繁创建Client导致连接飙升早期的工具类示范代码最喜欢在方法内部new TmsClient我第一版也这么干结果压测时连接数直接飙到几千服务差点被拖垮。SDK内部会持有HTTP连接池每个Client实例都有自己的连接池实例创建得越多连接浪费越严重。改成单例之后连接复用正常经过压测稳定在一个低水平。这个经验同样适用于其他云厂商SDK凡是重量级Client一律全局复用。5.5 内网环境请求超时还有一种常见情况服务部署在内网只开放了部分公网访问能力而腾讯云TMS接口域名没有加白。工具类本身没问题但请求一直超时。遇到这种情况先单独用curl测试接口连通性确认是否是网络层问题。同时建议在工具类里设置合理的超时时间SDK默认超时对有些业务来说太长会拖慢主流程。HttpProfile httpProfile new HttpProfile(); httpProfile.setConnTimeout(3000); // 连接超时3秒 httpProfile.setReadTimeout(10000); // 读超时10秒以上配置可以在初始化TmsClient时传入ClientProfile。调整超时后接口调用失败会更快暴露避免用户请求无限等待。6. 最后再分享几个实践经验工具类写完不代表事情结束了。我在实际运行中还会做几件事第一控制台报警和监控要接上腾讯云后台能看到每天的调用量和命中分布如果某个BizType的命中率突然飙升那往往意味着有新的对抗手法出现了。第二工具类日志里不要记录完整文本尤其是用户昵称、私信这类敏感内容我一般只记录哈希值和长度必要时记录前几后几字符避免日志成为新的数据泄露点。第三降级策略不要一律FailOpen像支付、账号相关的高危场景宁可FailClosed也不能放过去这个要根据业务风险单独配置。后续如果要扩展可以在工具类基础上增加结果缓存。短时间内重复出现的评论或昵称直接复用上次审核结果能省下不少调用量。审核结果不是“查一次就永远正确”的关键词库和模型会持续更新所以缓存时间不宜过长我个人建议控制在五分钟以内。腾讯云文本审核这个能力本身是成熟的但把它用好封装成团队内通用的基础设施需要考量的细节一点不比业务代码少。希望这个工具类的设计思路和代码能给你一些启发。你在接入过程中遇到过什么奇怪的问题或者有更好的封装技巧也欢迎拿出来交流。

相关新闻

神龙汽车新春守护直播:车企节点型用户关系运营如何落地

神龙汽车新春守护直播:车企节点型用户关系运营如何落地

看到“知音相伴 一路同行|神龙汽车‘新春守护暖心发布’专场直播即将温情启幕”这个标题时,我第一反应是:这不只是一场普通的车企直播预告,而是一个典型的“节点型用户关系运营”样本。作为一个常年做汽车品牌营销和直播策划的人&…

2026/10/19 3:15:05 阅读更多 →
JSON-RPC 2.0实战:Python实现、TCP/HTTP接入与避坑指南

JSON-RPC 2.0实战:Python实现、TCP/HTTP接入与避坑指南

简介:jsonRPC.rar是一份面向Java Web开发者的JSON-RPC入门案例资源包,集中演示了轻量级远程调用协议的基本原理与实现方式,解决分布式服务通信及前后端API交互场景中的实际搭建问题。资源共35个文件,包含25个jar依赖库&#xff08…

2026/10/19 3:15:05 阅读更多 →
TTHHO优化RBF神经网络参数:分类预测准确率突破91%的实战解析

TTHHO优化RBF神经网络参数:分类预测准确率突破91%的实战解析

如果你手头有一个分类任务,先试了试直接训练径向基函数网络(RBF),结果测试集准确率卡在八成出头再也上不去,你大概率会怀疑:是不是数据没处理好?特征选择有问题?我告诉你&#xff0c…

2026/10/19 3:15:04 阅读更多 →

最新新闻

开源代码评审工作流:基于 Git Diff 与本地 LLM 的轻量级实践

开源代码评审工作流:基于 Git Diff 与本地 LLM 的轻量级实践

1. 项目概述:这不是一个“工具”,而是一套可落地的开源代码评审工作流“open-code-review”这个标题乍看像某个开源项目名,但结合当前技术社区的真实脉络——它根本不是指某款现成的 CLI 工具,也不是某个已上架 npm 或 GitHub 的具…

2026/10/19 4:06:33 阅读更多 →
集装箱方向分类实战:小样本图像分类的数据策略与模型训练

集装箱方向分类实战:小样本图像分类的数据策略与模型训练

1. 从“集装箱方向分类”这个不起眼的任务说起我在自动化码头和智能吊装项目里头泡了几年,会发现一个规律:越不起眼的小任务,越容易成为整套系统的瓶颈。集装箱侧面方向分类就是一个典型例子。所谓“方向分类”,就是输入一张集装箱…

2026/10/19 4:06:33 阅读更多 →
集装箱方向分类数据集制作与模型训练全流程复盘

集装箱方向分类数据集制作与模型训练全流程复盘

这个分类数据集是我在某次自动化改造项目里,为了做集装箱位姿判断和箱门朝向识别而整理的,总共2408张图片,划分了5个类别。单独看数字不算大,但方向分类和通用目标检测不一样,它是一个状态有限的细粒度问题&#xff0c…

2026/10/19 4:06:33 阅读更多 →
C#与C++串口通信实战:机械手臂实训项目代码全解析

C#与C++串口通信实战:机械手臂实训项目代码全解析

简介:这是一套面向工业自动化学习者的机械手臂控制系统实训源码,基于C#与C混合开发,覆盖客户端与服务器两端。C#部分实现用户注册登录、数据库加密验证及摄像头视频流显示,涉及AForge/Emgu CV图像处理;C部分负责底层通…

2026/10/19 4:06:33 阅读更多 →
开源代码审查CLI工具:基于Git与本地LLM的结构化审查实践

开源代码审查CLI工具:基于Git与本地LLM的结构化审查实践

1. 项目概述:一个真正能落地的开源代码审查 CLI 工具“open-code-review”这个名字乍一听像某个 GitHub 上刚起步的玩具项目,但如果你真把它当成一个普通命令行工具去试,很快就会发现——它根本不是在模拟 review,而是在重构代码审…

2026/10/19 4:06:33 阅读更多 →
Unity UGUI Scroll View 核心:Content 锚点与尺寸设置详解

Unity UGUI Scroll View 核心:Content 锚点与尺寸设置详解

做 UGUI 开发这几年来,Scroll View 应该是被问得最多的组件之一。很多朋友第一次接触 Unity 3D 里的 Scroll View,都是直接往场景里一拖,然后顺手就把图片、按钮、文本全部塞进 Scroll View 节点下面,结果运行起来发现列表不滚动、…

2026/10/19 4:05:33 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →