Agent框架插件化机制深度拆解:从生命周期到事件总线

最近我把一套开源的 Agent 框架源码完整过了一遍,本来没抱太大期待——市面上叫 Agent 框架的项目太多了,多数是把模型调用和工具函数包一层,换个名字就开源。但真正读下来,我意识到自己低估了它的插件化设计。不是那种“你写一个函数,我帮你注册一下”的玩具级插件,而是从进程启动、配置加载、能力注入到运行隔离都做成了标准链路。这篇笔记会把这套框架的插件化机制拆开讲清楚,适合刚接触 Agent 开发、想知道框架内部怎么设计的同学,也适合已经在做工具编排、想让自己的代码更容易扩展的人。

先说结论:插件化做到这种程度,核心思路不是“多做几个扩展点”,而是把框架自身压缩到极薄,把一切可变化的东西都推到插件边界之外。这个设计带来的直接好处是,核心代码几乎不用动,新能力全部通过插件叠加。下面我从架构拆解、插件机制、实操验证、问题排查四个维度展开。

1. 框架整体设计思路:Agent 系统的变化点到底在哪

1.1 Agent 框架不是在封装模型,而是在封装“决策循环”

很多人对 Agent 框架的第一反应是“调用大模型嘛,包装一下 API 就行”。实际上,Agent 和普通模型调用的本质区别在于它多了一个循环:用户给一个目标,Agent 要拆解任务、选择工具、调用外部能力、观察返回值、判断是否完成、不完成就继续迭代。这个循环里真正复杂的是“下一步做什么”的决策逻辑,而不是模型请求本身。

拆这套框架时,我先看它的模块划分,基本可以分成五层:模型接入层、工具层、编排层、记忆层、执行反馈层。模型接入层解决不同模型服务的差异,比如请求格式、超时时间、流式输出;工具层把搜索、数据库、计算器这类外部能力封装成标准接口;编排层负责决定 Agent 当前该调用哪个工具、下一步该做什么;记忆层管理短期上下文和长期存储;执行反馈层处理错误重试、结果校验、成本控制。

如果框架把五层全部硬编码在一个类里,后续每加一个工具就要改核心代码。这套框架的解法是把五层都抽象成插件接口,模型后端可以是插件,工具可以是插件,记忆存储可以是插件,甚至编排策略也可以是插件。核心只留了一个极薄的事件循环。

1.2 拆解前先回答三个问题:边界在哪、扩展点在哪、配置怎么进

读源码之前,我先向自己提了三个问题,这三个问题决定了后续阅读的路径。

第一个是插件边界。到底什么算一个插件?工具是插件,这最好理解;模型服务商接入也是插件;知识库的向量存储是插件;Agent 的多步规划策略也可以是插件。边界划分决定了框架能扩展的广度。

第二个是扩展点在哪。插件不是零散挂几个函数,而是要在合适的时机被框架调用。比如 Agent 每次决策前触发一个钩子,工具执行成功后发布一个事件,会话开始和结束时各有一组回调。扩展点找全了,插件的挂载逻辑就清楚了。

第三个是配置怎么进入插件。插件不是写死在自己的代码里,而是通过统一的配置中心拿到参数。这意味着框架要为每个插件提供配置 schema 校验、默认值合并、环境变量覆盖。

带着这三个问题去读源码,效率比漫无目的地翻代码高得多。

1.3 函数注册表和真正的插件化之间的差距

有些项目也说自己支持插件,实际上就是一个全局数组存了一堆函数,运行时按名字查到就调用。这种实现的问题在于:插件之间没有生命周期管理,组件没有办法优雅释放连接;插件配置没有统一注入,每个插件自己找配置、自己解析;插件异常会直接拖垮整个进程;开发者根本说不清某个插件依赖另一个插件的哪些能力。

这套框架的处理方式是,每个插件是一个独立类,有明确的加载、初始化、启动、停止阶段,有自己的配置块和权限声明。框架在启动时统一扫描插件目录、按依赖排序、逐阶段拉起。这才是我说的“插件化做到这种程度”的底气。

2. 插件化核心机制拆解:注册、生命周期、隔离与通信

2.1 插件注册与发现:默认约定优于手动配置

这套框架的插件发现机制非常简单粗暴,但也非常有效:运行目录下有一个plugins目录,框架启动时会递归扫描里面所有包含manifest.yaml的子目录。每个子目录代表一个独立插件。

