ADK-Python安全令牌传递机制:Agent身份穿越与防护实践

你们有没有遇到过这种情况:Agent 每执行一步都要去调第三方 API,令牌在工具函数里传了一圈,最后不知怎么就打到日志里了。我最早做多工具 Agent 的时候,就是这么把生产环境的 API Key 打满了一整个日志面板。标题里这个“Google ADK-Python 安全令牌传递机制”,说白了就是在用 ADK-Python 搭 Agent 的时候,把 API Key、OAuth 令牌、服务账号凭证这类敏感身份信息,安全地在 Agent 内部、工具函数之间、外部服务之间传递的一套玩法。它解决的问题很具体:凭证不硬编码、不落日志、不超范围授权。适合正在用 ADK-Python 做 Agent 开发的读者,哪怕你只是刚接触 Agent,这套思路也能直接拿过去抄。

1. Agent 令牌传递机制,拆开看就是“身份穿越”

1.1 工具调用链里被忽略的“身份穿越”问题

要先理解这个问题,得先看 Agent 的本质。Agent 程序不是一条直线执行到底的,它是“规划-执行-反馈”的循环。LLM 大模型先理解用户请求,拆成多个步骤,然后每个步骤去调用一个工具,工具返回结果,再喂给模型做下一步决策。这个过程中,每调用一个工具,都可能要访问一个受保护的服务。比如一个检索类 Agent:用户问“帮我查项目上周的构建报告”,Agent 内部实际要依次请求权限校验服务、日志聚合服务、对象存储服务。每个服务都要确认“你是谁、你能看什么”,这时候令牌就得一直跟着请求走。

我把这个过程叫“身份穿越”,因为令牌不是静态坐在某一个函数里的,它要穿过 Agent 的每一次工具调用的边界。一个工具拿到令牌做了某件事,下一个工具可能要用同一份令牌去访问另一个服务,也可能要根据服务差异换成另一份令牌。更现实的问题是:有的工具调用是串行的,有的工具调用是并行的,令牌在并发场景下怎么保证不串、不丢、不重复刷新,这就是机制要解决的问题。

类比一下现实场景:一个大型公司的访客机制。员工带访客去不同楼层,每到一个区域,前台要验证访客牌的权限级别,决定放不放行。如果访客牌只有一个万能权限,整个办公区随便进出,一旦丢失,灾难。Agent 的令牌传递也是同一个道理,区别只是把“前台验证”变成了服务端的鉴权中间件,把“访客牌”变成了 Access Token。你设计得好,令牌在每层都只暴露必要权限;设计得不好,一个万能钥匙满天飞。

1.2 硬编码凭据为什么一定走不远

很多人写第一版 Agent 的时候,代码都是这个画风:

API_KEY = "sk-xxxx-xxxx" SERVICE_ACCOUNT = "svc-report-reader@example"

然后需要调用服务的地方直接引用这个全局变量。先不说代码仓库权限控制得怎么样,单是“全局变量 + Agent 多步调用”这个组合就够危险的。问题一,泄露面太大。Agent 的完整调用链里往往包含模型输入、模型输出、工具入参、工具出参、日志、监控上报,任何一个环节把变量内容带出来,全局变量里的密钥就出去了。我自己踩过最典型的一个坑是:工具函数把请求 URL 拼错了,异常消息里直接带了 Authorization header 的完整值,然后异常被链路追踪系统收集,密钥就这么“合法地”进了监控平台。问题二,权限过大。一把 Key 能读对象存储,也能写对象存储,还能调管理接口。Agent 本来只需要读一份报告,结果 Key 的权限范围是“全部读写”。万一来一次提示词注入,让 Agent 调用了不该调用的工具,多余权限直接变成真实风险。问题三,没法审计。谁在什么时间用了哪个令牌去访问什么服务,这个信息完全没有记录,出了问题根本没法追溯。

1.3 ADK-Python 是怎么设计这件事的

ADK-Python 是搭建 Agent 应用的框架,核心概念包括 Agent、Tool、Session、Context。它的设计里有一个很关键的思路:Agent 执行过程中的状态不是散落在全局变量里,而是封装在一个上下文对象中,这个上下文会贯穿整个调用链,从 Agent 启动到每个工具执行,都能访问到同一份数据。令牌这种敏感信息,就应该放在这个上下文里。

