手写Tool Use的三种方案:从简单循环到状态机管理
面试官让我手写一个 Tool Use我用了 3 种方案上周面试一家做 AI 应用的公司对方让我手写一个 Tool Use。面试官还特意强调“不用真的调用外部工具把 Tool Use 的调度逻辑写出来就行。”我一听就明白了这是要看我对 LLM Agent 这条链路理解到什么程度。整个面试过程比我预期要顺利因为我知道手写 Tool Use 本质上考的不是能不能记住某个框架的 API而是三个核心问题第一你知不知道模型怎么决定调哪个工具、传什么参数第二你拿到工具结果之后怎么继续让模型生成回复第三你写的这套调度逻辑在真实环境中能不能稳定跑起来。这篇文章把我现场写的三种方案完整还原出来从最简单的循环到带参数校验和状态机管理的完整流程顺便把面试官现场追问的问题和我的应对思路也一并分享。1. 先拆一下 Tool Use 到底是什么不把概念先理清楚后边手写代码一定跑偏。Tool Use 在 LLM 应用里指的是让模型在生成回复的过程中把“调用某个外部函数”作为一个输出动作。我用一个生活例子说明。你问一个助手“明天上海天气怎么样”如果这个助手没有 Tool Use它只能凭训练数据里的知识瞎猜或者说“我无法获取实时信息”。有 Tool Use 之后模型会先输出一个结构化的“调用请求”里面包含工具名和参数。系统收到这个请求去真实环境里执行比如查天气接口、查数据库、执行一段代码再把执行结果返回给模型。模型看到结果后生成最终面向用户的回复。整个链路前前后后是这样的模型输出 Tool Call → 系统执行工具 → 结果返回给模型 → 模型生成最终回复所以手写 Tool Use本质上是在写一个循环多轮调用、参数校验、错误处理、终止条件全都要在这个循环里体现。面试官要看的通常不是你用了多复杂的框架而是你能不能把这条闭环写清楚。在开始写方案之前有一点必须强调手写 Tool Use 时你写的其实是“框架”不是一个具体业务工具。这意味着你需要自己定义内存中的函数列表、定义调用请求的数据结构、定义执行与返回的流程。框架搭得好加多少个工具都不乱框架搭得差写两个工具你就开始复制粘贴了。2. 方案 A最简实现基于 OpenAI 标准调用格式我先给面试官写了一个最通用、最容易解释的方案。这个方案假设外部系统能拿到模型原始的 tool_calls 输出并且执行环境支持 OpenAI 格式这也是绝大多数 LLM 应用的标准场景。工具定义只需要两步给每个工具写一个 name、description、parameters然后实现一个 dispatch 函数。dispatch 是整个 Tool Use 框架的心脏它根据模型输出的工具名找到对应函数并执行。一个精简的 dispatch 长这样def dispatch(tool_name: str, arguments: dict) - str: if tool_name not in TOOL_REGISTRY: return fError: unknown tool {tool_name} func TOOL_REGISTRY[tool_name][func] try: result func(**arguments) return json.dumps(result, ensure_asciiFalse) except TypeError as e: return fError: invalid arguments for {tool_name}: {e} except Exception as e: return fError: tool {tool_name} execution failed: {e}这里有几个关键设计点也是我在现场重点跟面试官解释的部分。第一TOOL_REGISTRY 用字典维护工具名是 key函数是 value。这样扩展新工具时只需要在注册表里增加一行dispatch 本身不需要改。新增一个工具的成本极低这是 Tool Use 框架最重要的扩展性保障。第二func(**arguments) 这种写法依赖 Python 的参数展开。arguments 如果多了字段TypeError 会被捕获并当作错误信息返回给模型如果少了必填字段同样会抛 TypeError。这个机制保证模型输出不合法参数时不会让整个进程崩溃。第三错误信息一定要返回给模型而不是只打印到日志里。很多人写 Tool Use 会把异常吞掉只 print 到终端模型根本看不到。但模型是靠这些错误信息自我纠错的比如它发现参数不对可能会重新生成一次 tool_call。这是 Tool Use 优雅失败的关键。模型循环的伪代码我也现场写了出来messages [{role: user, content: user_query}] for step in range(MAX_STEPS): response client.chat.completions.create( modelMODEL_NAME, messagesmessages, toolsTOOL_LIST ) msg response.choices[0].message if not msg.tool_calls: final_answer msg.content break messages.append(msg) for tc in msg.tool_calls: tool_result dispatch(tc.function.name, json.loads(tc.function.arguments)) messages.append({ role: tool, tool_call_id: tc.id, content: tool_result, }) print(final_answer)MAX_STEPS 为什么必须有因为没有它模型可能陷入“调用工具 → 返回结果 → 再调用工具”的死循环。尤其是一个工具的执行结果不理想时模型会产生连续尝试。你可以把 MAX_STEPS 理解成一个安全阀一旦达到设定步数就强制终止把最后的内容返回给用户避免无限消耗 token 和时间。这个方案是最小可用实现当场就能跑通适合用来给面试官展示你理解“模型输出结构化调用请求”这个核心机制。它的局限性在于所有判断都依赖模型自动决定调用哪个工具没有人工干预或二次确认的空间。3. 方案 B动态注册 自动参数校验应对大规模工具写完方案 A面试官没有打断我但我知道他大概率会追问如果工具数量变多这个方案还能不能撑住所以我主动递进了第二套方案。方案 A 能跑但存在一个很实际的痛点当工具数量超过 10 个时你还能靠手写函数签名保证参数不出错吗而且模型是概率输出arguments 里的字段名只要错一个字母工具就执行失败。方案 B 要解决的就是这两个问题。先看动态工具注册。我们把工具定义做成装饰器业务函数只需要标注“我是工具”系统自动读取函数的签名和 docstring生成给模型看的工具描述。import inspect TOOL_REGISTRY {} TOOL_SCHEMAS [] def tool(name: str, description: str): def decorator(func): sig inspect.signature(func) params { p_name: { type: string, description: p_name, } for p_name in sig.parameters } schema { type: function, function: { name: name, description: description, parameters: { type: object, properties: params, required: list(sig.parameters.keys()), }, }, } TOOL_REGISTRY[name] func TOOL_SCHEMAS.append(schema) return func return decorator tool(get_weather, 获取指定城市的当前天气) def get_weather(city: str): Retrieve weather for city. return {city: city, weather: sunny, temperature: 26}inspect.signature 是这里最核心的函数它能在运行时拿到函数参数名自动生成 JSON Schema。这样你写业务函数时只需要保证类型标注和 docstring 质量工具接入成本几乎为零。但动态注册只解决“生成 schema”的问题没有解决“参数校验”的问题。实际上模型输出的 arguments 可能会多出了不需要的字段缺少必填字段字段类型错误比如 city 传成了数组字段值非法比如温度传了 -999可靠的做法是引入 pydantic。pydantic 能从 schema 直接生成模型类用它做反序列化和校验错误信息非常结构化。from pydantic import create_model, ValidationError def safe_execute(tool_name: str, arguments: dict): func TOOL_REGISTRY[tool_name] schema find_schema(tool_name) Model create_model(tool_name, **generate_fields_from_schema(schema)) try: validated Model(**arguments) return func(**validated.model_dump()) except ValidationError as e: return fValidation failed: {e.errors()}这一版相比方案 A 的进步是工具方不需要写任何校验代码维护成本从“每个工具内部各自防御”变成了“框架统一处理”。对面试官来说这展示了你对生产级代码的思考错误处理不是靠运气而是靠机制。不过引入 pydantic 也有代价。如果你只需要 3 个工具为了校验引入一个重型依赖就属于过度设计。所以我会把方案 B 定位为“工具数量超过 10 个、团队协作开发的中间态方案”。它的收益曲线陡峭但前期学习成本也高。4. 方案 C用状态机管理多轮调度和人工确认写到第三个方案我开始把 Tool Use 从“函数调用”升级到“任务状态机”。这也是面试官最希望听到的进阶思路因为前面的方案本质上都是“模型说什么我做什么”没有对任务本身的生命周期做管理。为什么要状态机因为真实场景里Tool Use 不是单次调用而是多次调用组合完成任务。比如一个“帮我订机票”的任务模型需要先查日期、查目的地、查价格可能还要多次确认乘客信息。如果只是无脑循环调用一旦中途用户改主意、工具返回异常、某个步骤需要人工确认整个流程就会失控。状态机的核心是有明确的“状态”和“迁移条件”。我把它简化成四个状态待执行、执行中、等待确认、已完成。from enum import Enum class ToolUseState(Enum): PENDING pending RUNNING running AWAITING_CONFIRMATION awaiting_confirmation COMPLETED completed class ToolUseSession: def __init__(self, session_id: str, initial_query: str): self.session_id session_id self.query initial_query self.state ToolUseState.PENDING self.tool_history [] self.result None def step(self, model_response): if self.state ToolUseState.PENDING: if model_response.tool_calls: self.state ToolUseState.RUNNING else: self.state ToolUseState.COMPLETED elif self.state ToolUseState.RUNNING: for tc in model_response.tool_calls: self.tool_history.append(tc) result dispatch(tc.function.name, json.loads(tc.function.arguments)) if result.startswith(Error): # 错误时回到待执行让模型重试 self.state ToolUseState.PENDING return result self.state ToolUseState.AWAITING_CONFIRMATION elif self.state ToolUseState.AWAITING_CONFIRMATION: if model_response confirmed: self.state ToolUseState.COMPLETED else: self.state ToolUseState.PENDING return self.state注意我在 RUNNING 状态里如果工具执行出错会让状态回退到 PENDING而不是直接终止。这模拟了“模型看到错误信息后重新选择工具或调整参数”的行为。如果模型连续多次都失败再触发 MAX_STEPS 的兜底。这个方案最大的价值不是代码本身多复杂而是它把“工具调用”和“任务生命周期”分开考虑。工具只是执行者而状态机是管理者。当任务需要多步骤、多工具协作时这种抽象让代码结构变得非常清晰。顺便说一下状态机方案也很容易扩展出“权限确认”能力。比如某个工具被标记为高权限执行前必须先让用户确认。在 AWAITING_CONFIRMATION 状态下系统暂停执行把待确认信息发给用户用户点了确认之后再继续。这在转账、删除、发送消息这类高风险操作里非常有用。我在现场跟面试官特意强调了这个设计方案 C 的“确认状态”不是简单加一个 if而是把人工确认变成流程中的一等公民。模型不是单方面决定一切系统可以随时把控制权交回给用户。这一手如果能在面试中讲明白基本能证明你不是只会调 API而是对 Agent 的运行时架构有体系化思考。5. 三个方案的对比与适用边界把三个方案放到一起对比结论就很清晰了。面试官通常会让你选一个场景来说明哪个方案最合适这时候你的回答要果断要有判断力。方案核心思路优点缺点适用场景A简单循环 注册表实现最快容易理解错误控制弱扩展性一般原型验证、面试演示、工具数量少B动态注册 自动校验工具接入成本低错误管理规范依赖较重需要学习 pydantic中大型项目、团队协作C状态机驱动流程可控支持多轮会话和确认机制代码量最大抽象成本高复杂 Agent 任务、生产级系统选择建议只有一条先跑通再架构。不要一上来就写状态机如果你的工具只有 3 个状态机的抽象只会浪费时间。但如果你做的是一个需要长期维护的 Agent 平台那方案 C 一定是值得投入的方向。这里有一个我的经验判断面试时写方案 A 加方案 C 的组合最加分。方案 A 证明你具备快速实现能力方案 C 证明你有架构思维。方案 B 更适合放在项目经验里口头提及因为它解决的实际痛点需要在有一定规模的代码库中才能体现。6. 手写 Tool Use 时的四个常见坑我这些年调过不少 Agent 项目也在面试现场看过很多人手写 Tool Use基本上问题都集中在这四个地方。提前说出来能帮你避免 90% 的返工。第一个坑忽略错误信息返回。有些人的 dispatch 把异常吞了只打印到日志模型拿不到任何反馈。结果就是模型在下一轮继续生成同样的错误调用陷入死循环。我的经验是错误信息必须变成 tool message 回给模型这就是模型自我纠错的唯一素材。你甚至可以特意在错误信息里带上正确的参数格式示例模型看到后下一轮大概率能纠正过来。第二个坑不设置最大轮数。模型一旦进入“不断调用工具”的状态没有 MAX_STEPS 兜底整个请求会一直挂着浪费 token 和响应时间。这个参数不能少而且要根据真实调用情况调优。比如简单问答 5 步足够复杂任务可能要到 20 步。我一般会在日志里记录每个任务的真实步数定期分析后再调整上限避免设太大浪费资源也避免设太小截断正常流程。第三个坑工具注册表没有命名空间隔离。当多个业务方同时往注册表里加工具很容易出现同名覆盖。比如两个团队都定义了 get_user_info后注册的会把前面的覆盖掉。这在单人 demo 里不会暴露一上多人协作就出问题。我在实践中会给每个业务方加前缀比如 weather_get、user_query避免冲突。如果用的是 Python也可以把不同业务方的工具放在不同模块下注册时带上模块名。第四个坑忘记清理历史消息。Tool Use 循环里messages 是持续增长的。如果同一个 session 内多次任务历史消息不清模型很快会被上下文窗口截断。一个简单的做法是每次新一轮任务开始时重置到初始 system prompt或者只保留最近 N 轮摘要。我之前踩过这个坑用户在一个对话里连续问了十几个问题后模型突然“失忆”排查了半天才发现是上下文塞满了。7. 面试官追问的进阶问题怎么答很多面试官在听完手写方案后会继续追问几个更深的问题。这些问题的价值在于考察你的应变能力和系统设计思维。我整理了三个最常见的追问和我的应对方式。“如果工具返回结果特别长上下文塞不下怎么办”答做结果摘要或者只返回关键字段。可以在 dispatch 返回前加一层 summarize 函数对大结果做截断或提炼。极端情况下用检索方式把结果落库并返回一个结果 ID模型需要时再按 ID 查询。这种“结果外置”的思路在长文档处理里很常见。“你怎么保证工具调用的安全性”答三层控制。第一层是参数校验防止非法输入第二层是权限机制高敏感工具需要用户确认第三层是操作审计所有工具调用记录都写入日志方便回溯。我还会补充一点有些工具必须做“白名单”限制比如发短信接口只能发给特定号码删除操作必须有 delete 权限才能注册为工具。“多个工具都可以满足用户请求时你怎么做路由”答靠模型的函数选择能力但你可以通过工具描述的措辞影响选择。比如把更常用工具的描述写得更详细、更明确模型会更倾向于选择它。如果要硬路由可以在 dispatch 之前加一层规则引擎人工指定某些关键词走特定工具。这个方案适合工具语义高度重叠的场景比如两个查询库存的工具只是数据源不同。这三个回答不追求唯一标准答案但要有结构、有层次、有真实场景支撑。面试官要的是看到你思考过这些问题而不是背答案。8. 一点实操心得和总结我自己踩过的最深的坑是“工具描述写得太敷衍”。给模型看的 description不是给自己看的注释它决定了模型能不能在众多工具中选对。比如 get_weather 的 description 如果是“获取天气”模型在用户说“明天出门要不要带伞”时可能根本不会调用它。但如果描述成“获取指定城市未来几天的天气数据用于回答出行建议、穿衣建议、是否需要携带雨具等问题”模型就能更好地关联用户意图。这个细节比多写几个工具函数重要得多。另外在真实项目中我还习惯给每个工具加上 examples 字段在参数 schema 里放一个示例参数值。这样模型看到的不只是参数名还有具体的填法。比如 get_weather 的参数示例是 {city: 上海}模型的调用准确率会明显提升。这个小技巧是我在项目数据对比中发现的同样的模型加了 examples 后参数错误率能下降不少。手写 Tool Use 这件事看起来考的是代码实际上考的是工程化思维错误处理、扩展性、流程控制、上下文管理每一项都直接影响生产环境的稳定性。把这三个方案吃透再补上这些避坑经验应付面试绰绰有余更重要的是你会在实际项目里写出真正能上线的 Agent 系统。