为什么用目录约定而不是手动注册?因为手动注册意味着每次加插件都要改主程序代码,这会让框架核心和插件耦合在一起。目录约定加上清单文件声明,既保留了“放入即用”的简单体验,又避免了硬编码。

Manifest 文件长这样:

name: weather_tool version: 0.1.0 description: 提供天气查询工具 author: anonymous dependencies: - http_client entry: plugin.py:WeatherPlugin config_schema: type: object properties: api_url: type: string default: "https://api.example.com/weather" required: []

框架扫描到 manifest 后,先校验 name 是否重复,再解析 dependencies 做拓扑排序,最后动态 import entry 指定的类和模块路径。注意依赖排序这一步很关键,如果插件 A 依赖插件 B,那么 B 必须比 A 先初始化。拓扑排序保证这一点,乱序编写 manifest 也不会出问题。

2.2 生命周期四阶段:load、init、start、stop

插件不是一加载就直接运行。这套框架把插件的生命周期分成四个阶段,每个阶段做不同的事。

load 阶段只做两件事:解析 manifest、生成配置对象。此时插件代码还没有被执行,框架只是把声明信息加载进来,相当于“看到了这个人,还没让他开口”。

init 阶段实例化插件类,调用构造函数,传入合并后的配置。这个阶段仍然不能访问网络或启动线程,因为框架要保证所有插件都被正确实例化之后,才能进入下一步。

start 阶段才是真正的“开工”。数据库连接池启动、HTTP 服务拉起、工具函数注册到工具中心,全部在 start 里完成。框架会等所有插件的 start 都返回成功后,才把控制权交给 Agent 主循环。

stop 阶段则反过来,停止后台任务、关闭连接、释放资源。

拆解时我印象最深的是,框架把“实例化”和“启动”分开,这避免了一个经典问题:插件 A 的构造函数里尝试调用插件 B,但 B 还没 start,导致空指针。如果只是简单注册函数,这个问题在写插件的人那边才会暴露;有了明确的生命周期,插件开发者就知道了“我不能在构造函数里依赖别人,只能在 start 阶段拿别人的运行实例”。

2.3 能力注入与运行时隔离:给插件“工位”但不给“总门禁”

插件有多少权限?这是框架设计里最有意思的部分。它不是把整个 Agent 对象传给插件,而是提供一组受限的 API:注册工具、订阅事件、发布事件、读写自己的配置、访问日志器。

反过来,插件拿不到的是:框架核心对象的内部状态、其他插件的私有属性、全局配置中心的完整内容。每个插件只能读取自己命名空间下的配置项。

这就像公司给每个员工一个工位和必要的办公软件,但不是所有人都拿着总门禁卡。这种隔离带来一个直接的工程收益:插件可以随便出错,影响范围被限制在自己的沙盒内。框架捕获到某个插件抛出的异常后,会标记该插件为失效状态,但 Agent 主循环不会因此崩溃。

隔离不彻底的框架我见过太多,一个插件内存泄漏能把整个进程拖垮。这套框架至少在有异常边界这点上做得很到位。

2.4 事件总线:插件之间不允许直接调用

插件之间怎么协作?答案是:不直接调用,走事件总线。

比如天气插件查询完天气后,发布一个weather.queried事件,事件里带着城市和结果数据。行动规划插件订阅了这个事件,拿到数据后决定下一步走哪个分支。插件 A 不需要 import 插件 B 的类,A 甚至不知道 B 是否存在。

这套机制的优点是彻底解耦。缺点是事件满天飞时很难调试,所以框架在事件总线上加了命名空间前缀和订阅过滤,也提供了事件链路追踪 ID。实操中,我建议每个插件发布事件时统一用插件名.动作.状态这种格式,比如database.query.success,排查日志时能省大量时间。

2.5 配置合并顺序:默认值、文件、环境变量三层级

配置系统也是插件化能否落地的关键。插件不能自己写死一组参数,也不能依赖使用者改源码。这套框架的配置合并顺序是:代码默认值、manifest 里的默认值、运行配置文件、环境变量。后边的覆盖前边的,环境变量优先级最高。

