AI Agent Harness Engineering 的“手”不够用?用 MCP 协议统一工具调用标准的 TypeScript 配置骨架
1. 当 Agent 的“手”开始打架工具调用碎片化的真实困境如果你正在做 AI Agent 的 Harness Engineering大概率遇到过这种场面代码助手 Agent 已经接好了本地文件读写、Git 操作、终端命令执行跑得挺顺。结果产品说“再加个 Jira 查询吧”你打开项目一看工具注册表里已经躺着十几个函数每个函数的参数 Schema 写法都不一样有的用zod有的手写 JSON Schema有的干脆把参数拼成字符串让模型自己解析。更头疼的是换一个 Harness 框架——比如从自研的调度循环切到某个开源 Agent 框架——这些工具定义几乎要全部重写一遍。这就是 AI Agent 工具调用碎片化的典型症状。Agent 的“大脑”LLM越来越强但它的“手”工具调用层却因为缺乏统一标准而各自为政。每个 Harness 有自己的工具注册方式每个工具提供方有自己的接口约定结果就是工具复用率极低接入成本极高维护起来像在打地鼠。MCP 协议Model Context Protocol试图解决的就是这个问题。它把工具调用从“每个 Harness 自己定一套”变成“大家共用一套描述和调用标准”。你可以把它理解成 Agent 工具世界的 USB-C不管你是笔记本、手机还是平板接口形状统一了插上就能用。本文聚焦 TypeScript 项目场景给出一套可复制的 MCP 工具注册配置骨架和类型定义并带你走通本地验证工具调用链路的完整步骤。适合正在做 Agent 工程化、被工具接入折磨过的开发者。2. 前置准备TaoToken 接入与 TypeScript 项目环境在开始写 MCP 工具注册骨架之前需要先把模型调用通道准备好。我试过用 TaoToken 作为统一接入层它兼容 OpenAI 风格的接口同时支持 Claude Code、Coding Plan 等场景对于需要频繁切换模型做工具调用验证的 Harness Engineering 来说比较省事。2.1 获取 API Key 与配置环境变量首先到 TaoToken 控制台创建一个 API Key。地址是https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_harness_ts。创建完成后把 Key 写入项目根目录的.env文件# .env TAOTOKEN_API_KEYsk-你的实际key TAOTOKEN_BASE_URLhttps://taotoken.net/api注意API 地址不要加 UTM 参数直接用https://taotoken.net/api即可。如果你用的是 Claude Code 或 Anthropic 风格的调用可以参考接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_harness_ts。2.2 初始化 TypeScript 项目创建一个新的 TypeScript 项目安装 MCP 官方 SDK 和必要的类型依赖mkdir mcp-harness-skeleton cd mcp-harness-skeleton npm init -y npm install modelcontextprotocol/sdk zod npm install -D typescript types/node tsx然后在tsconfig.json中开启严格模式和 ESM 支持{ compilerOptions: { target: ES2022, module: NodeNext, moduleResolution: NodeNext, strict: true, esModuleInterop: true, outDir: dist, rootDir: src }, include: [src/**/*] }在package.json中加上type: module和运行脚本{ type: module, scripts: { dev: tsx src/server.ts, build: tsc } }到这里项目骨架就准备好了。接下来进入核心部分用 TypeScript 定义 MCP 工具注册的标准结构。3. 可复制配置MCP 工具注册骨架与 TypeScript 类型定义MCP 协议的核心思路是工具提供方MCP Server用标准格式描述自己有哪些工具、每个工具接受什么参数、返回什么结果工具使用方MCP Client / Harness按照同样的标准去发现和调用。下面这套骨架可以直接复制到你的项目里。3.1 定义工具描述的类型结构先创建一个src/types.ts把工具注册需要的类型集中管理// src/types.ts import { z } from zod; /** MCP 工具的标准描述结构 */ export interface McpToolDefinitionT extends z.ZodTypeAny z.ZodTypeAny { /** 工具名称全局唯一建议用 snake_case */ name: string; /** 工具用途描述会直接进入模型上下文写清楚“什么时候用” */ description: string; /** 参数 Schema用 zod 定义运行时校验 生成 JSON Schema */ inputSchema: T; /** 工具执行函数入参类型由 inputSchema 推导 */ handler: (args: z.inferT) PromiseMcpToolResult; } /** 工具调用返回结构 */ export interface McpToolResult { content: Array{ type: text | resource; text?: string; resource?: { uri: string; mimeType: string; text: string }; }; isError?: boolean; } /** 工具注册表Harness 通过它统一发现和调用 */ export class McpToolRegistry { private tools new Mapstring, McpToolDefinition(); registerT extends z.ZodTypeAny(tool: McpToolDefinitionT): void { if (this.tools.has(tool.name)) { throw new Error(工具名称冲突: ${tool.name}); } this.tools.set(tool.name, tool as McpToolDefinition); } list(): McpToolDefinition[] { return Array.from(this.tools.values()); } get(name: string): McpToolDefinition | undefined { return this.tools.get(name); } /** 生成 MCP 标准的 tools/list 响应 */ toMcpToolList() { return this.list().map((tool) ({ name: tool.name, description: tool.description, inputSchema: zodToJsonSchema(tool.inputSchema), })); } }这里的关键点是用 zod 作为单一事实来源。参数校验、类型推导、JSON Schema 生成都从同一个定义出发避免手写 Schema 和实际校验逻辑不一致。3.2 实现 zod 到 JSON Schema 的转换MCP 协议要求工具的inputSchema是 JSON Schema 格式。写一个轻量转换函数覆盖常用类型即可// src/schema.ts import { z } from zod; export function zodToJsonSchema(schema: z.ZodTypeAny): Recordstring, unknown { if (schema instanceof z.ZodObject) { const shape schema.shape; const properties: Recordstring, unknown {}; const required: string[] []; for (const [key, value] of Object.entries(shape)) { properties[key] zodToJsonSchema(value as z.ZodTypeAny); if (!(value instanceof z.ZodOptional)) { required.push(key); } } return { type: object, properties, required }; } if (schema instanceof z.ZodString) { return { type: string, description: schema.description }; } if (schema instanceof z.ZodNumber) { return { type: number, description: schema.description }; } if (schema instanceof z.ZodBoolean) { return { type: boolean, description: schema.description }; } if (schema instanceof z.ZodOptional) { return zodToJsonSchema(schema.unwrap() as z.ZodTypeAny); } if (schema instanceof z.ZodEnum) { return { type: string, enum: schema.options }; } return { type: object }; }这段代码不追求覆盖所有 zod 特性但足够支撑大部分工具定义场景。如果你的工具参数更复杂可以按需扩展。3.3 注册两个示例工具创建src/tools.ts注册两个典型工具一个读文件一个查 Git 状态。这两个工具在 Harness Engineering 里出现频率很高。// src/tools.ts import { z } from zod; import { readFile } from node:fs/promises; import { exec } from node:child_process; import { promisify } from node:util; import { McpToolRegistry } from ./types.js; const execAsync promisify(exec); export function createToolRegistry(): McpToolRegistry { const registry new McpToolRegistry(); registry.register({ name: read_local_file, description: 读取本地文件内容。当需要查看项目中的源码、配置或文档时使用。, inputSchema: z.object({ path: z.string().describe(文件的绝对路径或相对于项目根目录的路径), encoding: z.enum([utf-8, ascii]).optional().describe(文件编码默认 utf-8), }), handler: async ({ path, encoding }) { try { const content await readFile(path, { encoding: encoding ?? utf-8 }); return { content: [{ type: text, text: content }], }; } catch (err) { return { content: [{ type: text, text: 读取失败: ${(err as Error).message} }], isError: true, }; } }, }); registry.register({ name: git_status, description: 查看当前 Git 仓库的状态包括修改、暂存和未跟踪文件。, inputSchema: z.object({ cwd: z.string().describe(Git 仓库根目录路径), short: z.boolean().optional().describe(是否使用简短输出格式), }), handler: async ({ cwd, short }) { try { const { stdout } await execAsync(git status${short ? --short : }, { cwd }); return { content: [{ type: text, text: stdout || 工作区干净 }], }; } catch (err) { return { content: [{ type: text, text: Git 命令执行失败: ${(err as Error).message} }], isError: true, }; } }, }); return registry; }注意description的写法不要只写“读取文件”而要写清楚“什么时候用”。模型在选择工具时描述的质量直接影响调用准确率。3.4 接入 MCP Server 标准协议最后创建src/server.ts把注册表挂到 MCP Server 上// src/server.ts import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { CallToolRequestSchema, ListToolsRequestSchema, } from modelcontextprotocol/sdk/types.js; import { createToolRegistry } from ./tools.js; const registry createToolRegistry(); const server new Server( { name: harness-tool-server, version: 1.0.0 }, { capabilities: { tools: {} } } ); server.setRequestHandler(ListToolsRequestSchema, async () ({ tools: registry.toMcpToolList(), })); server.setRequestHandler(CallToolRequestSchema, async (request) { const tool registry.get(request.params.name); if (!tool) { return { content: [{ type: text, text: 未知工具: ${request.params.name} }], isError: true, }; } const parsed tool.inputSchema.safeParse(request.params.arguments); if (!parsed.success) { return { content: [{ type: text, text: 参数校验失败: ${parsed.error.message} }], isError: true, }; } return tool.handler(parsed.data); }); const transport new StdioServerTransport(); await server.connect(transport);这套骨架的核心价值在于工具定义、参数校验、协议响应三者统一。新增工具只需要在createToolRegistry里加一段注册代码不需要改 Server 逻辑也不需要手写 JSON Schema。4. 验证请求本地跑通工具调用链路配置写完了接下来验证它是否真的能跑通。分两步先用 MCP Inspector 做协议层验证再用 TaoToken 的模型对话做端到端验证。4.1 用 MCP Inspector 检查工具列表MCP 官方提供了一个调试工具modelcontextprotocol/inspector可以直接查看 Server 暴露的工具npx modelcontextprotocol/inspector npx tsx src/server.ts启动后浏览器会自动打开一个界面左侧能看到read_local_file和git_status两个工具点击每个工具可以查看它的 JSON Schema 和描述。如果这里能看到正确的工具列表说明协议层配置没问题。4.2 用模型对话验证工具调用协议层通了之后需要验证模型能否正确选择工具并生成合法参数。到 TaoToken 的模型对话页面创建一个测试会话https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_harness_ts。把上面toMcpToolList()生成的工具列表作为tools参数传给模型然后发一条测试消息{ model: claude-sonnet-4-20250514, messages: [ { role: user, content: 帮我看看 /Users/demo/project 这个仓库当前的 Git 状态用简短格式 } ], tools: [ { name: git_status, description: 查看当前 Git 仓库的状态包括修改、暂存和未跟踪文件。, inputSchema: { type: object, properties: { cwd: { type: string, description: Git 仓库根目录路径 }, short: { type: boolean, description: 是否使用简短输出格式 } }, required: [cwd] } } ] }如果模型返回的tool_calls里包含git_status并且arguments是{cwd: /Users/demo/project, short: true}说明工具描述和 Schema 的质量足够让模型正确理解。实测下来描述写得越具体模型选错工具的概率越低。4.3 完整链路从模型输出到工具执行把模型返回的tool_calls参数传给registry.get(name).handler()执行结果再作为tool角色的消息回传给模型。这一步在 Harness 里通常是一个循环async function runToolLoop(userMessage: string) { const messages [{ role: user, content: userMessage }]; const tools registry.toMcpToolList(); while (true) { const response await callModel({ messages, tools }); const choice response.choices[0]; if (choice.finish_reason tool_calls) { for (const call of choice.message.tool_calls) { const tool registry.get(call.function.name); const args JSON.parse(call.function.arguments); const result await tool!.handler(args); messages.push({ role: tool, tool_call_id: call.id, content: result.content.map((c) c.text).join(\n), }); } continue; } return choice.message.content; } }这段循环就是 Harness Engineering 里最核心的“手脑协同”逻辑。MCP 的价值在于tools的格式是标准的tool_calls的解析是标准的工具执行结果的回传格式也是标准的。换一个模型、换一个 Harness这套循环几乎不用改。5. 本篇常见错排查工具调用链路跑不通时问题通常集中在几个地方。下面按出现频率从高到低排列。5.1 工具列表为空或 Schema 格式错误现象MCP Inspector 里看不到工具或者模型返回“没有可用工具”。排查方向检查ListToolsRequestSchema的 handler 是否真的返回了tools数组检查zodToJsonSchema对z.ZodOptional的处理是否正确——如果 optional 字段被错误地放进了required部分模型会拒绝调用。另外确认inputSchema的顶层type是objectMCP 协议要求工具参数必须是对象类型。5.2 参数校验失败但模型生成的参数看起来没问题现象safeParse返回失败但打印出来的arguments肉眼看着是对的。常见原因是类型不匹配。比如模型把short传成了字符串true而不是布尔值true或者把数字传成了字符串。zod 的z.boolean()不会自动转换字符串。解决办法是在 Schema 里用z.coerce.boolean()做强制转换或者在 handler 里做一次规范化。另一个原因是模型把可选字段传成了null而 zod 的.optional()只接受undefined这时需要用.nullable().optional()。5.3 工具名称冲突导致注册失败现象启动时报工具名称冲突。MCP 协议要求工具名称在单个 Server 内唯一。如果多个模块各自注册了同名工具McpToolRegistry.register会直接抛错。建议在工具命名时加前缀比如file_read、git_status、jira_query避免不同模块之间撞名。如果确实需要覆盖可以在注册表里加一个override选项但更推荐从命名规范上解决。5.4 模型选错工具或反复调用同一个工具现象模型明明该调read_local_file却调了git_status或者调用失败后不换工具反复重试同一个。这通常不是代码问题而是工具描述的问题。description里要写清楚“什么时候用这个工具”和“什么时候不要用”。比如read_local_file的描述可以加上“仅用于读取文件内容不用于查看目录结构”。另外工具执行失败时返回的isError: true和错误信息要足够具体模型会根据错误信息决定下一步。如果错误信息只写“失败”模型很难做出正确判断。5.5 Stdio 传输下日志输出干扰协议通信现象MCP Inspector 能连上但工具调用没有响应或者连接随机断开。Stdio 传输模式下Server 的stdout被协议占用任何console.log都会污染 JSON-RPC 消息流。解决办法是把所有调试日志写到stderr用console.error而不是console.log。如果用了第三方库确认它没有往stdout打印内容。这个问题在本地开发时特别隐蔽因为单独跑 Server 看起来一切正常一接入 Harness 就出问题。6. 把工具调用标准落到你的 Harness 里回到最开始的问题Agent 的“手”不够用本质不是工具太少而是工具的接入和复用成本太高。MCP 协议提供的统一描述和调用标准让工具从“每个 Harness 自己适配”变成“一次定义、多处使用”。上面这套 TypeScript 骨架可以直接作为你项目里的工具注册层新增工具只需要加一段registry.register协议响应、参数校验、类型推导都自动完成。如果你正在做长期编码类 Agent 或需要频繁切换模型的场景可以了解一下 TaoToken 的 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_harness_ts。它把模型调用和工具调用链路的验证放在同一个环境里省去来回切换配置的麻烦。API Key 管理入口在https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_harness_ts接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_harness_ts。最后留一个实用建议工具注册表里的description值得反复打磨。我踩过的坑是一开始把描述写得太简略模型经常在read_local_file和git_status之间犹豫。后来把每个工具的“适用场景”和“不适用场景”都写进描述调用准确率明显提升。工具调用的稳定性一半靠协议标准一半靠描述质量。

