基于LLM的智能代码修复代理:原理、实现与挑战
1. 项目初探RepairAgent是什么以及它想解决什么问题最近在GitHub上闲逛发现了一个挺有意思的项目叫“sola-st/RepairAgent”。点进去一看仓库描述和文档都还比较早期但项目名和代码结构透露出一些关键信息。简单来说RepairAgent这个名字直译就是“修复代理”结合其代码库中大量与代码、文本处理相关的文件我推测这是一个利用大语言模型LLM来自动修复代码错误或文本问题的智能代理框架。这让我想起了日常开发中那些烦人的小问题一个拼写错误导致编译失败一个API调用参数类型不匹配或者一段JSON格式错误导致整个解析流程崩溃。很多时候修复这些问题本身并不复杂但定位和修正的过程却要打断我们连贯的思考。RepairAgent瞄准的很可能就是这类“低复杂度、高打断性”的修复场景。它试图扮演一个“智能助手”的角色在你编码或处理文本时自动识别并尝试修复其中的问题让你能更专注于核心逻辑的构建。从技术栈来看项目里出现了openai、anthropic等LLM供应商的客户端依赖以及pydantic用于结构化数据验证这基本坐实了它是一个基于现有大模型API构建的应用层框架。它的核心价值不在于从零训练一个模型而在于设计一套高效的流程Agent将用户的问题代码/文本、错误信息、上下文等组织成高质量的提示Prompt调用合适的LLM并解析、验证返回的修复结果。这听起来简单但要做好里面门道可不少。2. 智能修复代理的核心架构与工作流拆解虽然官方文档可能还不完善但通过分析代码结构和常见的AI Agent设计模式我们可以勾勒出RepairAgent大致的核心架构。一个高效的修复代理绝不仅仅是“把错误扔给GPT然后替换原文”那么简单它需要一套严谨的流程来保证修复的准确性、安全性和上下文相关性。2.1 典型工作流从问题输入到修复输出一个完整的修复流程我认为RepairAgent很可能遵循以下步骤这也是此类智能体设计的通用范式问题捕获与上下文收集代理首先需要知道“哪里出了问题”。输入可能是一个包含错误的代码片段、一段报错信息Traceback或者用户直接描述的问题。更重要的是代理需要收集足够的上下文比如错误发生位置前后的几行代码、相关的导入语句、甚至整个文件的简化版如果涉及。没有上下文的修复是盲目的很容易产生语法正确但逻辑错误的“修复”。问题分析与任务规划收到原始输入后代理需要进行分析。这一步可能包括判断错误类型语法错误、运行时错误、逻辑错误、格式错误、提取关键错误信息如行号、错误类型、错误描述、评估修复的复杂度。基于分析代理会规划具体的修复任务例如“这是一个第5行的缩进错误需要调整缩进级别”或者“这是一个未定义变量的错误需要检查变量作用域或添加导入”。提示工程与LLM调用这是核心环节。代理需要根据任务规划构造一个给大模型的指令。一个高质量的提示Prompt通常会包含系统角色设定告诉模型“你是一个专业的代码修复助手”。问题描述清晰陈述需要修复的代码和具体的错误。上下文提供给出相关的代码上下文。输出格式要求严格要求模型以特定格式如JSON、包含“原代码”和“修复后代码”的Markdown代码块返回结果。这是实现自动化处理的关键。约束与规则例如“只修复指定的错误不要改动其他无关代码”、“确保修复后的代码符合PEP 8规范”。结果解析与验证LLM返回的文本需要被解析成结构化的数据。RepairAgent很可能使用Pydantic模型来定义返回的数据结构确保必填字段存在且类型正确。解析成功后还需要进行基础验证比如检查修复后的代码是否能通过语法解析例如使用ast模块检查Python代码或者修复后的文本是否符合基本的格式规则。修复应用与用户确认可选将验证通过的修复方案应用到原文件中。在自动化程度高的场景可以直接替换在需要人工审核的场景则可以生成一个差异对比Diff供用户查看和确认。2.2 架构中的关键组件猜想基于上述工作流RepairAgent的代码库中应该包含以下关键组件Agent 核心类这是大脑协调整个工作流。它持有LLM客户端配置管理状态并调用其他组件。Prompt 模板管理器负责存储和渲染不同修复场景Python语法错误、JSON格式化、API调用错误等下的提示模板。模板可能是Jinja2格式的字符串能够动态插入问题代码、错误信息等变量。LLM 客户端封装统一封装对OpenAI、Anthropic等不同供应商API的调用处理认证、重试、速率限制等通用问题向上提供一致的接口。解析与验证器专门负责解析LLM的返回结果并将其转换为内部数据结构。同时包含一些轻量级的验证规则比如代码语法检查器、JSON格式验证器等。上下文提取器这是一个容易被忽略但至关重要的组件。它的职责是从用户提供的原始输入可能是一个文件路径、一个代码片段加错误信息中智能地提取出对修复最有用的上下文信息避免给LLM输入过多噪音或过少信息。注意在实际使用这类工具时一个常见的误区是认为“给的上下文越多越好”。其实不然。过多的无关上下文会消耗宝贵的Token限额还可能分散模型的注意力导致修复质量下降。一个好的上下文提取器需要一定的启发式规则比如围绕错误行号提取前后N行识别并包含相关的函数定义或类定义等。3. 实战演练如何基于RepairAgent的设计思想构建自己的修复工具由于sola-st/RepairAgent项目可能处于早期文档和示例不全我们不妨借鉴其设计思想动手搭建一个简化版的、针对特定场景的修复代理。这里我以“自动修复Python代码中常见的NameError和IndentationError”为例演示核心实现过程。3.1 环境准备与依赖安装首先我们需要一个Python环境建议3.8和必要的包。除了OpenAI的库我们还会用到python-dotenv管理密钥pydantic做数据验证。pip install openai python-dotenv pydantic接下来在项目根目录创建.env文件存放你的OpenAI API密钥OPENAI_API_KEYsk-your-api-key-here3.2 定义核心数据模型使用Pydantic定义我们期望LLM返回的数据结构这是保证流程稳定的基石。from pydantic import BaseModel, Field from typing import Optional class CodeFix(BaseModel): 描述一次代码修复的结构。 original_code: str Field(description出现问题的原始代码片段) fixed_code: str Field(description修复后的代码片段) explanation: str Field(description对修复原因和方式的简要解释) error_type: Optional[str] Field(defaultNone, description识别出的错误类型如NameError, IndentationError)3.3 构建提示模板我们将设计一个针对简单Python错误的提示模板。注意我们明确要求模型以JSON格式返回并匹配我们的CodeFix模型。SIMPLE_PYTHON_FIX_PROMPT_TEMPLATE 你是一个专业的Python代码修复助手。你的任务是修复用户提供的代码片段中的错误。 请遵循以下规则 1. 只修复导致代码无法运行的具体错误如NameError, IndentationError, SyntaxError。 2. 不要改变代码的原有功能和逻辑。 3. 保持代码风格简洁。 4. 你必须以JSON格式回复且JSON必须严格符合以下结构 {{ original_code: 原始代码字符串, fixed_code: 修复后的代码字符串, explanation: 修复说明, error_type: 错误类型 }} 下面是需要修复的代码和错误信息 代码片段{code_snippet}错误信息如果有{error_message}请直接输出JSON不要有任何额外的解释或标记。 3.4 实现修复代理核心类现在我们将各个部分组合起来形成一个可工作的简单代理。import os import json from openai import OpenAI from dotenv import load_dotenv from .models import CodeFix # 假设上面的模型定义在models.py中 from .prompts import SIMPLE_PYTHON_FIX_PROMPT_TEMPLATE # 假设提示模板在prompts.py中 load_dotenv() class SimplePythonRepairAgent: def __init__(self, model: str gpt-3.5-turbo): self.client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) self.model model def _call_llm(self, prompt: str) - str: 调用LLM并返回其回复内容。 try: response self.client.chat.completions.create( modelself.model, messages[{role: user, content: prompt}], temperature0.1, # 低温度使输出更确定、更专注于修复 ) return response.choices[0].message.content.strip() except Exception as e: raise RuntimeError(f调用LLM API失败: {e}) def _parse_llm_response(self, response_text: str) - CodeFix: 解析LLM的回复尝试提取JSON并验证为CodeFix模型。 # 尝试从回复中提取JSON块。有时模型会在JSON外加一层markdown代码块或说明。 import re json_match re.search(rjson\n(.*?)\n, response_text, re.DOTALL) if json_match: json_str json_match.group(1) else: # 如果没有代码块假设整个回复就是JSON根据我们的提示要求 json_str response_text try: data json.loads(json_str) fix CodeFix(**data) return fix except (json.JSONDecodeError, Exception) as e: # 如果解析失败这是一个需要记录和处理的严重问题 raise ValueError(f无法解析LLM的返回结果为有效的CodeFix对象。原始返回{response_text[:200]}... 错误{e}) def repair(self, code_snippet: str, error_message: str ) - CodeFix: 主修复方法。 :param code_snippet: 需要修复的代码字符串 :param error_message: 可选的错误信息如traceback :return: CodeFix对象包含修复结果 # 1. 构建提示 prompt SIMPLE_PYTHON_FIX_PROMPT_TEMPLATE.format( code_snippetcode_snippet, error_messageerror_message ) # 2. 调用LLM print(正在调用LLM分析问题...) llm_response self._call_llm(prompt) # 3. 解析和验证结果 print(正在解析LLM返回结果...) code_fix self._parse_llm_response(llm_response) # 可选4. 基础语法验证 - 这里可以添加ast.parse检查fixed_code的语法 # 如果验证失败可以抛出异常或进行重试 try: import ast ast.parse(code_fix.fixed_code) except SyntaxError as e: raise ValueError(fLLM返回的修复代码存在语法错误修复可能失败。错误{e}) return code_fix3.5 测试我们的简易修复代理让我们用一个经典的NameError例子来测试一下。if __name__ __main__: agent SimplePythonRepairAgent() buggy_code def greet(): message Hello, name # 这里name未定义 print(message) greet() error_msg NameError: name name is not defined try: fix agent.repair(buggy_code, error_msg) print(\n 修复报告 ) print(f错误类型: {fix.error_type}) print(f修复说明: {fix.explanation}) print(f\n原始代码:\n{fix.original_code}) print(f\n修复后代码:\n{fix.fixed_code}) except Exception as e: print(f修复过程中出现错误: {e})运行这个脚本你可能会得到类似下面的输出正在调用LLM分析问题... 正在解析LLM返回结果... 修复报告 错误类型: NameError 修复说明: 变量name在使用前未定义。在函数greet内部或外部为其赋予一个值例如空字符串或从参数获取。 原始代码: def greet(): message Hello, name print(message) greet() 修复后代码: def greet(): name World # 或者改为从参数获取def greet(name): message Hello, name print(message) greet()看代理成功地识别了NameError并给出了一个合理的修复方案——在函数内部定义了变量name。当然更优的修复可能是将name改为函数参数但当前简单的提示下模型给出的修复在语法和基本逻辑上是正确的。4. 深入思考RepairAgent类项目的挑战与优化方向构建一个玩具版的修复代理相对简单但要让其达到生产可用的可靠度sola-st/RepairAgent这类项目必须面对并解决一系列严峻挑战。这也是评价其设计是否精妙的关键。4.1 核心挑战一修复的准确性与安全性这是最大的挑战。LLM可能会产生“幻觉”给出看似合理实则错误的修复。错误修复比如将list.append(x)“修复”成list.add(x)。引入新问题修复了A错误却意外引入了B错误或安全漏洞。过度修改改变了用户原本的代码意图或逻辑。优化策略多层验证除了语法验证可以引入更严格的验证如对修复后的代码运行单元测试如果项目有、进行简单的静态分析如使用pylint进行基础检查、在安全沙箱中执行修复后的代码片段看是否抛出异常。置信度评分与人工审核让LLM在返回修复方案的同时给出一个置信度分数。对于低置信度的修复不自动应用而是生成Diff供用户决策。RepairAgent可以设计一个审核工作流。迭代修复一次修复不成功可以将错误信息和新的代码再次输入给LLM进行多轮迭代但需要设置最大轮数以防死循环。4.2 核心挑战二上下文的精准管理与成本控制LLM API按Token收费无脑传入整个文件甚至整个项目上下文是不现实的。问题如何从庞大的代码库中精准提取与当前错误最相关的几十行代码难点对于“未定义变量”错误变量可能定义在另一个文件对于“方法不存在”错误需要知道类的继承链。优化策略基于抽象语法树AST的分析开发更智能的上下文提取器。当遇到一个函数内错误时提取器可以解析AST找到该函数所在的类、其导入的模块、以及同一文件中相关的函数和全局变量。向量检索对于大型项目可以为代码库建立向量索引。当错误发生时将错误信息转换为向量去索引中检索最相关的代码片段如函数定义、类定义、文档字符串作为补充上下文。分层提示采用“链式思考”Chain-of-Thought或“规划-执行”模式。先让一个LLM分析错误并规划出需要查看哪些上下文再根据规划去收集具体上下文最后让另一个或同一个LLM基于精确的上下文进行修复。4.3 核心挑战三提示工程的稳定性与泛化能力不同的错误类型语法、运行时、逻辑、不同的编程语言、甚至不同LLM模型GPT-4, Claude, 本地模型都需要不同的提示词。维护一大堆提示模板会非常痛苦。优化策略动态提示构建不写死模板而是设计一个“提示构建器”。它根据错误类型、语言、严重程度等元数据从一组基础模块角色设定、规则列表、示例、格式要求中动态组装出最终的提示。示例驱动Few-Shot Learning在提示中包含几个高质量、针对性的修复示例Input-Output对。这对于引导LLM遵循正确的输出格式和理解修复风格非常有效。RepairAgent可以内置一个示例库。提示的版本化与A/B测试像管理代码一样管理提示模板进行版本控制。可以设计实验对比不同提示词在相同错误集上的修复成功率和质量持续迭代优化。4.4 集成与扩展性设计一个好的框架不能只解决一种问题。RepairAgent需要考虑如何让用户方便地扩展。插件化架构允许用户为新的错误类型如SQL语句错误、YAML配置错误或新的验证器如特定框架的规则检查编写插件。核心Agent负责流程调度具体工作由插件完成。多语言支持核心流程抽象化针对不同语言Python, JavaScript, Go, Java实现特定的上下文提取器、语法验证器和示例库。与开发工具链集成提供CLI工具、IDE插件VS Code, PyCharm、或Git钩子让修复能力无缝嵌入到开发者的现有工作流中。5. 从RepairAgent出发构建更广泛的“AI辅助开发”工作流RepairAgent解决的是“事后修复”问题。但AI在开发流程中的潜力远不止于此。我们可以借鉴其Agent设计模式构建一个更完整的AI辅助开发工作流。1. 预防阶段实时代码建议与审查在编码时Agent可以分析正在编写的代码预测可能的问题如未处理的异常、潜在的性能瓶颈、不符合编码规范的写法并实时给出建议。这比写完再修复更前置。2. 调试阶段智能根因分析当程序崩溃或测试失败时当前的RepairAgent可能只看了错误行。一个更强大的Agent可以分析完整的调用栈、日志、变量状态甚至结合代码变更历史推测出根本原因而不仅仅是表面错误。它可以回答“为什么这个参数在这里是None”3. 重构与优化阶段Agent可以接受更高阶的指令如“将这个函数拆分成两个更小的函数以提高可读性”或者“用更高效的数据结构重写这部分循环”。这需要Agent对代码的语义有更深的理解。4. 知识问答与文档生成基于项目代码库Agent可以回答开发者的问题“这个calculate()函数通常在哪些场景下被调用”或者“请为这个新的API类生成一份初步的文档。”要实现这些底层技术栈是相通的LLM 精心设计的Agent工作流 代码上下文管理 安全验证。sola-st/RepairAgent项目如果定位清晰专注于把“修复”这一个点做深做透解决上述的准确性、安全性、上下文管理等问题就能成为一个非常有价值的基石。在此基础上社区可以围绕它构建出上述更丰富的应用场景。在我自己的尝试中最大的体会是永远不要完全信任AI的第一次输出。无论提示词写得多好验证环节必不可少。将AI Agent视为一个“超级实习生”——它很有想法能快速给出方案但它的输出必须经过一位“资深工程师”即你设计的验证逻辑和你的最终审核的检查才能合并到主分支。设计RepairAgent这类系统的艺术就在于如何在“自动化效率”和“人工控制权”之间找到最佳平衡点。