这种设计在部署时非常有用:开发环境用配置文件,生产环境可以只用环境变量覆盖 api key 和接口地址,不改任何文件就能完成配置切换。插件代码里只有一行self.settings.get("api_url"),不用关心这个值从哪里来。

3. 实操复盘:从源码阅读到写一个能跑的插件

3.1 源码阅读路径:别从抽象类啃起

如果照着源码目录逐个文件读,很容易在前 2000 行就迷失。我的路径是:先跑起来,再看入口,再找注册中心,最后倒着读回调用链。

具体做法是,先把仓库 clone 下来,按 README 跑一个最小 Demo,确认框架能跑通。然后去main.py或者cli.py找启动入口,看到创建AgentRuntime的代码,接着进到runtime.py,找到插件扫描和生命周期触发的地方。最后回到插件接口定义文件看方法签名。

这个路径比线性读代码高效得多,因为每个文件我都能知道“这个类是给谁用的、在哪个环节被调”。

3.2 写一个最小工具插件:天气预报

作为测试,我写了一个最简单的天气查询插件,用来验证插件机制本身的链路。目录结构如下:

plugins/ ├── weather_tool/ │ ├── manifest.yaml │ └── plugin.py

plugin.py 的核心代码:

from agent_core.api import BasePlugin, Tool from agent_core.api.events import Event class WeatherPlugin(BasePlugin): def register_tools(self): return [ Tool( name="get_weather", description="查询指定城市的当前天气", parameters_schema={ "type": "object", "properties": { "city": {"type": "string", "description": "城市名"} }, "required": ["city"], }, handler=self.handle_get_weather, ) ] async def handle_get_weather(self, city: str) -> str: # 实际开发中这里会去调用外部接口 result = f"{city} 当前天气:晴,24℃" self.events.publish( Event("weather.queried", payload={"city": city, "result": result}) ) return result

注意两个关键点:第一,插件通过register_tools声明自己提供的工具,不要在框架里写死;第二,工具函数是 async 的,Agent 主循环可以并发调度多个工具,而不会因为一个慢工具阻塞整个决策循环。

manifest 写好后,重新启动框架,日志里会出现:

[plugin-manager] discovered plugin: weather_tool [plugin-manager] dependencies resolved: weather_tool -> [] [plugin-manager] init: weather_tool [plugin-manager] start: weather_tool [plugin-manager] tool registered: get_weather

到这里,整个“放入目录即生效”的链路就验证通了。

3.3 让插件影响 Agent 的工作流:不是只有工具才算插件

工具插件是最表层的能力。再往下挖一层,插件还可以挂钩到 Agent 的决策流程里。

比如有一个prompt_enhancer插件,订阅agent.deciding事件。每次 Agent 在决定下一步动作之前,这个插件都会在上下文里追加一段提示词,指导模型优先考虑数据库查询而不是直接生成答案。同样是订阅事件,还有插件可以在agent.tool_result事件里做结果后处理,清洗掉不必要的内容。

实操时我建议至少在四个事件点上各写一个测试插件,把事件触发时序彻底搞清楚:session.started、agent.deciding、tool.executed、session.ended。事件发生后,插件执行顺序取决于订阅优先级,而优先级数越大越先执行。

3.4 验证插件加载时序:日志是唯一可靠的依据

插件启动顺序对不对,单看代码很难确定,必须在运行时打日志验证。我写了两个有依赖关系的插件做测试,A 声明依赖 B。启动日志显示 B 先 init、先 start,然后 A 才 init、start。这个顺序完全符合 manifest 里的依赖声明。

但有一个坑:停止顺序不是简单的逆序。框架停止时,先从 A 开始 stop,再 stop B。原因是 A 可能正在调用 B 的能力,先停掉依赖方,被依赖方才能安全退出。

3.5 热更新和性能观察:插件机制不是银弹

热更新是很多人都关心的点。这套框架的插件机制默认不支持代码热替换,修改插件代码后需要重启进程。原因很明确:Python 的解释环境决定了模块重载容易引发一系列不可预期的问题,尤其是类定义和全局状态。

性能方面,插件本身的开销非常小,事件总线是内存队列,注册工具是字典查找。真正的性能瓶颈在工具执行的 IO 耗时,以及编排层调用大模型返回结果的时延。插件机制没有添加明显的运行时开销。

4. 常见问题与排查技巧实录