这里要说明一下,ADK-Python 的 API 在不同版本里字段名可能会调整,下面涉及的代码是我基于常见实践做的合理示例,重点是思路。核心思路是三层:第一层是启动时统一注入凭证,Agent 入口从一个安全的凭证源拿到所有需要的令牌;第二层是上下文携带,令牌通过上下文对象传给当前这一步要执行的工具函数;第三层是工具内部按需取用,每个工具只从上下文里取自己服务对应的那一个凭证,而不是全局拿一把万能 Key。这个设计比起“全局变量 + 到处 import”,最大的好处是:凭证的生命周期、可见范围、审计点都是可控的。

2. 令牌传递的四种常见模式,以及怎么选

2.1 四种主流令牌模式的对比

做 Agent 令牌传递,首先不是写代码,而是选模式。我整理了一下实际项目里最常见的四种:

令牌模式典型场景有效期轮换难度风险等级
静态 API Key内部服务间调用、测试环境长期有效高中高
OAuth 2.0 令牌交换Agent 模拟用户操作、访问用户资源短时令牌 + 刷新令牌中中
服务账号委托后台任务、无人值守的定时 Agent证书签发、短期令牌中低
临时 STS 凭证云资源访问、跨账号授权分钟级到小时级低低

静态 API Key 最直接,配置一个密钥字符串,Agent 启动时读进来。缺点是长期有效,一旦泄露就要人工吊销重发,整个过程要等配置生效、各缓存过期,挺费劲。OAuth 2.0 的基本逻辑是:用户先授权给 Agent,Agent 拿到一个短时 Access Token 和 Refresh Token,Access Token 过期后用 Refresh Token 换新的。服务账号委托则是 Agent 自己有一个身份,不需要用户参与,适合定时任务。临时 STS 凭证是云平台提供的受限临时身份,用完就失效,权限范围可以由参数严格限定,适合跨账号拿资源的场景。

2.2 选型思路:先看“信任边界”

我个人的选型经验是:先别管技术栈,先回答三个问题。第一个问题:Agent 是代表用户操作,还是代表自己操作?代表用户,选 OAuth 2.0 令牌交换;代表自己,选服务账号或静态 Key。第二个问题:Agent 要访问的资源和环境是怎么隔离的?如果生产环境和测试环境用的是同一个账号体系,再好的令牌机制也是白搭,信任边界先在环境层面划清楚。第三个问题:令牌泄露之后,你最怕什么?是怕范围过大被人利用,还是怕无法及时吊销?如果最怕是范围过大,那临时 STS 凭证就比静态 Key 合适得多。

这里我想强调一个经验:不要为了省事,把所有外部服务都塞进同一个 OAuth scope 里。scope 是 API 用来做“最小权限”的天然工具,你给 Agent 申请权限的时候,scope 列表就是你的第一道防线。我用过一个项目:Agent 要读对象存储里的文件,也要写另一个 bucket,结果申请令牌的时候图省事直接申请了“全部资源管理权限”。后面做权限收紧时,为了改这个 scope,所有 Agent 实例要重新换取令牌,光协调各服务方就花了两天。如果一开始就按服务拆分 scope,收紧是几分钟的事。

2.3 一个容易被忽略的“令牌缓存与刷新”设计

选完模式之后,还有个实际问题:令牌不能每次都重新去换,也不能永远不换,得有个缓存和刷新机制。缓存的目的很明确:服务端签发的令牌通常有时效,频繁换取会给认证服务造成压力,而且每次换取都是一次网络往返,会拖慢 Agent 的响应速度。刷新策略这块,我踩过的最典型的坑是:多个工具并行执行,同时发现令牌过期,同时去刷新令牌,结果认证服务收到一堆相同请求,其中一部分返回了刷新失败。解决方案是加一个进程内的互斥锁,保证同一时刻只有一个刷新动作在跑,其他调用等待刷新完成之后直接拿新令牌。

3. 实操:在 ADK-Python 里落地安全令牌传递

3.1 先把目录结构和凭证模块搭起来

代码落地前,先规划一下目录。最简单的结构是这样:

agent_workspace/ ├── credential_manager.py # 凭证管理:缓存、刷新、脱敏 ├── agents.py # Agent 定义与工具注册 ├── tools/ │ ├── report_tool.py # 查报告的工具 │ └── storage_tool.py # 读存储的工具 └── main.py # 程序入口

