从700行自研Agent调度系统迁移到OpenClaw:架构重构与工程实践
1. 项目缘起一个自研Agent调度系统的“甜蜜负担”去年下半年我们团队为了支撑一个内部AI助手项目决定自研一个Agent调度系统。当时的想法很直接市面上的开源框架要么太重要么功能不匹配自己写一个“轻量级”的既能完全掌控又能完美贴合业务。于是我和另一位同事花了大概两周时间吭哧吭哧写出了第一版。核心逻辑并不复杂就是一个基于Python asyncio的事件循环负责接收任务、解析指令、根据指令类型调用不同的工具函数比如查数据库、调用外部API、生成文本最后再把结果组装返回。整个调度器的核心代码大约700行。初期跑起来确实很爽功能完全自定义加个新工具、改个路由逻辑分分钟搞定。但随着业务场景增多这个“亲儿子”开始暴露出越来越多的问题。首先是可维护性为了处理各种边界情况比如工具调用超时、依赖的工具服务宕机、结果格式校验代码里塞满了if-else和try-except逻辑分支变得极其复杂。其次是扩展性每增加一个新的Agent能力我们称之为一个Skill就需要手动去修改调度器的路由映射和初始化逻辑还要小心翼翼地处理可能存在的依赖冲突。最后是心智负担任何关于Agent执行流程的改动比如想加入一个统一的权限校验层或者修改任务的生命周期管理都需要深入这个700行的“泥潭”里小心翼翼地修改测试成本极高。这个自研调度器就像一个手工打造的精密瑞士军刀刚开始用着顺手但当你需要它变成一套标准化、可批量生产的厨房刀具时它就力不从心了。我们意识到继续投入时间在这个“轮子”上边际效益越来越低而技术债务却在快速累积。是时候看看“外面的世界”了。2. 技术选型为什么最终选择了OpenClaw当我们决定寻找替代方案时目标很明确需要一个足够轻量、架构清晰、专注于调度与编排的开源Agent框架而不是一个全家桶式的AI应用平台。我们对比了几个当时热门的选项包括LangChain、AutoGPT的一些衍生框架以及一些新兴的轻量级项目。LangChain的功能无疑是最全面的但其设计哲学是“大而全”抽象层级较高。对于我们的核心需求——一个纯净、高效的调度中枢——LangChain显得有些臃肿学习曲线也陡峭。我们需要的是调度逻辑的“替换”而不是引入一套全新的、复杂的开发生态。一些新兴的轻量级框架则走向另一个极端它们往往为了极致简洁而牺牲了必要的工程化特性比如缺乏良好的错误处理机制、状态管理简陋或者社区活跃度低遇到问题很难找到解决方案。正是在这个背景下OpenClaw进入了我们的视野。它吸引我的点非常具体极简的核心理念OpenClaw的定位就是“Agent调度与编排框架”。它的核心抽象非常干净Agent执行单元、Skill能力单元、Scheduler调度器和Context执行上下文。这种设计与我们自研系统的核心思想不谋而合但实现得更加优雅和规范。清晰的执行流水线OpenClaw明确地将一次Agent执行分解为Parse指令解析、Plan任务规划、Execute技能执行、Handle结果处理等阶段。每个阶段都是可插拔的组件。这种流水线模型让我们能清晰地看到任务的生命周期便于调试和定制。原生的异步支持基于asyncio构建与现代Python异步生态无缝集成这对于需要同时调度多个I/O密集型Agent任务的场景至关重要。活跃的中文社区与接地气的文档作为国内团队主导的项目其文档和社区讨论更贴近我们的实际开发场景遇到问题时能够更快地获得反馈和思路。决策的关键一击来自于一个简单的对比我用OpenClaw重新实现了我们最核心的一个业务流程——一个包含用户意图识别、多轮对话状态管理、以及调用三个不同外部工具的组合Agent。用OpenClaw的实现只用了72行代码就清晰、结构化地替代了原来散落在调度器和各个工具函数中的近200行胶水代码。这72行代码主要就是定义Skill、配置Agent、并描述执行流程业务逻辑一目了然。代码量的急剧缩减和结构的清晰化让我们立刻看到了迁移的价值。3. 迁移实战从700行“面条代码”到72行声明式配置迁移过程并非简单的“查找替换”而是一次对原有业务逻辑的重新审视和结构化重构。我们的自研调度器代码700行大致可以分解为以下几个混乱的模块模块A (约150行)主事件循环和任务队列管理混杂着日志初始化。模块B (约200行)一个庞大的指令解析函数用正则和字符串匹配硬编码了数十种指令模式。模块C (约180行)工具路由与执行器里面是长长的if-elif链每个分支处理不同的工具调用、错误处理和结果格式化。模块D (约170行)状态管理、上下文存储和响应组装逻辑与前面几个模块深度耦合。我们的迁移策略是“分而治之各个击破”。3.1 第一步梳理与解耦——将“技能”抽象出来首先我们不再将业务能力视为散乱的“工具函数”而是按照OpenClaw的范式将其抽象为独立的Skill。这是一个关键的思维转变。以前自研系统中的一个工具函数:async def query_user_data(user_id: str, query_type: str): 一个混杂的函数查数据库、处理异常、格式化结果 try: if query_type “profile”: data await db.fetch_user_profile(user_id) return {“success”: True, “data”: data, “template”: “profile_v1”} elif query_type “order”: data await db.fetch_user_orders(user_id) # 这里可能还嵌入了某种业务逻辑计算 processed_data some_business_logic(data) return {“success”: True, “data”: processed_data, “template”: “order_list”} else: return {“success”: False, “error”: “Unknown query type”} except DatabaseError as e: log.error(f“Database error for user {user_id}: {e}”) return {“success”: False, “error”: “Service unavailable”}这个函数承担了太多职责路由判断、数据获取、业务处理、错误捕获、返回结构定义。它在调度器中被直接调用两者紧密绑定。现在OpenClaw Skill:from openclaw.skill import BaseSkill, SkillMetadata class UserProfileSkill(BaseSkill): “”“查询用户基本信息”“” metadata SkillMetadata(name“user_profile”, description“Get user‘s basic profile information”) async def execute(self, user_id: str): “”“执行技能只关心核心业务逻辑”“” # 专注数据获取 data await db.fetch_user_profile(user_id) return data # 返回原始数据或简单处理后的数据 class UserOrderSkill(BaseSkill): “”“查询用户订单列表”“” metadata SkillMetadata(name“user_order”, description“Get user’s order list”) async def execute(self, user_id: str): data await db.fetch_user_orders(user_id) # 复杂的业务逻辑可以放在这里或者进一步拆分成服务 processed_data some_business_logic(data) return processed_data变化是显著的单一职责每个Skill只做一件事代码更纯粹。依赖注入Skill的依赖如db可以通过框架的上下文Context或初始化参数注入而不是硬编码。错误处理分离Skill内部可以抛出特定异常由OpenClaw框架统一的错误处理中间件来捕获和转换Skill自身不再需要处理返回格式。3.2 第二步重构调度逻辑——从“过程式”到“声明式”这是迁移的核心。我们拆掉了原来那个庞大的、过程式的调度器。以前调度器需要知道所有指令模式并手动调用对应的工具函数还要处理后续流程。现在我们定义一个Agent它由一系列Skill组成并通过Scheduler来管理执行流程。调度逻辑变成了配置。import asyncio from openclaw import Agent, Scheduler, Context from openclaw.skill import SkillSet from my_skills import UserProfileSkill, UserOrderSkill, WeatherSkill, CalculatorSkill # 导入我们定义的Skill # 1. 定义技能集 skills SkillSet() skills.register(UserProfileSkill()) skills.register(UserOrderSkill()) skills.register(WeatherSkill()) skills.register(CalculatorSkill()) # 2. 创建Agent并为其装配技能 my_agent Agent(name“assistant”, skillsskills) # 3. 创建调度器 scheduler Scheduler() # 4. 运行Agent示例 async def main(): context Context(task“What‘s the weather in Beijing and user 123’s profile?”) result await scheduler.run(agentmy_agent, contextcontext) print(result.output) # asyncio.run(main())这短短二十几行代码就完成了原来调度器核心的初始化、注册和启动工作。复杂的指令解析和路由现在可以通过配置OpenClaw的Parser组件例如基于LLM的意图识别或我们自定义的规则解析器来实现这部分逻辑也从调度器主代码中剥离了出去。3.3 第三步处理边界与增强——利用中间件和钩子自研系统中那些散落的错误处理、日志记录、性能监控代码在OpenClaw中可以通过中间件Middleware和钩子Hook来优雅地实现。例如我们需要为所有Skill调用添加统一的超时控制和指标上报from openclaw.middleware import BaseMiddleware import time from statsd import StatsClient # 假设使用StatsD上报指标 class TimeoutAndMetricsMiddleware(BaseMiddleware): “”“超时控制与指标上报中间件”“” def __init__(self, timeout: float 30.0): self.timeout timeout self.statsd StatsClient() async def around_execute(self, skill, context, fn, *args, **kwargs): “”“在技能执行前后加入逻辑”“” skill_name skill.metadata.name start_time time.time() self.statsd.incr(f“skill.{skill_name}.called”) # 调用次数1 try: # 使用asyncio.wait_for实现超时 result await asyncio.wait_for(fn(*args, **kwargs), timeoutself.timeout) duration (time.time() - start_time) * 1000 # 毫秒 self.statsd.timing(f“skill.{skill_name}.duration”, duration) self.statsd.incr(f“skill.{skill_name}.success”) return result except asyncio.TimeoutError: self.statsd.incr(f“skill.{skill_name}.timeout”) raise TimeoutError(f“Skill {skill_name} execution timeout after {self.timeout}s”) except Exception as e: self.statsd.incr(f“skill.{skill_name}.error”) raise # 将异常原样抛出由上层错误处理中间件处理 # 在创建Scheduler时添加中间件 scheduler Scheduler(middlewares[TimeoutAndMetricsMiddleware(timeout10.0)])通过这种方式我们将横切关注点Cross-cutting Concerns从业务逻辑中彻底解耦。以后要加缓存、加鉴权、改日志格式只需要新增或修改中间件而无需触动任何Skill或Agent的核心代码。4. 降本增效不仅仅是代码行数的减少从700行到72行这个直观的数字背后是多项成本的显著下降和效率的全面提升。1. 开发与维护成本骤降上手成本新同事接手Agent相关开发不再需要理解那700行错综复杂的调度逻辑。他只需要学习OpenClaw的几个核心概念Agent, Skill, Scheduler然后阅读我们清晰定义的各个Skill类即可。 onboarding时间从几天缩短到几小时。修改成本添加一个新功能就是创建一个新的Skill类然后在Agent配置里注册一下。完全不需要考虑它如何被调度、错误如何统一处理、日志如何打。这符合“开闭原则”对扩展开放对修改关闭。调试成本OpenClaw清晰的流水线阶段和活跃的社区使得定位问题变得简单。是Parser没识别对意图还是某个Skill抛了异常或者是中间件处理有问题日志和链路追踪可以很清晰地指向问题阶段而不是在700行代码里大海捞针。2. 系统可靠性与可观测性提升统一的错误处理通过全局错误处理中间件所有未捕获的异常都能被以一种一致的方式处理、记录并转化为用户友好的响应。再也不会因为某个工具函数忘了写try-catch而导致整个服务进程崩溃。标准化指标如上例所示通过中间件可以无侵入地为所有Skill调用添加耗时、成功率、调用量等指标监控告警体系瞬间建立起来。链路追踪可以很容易地在中间件中注入TraceID实现单个用户请求在所有Skill调用间的全链路追踪对于排查复杂问题 invaluable。3. 技术架构的标准化与未来性摆脱“锁死”自研系统是一个孤岛其设计和技术栈完全依赖于最初的开发者。迁移到OpenClaw实际上是接轨了一个有社区支持、持续演进的标准范式。我们可以享受到社区带来的新特性、性能优化和安全修复。组件化与复用定义好的Skill可以像乐高积木一样在不同的Agent间复用和组合。今天为“客服助手”写的QueryFAQSkill明天可以直接用在“运维助手”里。这种复用性带来了巨大的长期收益。为复杂编排铺平道路OpenClaw内置的Scheduler和Plan阶段为未来实现更复杂的任务规划如根据条件动态选择Skill、顺序/并行执行多个Skill提供了坚实的基础。如果靠自研系统来实现这些可能又需要增加几百行难以维护的代码。5. 踩坑与适配迁移过程中的关键决策点迁移并非一帆风顺我们也遇到了一些需要权衡和决策的地方。坑点一异步上下文Context的管理自研系统中我们用一个全局字典来传递请求上下文。OpenClaw使用Context对象。这里的关键决策是哪些数据应该放在Context里哪些应该作为Skill的execute参数我们的原则是与单次请求强相关、且多个Skill可能都需要的信息如user_id,session_id,request_id放入Context。而Skill特定的执行参数如查询的city名称、计算的expression则通过execute方法参数传递。这保证了Skill接口的清晰和可测试性。坑点二Skill的粒度设计最初我们倾向于设计“大而全”的Skill比如一个DataQuerySkill处理所有数据查询。但这很快导致了Skill内部逻辑复杂化违背了初衷。我们最终采纳了“单一职责精细粒度”的原则。例如将DataQuerySkill拆分为UserProfileSkill、OrderQuerySkill、ProductSearchSkill等。这样每个Skill更简单、更易测试、复用性也更高。虽然Skill数量变多了但管理起来反而更轻松因为框架帮我们处理了注册和发现。坑点三与现有服务体系的集成我们的很多“工具”本质上是内部 gRPC 或 HTTP 服务的客户端。自研系统里这些客户端实例是全局单例。在OpenClaw中我们通过依赖注入来解决。具体做法是在创建Skill实例时通过构造函数或框架提供的依赖注入机制将这些客户端实例传入。或者更优雅的方式是利用OpenClaw的Context或自定义的Service Container来托管这些共享依赖。# 方式一通过构造函数注入简单直接 class UserProfileSkill(BaseSkill): def __init__(self, user_service_client): self.client user_service_client async def execute(self, user_id: str): return await self.client.get_profile(user_id) # 使用时 skills.register(UserProfileSkill(user_service_clientmy_grpc_client)) # 方式二利用Context存储更灵活 # 可以在一个前置中间件中将client放入context context.set(“user_service_client”, my_grpc_client) # 在Skill中通过context获取 client context.get(“user_service_client”)坑点四自定义解析器Parser的复杂度OpenClaw默认的解析器可能不适合我们已有的指令格式。我们原有的指令系统是基于关键词和规则的。我们面临选择1彻底改用LLM进行意图识别2基于OpenClaw的BaseParser接口实现自己的规则解析器。 考虑到成本和稳定性我们选择了方案二。实现一个自定义Parser大约花了100行代码但它完美复用了原有的规则库并且将解析逻辑从调度器主代码中彻底剥离成为了一个可独立维护和升级的组件。这本身也是一次有价值的架构优化。6. 总结与展望从“成本中心”到“效率平台”这次从700行自研代码迁移到72行OpenClaw配置的实践远不止一次简单的技术栈更换。它是一次将Agent调度能力从“项目特有的成本中心”转变为“团队共享的效率平台”的过程。对于技术管理者它意味着更低的维护成本、更快的功能迭代速度和更高的系统稳定性。团队可以将精力从维护脆弱的“轮子”转移到创造更有价值的业务逻辑Skill上。对于开发者它意味着更清爽的代码、更明确的职责划分和更强大的调试工具。开发体验从“在迷宫中修改电路”变成了“在图纸上组装模块”。对于系统本身它获得了标准化、可观测、可扩展的坚实基础能够从容应对未来更复杂的Agent编排需求。当然OpenClaw并非银弹。它最适合的场景是构建需要清晰调度和编排逻辑的、多技能协作的Agent系统。如果你的需求极其简单或许一个脚本就够了如果你需要构建一个包含复杂记忆、知识库检索和LLM深度集成的全能型Agent可能需要结合LangChain等更重的框架。但对于我们——以及我相信对于很多需要构建轻量级、高性能、易维护Agent系统的团队——OpenClaw在简洁性、设计理念和工程友好度之间找到了一个非常出色的平衡点。迁移完成后我们那700行“祖传代码”已经被归档封存。新的系统以72行核心配置为骨架辅以数十个清晰定义的Skill和几个通用的中间件平稳运行并支撑着越来越多的业务场景。这次“降本”降的不仅是代码行数更是长期的维护心智负担和潜在的故障风险而“增效”则体现在开发效率、系统可靠性和团队协作的方方面面。