相关新闻

Git常用命令-工具箱

Git常用命令-工具箱

目录 一、Git 下载与安装 1.1 下载 1.2 安装 二、Git 配置 2.1 基本配置 2.2 远程分支清理 三、Git 基本操作 3.1 克隆仓库 3.2 拉取更新 3.3 提交与推送 四、Git 进阶操作 4.1 切换分支 4.2 放弃本地修改 五、Git 认证管理 5.1 记住 Git 密码 六、前端项目提交…

2026/9/30 11:40:57 阅读更多 →
计算机毕业设计之基于uni-app的安保管理小程序的设计与实现

计算机毕业设计之基于uni-app的安保管理小程序的设计与实现

近年来互联网络的迅猛发展和电子终端设备的普及,赋予了各行业充足的发展空间。安保管理小程序相比于传统信息技术,时效性是它最大的特色,已经在电子娱乐、经济等中发挥着举足轻重的作用。短时间内迅速扩大了线上管理系统的规模。尽管安保管理…

2026/9/30 11:40:58 阅读更多 →
Android Studio Debug 调试全篇:TaoToken 统一 Key 接入 settings.json 配置与断点验证

Android Studio Debug 调试全篇:TaoToken 统一 Key 接入 settings.json 配置与断点验证

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

2026/9/30 12:19:12 阅读更多 →