相关新闻

Python并发编程:解决RuntimeError: cannot schedule new futures after interpreter shutdown

Python并发编程:解决RuntimeError: cannot schedule new futures after interpreter shutdown

1. 问题场景:当你的程序“优雅”地崩溃时如果你在写Python程序,尤其是那些涉及多线程、异步任务或者使用了像concurrent.futures、asyncio这类并发库的程序,那么你很可能在某个深夜,被控制台里蹦出的RuntimeError: cannot schedul…

2026/10/14 18:38:45 阅读更多 →
自动化压缩:从通用大模型到专精网页代理的落地实践

自动化压缩:从通用大模型到专精网页代理的落地实践

1. 从“大模型”到“接地气”的网页代理:一个被忽视的鸿沟如果你最近也在关注大语言模型(LLM)和智能体(Agent)的进展,可能会发现一个有趣的现象:一方面,我们惊叹于GPT-4、Claude 3等…

2026/10/14 6:36:29 阅读更多 →
SpringBoot Actuator监控实战:从核心端点到可视化仪表盘搭建

SpringBoot Actuator监控实战:从核心端点到可视化仪表盘搭建

1. 项目概述:为什么我们需要一个“仪表盘”来监控应用? 在开发和运维一个基于SpringBoot的Web应用时,我们常常会遇到这样的场景:应用上线后,你只知道它在“跑”,但它到底“跑”得怎么样?内存用了…