相关新闻

微积分与梯度

微积分与梯度

微积分与梯度 导数 对一个函数yf(x)yf(x)yf(x)进行求导(微分),可以得到这个函数 y 对于 x 的导数,表示为Df(x)Df(x)Df(x)。 导数的意义在于,其可以表示一个函数在某一点上的斜率。 函数yx2yx^2yx2的导数是 y′2xy2xy′…

2026/10/19 7:03:25 阅读更多 →
基于Arduino UNO R4构建AI Cyberdeck:边缘计算与实体交互的入门实践

基于Arduino UNO R4构建AI Cyberdeck:边缘计算与实体交互的入门实践

你肯定见过那些科幻电影里的场景:主角打开一个看起来像是从废品站捡来的金属盒子,里面却装着足以黑进任何系统的超级计算机。这种充满 DIY 感、又酷又实用的个人计算终端,就是“Cyberdeck”。它不只是极客的玩具,更是一种将计算能…

2026/10/18 21:03:21 阅读更多 →
泳道图绘制全攻略:从核心原理到实战技巧

泳道图绘制全攻略:从核心原理到实战技巧

1. 泳道图:从混乱到清晰的流程梳理利器如果你在工作中经常需要梳理跨部门协作流程、分析业务瓶颈,或者向团队解释一个复杂项目的运转机制,那么泳道图(Swimlane Diagram)绝对是你工具箱里不可或缺的一件利器。我第一次接…