最新新闻

连点器全攻略:鼠标自动连点、键盘输入与免费工具筛选

连点器全攻略:鼠标自动连点、键盘输入与免费工具筛选

连点器这三个字一出来,很多人第一反应是"游戏挂机脚本",但说实话我用了这么多年,鼠标自动连点真正帮我省下大把时间的场景,反而是那些枯燥到让人怀疑人生的重复工作:一遍遍点"下一步"、批量填表、…

2026/10/1 12:50:58 阅读更多 →
Jev模型结构化输出实战:JSON Schema约束如何让判断题成本直降5倍

Jev模型结构化输出实战:JSON Schema约束如何让判断题成本直降5倍

1. 从一张账单说起:为什么同一个判断题,成本能差 5 倍 先把场景摆出来。我最近在做一个批量内容审核的小工具,核心逻辑特别简单:给一段文本,让模型判断它是否属于某个类别,输出只有两个值——是或否。这种任…

2026/10/1 12:50:58 阅读更多 →
ECC公钥压缩与非压缩格式:汽车电子选型与避坑指南

ECC公钥压缩与非压缩格式:汽车电子选型与避坑指南

1. 汽车电子里的ECC公钥,为什么格式值得拿出来单聊做车载安全的人应该都有感触:ECC(椭圆曲线密码)在车联网和汽车电子里的应用已经非常密集,V2X车路协同、OTA升级签名、安全启动、SecOC报文认证、PKI证书链&#xff0c…