2026/10/4 17:06:57 阅读更多 →

最新新闻

Java汽车4S店管理系统源码实战:Spring Boot+Vue端到端业务闭环

Java汽车4S店管理系统源码实战:Spring Boot+Vue端到端业务闭环

简介:这是一套完整的Java汽车4S店管理系统源码,面向Java Web初学者与中小型汽车服务企业信息化建设者,聚焦销售、售后、库存、客户关系等核心业务场景,提供可直接部署、二次开发的全栈解决方案。资源包共2000个文件,涵…

2026/10/15 0:09:03 阅读更多 →
MATLAB实现MIMO系统广义奈奎斯特曲线:特征轨迹法绘制与稳定性判据

MATLAB实现MIMO系统广义奈奎斯特曲线:特征轨迹法绘制与稳定性判据

简介:一套面向无线通信与自动控制专业学生及研究者的MATLAB工具包,围绕多输入多输出(MIMO)系统广义奈奎斯特(Nyquist)曲线绘制而设计,可用于分析多变量系统的闭环稳定性与奈奎斯特判据在MIMO场景…

2026/10/15 0:09:03 阅读更多 →
openrig实战:用Node.js与tmux搭建稳定的AI编程工作台

openrig实战:用Node.js与tmux搭建稳定的AI编程工作台