相关新闻

马斯克放话:Grok 4.6 下周上线,2 万亿参数要挑战月之暗面——用 TaoToken 统一 Key 实测 API 接入

马斯克放话:Grok 4.6 下周上线,2 万亿参数要挑战月之暗面——用 TaoToken 统一 Key 实测 API 接入

/* 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 20:32:05 阅读更多 →
Claude Code 配置实战:从安装到打造 AI 工程团队

Claude Code 配置实战:从安装到打造 AI 工程团队

从第一次在终端里敲下claude,到它自己读代码、改文件、跑测试、提 PR,整个过程我只给了几句需求描述——这种体验确实会让人上瘾。但大多数人的痛点不在于“不会问问题”,而在于配置没做到位:装好了却不知道如何把 Claude Code 变…

2026/10/18 20:32:05 阅读更多 →
毕业季论文查重遇AIGC检测?一键双降工具实操拆解

毕业季论文查重遇AIGC检测?一键双降工具实操拆解

毕业季一到,论文查重就成了压在毕业生心口的一块石头。以前大家只担心“重复率”超标,现在又多了一道紧箍咒——AIGC检测率。很多学校明确要求全文AIGC检测结果不能超过一定比例,于是“降重”和“降AIGC率”变成了两道必须同时跨过的门槛。市…

2026/10/18 20:31:03 阅读更多 →

最新新闻

增量索引实战:新文档怎么做到分钟级可检索

增量索引实战:新文档怎么做到分钟级可检索

个人主页&#xff1a;> for_ever_love__ <&#xff08;欢迎各位大佬莅临&#x1f60a;&#xff09; 其他栏目: > 大模型开发从0到1 < 其他栏目: > iOS项目总结大全 < 其他栏目: > 我想学python了 < 其他栏目: > iOS UI < 文章目录增量索引实战&a…

2026/10/18 21:45:10 阅读更多 →
Python多模态情感分析实战:CCA对齐+门控融合工程实现

Python多模态情感分析实战:CCA对齐+门控融合工程实现

简介&#xff1a;本资源是一套完整的多模态融合情感分析实战项目&#xff0c;面向计算机专业本科生及人工智能初学者&#xff0c;聚焦文本、语音、图像、视频四模态数据的情感联合建模与分析&#xff0c;适用于毕业设计、期末大作业及课程设计等高要求实践场景。压缩包共20个文…

2026/10/18 21:45:10 阅读更多 →
Onyx开源企业搜索深度解析:RAG与知识管理实践

Onyx开源企业搜索深度解析:RAG与知识管理实践

1. 为什么需要 Onyx&#xff1a;企业知识检索的三个死结1.1 知识分散&#xff1a;搜索引擎覆盖不到的角落先说我见过最多的场景。一个几十人的团队&#xff0c;文档其实不少&#xff1a;Wiki 里躺着架构设计、聊天工具里埋着半年前的决策、在线文档里散落着各种会议纪要和排期表…

2026/10/18 21:45:10 阅读更多 →
国产大模型对比:文心、豆包、通义...哪家最好?基于最公允大模型排行榜细致整理

国产大模型对比:文心、豆包、通义...哪家最好?基于最公允大模型排行榜细致整理

/* 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 21:45:10 阅读更多 →
协同过滤电影推荐系统毕设:Python实现核心算法与工程避坑全解析

协同过滤电影推荐系统毕设:Python实现核心算法与工程避坑全解析

简介&#xff1a;基于协同过滤推荐算法的电影推荐系统Python毕业设计项目&#xff0c;面向计算机相关专业毕业生、课程设计及期末大作业学生&#xff0c;也适合有Python基础、希望实战推荐系统开发的进阶学习者。整包共2000个文件&#xff0c;以py源码、html页面、css样式、js脚…

2026/10/18 21:45:10 阅读更多 →
大模型工具使用与函数调用:从原理到落地的完整指南

大模型工具使用与函数调用:从原理到落地的完整指南

1. 从"能聊天"到"能干活"&#xff1a;工具使用与函数调用到底在解决什么问题大模型刚火起来那阵子&#xff0c;大家最直观的体验就是"能聊"。你问它答&#xff0c;写诗写文案写代码&#xff0c;样样都行。但真把它扔进一个实际业务里&#xff0c…

2026/10/18 21:44:10 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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 阅读更多 →