2026/10/1 12:50:58 阅读更多 →
IEC 62351-100-3一致性测试全解析:从安全机制到工程实践

IEC 62351-100-3一致性测试全解析:从安全机制到工程实践

拿到IEC 62351-100-3一致性测试任务的时候,我第一反应是找标准原文、打开产品功能清单,准备一项一项打勾。真正进了实验室才发现,这套测试折腾的根本不是“功能是否正常”,而是“产品对安全机制的理解是否和标准一致”。业务功能正…

2026/10/1 12:50:58 阅读更多 →
Wine+FEX-Emu+DXMT:在Apple Silicon上运行Windows游戏的完整指南

Wine+FEX-Emu+DXMT:在Apple Silicon上运行Windows游戏的完整指南

1. 从“Madeira”这个名字说起:它到底指什么第一次看到“Madeira”这个词,很多人第一反应是葡萄牙那个盛产葡萄酒的海岛,或者是一道叫“马德拉酱汁”的西餐配料。但在技术圈里,尤其是围绕 Wine、FEX-Emu、DXMT 这些关键词的讨论中…

2026/10/1 12:50:58 阅读更多 →
Ubuntu Desktop 安装全流程避坑指南:从分区到驱动配置

Ubuntu Desktop 安装全流程避坑指南:从分区到驱动配置

Ubuntu Desktop 的安装,单看流程图确实简单到让人提不起兴趣:下镜像、写U盘、引导、分区、下一步。但真正动手装过十几台机器之后你会发现,翻车的点几乎全在"下一步"之外——UEFI 和 Legacy 引导混着用导致装完进不去系统、硬盘分区…

2026/10/1 12:49: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/1 0:00:30 阅读更多 →
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/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练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/1 1:01:17 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/30 13:14:22 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 18:13:06 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/30 13:14:49 阅读更多 →

月新闻

我发现了一个新思路:用 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/1 0:00:30 阅读更多 →
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/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练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/1 1:01:17 阅读更多 →