2026/10/20 20:49:59 阅读更多 →

最新新闻

spooling假脱机模拟:2请求进程+1输出进程的并发控制与信号量实现

spooling假脱机模拟:2请求进程+1输出进程的并发控制与信号量实现

简介:这份资源围绕操作系统中的Spooling假脱机输入输出模拟技术展开,面向计算机专业学生及系统设计学习者,帮助理解如何通过磁盘缓冲将慢速I/O设备虚拟化,实现多进程共享打印机等外设。包内共11个文件,以5个class编译文…

2026/10/21 2:52:02 阅读更多 →
原生应用接入AI的伦理与合规:从开源许可到隐私边界

原生应用接入AI的伦理与合规:从开源许可到隐私边界

1. 为什么原生应用开发者绕不开“AI伦理”这道门槛“AI伦理”这四个字放在前几年,很多做原生应用开发的同行会觉得这是大厂法务和研究院才需要操心的事。但这两年情况完全不一样了——几乎每个原生应用都在往里面塞AI能力,从刷脸登录、智能客服&#xff…

2026/10/21 2:52:02 阅读更多 →
PowerBuilder 11.5 环境搭建与数据窗口连库实战指南

PowerBuilder 11.5 环境搭建与数据窗口连库实战指南

简介:PowerBuilder 11.5 安装文件资源包,面向需要搭建经典 PB 开发环境的学习者与维护老项目的开发者。PowerBuilder 是 Sybase 推出的快速应用开发工具,11.5 版本在数据窗口、数据库连接与客户端应用开发方面较为成熟,适合用于课…