1. 从"openrig"这个名字说起:它到底想解决什么问题第一次看到"openrig"这个词,我脑子里蹦出来的不是某个具体软件,而是一种"开放式工作台"的意象。rig在英文里有"装配、搭台、装置"的意思&#xff0…

2026/10/15 0:09:03 阅读更多 →
ponytail 插件实战:用技能包管理器终结命令行重复劳动

ponytail 插件实战:用技能包管理器终结命令行重复劳动

先说清楚一件事,我看到"ponytail"这个词的第一反应也是发型,直到有个同事把它的插件仓库丢给我,说"这玩意儿能省掉你一下午的重复劳动",我才意识到这是个命令行工具圈的插件。这个 ponytail 插件的定位很有意…

2026/10/15 0:09:03 阅读更多 →
Charles抓包工具实战指南:HTTPS解密与移动端真机调试

Charles抓包工具实战指南:HTTPS解密与移动端真机调试

1. 为什么今天还要学Charles——一个被低估的“网络显微镜”很多人第一次听说Charles,是在某次接口调试失败后,同事甩过来一句:“你抓个包看看?”然后打开一个界面略显陈旧、图标像放大镜加波形图的桌面应用。它不像Wireshark那样…

2026/10/15 0:09:03 阅读更多 →
064、广播数据长度超出承载上限时的分包处理

064、广播数据长度超出承载上限时的分包处理

现场:一条永远发不出去的广播 几年前做一个分布式采集系统,主控通过广播通道向所有节点下发同步命令和参数。协议栈的单帧净荷上限是 31 字节,这是链路层硬约束,改不了。前期功能很简单,同步命令就几个字节&#xff0c…

2026/10/15 0:08:01 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/14 7:53:34 阅读更多 →