4.1 插件加载失败:先看 manifest,再看依赖

实际跑的时候,最容易出问题的不是插件代码,而是 manifest 文件。YAML 缩进错误、entry 路径写错、依赖声明的插件名和目录名不一致,这些错误在启动日志里通常表现为plugin load failed后跟着一条 ParseError 或 ModuleNotFoundError。

我的排查顺序是:看日志定位到具体插件,然后打开 manifest 检查 name 是否唯一、dependencies 是否都已存在、entry 的模块路径是否以插件根目录为基准。第二步再用 Python 单独 import 一下 entry,确认是路径问题还是语法问题。

4.2 插件之间互相干扰:事件风暴和全局状态

事件总线也不是没有代价。一个插件发布事件,所有订阅者都会收到。如果事件设计成中文全局名称,很容易出现语义重叠,订阅方误处理别人的事件,导致行为异常。

我在测试中遇到过一个问题:两个插件分别发布data.updated事件,一个表示数据库记录更新,一个表示缓存刷新,下游插件订阅后同时触发两次处理逻辑。解决办法是把事件名改成带命名空间的格式:database.record.updated和cache.entry.updated。这个工作是纯设计层面的,不需要改框架代码。

4.3 插件调试的三个技巧

先给框架开 DEBUG 日志级别。这套框架内部记录了完整的插件生命周期和事件投递日志,很多时候问题不用打断点,看日志就能定位。

如果怀疑某个插件有问题,用框架提供的单插件模式启动,只加载被怀疑的插件,排除依赖干扰。对 Agent 主循环来说,单插件环境能让调用链缩短一大截。

断点调试时,重点放在BasePlugin基类的方法入口和事件订阅回调函数上,这两处是插件与框架交互的关键边界。

4.4 设计插件时的四点经验

插件接口要尽量小。不要把整个框架对象传给插件,否则插件和框架的耦合会越来越深,最后又变成了一个单体应用。配置优先于代码:任何可变的行为都优先做成配置项,而不是让使用者改插件代码。失败降级:插件启动失败时,框架会标记失效,但不会阻止其他插件运行,因此插件要自己处理“能力不可用”的情况,不要假设上游一定会成功。最后一个经验是尽量用事件做跨插件通信,不要直接调用依赖对象的方法,事件在日志里可以被追踪,直接方法调用出了问题时排查成本更高。

4.5 问题速查表

现象可能原因解决方案
插件没有出现在启动日志中插件目录未放在 plugins 根目录;目录名不符合命名规范检查目录位置和命名,重启框架
启动报 manifest 读取失败YAML 缩进错误;必填字段缺失用 YAML 校验工具先验证格式
import 不到 entry 模块entry 路径写错;依赖库未安装检查 entry 是否以插件根目录为基准,确认依赖安装
插件 A 调用插件 B 时拿到空引用A 在 init 阶段访问 B,但 B 尚未 start把访问对方运行实例的逻辑移到 start 阶段
事件被重复处理事件名过于通用,多个插件的订阅语义重叠使用命名空间前缀,细化事件名
插件异常导致框架退出插件抛出的异常没有被捕获确认插件代码捕获了自身异常,或在事件回调里做 try/except
修改插件代码后不生效框架默认不支持代码热替换重启框架,或查框架文档确认是否有 reload 开关

我在实际使用中最大的体会是:插件化做到这种程度,不是炫技,而是把“变化”与“稳定”严格分开。框架核心代码可以连续几个版本不动,新增的需求全部落在插件层。对团队协作来说,这也意味着不同小组可以各自负责一个插件,互不阻塞、互不污染,只要遵守 manifest 和事件规范,就能并行交付。

最后再分享一个小技巧:拆解框架时,别只满足于让 Demo 跑通,强烈建议写一个“故意出错”的插件,比如在 start 阶段抛异常、发布一个没有订阅者的事件、注册一个带脏数据的工具。观察错误日志和框架的降级行为,这个过程学到的东西远比我按正常路径读十遍文档更扎实。

相关新闻

基于Django与微信小程序的校园活动抽奖系统开发实践

基于Django与微信小程序的校园活动抽奖系统开发实践

1. 项目整体设计与核心需求拆解1.1 这个抽奖系统到底解决什么问题先说结论:这个系统解决的是校园活动里最让人头疼的“现场抽奖怎么搞”的问题。我见过太多活动主办方的操作方式——要么用纸质抽奖箱,人工抽号,效率低还容易被质疑公平性&…