凭证管理模块是整个机制的地基。它的职责是:启动时从环境变量或密钥管理服务读取基础凭证,按服务名缓存当前有效的 Access Token,提供加锁的刷新逻辑,并且只在内存中保留令牌明文。模块本身不负责具体业务逻辑,不主动打印任何包含令牌的内容。这样设计的好处是:Agent 的其他模块根本不需要知道令牌从哪来、怎么刷新的细节,它们只需要调用credential_manager.get_token(service_name)就能拿到当前可用的令牌。

3.2 使用工具函数解耦令牌传递

ADK-Python 里工具函数是 Agent 能力的载体。每个工具函数都接收一个上下文参数,令牌就放在上下文里。代码示例:

# tools/report_tool.py from google.adk import tool @tool def fetch_build_report(date: str, ctx) -> dict: """拉取指定日期的构建报告。""" cred = ctx.credentials["report_service"] headers = {"Authorization": f"Bearer {cred.access_token}"} url = f"https://report-internal.example/v1/builds/{date}" response = http_get(url, headers=headers, timeout=10) return response.json()

这里有几个关键点。第一,工具函数里没有从环境变量直接读取令牌,而是从ctx.credentials里面拿,这样令牌来源和刷新逻辑都被集中管理了。第二,某个工具只取自己服务对应的凭证,而不是全局遍历所有凭证,这可以避免把无关服务的令牌带进当前调用链。第三,工具函数返回的是业务结果,不含任何令牌信息。

3.3 连接受保护服务的完整流程示例

下面这个示例是模拟场景,但流程上和我实际跑过的项目几乎一致。假设有两个工具:fetch_build_report负责从报告服务拉数据,fetch_report_file负责从对象存储下载原始文件。Agent 收到用户请求“把最新构建报告和原始日志文件打包下载”时,会串行调用这两个工具。

凭证管理模块先定义服务注册表:

# credential_manager.py import threading import time class CredentialManager: def __init__(self): self._tokens = {} self._lock = threading.Lock() def get_token(self, service: str) -> str: with self._lock: cred = self._tokens.get(service) if cred and cred["expires_at"] > time.time() + 60: return cred["access_token"] return self._refresh(service) def _refresh(self, service: str) -> str: # 这里是统一的刷新逻辑,只从安全配置源读取刷新凭据 # 实际项目中可以从密钥管理服务拉取 pass

然后在 Agent 启动时,把当前用到的服务列表注册进去:

# main.py from credential_manager import CredentialManager from agents import build_report_agent cm = CredentialManager() cm.register("report_service", refresh_token_env="REPORT_SERVICE_REFRESH") cm.register("storage_service", refresh_token_env="STORAGE_SERVICE_REFRESH") agent = build_report_agent(credential_manager=cm) result = agent.run("把最新构建报告和原始日志文件打包下载")

工具函数通过上下文拿到令牌后,发请求、取数据、返回结果。整体调用链就是:Agent 入口 → 上下文携带 credentials → 工具按需取用 → 服务返回结果 → 上下文继续传给下一个工具。这个链路中最容易出问题的是“上下文里的 credentials 从哪初始化”这一步,必须在一个统一入口做,不能每个工具自己初始化一份。

3.4 日志脱敏与审计的落地写法

令牌最容易泄露的两个出口,一个是异常堆栈,一个是日志。处理办法有两层。第一层是在日志输出层做脱敏,只要字段名包含token、key、secret、authorization这些关键词,一律替换成掩码。第二层是在凭证管理模块返回令牌时就带上类型标记,这样工具打印字典、序列化响应时,能自动识别敏感字段。

代码示例:

# log_sanitizer.py import re SENSITIVE_PATTERN = re.compile( r"(token|api_key|secret|authorization|password)", re.IGNORECASE, ) def sanitize(value, mask="***"): if isinstance(value, str) and SENSITIVE_PATTERN.search(value): return mask if isinstance(value, dict): return {k: sanitize(v) for k, v in value.items()} return value

审计方面,不需要搞复杂的系统,先把两个字段记下来就足够:某个工具调用时用的是哪个 service 的令牌、这个令牌的 scope 是什么。这两个字段能支撑绝大多数排查场景。我自己在项目里会在每次凭证刷新时打一条结构化日志,包含服务名、新令牌的过期时间、scope 列表,但绝对不打令牌本身。

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

