C++高性能AI Agent系统架构设计与工程实践
1. 项目概述为什么是C与AI Agent的碰撞最近在技术社区和招聘网站上一个趋势越来越明显那些能深入系统底层、用C构建高性能AI Agent的开发者正在成为市场上最抢手、议价能力最强的一批人。这听起来可能有点反直觉毕竟现在AI领域的主流叙事是Python为王各种高级框架和云服务让“快速搭建”变得无比容易。但恰恰是这种“易用性”的泛滥让真正理解计算本质、能榨干硬件每一分性能的系统级开发技能变得极度稀缺。我之所以想从零开始实现一个高性能的C AI Agent并非为了标新立异。而是在实际工作中我遇到了太多Python Agent无法逾越的瓶颈当需要处理海量实时数据流时Python的GIL全局解释器锁和动态类型带来的开销成了性能黑洞当需要将Agent嵌入到资源受限的边缘设备如自动驾驶控制器、工业机器人时Python庞大的运行时环境和依赖库显得臃肿不堪当需要实现极低延迟的决策循环比如高频交易策略Agent时从Python调用C扩展带来的上下文切换开销也变得不可接受。这些场景下C的零成本抽象、手动内存管理、以及对硬件架构的紧密贴合就成为了不可替代的优势。这个项目的核心目标就是穿越“用框架搭积木”的舒适区深入到AI Agent的系统架构层面。我们将从最基础的Socket通信、并发模型设计开始构建一个能自主规划、调用工具、并维持长期记忆的智能体核心。这不仅仅是写代码更是一次对计算机系统从CPU缓存一致性到网络IO多路复用的深度复习。掌握这项技能意味着你不仅能做出一个Agent更能理解它从高级指令到机器码的完整生命周期具备在2025年及以后解决最复杂、最苛刻的AI工程化问题的能力。2. 核心架构设计构建一个“系统级”智能体2.1 摒弃“胶水代码”思维拥抱原生并发很多从Python转向C的开发者第一个误区就是试图用C语法去模仿Python asyncio或Go goroutine的写法结果写出一堆难以维护的、基于回调或std::thread的“胶水代码”。系统级开发的核心思维是资源管理和执行模型的精确控制。对于AI Agent这种典型的I/O密集型等待LLM响应、数据库查询、API调用兼计算密集型数据预处理、结果解析、决策逻辑的任务传统的“一个请求一个线程”thread-per-request模型在C中会迅速耗尽资源。我们必须采用更高效的并发模型。这里Reactor模式配合IO多路复用是更地道的选择。我们可以使用libuv或者直接使用Linux的epoll或Windows的IOCP作为事件循环的核心。一个简单的基于libuv的事件循环骨架如下#include uv.h #include functional #include queue #include mutex class UvLoop { public: UvLoop() { uv_loop_init(loop_); } ~UvLoop() { uv_loop_close(loop_); } void run() { uv_run(loop_, UV_RUN_DEFAULT); } uv_loop_t* get() { return loop_; } // 将任务投递到事件循环线程执行 void post(std::functionvoid() task) { { std::lock_guardstd::mutex lock(queue_mutex_); task_queue_.push(std::move(task)); } // 通知事件循环有新的任务 uv_async_send(async_); } private: uv_loop_t loop_; uv_async_t async_; std::queuestd::functionvoid() task_queue_; std::mutex queue_mutex_; static void async_cb(uv_async_t* handle) { auto* self static_castUvLoop*(handle-data); std::queuestd::functionvoid() tasks; { std::lock_guardstd::mutex lock(self-queue_mutex_); tasks.swap(self-task_queue_); } while (!tasks.empty()) { tasks.front()(); tasks.pop(); } } };这个设计的关键在于所有网络请求、定时任务、甚至LLM的异步调用回调都被封装成std::function对象通过post方法投递到同一个事件循环中执行。这避免了线程频繁创建销毁的开销也避免了复杂的锁竞争使得成千上万的并发连接成为可能。这就是系统级思维我们不是在管理线程而是在管理事件和任务。2.2 状态机Agent“大脑”的精确运转逻辑AI Agent的核心是状态机。它需要根据当前状态如“等待用户输入”、“执行工具调用”、“解析LLM响应”和接收到的事件如“用户消息到达”、“工具调用完成”、“超时”来决定下一步行动。用if-else或switch-case堆砌的状态机难以维护和扩展。我们可以采用状态模式的变体结合查表法来实现一个清晰高效的状态机。首先我们定义状态和事件枚举enum class AgentState { Idle, ProcessingInput, CallingTool, WaitingForLLM, GeneratingResponse, Error }; enum class AgentEvent { UserMessageReceived, ToolExecutionCompleted, ToolExecutionFailed, LLMResponseReceived, LLMResponseTimeout, InternalError };然后我们用一个std::unordered_map来定义状态转移表值是一个处理函数lambda。class AgentStateMachine { using TransitionHandler std::functionvoid(const EventData); std::unordered_mapAgentState, std::unordered_mapAgentEvent, std::pairAgentState, TransitionHandler transition_table_; AgentState current_state_; public: AgentStateMachine() : current_state_(AgentState::Idle) { // 配置状态转移表 // 例如从Idle状态收到UserMessageReceived事件转移到ProcessingInput状态并执行handleInput函数 transition_table_[AgentState::Idle][AgentEvent::UserMessageReceived] { AgentState::ProcessingInput, [this](const EventData data) { this-handleInput(data); }}; // ... 配置所有其他状态和事件的转移关系 } bool dispatch(const AgentEvent event, const EventData data) { auto state_map transition_table_[current_state_]; auto it state_map.find(event); if (it state_map.end()) { // 未定义的状态转移进入错误状态 transitionToError(data); return false; } // 执行转移处理函数 it-second.second(data); // 更新状态 current_state_ it-second.first; return true; } private: void handleInput(const EventData data) { // 解析输入准备调用LLM或工具 std::string processed preprocess(data.message); postToLLM(processed); } // ... 其他处理函数 };这种设计将状态转移逻辑与业务逻辑解耦。增加新的状态或事件只需要在转移表中添加新的映射关系而无需修改庞大的条件判断语句。这对于需要频繁迭代和增加新功能的AI Agent来说可维护性有质的提升。系统级开发讲究的就是这种“契约明确、各司其职”的模块化设计。2.3 内存管理性能与稳定性的基石在Python中你可以几乎忘记内存的存在。但在C中错误的内存管理轻则导致内存泄漏重则引发程序崩溃。对于长期运行、7x24小时服务的AI Agent内存稳定性是生命线。首选智能指针彻底放弃裸指针new/delete。对于独占所有权的资源使用std::unique_ptr对于需要共享所有权的资源使用std::shared_ptr。但要注意std::shared_ptr的循环引用问题必要时使用std::weak_ptr来打破循环。使用对象池对于频繁创建和销毁的小对象如网络请求包、解析后的令牌反复的malloc/free或new/delete会带来严重的性能开销和内存碎片。实现一个简单的对象池可以极大提升性能。template typename T class ObjectPool { public: ObjectPool(size_t chunk_size 64) : chunk_size_(chunk_size) {} template typename... Args std::shared_ptrT acquire(Args... args) { std::lock_guardstd::mutex lock(mutex_); if (pool_.empty()) { allocate_chunk(); } auto obj std::move(pool_.back()); pool_.pop_back(); // 使用placement new在预分配的内存上构造对象 new (obj.get()) T(std::forwardArgs(args)...); // 自定义删除器将对象放回池中而非真正销毁 return std::shared_ptrT(obj.release(), [this](T* ptr) { ptr-~T(); // 显式调用析构函数 std::lock_guardstd::mutex lock(mutex_); pool_.push_back(std::unique_ptrT[](ptr)); }); } private: void allocate_chunk() { auto chunk std::make_uniqueT[](chunk_size_); // 将原始内存块指针存入池中 for (size_t i 0; i chunk_size_; i) { pool_.push_back(std::unique_ptrT[](chunk[i])); } // 保持chunk内存不被释放 chunks_.push_back(std::move(chunk)); } std::vectorstd::unique_ptrT[] chunks_; std::vectorstd::unique_ptrT[] pool_; std::mutex mutex_; size_t chunk_size_; };避免std::string和std::vector的频繁复制在消息传递、数据解析过程中大量使用std::string的复制会带来不必要的开销。多使用std::string_viewC17来传递字符串的只读视图或者使用移动语义std::move来转移所有权。3. 核心模块实现拆解Agent的三大支柱3.1 通信模块不只是HTTP客户端一个AI Agent需要与多种服务通信OpenAI/Claude等LLM API、数据库、外部工具API等。我们不能简单地用libcurl写一堆散落的HTTP调用。需要设计一个统一的、支持重试、熔断、负载均衡的通信客户端。连接池管理为每个目标服务如api.openai.com维护一个HTTP/HTTPS连接池。复用TCP连接可以避免每次请求都进行三次握手显著降低延迟。请求编排与超时控制每个请求都应该有独立的超时控制。我们可以利用事件循环的定时器功能。例如将一个HTTP请求的发送和接收封装成一个AsyncHttpRequest对象该对象内部启动一个定时器。如果定时器先于响应触发则取消请求并触发超时回调。class AsyncHttpRequest { public: using Callback std::functionvoid(const HttpResponse, const std::error_code); void execute(uv_loop_t* loop, const HttpRequest req, Callback cb, int timeout_ms 5000) { callback_ std::move(cb); // 1. 从连接池获取或创建连接 auto conn connection_pool_.acquire(req.host); // 2. 发送请求 conn-send(req, [this](const HttpResponse rsp) { this-on_response(rsp); }); // 3. 启动超时定时器 uv_timer_init(loop, timeout_timer_); timeout_timer_.data this; uv_timer_start(timeout_timer_, [](uv_timer_t* handle) { auto* self static_castAsyncHttpRequest*(handle-data); self-on_timeout(); }, timeout_ms, 0); } private: void on_response(const HttpResponse rsp) { uv_timer_stop(timeout_timer_); uv_close((uv_handle_t*)timeout_timer_, nullptr); callback_(rsp, std::error_code{}); } void on_timeout() { // 取消底层的网络请求具体实现依赖HTTP客户端库如设置取消标志 cancel_current_request(); callback_(HttpResponse{}, std::make_error_code(std::errc::timed_out)); } uv_timer_t timeout_timer_; Callback callback_; ConnectionPool connection_pool_; };熔断器模式当某个服务连续失败多次应自动“熔断”短时间内不再发送请求直接返回失败给下游服务恢复的时间。这可以用一个简单的计数器配合状态机来实现。3.2 工具调用与执行引擎Agent的核心能力之一是调用外部工具。我们需要一个安全、可控的工具执行环境。工具注册与发现维护一个工具注册表。每个工具需要提供名称、描述、参数JSON Schema和一个执行函数。class ToolRegistry { struct ToolDef { std::string name; std::string description; nlohmann::json parameters_schema; // 使用nlohmann/json库 std::functionnlohmann::json(const nlohmann::json) executor; }; std::unordered_mapstd::string, ToolDef tools_; public: void register_tool(ToolDef def) { tools_[def.name] std::move(def); } std::optionalnlohmann::json execute(const std::string name, const nlohmann::json args) { auto it tools_.find(name); if (it tools_.end()) { return std::nullopt; } // 可选在此处进行参数验证根据schema检查args return it-second.executor(args); } };沙箱化执行针对高风险操作对于执行系统命令、文件操作等高权限工具绝不能直接在主进程中执行。可以考虑使用fork()创建子进程并在子进程中使用seccomp、chroot或namespaces进行沙箱隔离或者将工具调用委托给一个独立的、权限受控的“工具运行器”微服务。3.3 记忆与上下文管理Agent需要有短期记忆当前会话和长期记忆向量数据库。这里的关键是效率。对话上下文窗口管理LLM的上下文长度有限如128K。我们需要一个智能的上下文摘要或滑动窗口机制。一种策略是维护一个固定长度的消息队列当超过长度时将最早的一部分消息通过另一个LLM调用进行总结并将总结文本作为新的系统提示或早期记忆插入。向量检索的优化长期记忆通常存储在向量数据库如Chroma、Qdrant中。频繁的网络查询是性能瓶颈。我们可以客户端缓存对常见的查询在内存中缓存最近的N个查询结果。批量查询如果一次推理需要检索多个记忆片段尽量合并成一个批量查询请求。本地轻量级向量索引对于核心的、访问频率极高的记忆可以在Agent进程内维护一个小的、基于faissFacebook AI Similarity Search的本地索引。faiss提供了高效的C API可以进行快速的向量相似度搜索。#include faiss/IndexFlat.h class LocalMemoryCache { faiss::IndexFlatIP index_; // 使用内积作为相似度度量 std::vectorstd::string memory_texts_; std::vectorstd::vectorfloat memory_embeddings_; public: void add_memory(const std::string text, const std::vectorfloat embedding) { memory_texts_.push_back(text); memory_embeddings_.push_back(embedding); // 将新向量添加到索引 index_.add(1, embedding.data()); } std::vectorstd::string search(const std::vectorfloat query_embedding, int k 5) { std::vectorfaiss::idx_t labels(k); std::vectorfloat distances(k); index_.search(1, query_embedding.data(), k, distances.data(), labels.data()); std::vectorstd::string results; for (int i 0; i k; i) { if (labels[i] 0 labels[i] memory_texts_.size()) { results.push_back(memory_texts_[labels[i]]); } } return results; } };4. 性能调优与问题排查实战4.1 性能剖析找到真正的瓶颈不要靠猜。使用专业的性能剖析工具。CPU Profiling在Linux下perf是首选。perf record -g ./your_agent运行你的Agent并执行典型负载然后用perf report查看热点函数。你会发现时间可能并非花在LLM调用等待上而是在JSON解析、字符串处理或锁竞争上。内存 ProfilingValgrind的massif工具可以分析内存使用情况。对于C更推荐使用heaptrack或gperftools的tcmalloc它们能提供更清晰的内存分配和泄漏报告。系统调用跟踪使用strace或bpftrace查看Agent进行了哪些系统调用。过多的write/read到同一个文件描述符可能是日志库同步写导致的性能问题。4.2 典型问题与排查清单问题现象可能原因排查步骤与解决方案Agent运行一段时间后响应变慢内存持续增长内存泄漏对象池或缓存未正确清理std::shared_ptr循环引用。1. 使用Valgrind --leak-checkfull或AddressSanitizer (-fsanitizeaddress) 编译运行定位泄漏点。2. 检查所有自定义的资源管理类如连接池、对象池的析构函数和清理逻辑。3. 审查std::shared_ptr的使用用std::weak_ptr打破可能的循环。高并发下CPU占用率异常高但吞吐量上不去锁竞争激烈事件循环被阻塞操作如同步文件IO、CPU密集型计算卡住。1. 使用perf查看热点是否在mutex::lock上花费大量时间。2. 将阻塞操作异步化。例如文件读写使用libuv的uv_fs_*异步API。3. 对于必须的CPU密集型任务如向量计算考虑将其转移到单独的线程池中避免阻塞事件循环。网络请求偶尔超时错误率随负载升高连接池耗尽下游服务过载未实现重试和熔断。1. 监控连接池大小和等待队列长度。适当增加池大小或实现动态扩容。2. 为HTTP客户端实现指数退避重试机制如最多3次间隔1s, 2s, 4s。3. 实现熔断器当失败率超过阈值如50%时暂时停止请求定期尝试恢复。LLM响应解析出错导致状态机卡死JSON解析异常未捕获LLM返回格式不符合预期。1. 在所有JSON解析处使用try-catch。2. 对LLM返回的关键字段如tool_calls进行严格的格式验证和类型检查提供默认值或清晰的错误提示。3. 在状态机的错误状态Error中设计恢复机制如丢弃当前上下文并请求用户重新输入。4.3 编译与部署优化编译优化使用-O3 -marchnative进行发布构建。对于关键路径上的代码可以考虑使用链接时优化-flto。依赖管理使用现代C包管理器如vcpkg或Conan确保依赖库的版本一致性和编译选项最优。容器化使用Docker多阶段构建最终镜像只包含编译好的二进制和必要的运行时库如glibc镜像体积可以做到极小50MB非常适合边缘部署。# 第一阶段构建 FROM gcc:latest AS builder WORKDIR /app COPY . . RUN cmake -DCMAKE_BUILD_TYPERelease -B build cmake --build build # 第二阶段运行 FROM debian:stable-slim WORKDIR /root/ COPY --frombuilder /app/build/my_agent . CMD [./my_agent]5. 从项目到技能构建你的系统思维完成这个C AI Agent项目你收获的远不止几万行代码。你深入实践了并发编程模型从线程思维切换到事件驱动和协程如果有用到思维理解了如何用少量线程承载高并发。资源生命周期管理对内存、文件描述符、网络连接等系统资源有了“申请即负责”的深刻意识。性能分析与优化学会了用工具和数据而非直觉来定位性能问题。复杂系统状态建模用清晰的状态机来管理复杂的异步业务流程。跨语言/服务集成熟练地在C中处理JSON、HTTP、gRPC与Python/Go等生态进行协作。这些正是“系统级开发技能”的内核。在AI工程化从“能用”走向“好用”、“高效”、“可靠”的当下这种能向下触及硬件、向上抽象业务的能力会让你在2025年的技术浪潮中占据一个极其有利的位置。这个项目本身就是一个强有力的能力证明远比单纯熟悉某个AI框架更有深度和说服力。