2026/10/20 16:07:27 阅读更多 →
模型蒸馏 (Knowledge Distillation) 入门:把大模型能力“蒸“进小模型

模型蒸馏 (Knowledge Distillation) 入门:把大模型能力“蒸“进小模型

授权与合规声明 本文为作者原创技术分享,内容基于公开可查的工程实践与官方文档整理,仅用于学习交流。文中涉及的工具、命令与配置均在你自建的隔离环境中操作;引用第三方内容已尽量标注来源。若你对文中任何技术细节有疑问,以官方…

2026/10/20 16:06:26 阅读更多 →
滑模控制与自适应控制融合的Simulink路径跟踪控制器设计

滑模控制与自适应控制融合的Simulink路径跟踪控制器设计

我看到很多朋友一提到滑模控制,第一反应就是“抖振太大、不敢用”,提到自适应,又觉得“参数更新律调不明白、容易飘”。这次我用一个具体的Simulink仿真项目,把滑模控制和自适应控制融合到一起,做了一套带扰动抑制的路…

2026/10/20 16:06:26 阅读更多 →

最新新闻

Agentic Harness Workflow:从能跑通代码到可交付工程的AI编程框架

Agentic Harness Workflow:从能跑通代码到可交付工程的AI编程框架

1. 为什么"能跑通的代码"和"能交付的工程"之间隔着一道鸿沟 如果你最近半年深度用过任何一款 AI 编程助手,大概率经历过这样的场景:你让它写一个功能,它三下五除二吐出一大段代码,你复制粘贴进去,…

2026/10/20 17:04:26 阅读更多 →
Postman接口关联实战:从登录Token到下单的自动化流程

Postman接口关联实战:从登录Token到下单的自动化流程

某B2C在线商城的API接口文档里,最经典的一条业务就是“登录-选择商品加入购物车-下单”。我拿到这个需求的第一反应是:请求参数倒是不难,难在这些接口之间要传一堆动态数据,登录返回token、商品列表返回productId、购物车返回购物…

2026/10/20 17:04:26 阅读更多 →
Agentic Harness Workflow:用状态机+Agent工程化框架驾驭AI编码

Agentic Harness Workflow:用状态机+Agent工程化框架驾驭AI编码

1. 为什么“让AI写代码”这件事需要一套工程化框架 1.1 从“对话式写代码”到“流程化跑任务”的认知转变 过去一年多,我身边不少开发者对 AI Coding 的态度经历了三个阶段:最开始是好奇,拿它写个正则、补个单元测试;然后是依赖&…

2026/10/20 17:04:26 阅读更多 →
InstallShield中附属MSI的安装卸载实战与调试

InstallShield中附属MSI的安装卸载实战与调试

前两天帮人看一个安装包的诡异问题:主程序明明是装成功了,但被一起带上的那个MSI组件死活装不上,卸载的时候还赖在系统里不走。这个场景搞Windows安装包的朋友应该不陌生——主程序用InstallShield打成一个安装包,安装时还要顺带把…

2026/10/20 17:04:26 阅读更多 →
HUSB351快充芯片:小体积PD协议芯片的工程落地逻辑

HUSB351快充芯片:小体积PD协议芯片的工程落地逻辑

1. 从“机乐堂”供应链变动看快充芯片选型的底层逻辑最近在整理一批快充配件的BOM清单时,注意到一个细节:某款标称30W PD快充的迷你插线板,其协议识别模块的丝印从常见的“FP6606”悄然换成了“HUSB351”。进一步交叉查证发现,该产…

2026/10/20 17:04:26 阅读更多 →
MVMD联合小波阈值去噪:多通道非平稳信号MATLAB实战

MVMD联合小波阈值去噪:多通道非平稳信号MATLAB实战

简介:这份资源面向信号处理、故障诊断与振动监测方向的学习者和研究人员,提供一套基于MATLAB的完整联合去噪方案,用于从含噪多通道信号中恢复原始信号。核心流程涵盖MVMD多元变分模态分解、基于相关系数的有效模态筛选、小波阈值去噪、模态重…

2026/10/20 17:03:25 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50: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/20 9:50:45 阅读更多 →