4.1 令牌在工具间“平白消失”

这是我被问得最多的一个问题:第一个工具里还能取到ctx.credentials,第二个工具里就变成空字典了。排查的时候先别怀疑框架,先看工具函数的参数声明。ADK-Python 这类框架里,工具函数要显式接收上下文参数。如果你在中间某一层没有把上下文透传下去,或者把工具包装成了闭包,上下文链路就断了,令牌自然就“消失”了。我的习惯是:写工具函数时,第一个参数就放ctx,并且不随意改参数名,保持整个项目一致。还有一个隐藏坑:有些工具框架做了线程池调度,上下文对象不是线程安全的。如果工具实现里有异步回调,要先在主线程里把令牌取出来,再传给回调函数,不要在回调里反复读上下文。

4.2 日志把令牌打了出去

这类问题我见过的版本很多:有人把请求对象整个放进日志里,有人把 headers 字典打印了出来,还有人把异常对象直接序列化。最狠的一次是,一个工具函数把完整响应体打到了日志,响应体里包含服务端回显的 Access Token,那一行日志在监控系统里躺了一周。处理思路是:日志模块必须统一,接一个结构化日志器,所有 stdout 输出都走这个模块。然后在每个 Agent 工具入口加一个统一的脱敏装饰器,凡是工具函数的入参和返回值,进出去之前都过一遍脱敏逻辑。不用做得太复杂,正则清洗加上关键词掩码就够用了。

4.3 刷新令牌并发冲突

这个问题前面提过。现象是:Agent 里多个工具并行调用,某个瞬间所有工具都发现当前令牌过期,于是同时去刷新。刷新服务那边可能没有做幂等处理,结果其中一个刷新请求成功了,另外几个拿到的是旧刷新凭据换出来的新令牌,甚至直接报“refresh token already used”。解决办法就是加锁。注意这个锁必须是进程内的互斥锁,而不是分布式锁。如果是多实例部署,每个实例只对自己的内存缓存负责,分布式锁反而会把问题搞复杂。如果你用的是多进程部署,可以给不同进程分配不同的服务实例,或者接受每个进程独立维护一份令牌缓存,前提是刷新凭据本身是可重复使用的。

4.4 权限“收不回”

还有一个普遍头疼的问题:令牌配置成了“万能权限”,真要出事时无从下手。我建议做 Agent 项目的时候,专门做一次权限清单表,列出每个工具用到的服务、令牌类型、scope、过期策略。这个表不一定要给外部系统看,主要是自己排查用。每次新增一个工具,先更新这个表,再写代码。这个习惯帮我避过一次大坑:有一次要给 Agent 增加一个“删除旧归档”的功能,我看到权限清单上对应服务只有只读 scope,立刻意识到这个工具不能直接接进来,需要先申请新的受限 scope。多花了两天申请流程,但避免了 Agent 在生产环境拥有删除权限的风险。

5. 最后分享一点我的实践体会

做这个令牌传递机制,我实际的体会是:安全性和开发效率往往是对着干的,但令牌传递这件事,恰恰是两者能同时兼顾的少数环节。你只要把凭证源统一到一个模块、把传递通道统一到上下文、把日志出口统一做脱敏,后面再加工具、加服务,基本不需要改动既有逻辑,开发效率反而会变得更高。我最早做的时候也嫌这套东西繁琐,毕竟刚开始 Agent 只有两个工具,直接写全局变量最省事。后来工具数量一多,服务一多,当初省下的那点功夫连半天排查日志泄露都不够用。建议正在做 ADK-Python Agent 开发的朋友,哪怕场景再简单,也先把这套机制搭起来。先让令牌只在你规定的通道里流动,再在最小权限的基础上逐步放开,这比一开始放开后面再收紧要省太多力气。最后再补一句:令牌传递机制不是一次性的,每次新增工具类型、每次调整服务权限范围,都应该回头检查一遍上下文里到底装了什么、日志里可能带出什么。好记性不如烂笔头,把权限清单和上下文结构写进项目文档,比什么都管用。

相关新闻

Qt5下QFTP文件包接入实战:解决undefined reference与工程迁移

Qt5下QFTP文件包接入实战:解决undefined reference与工程迁移

