Java老系统AI改造实战:低成本接入大模型与RAG
很多企业手头都压着一套跑了好几年的Java老系统Spring Boot MyBatis数据库里攒了不少业务数据接口文档零零散散领导突然说要“接入AI”。我的建议是别慌别想着重写更别因为这事把一个好好的Java团队逼着去学Python和一堆新框架。低成本改造老系统的核心不是把技术栈推倒重来而是利用现有Java生态把AI能力当成一个外部服务接进来让老系统“长出新功能”同时原有业务逻辑照常运转。这篇文章不聊空泛理论只讲我们在两套真实的传统企业系统上做AI改造时踩过的坑和最终采用的落地方案——从选场景、搭脚手架、接大模型到把AI结果写回业务库整个过程摊开了讲希望能给你一条可以直接抄作业的路线。1. 整体设计老系统AI改造的底层逻辑与思路1.1 先回答一个关键问题老系统改造到底在改什么很多团队的误区是一提到AI改造就想着把系统整个重写换语言、换架构、上向量数据库甚至想把跑了几年、几十万行的业务代码全部推倒。我在实际评估过几套老系统后得出的结论是企业老系统的价值在于“确定”二字——业务流程是确定的、权限模型是确定的、多年积累的业务数据是确定的宝贵资产。AI改造要做的事情是给这些“确定”的部分加上一层“不确定但聪明”的能力比如从工单文本里自动提取分类、根据客户历史行为生成推荐话术、对长文档做结构化摘要。说白了核心业务逻辑不该被AI重写AI只是帮系统“看见”那些原本难以处理的非结构化信息再通过业务接口把结果喂回老逻辑。1.2 为什么Java生态是低成本的第一选择这里强调的“低成本”并不单指API调用费用更重要的是“改造过程本身的成本”。第一团队不必学习新的语言也不需要引入一套新的部署体系。Java工程师在国内存量巨大哪怕临时拉一个外包写个HTTP调用接口都很熟这直接就省掉了团队学习和培训的成本。第二老系统已有的HTTP、JSON、数据库连接池、定时任务、权限体系等基础设施都可以复用。像java面试里反复考的集合、异常处理、线程池这些基础能力在AI接入时同样够用不需要额外学什么高阶语法。第三Java生态里已有大量现成的AI集成资源无论是直接用Spring框架的RestClient/RestTemplate还是引入Spring AI或LangChain4j都是加依赖而不是改架构。我直接说结论在不考虑极端高并发的情况下完全没必要为了一个AI功能单独部署一个Python微服务那样反而把运维链路拉长了。1.3 改造的核心路径松耦合 渐进式替换我总结的老系统AI改造路径就八个字松耦合、渐进式。AI能力不要直接嵌进每个Service里而是独立封装成一层LlmService或AiProcessor通过接口对外提供服务。老系统业务侧只在需要的地方调用这一层其他地方保持不变。这样做的最大好处是可以独立升级AI模型的版本、切换不同供应商、调整提示词策略都不用动原有业务代码。渐进式替换则意味着不要一期想把所有功能都AI化哪怕第一个季度只落地一个工单分类场景也比憋大招半年然后失败强得多。这套思路在我参与的项目里几乎没有被推翻过。2. 选准第一个场景AI改造的“最小可行切片”2.1 从老系统里筛出“高频且可验证”的场景找一个合适的AI落地场景是改造成功的第一步也是最容易被忽略的一步。我的判断标准很简单这个功能如果原来有人手动做、每天要做很多次、结果还相对客观那就值得先用AI顶上去。常见的低风险切片包括客服工单分类和摘要、销售记录中的客户意向识别、合同或文档里的关键字段提取、运维日志分级。我处理过的一个典型老系统是一套企业内部IT服务台系统每天几百条工单分类全靠人肉看一遍再打标签。老系统本身没有机器学习能力但工单的文本、处理人、时间等字段都在数据库里。我们改造的第一步就是写一个定时任务把新增工单文本捞出来调用大模型API返回“分类、优先级、是否需要升级”再自动回写工单表。整个改动没有触碰原业务流程只是在老系统旁边加了一个“AI处理器”。2.2 场景确定后的三个配套准备数据出口、人工兜底、效果口径一旦场景定下来先别急着写代码有三件事必须提前想清楚。第一是数据出口干不干净工单文本是存在一个字段还是被拆到多个备注字段里这决定了要不要做文本拼接和清理。第二是人工兜底AI返回结果后系统里必须保留“人工确认/修改”的入口千万不能直接让AI去改数据库里的关键状态。第三是效果口径比如分类准确率达到多少算成功这个必须和业务方提前对齐否则上线后全凭感觉争论。我在实际项目中是把AI输出先放到一张临时表里由系统根据置信度阈值自动生效或进入人工复核队列。这个设计虽然多写了几十行代码但上线后业务部门和我们技术团队之间少吵了非常多架。3. 核心实操在Java老系统里接入AI能力3.1 选对接入方式轻量HTTP客户端对老系统最友好我在改造中实测过三种方式。第一种直接用JDK自带HttpURLConnection优点是零额外依赖但代码繁琐、可读性差第二种用Spring框架里的RestTemplate/RestClient这是老Spring项目里最常用的方式集成成本极低第三种引入Spring AI框架优点是封装了对话、提示词模板、函数调用很现代但如果老系统还在Spring Boot 2.x为了引入它去升级框架版本本身就是不小的工程。我的经验是老系统停留在Spring Boot 2.x的优先用RestTemplate或OkHttp风险几乎为零如果系统已经是Spring Boot 3.x可以考虑引入Spring AI但也要先做版本兼容性验证。下面这段代码是我在一套公开示例风格改造里常用的写法放在绝大多数老Spring项目里可以直接跑Service public class LlmService { Value(${llm.api.url}) private String apiUrl; Value(${llm.api.key}) private String apiKey; private static final RestTemplate restTemplate new RestTemplate(); public LlmResult chat(String userMessage) { HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(apiKey); // 按OpenAI兼容格式组装请求国内绝大多数模型服务也都兼容这种格式 MapString, Object body new HashMap(); body.put(model, model-name); body.put(messages, List.of(Map.of(role, user, content, userMessage))); body.put(temperature, 0.3); HttpEntityString entity new HttpEntity(JsonUtils.toJson(body), headers); ResponseEntityString response restTemplate.postForEntity(apiUrl, entity, String.class); return LlmResult.parse(response.getBody()); } }3.2 老系统配置与参数调优别把temperature拉满接入时需要理解两个容易被忽略的参数temperature和max_tokens。做分类、字段提取这类确定型任务temperature建议设置在0到0.3之间太高了AI会“自由发挥”可能出现全新标签做文案生成类任务可以放宽到0.7左右。max_tokens是成本控制的关键很多团队习惯把回复长度上限设成4096实际上分类任务返回几十个字符就够调成512能明显降低token消耗。老系统里的配置建议统一放在application.yml中并区分环境尤其要注意API Key不能硬编码在代码里更不能提交进Git仓库这个坑我在早期项目里踩过不止两次一旦key泄露损失的不只是费用还可能影响业务数据安全。3.3 结果回写老业务库的两种模式直接回写与置信度回调AI结果要进老系统最简单的做法是在原表旁边加几个扩展字段比如ai_category、ai_priority、ai_status把API返回内容解析后直接UPDATE。使用MyBatis-Plus的老项目尤其方便在实体类上直接加字段再调updateById就能回写连自定义SQL都不用写。但更稳妥的是“置信度回调”模式要求大模型返回JSON时同时输出confidence字段系统只对confidence大于0.8的结果自动生效其余进入人工确认列表。我们当时把阈值定在0.85每天几百条工单里需要人工复核的大概只有几条业务方看着这个比例也很放心。这里给个提醒大模型返回的confidence不是真正的概率它只是一个“自我估计”所以阈值要根据实际数据做小样本调参别照抄网上经验值。3.4 进阶让老系统支持“函数调用”式的高级交互如果想让AI能直接查询老系统里的订单接口或者主动触发某个动作就得用“函数调用/工具调用”。原理其实很朴素你告诉大模型“系统里有哪些函数、分别需要哪些参数”模型根据用户问题决定要不要调用某个函数以及传入什么参数。Java侧只需要实现一个通用dispatcher把模型返回的函数名和参数映射到对应的Service方法。这里有个容易翻车的地方函数调用的参数校验必须放在Java侧做因为大模型给出的参数经常出现“自创枚举值”比如客户类型传了一个“重要用户”但数据库里根本没有这个值直接反射调用或者拼SQL就会报错。我们后来在dispatcher里加了一个参数白名单校验宁可拒绝调用也不能盲目执行。4. 进阶低成本给老系统加上企业知识库4.1 老系统里散落的知识资产要怎么处理业务系统跑久了里面积累的FAQ、操作手册、客户备注、历史工单这些非结构化数据就是建设企业知识库的最佳素材。RAG的基本思路是先把文档切成小段用向量模型转成向量存起来用户提问时把问题也转成向量去库里找最接近的几段连同问题一起发给大模型让它基于这些片段回答。老系统改造时不需要自己实现向量算法只需要选一个轻量级的向量化方式和向量存储。有些团队一上来就引入Elasticsearch的向量模块或者独立部署一个Milvus如果数据量只有几十万条这明显过度设计。用关系型数据库加一个向量字段或者引入一个简单的向量搜索库成本要低一个数量级。4.2 用Java实现一个最小RAG链路的踩坑记录我给一个低成本可验证的思路文档切分可以先用字符数粗切每段500字符、重叠50字符后面再按效果调优向量化可以直接调用大模型服务商提供的embedding接口向量存储方面如果项目已经用了MySQL可以先用一个float数组字段或者字符串存储向量再用工具类计算向量相似度。我们当时是先用Hutool的HttpUtil调用embedding接口把向量转成float数组存进内存List里做离线验证等确认效果后再决定是否引入Redis存储。整个验证阶段没有引入任何新的中间件成本几乎为零。下面是我在验证阶段经常写的一段代码实际项目里可以换成你们项目已经统一的HTTP客户端public ListFloat embed(String text) { String body HttpRequest.post(embeddingUrl) .header(Authorization, Bearer apiKey) .body(JsonUtil.toJson(Map.of( model, embed-model, input, text ))) .execute().body(); JSONObject data JsonUtil.parseObj(body); return data.getJSONObject(data) .getJSONArray(embedding) .toList(Float.class); }4.3 embedding模型选型与切分策略的实战经验embedding模型的选择会直接影响检索质量。我测过几个主流服务商的通用模型中文场景差异并没有宣传的那么大真正影响效果的是“切分方式”。比如把整段工单原文压成几个长片段检索到的片段常常会在关键数字或结论处被切断导致问答不准。后来我们改成按句子切分并保留段落标题作为前后缀效果立刻好了不少。另外向量化后的存储格式要统一Java侧用float数组计算余弦相似度时要注意范数是否为0空向量经常出现在纯符号文本上不处理会直接报除以零的错误。5. 成本控制与老系统性能的平衡5.1 token成本怎么算以及三个立竿见影的省钱手段既然讲低成本就必须把API费用聊透。一个简易估算方式1个汉字在多数模型里约等于1.5到2个token。省钱有三招都很实操。第一招把max_tokens压到任务需要的合理范围比如分类任务设256就够第二招精简系统提示词把客套话和冗余背景信息拿掉我们自己的提示词从最初300字压缩到120字效果没降token直接省了一半第三招做结果缓存同一个客户ID、同一周的摘要请求如果没有新数据就直接用库里的缓存结果这个在老系统里用一张redis或普通表就能做效果立竿见影。还有一点别忽略回复内容里的固定头部和格式模板也会消耗token可以在解析后只保存有效内容减少下次输入拼接体积。5.2 老系统并发低不能因为AI把接口拖垮老系统普遍没有为长耗时接口做准备。大模型API的响应时间通常在1到5秒如果直接在用户请求线程里同步调用用户侧会感觉明显卡顿数据库连接池也可能被占满进而拖垮整个系统。改造时的铁律是所有AI调用一律异步化。做法很简单用Spring的Async或者MQ把AI任务丢到后台接口立即返回“处理中”AI完成后通过WebSocket或轮询把结果推给前端。如果不想引入MQ用Async加任务表轮询就能撑住绝大多数场景。我见过一个项目忘了这条在秒级超时的网关后面直接同步调大模型结果线上反馈“系统变慢”最后定位到是连接池被AI请求拖死了。5.3 灰度发布与回滚预案AI功能必须能一键关闭企业老系统改造最怕上线后出问题却没法快速恢复。我给团队定过三条规则今天也分享出来。第一AI功能全部走独立配置开关放在配置中心或application.yml里的llm.enabled字段关闭后立即回退到原人工流程第二AI的写操作不直接触碰原表统一经过一个可观测的服务层操作日志全记录第三灰度按用户分组进行一开始只对内部测试组开放跑两周没问题再逐步放量。这些听起来像老生常谈但我亲眼见过不少团队上线AI时连开关都没留一有问题就得改代码重新发布一个不必要的高成本事件。6. 常见问题与排查技巧实录6.1 大模型接口返回延时越来越高排查时先看是不是网络层没做超时配置。RestTemplate默认的超时可能很长一旦上游服务出现抖动老系统里的请求就会跟着大量阻塞。建议统一设置connectTimeout和readTimeoutconnect设5秒read设60秒基本够用。还要看是不是API Key有并发限制很多服务商默认限制每分钟请求数如果定时任务一次性捞了一万条工单并发请求会被限流甚至触发封禁。我们当时的对策是加一个信号量做限速令牌每秒钟最多放5个请求出去整个运行期都很稳。6.2 AI返回JSON经常解析失败大模型虽然被要求返回JSON但偶尔会夹带解释文字或者把中文引号换成全角甚至自动加一个markdown代码块。解决思路不是反复调模型而是Java侧做容错提取内容里第一个“{”到最后一个“}”再解析同时在提示词里明确要求“只输出JSON对象不要markdown不要解释”。我自己的工具类里专门写了一个stripJson方法把常见非法字符按规则剔除实测解析成功率能到99.5%以上。这里提醒一句不要指望正则一次到位模型输出格式会换着花样给你惊喜建议用三层降级先直接解析失败就清洗后再解析再失败就返回一个默认的“处理失败”结果并告警。6.3 老系统JDK版本太低能不能接AIJava 8仍然是很多老系统的主力好消息是调用大模型API本质上就是发HTTP请求Java 8自带的HttpURLConnection也能干只是代码比较丑。如果选第三方HTTP类库注意选兼容Java 8的版本比如OkHttp 3.x、Hutool、RestTemplate都没问题。Spring Boot 2.x本身就是基于Java 8的所以完全不用为了接AI去升级JDK。我自己就在一个Java 8的系统上完整跑通了AI接入核心依赖只加了Hutool和fastjson或Jackson老系统也能玩得很顺畅。6.4 业务方觉得AI“不够专业”怎么办这其实不是技术问题而是预期和反馈闭环的问题。我的经验是在AI处理结果展示区域加上“AI处理”的标识同时支持人工修改并记录修改原因积累两周后把人工修改比例高的场景拿出来重新优化提示词或补充知识库内容形成一个持续改进的闭环。让业务方看到AI和人在协作、AI确实减少了重复劳动比任何汇报文档都管用。你甚至可以做一个简单的“AI节省工时”统计看板把每天自动分类成功的工单数量换算成人工时间这个数字对向上汇报非常有效。如果让我总结这次Java老系统AI改造最值得记住的一点那就是“别把AI当神仙也别把改造当推倒重来的大工程”。我见过太多项目死在“想一步到位建一个完美的AI中台”上结果半年过去连第一个场景都没上线。反过来用最小的切片、最平实的技术、老团队原本就熟悉的代码方式先让一个工单分类功能跑起来再顺着业务反馈慢慢扩展RAG、函数调用这些能力反而走得又快又稳。改造老系统不是给老车换发动机更像给一台准点跑了十年的车配一套聪明的导航——发动机不动轮子照转但从此再也不怕走错路了。

相关新闻

每日打卡5:从微习惯到高效任务管理的实战指南

每日打卡5:从微习惯到高效任务管理的实战指南

“每日打卡5”这个标题乍一看特别简单,甚至有点没头没尾,但真正上手做过习惯养成、任务管理的人,一眼就能明白它背后的分量。打卡不是“每天记一笔”那么轻飘飘的事,它是一套把抽象目标变成具体动作的反馈系统,而“5”…

2026/10/9 8:23:06 阅读更多 →
系统全盘备份与还原实战:用Dism++打造一键恢复的工作环境

系统全盘备份与还原实战:用Dism++打造一键恢复的工作环境

重装系统最烦人的不是丢数据,而是丢"工作状态"。我的办公电脑里装了几十个软件,每个软件还有各自的账号登录态、窗口布局、快捷键设置和启动项,哪怕用U盘把安装包全留好,重新装一遍也要折腾大半天。更别说Windows更新抽…

2026/10/9 8:23:06 阅读更多 →
幼儿园管理系统开发实战:选班并发控制与膳食过敏原管理

幼儿园管理系统开发实战:选班并发控制与膳食过敏原管理

接到幼儿园管理系统这个项目的时候,我心里是有点发怵的。找我的人是一家民办连锁幼儿园的园长,她给我看了三个竞品系统的报价,然后补了一句:我们也想做个软件,但别像他们那样——光有个花名册,老师还得天天…

2026/10/9 8:23:06 阅读更多 →

最新新闻

AI智能体核心能力:从工具调用到团队协作的TaoToken实践

AI智能体核心能力:从工具调用到团队协作的TaoToken实践

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

2026/10/9 10:01:44 阅读更多 →
数据仓库用户权限管理:分层解耦架构实战

数据仓库用户权限管理:分层解耦架构实战

1. 项目概述:为什么“数据仓库-用户管理”不是一句空话,而是每天都在掉头发的实操现场“数据仓库-用户管理实践”这八个字,乍看平平无奇,像极了某次内部培训PPT第3页的标题——但凡在数据平台一线干过两年以上的人都知道&#xff…

2026/10/9 10:01:44 阅读更多 →
特殊工业镜头选型指南:远心、线扫、显微与紫外红外镜头原理与避坑

特殊工业镜头选型指南:远心、线扫、显微与紫外红外镜头原理与避坑

1. 从"看不见"到"看得清":特殊工业镜头的价值锚点工业视觉系统里,相机机身决定了图像的上限,而镜头决定了你能不能摸到那个上限。很多人做视觉方案时,习惯把预算大头砸在相机上,觉得分辨率越高越好…

2026/10/9 10:01:44 阅读更多 →
汽车修理数据库设计:从ER图到可运行Access系统全链路实操

汽车修理数据库设计:从ER图到可运行Access系统全链路实操

简介:本资源是一份面向高校数据库课程学习者的完整课程设计文档,聚焦汽车修理管理系统的数据库原理实践应用,适用于计算机、软件工程等专业学生巩固ER建模、关系规范化、SQL建表与业务逻辑设计能力。文档为单文件Word格式(.doc&am…

2026/10/9 10:01:44 阅读更多 →
数据、算法、算力:数字经济三大支柱的协同校准与落地实践

数据、算法、算力:数字经济三大支柱的协同校准与落地实践

1. 这不是概念堆砌,而是数字世界运转的“三原色”你打开手机点外卖,30秒内系统就给你推了5家评分4.8以上、距离2公里内、刚上新辣子鸡的店;你上传一张模糊的老照片,AI几秒钟就修复出清晰五官和自然肤色;某制造企业把产…

2026/10/9 10:01:44 阅读更多 →
k3d 中的 Go YAML 解析实践:go.yaml.in/yaml/v2 库使用指南

k3d 中的 Go YAML 解析实践:go.yaml.in/yaml/v2 库使用指南

云原生容器编排 【免费下载链接】k3d Little helper to run CNCFs k3s in Docker 项目地址: https://gitcode.com/gh_mirrors/k3/k3d 点击查看 免费下载 YAML 是 k3d 配置文件(k3d.io/v1alpha5 等版本)的载体,而 go.yaml.in/yaml…

2026/10/9 10:00:30 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 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/8 21:13:17 阅读更多 →
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/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练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/9 6:17:20 阅读更多 →