相关新闻

从比特到尧字节:数据单元演进与系统设计实战指南

从比特到尧字节:数据单元演进与系统设计实战指南

1. 数据单元的演进:从比特到超越的认知之旅在数字世界的日常工作中,我们每天都在和数据打交道。无论是处理一个几兆的文档,还是分析几个TB的数据库,我们口中蹦出的“KB”、“GB”这些单位,就像呼吸一样自然。但你是否真…

2026/10/6 17:11:39 阅读更多 →
FPGA与DSP协同处理系统架构与应用解析

FPGA与DSP协同处理系统架构与应用解析

1. FPGA与DSP协同处理系统的架构解析在数字信号处理领域,FPGA(现场可编程门阵列)和DSP(数字信号处理器)的协同架构已经成为高性能计算的重要解决方案。这种混合架构结合了FPGA的并行处理能力和DSP的专用计算优势&#…

2026/10/8 12:56:52 阅读更多 →
Flagger渐进式交付深度解析:5个步骤实现零风险Kubernetes金丝雀部署

Flagger渐进式交付深度解析:5个步骤实现零风险Kubernetes金丝雀部署

Flagger渐进式交付深度解析:5个步骤实现零风险Kubernetes金丝雀部署 【免费下载链接】flagger Progressive delivery Kubernetes operator (Canary, A/B Testing and Blue/Green deployments) 项目地址: https://gitcode.com/gh_mirrors/fl/flagger 在现代微…