简介:QT5移除了内置QFTP模块,使依赖FTP功能的开发者面临重新实现客户端能力的难题。这份资源正面向需要自建QT5 FTP模块的C开发者,提供一套可运行的QFTP实现,支持FTP连接、用户登录、服务器目录切换与列表获取,并留有扩…

2026/10/21 5:12:04 阅读更多 →
C++ union 的一种妙用法

C++ union 的一种妙用法

前言 union 是 C 时代留下来的少数几个「看起来简单、实际很容易用错」的语言设施之一。它的表面语义只有一句:所有成员共用同一块存储,同一时刻只有一个成员是「活的」。但「活的」这三个字在 C 里意味着对象的生命周期,而生命周期恰恰是很多…

2026/10/21 5:12:04 阅读更多 →
手写JSON解析器:从词法分析到语法树的编译原理实战

手写JSON解析器:从词法分析到语法树的编译原理实战

1. 从一段JSON文本到内存对象:解析器到底在做什么很多人第一次接触编译原理,都是从"写一个JSON解析器"开始的。这个选择很聪明——JSON的语法足够简单,简单到一页纸就能写完文法;但它又足够完整,完整到能覆盖…

2026/10/21 5:11:04 阅读更多 →

最新新闻

Elasticsearch集群安全配置:TLS/SSL与密钥库管理全解析

Elasticsearch集群安全配置:TLS/SSL与密钥库管理全解析

给Elasticsearch集群做安全配置这件事,远比想象中麻烦。很多人以为在elasticsearch.yml里加几行TLS/SSL配置就能收工,结果往往是节点之间互相不认证书、Kibana连不上、客户端报handshake failed,最后只能回滚。我这次把一套三节点ES集群从裸H…

2026/10/21 5:49:18 阅读更多 →
嘎嘎降AI实战教程:从注册到多轮降重,让AI文本更自然

嘎嘎降AI实战教程:从注册到多轮降重,让AI文本更自然

"嘎嘎降AI"这名字听起来有点随意,但用起来确实能解决一个实际问题:AI生成的内容,如何改得更像人写的。很多朋友现在写材料、做方案、写公众号,第一稿都习惯让AI对话软件先起个草,然后再自己改。可草稿一到手…

2026/10/21 5:49:18 阅读更多 →
广播、未知、同网、异网:交换机转发与排障核心解析

广播、未知、同网、异网:交换机转发与排障核心解析

干网络这行久了,最常被新人追问的一个问题是:交换机把一个帧从某个端口收进来之后,它凭什么决定从哪个端口送出去?教科书上会讲查MAC地址表、泛洪、丢弃,但真到排障时,大家还是容易懵。后来我发现&#xff…

2026/10/21 5:49:18 阅读更多 →
嘎嘎降AI工具详解:从注册到实操的AI文本自然化处理指南

嘎嘎降AI工具详解:从注册到实操的AI文本自然化处理指南

“你手头有一段AI生成的文章,第一眼读起来特别工整,逻辑也没什么毛病,但总觉得有点‘板’,像戴着面具在说话”——这就是我最初接触到“嘎嘎降AI”这个工具时最想解决的问题。它不是用来做文案初稿的,也不是单纯改错别…

2026/10/21 5:49:18 阅读更多 →
技能管理系统:从技能地图到刻意练习的完整框架

技能管理系统:从技能地图到刻意练习的完整框架

很多人看到“skills”这个词,第一反应是“又一篇讲职场技能提升的文章”。但我今天想换个角度聊:技能不是一个名词,而是一套需要持续维护的系统。你收藏过一堆课程、买过一堆书、报过训练营,最后真正变成自己能力的,其…

2026/10/21 5:49:18 阅读更多 →
AI信息挖掘实战:从提问到交叉验证,把碎片信息串成认知

AI信息挖掘实战:从提问到交叉验证,把碎片信息串成认知

元旦的钟声刚敲过,我猜不少人正把“新的一年多学点东西、把手头的事做精”写进计划表。这几天我发现一个很有意思的现象:大家不是不想查资料,而是被资料淹没了。打开浏览器想看某个行业的最新动态,结果十条链接里八条是去年的&…

2026/10/21 5:48:18 阅读更多 →

日新闻

统一内存+量化:锐龙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/21 4:27:17 阅读更多 →

月新闻

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