2026/10/21 2:52:02 阅读更多 →
JSP+Servlet学生管理系统毕设实战指南

JSP+Servlet学生管理系统毕设实战指南

简介:这是一份面向Java Web初学者与高校课程实践者的完整学生信息管理系统教学资源,基于JSP与Servlet技术栈实现MVC分层架构,覆盖动态网页开发、HTTP请求处理、JDBC数据库操作及Tomcat容器部署等核心知识点。资源包共576个文件,包…

2026/10/21 2:52:02 阅读更多 →
MPlayer启用VAAPI硬解:编译、配置与验证实战

MPlayer启用VAAPI硬解:编译、配置与验证实战

手里还有一台老机器,跑着轻量级Linux桌面,平时看本地视频一直用MPlayer的-vo和-slave接口,自动化脚本都绑死在上面。后来换了4K片源,CPU直接拉满,画面一卡一卡的,我才认真考虑给MPlayer接上VAAPI硬解。折腾…

2026/10/21 2:52:02 阅读更多 →
知网AIGC检测原理与降AI工具实测:从标红到通过的完整攻略

知网AIGC检测原理与降AI工具实测:从标红到通过的完整攻略

去年有一阵子,实验室里的几位同学轮流来找我,说同一个问题:初稿明明是自己熬夜搭出来的框架,再用AI润了一轮,结果往知网AIGC检测系统一放,直接标红一片,相似度干到百分之四五十,要求…

2026/10/21 2:51:01 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事:AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里,按宣传口径,本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”,而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起:用户到底在追求什么“封号重开”这四个字,放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流,背后是大量的时间和精力成本。正常情况下,没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”,而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时,就意识到:单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在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 阅读更多 →