2026/10/14 1:56:56 阅读更多 →

最新新闻

深度学习框架选型指南:PyTorch、TensorFlow、JAX、PaddlePaddle对比与实战

深度学习框架选型指南:PyTorch、TensorFlow、JAX、PaddlePaddle对比与实战

1. 从“选哪个框架”这个老问题说起如果你在深度学习这个圈子里待过一段时间,大概率会被问到或者问过别人一个问题:“我到底该学哪个框架?”这个问题几乎和“该学Python还是R”一样,属于入门阶段的经典纠结。但有意思的是&#xf…

2026/10/14 12:05:09 阅读更多 →
SQLite+FTS5+BM25实现可落地的上下文管理范式

SQLite+FTS5+BM25实现可落地的上下文管理范式

1. “context-mode”到底是什么?不是新框架,也不是黑科技,而是开发者日常里被忽略的上下文工程实践“context-mode”这个词最近在技术社区突然密集出现,尤其和MCP、SQLite、FTS5、BM25这些词高频共现。它既不是某个开源项目的官方…

2026/10/14 12:05:09 阅读更多 →
轻量大模型智能客服系统落地实践:RAG+状态感知+私有化部署

轻量大模型智能客服系统落地实践:RAG+状态感知+私有化部署

简介:本资源是一套基于大模型构建的智能客服对话系统完整工程实现,面向Java后端开发者、AI应用工程师及NLP方向学习者,解决企业级客服场景中意图理解、多轮对话管理与个性化响应生成等核心问题。项目以SpringAI为关键中间件,封装大…

2026/10/14 12:05:09 阅读更多 →
短视频无水印下载工具:原理与实现详解

短视频无水印下载工具:原理与实现详解

1. 项目背景与需求解析在短视频内容爆发的时代,很多用户都有保存优质视频素材的需求。但直接通过手机录屏会带有平台水印,影响二次创作和收藏体验。市面上虽然存在一些在线解析工具,但往往需要频繁复制链接、操作步骤繁琐,且存在隐…

2026/10/14 12:05:09 阅读更多 →
artcraft:构建创意工具链,实现从灵感到成品的可复用生产流程

artcraft:构建创意工具链,实现从灵感到成品的可复用生产流程

1. 从“artcraft”这个名字说起:它到底想解决什么问题第一次看到“artcraft”这个标题,我脑子里蹦出来的第一反应是——这大概率不是一个单纯的艺术项目,也不是一个纯粹的手工教程。把“art”和“craft”拼在一起,本身就带着一种“…

2026/10/14 12:05:09 阅读更多 →
artcraft 实战:从模糊灵感到可复现的创作流水线

artcraft 实战:从模糊灵感到可复现的创作流水线

1. 从“artcraft”这个名字说起:它到底想解决什么问题第一次看到“artcraft”这个词,我脑子里蹦出来的不是某个具体工具,而是一种很朴素的组合感——art 加 craft,艺术加手艺。这两个词放在一起,本身就带着一种张力&am…

2026/10/14 12:04:09 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29: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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/14 7:53:34 阅读更多 →