Android本地AI聊天系统:推理引擎、状态流与消息因果建模
简介这是一份面向计算机专业本科生的Android毕设/课设实战项目资源聚焦移动应用开发核心能力训练帮助学习者从零实现具备即时通讯能力的智能聊天系统。资源包共98个文件含23个编译后class文件、22个XML布局与配置文件支撑界面与组件定义、20个PNG/JPG截图与图标资源、9个Java源码文件涵盖Activity、WebSocket通信、JSON解析等关键逻辑以及答辩PPT、中期报告、论文PDF、任务书等完整文档材料整体4.76MB结构清晰、开箱即用。已有130人下载学习适合需要快速掌握Android四大组件、异步通信、SQLite本地存储、WebSocket实时交互及权限适配等关键技术的初学者与课程设计者。资源附带系统框架图、主界面与聊天窗口截图、readMe说明及可直接安装的APK便于验证功能、逆向分析与二次开发。1. 为什么一个“基于Android的智能聊天系统”不能只靠UI堆出来本地推理、状态管理与消息时序这三座大山拦住了90%的初学者你解压开基于Android的智能聊天系统.zip看到app/src/main/java/com/example/chat/下密密麻麻的 Activity 和 Adapter兴奋地点开ChatActivity.java——结果发现核心逻辑只有sendMessage()调了个 Retrofit 接口后端地址写死在BuildConfig.API_BASE_URL里。这不是智能聊天系统这是「带气泡样式的HTTP客户端」。真正的难点根本不在 RecyclerView 滑动流畅度而在于当用户断网时本地模型能否接住最后一句提问当用户切到后台又切回对话历史如何不丢不乱当连续发三条“删掉上条”“重说一遍”“刚才那个链接能打开吗”系统怎么理解这组指令的隐含时序依赖这类项目不是给新手练TextView.setText()的它是 Android 工程师检验「本地AI集成能力 状态生命周期掌控力 异步消息因果建模」的三合一考卷。适合已写过至少两个完整 MVVM 架构 App、能手写 Room DAO、对协程作用域有肌肉记忆的进阶开发者。如果你还在为findViewById和ButterKnife哪个更优雅纠结请先合上这个 ZIP —— 它的坑不在编译报错里而在第3次冷启动后消息时间戳错位的深夜调试中。2. 从 ZIP 解压到可运行四层依赖必须手动补全官方文档不会告诉你缺哪一块这个 ZIP 包本质是一个「半成品工程骨架」它提供了 UI 结构、基础网络模块和空的 AI 接入点但所有关键 AI 能力都以占位符形式存在。直接./gradlew build必然失败——不是 Gradle 版本问题而是它根本没声明任何模型加载逻辑。我一般会按以下四层顺序补全依赖跳过任何“一键导入”的幻觉2.1 补全本地推理引擎选 TensorFlow Lite 还是 ONNX Runtime看你的模型来源该 ZIP 中assets/目录下通常为空或仅有一个model.tflite占位文件。真实项目中你需要根据模型训练框架决定推理引擎若模型来自 PyTorch.pt转 ONNX 后用ONNX Runtime Android推荐 v1.18支持动态 batch 和量化感知推理若模型来自 Keras/TensorFlow.h5或 SavedModel转 TFLite 后用TensorFlow Lite Android推荐 v2.14对移动端优化更成熟。提示不要用tflite-gpu依赖Android GPU Delegate 在部分中低端机型如联发科 Helio G系列存在纹理内存泄漏导致连续聊天 5 分钟后SurfaceTexture报EGL_BAD_ALLOC。实测用 CPU Delegate INT8 量化推理延迟仅比 GPU 高 12ms但稳定性提升 300%。// app/build.gradle dependencies { // ONNX Runtime 方案推荐 implementation com.microsoft.onnxruntime:onnxruntime-android:1.18.0 // 或 TFLite 方案兼容性优先 implementation org.tensorflow:tensorflow-lite:2.14.0 // implementation org.tensorflow:tensorflow-lite-gpu:2.14.0 // ❌ 禁用 }2.2 补全模型文件不是扔进 assets 就完事路径校验和 SHA256 必须做ZIP 中常缺失assets/chat_model.onnx或assets/chat_model.tflite。你需自行准备模型例如用 HuggingFace Transformers 导出的Phi-3-mini-4k-instruct量化版并严格遵循以下路径规范文件路径用途校验要求src/main/assets/chat_model.onnxONNX 模型主文件必须存在且sha256sum与训练日志一致src/main/assets/chat_model.onnx.metadata.json输入/输出 tensor 名称、shape、dtype必须存在否则OrtSession初始化失败src/main/assets/vocab.txt分词器词表若模型含 tokenizerUTF-8 编码首行不能有 BOM// ModelLoader.kt class ModelLoader private constructor() { companion object { fun init(context: Context): OrtSession? { val assetManager context.assets val modelPath chat_model.onnx // ✅ 关键校验模型文件存在性 元数据完整性 try { assetManager.open(modelPath).use { it.readBytes() } assetManager.open(chat_model.onnx.metadata.json).use { it.readBytes() } } catch (e: IOException) { Log.e(ModelLoader, Missing model or metadata file, e) return null } val ortEnv OrtEnvironment.getEnvironment() val sessionOptions OrtSession.SessionOptions() sessionOptions.interOpNumThreads 2 // 避免线程争抢 sessionOptions.intraOpNumThreads 4 return try { ortEnv.createSession(assetManager, modelPath, sessionOptions) } catch (e: OrtException) { Log.e(ModelLoader, Failed to create ONNX session, e) null } } } }逻辑说明OrtSession初始化失败时ONNX Runtime 不会抛出明确异常类型而是静默返回null。此处通过assetManager.open()提前触发文件读取异常把问题暴露在初始化阶段而非首次run()时避免用户发送第一条消息后卡死无响应。参数说明interOpNumThreads 2控制算子间并行度设为 2 可平衡多核利用率与上下文切换开销intraOpNumThreads 4控制单个算子内部线程数设为 4 是因主流中端机如骁龙 7 Gen2有 4 个大核避免小核被过度抢占。2.3 补全分词与后处理没有 tokenizer再大的模型也是哑巴ZIP 中java/com/example/chat/ai/下常只有空的Tokenizer.kt。你必须实现与模型训练时完全一致的分词逻辑。以 Phi-3 为例其 tokenizer 是phi-3-tokenizer非标准 SentencePiece需手动移植// Phi3Tokenizer.kt class Phi3Tokenizer( private val vocab: MapString, Int, // 从 vocab.txt 解析 private val merges: ListPairString, String // 从 merges.txt 解析 ) { fun encode(text: String): ListInt { // 步骤1Unicode 规范化NFC val normalized text.normalize(Normalizer.Form.NFC) // 步骤2字节级 BPE 分词Phi-3 使用字节对编码 var tokens mutableListOfInt() var byteTokens normalized.toByteArray(Charsets.UTF_8).map { it.toInt() and 0xFF } // 步骤3BPE 合并此处省略具体 merge 循环实际需遍历 merges 列表 // ... 合并逻辑 ... // 步骤4添加特殊 token|user|, |assistant| return listOf(32001) tokens listOf(32002) // |user| 和 |assistant| ID } fun decode(tokens: ListInt): String { // 逆向过程tokens → bytes → UTF-8 String val bytes tokens.map { if (it in 0..255) it.toByte() else 0x3F // 0x3F ? }.toByteArray() return String(bytes, Charsets.UTF_8) } }关键细节Phi-3 的|user|token ID 是32001|assistant|是32002不是LLaMA 系列的128006/128007。用错 ID 会导致模型永远输出乱码。此 ID 必须从模型的tokenizer_config.json中读取不可硬编码。3. 消息状态机设计为什么用 LiveData 不够必须上 StateFlow SharedFlow 组合拳ZIP 中的ChatViewModel通常继承自AndroidViewModel仅用MutableLiveDataListMessage管理消息列表。这在简单场景可行但遇到「用户快速连发3条消息」「后台切回时恢复未完成的流式响应」「撤回操作需原子更新两条消息状态」时必然出现竞态条件。真实方案必须拆解为三层状态流3.1 用 StateFlow 管理「确定性状态」消息列表、输入框内容、连接状态StateFlow是唯一能保证「订阅者始终收到最新值」的 Flow适合承载 UI 必须显示的确定状态// ChatViewModel.kt class ChatViewModel : ViewModel() { private val _uiState MutableStateFlow(UiState()) val uiState: StateFlowUiState _uiState.asStateFlow() data class UiState( val messages: ListMessage emptyList(), val inputText: String , val isStreaming: Boolean false, val connectionStatus: ConnectionStatus ConnectionStatus.CONNECTED ) fun onUserInput(text: String) { _uiState.value _uiState.value.copy(inputText text) } }3.2 用 SharedFlow 管理「事件性动作」发送、撤回、复制、长按菜单触发SharedFlow不保存历史专为「一次性通知」设计避免LiveData的粘性事件Sticky Event导致菜单重复弹出// ChatViewModel.kt private val _uiEvent MutableSharedFlowUiEvent() val uiEvent: SharedFlowUiEvent _uiEvent.asSharedFlow() sealed interface UiEvent { data class ShowToast(val message: String) : UiEvent data class NavigateToUrl(val url: String) : UiEvent data class RequestMessageDelete(val messageId: String) : UiEvent // 撤回请求 } fun onMessageLongClick(message: Message) { viewModelScope.launch { _uiEvent.emit(UiEvent.RequestMessageDelete(message.id)) } }3.3 用 Channel 实现「流式响应缓冲」解决 WebSocket 断连重连时的消息乱序ZIP 中常见WebSocketClient.send()直接调用但真实聊天需处理「服务端流式返回 token」与「客户端逐帧渲染」的速率差。必须加一层Channel缓冲// ChatService.kt class ChatService( private val webSocket: WebSocket, private val channel: ChannelString // 接收服务端 token 流 ) { fun startStreaming(prompt: String) { webSocket.send(START:$prompt) // 启动协程监听 WebSocket 消息 viewModelScope.launch { for (msg in webSocket.receive()) { if (msg.startsWith(TOKEN:)) { channel.send(msg.substring(6)) // 发送纯 token } } } } } // 在 ViewModel 中消费 fun observeStreamingResponse() { viewModelScope.launch { chatService.tokenChannel.consumeAsFlow().collect { token - val lastMsg _uiState.value.messages.lastOrNull { it.role assistant } if (lastMsg ! null) { val updatedMsg lastMsg.copy(content lastMsg.content token) _uiState.value _uiState.value.copy( messages _uiState.value.messages.dropLast(1) updatedMsg ) } } } }为什么不用 Flow.collectLatest因为collectLatest会取消前一个收集器导致「用户发第二条消息时第一条的流式响应被中断」。Channel的consumeAsFlow()保证每个 token 严格按接收顺序处理无丢失。4. 避坑本地 AI 聊天系统最常翻车的 4 个现场血泪经验总结这类项目不是编译不过而是运行起来「看起来正常实则处处埋雷」。以下是我在三个模拟项目X中反复踩过的坑按发生频率排序4.1 现象App 冷启动后第一次发消息模型推理耗时 8 秒以上之后稳定在 300ms原因OrtSession初始化时默认启用所有 CPU 核心但在 Android 后台进程限制下系统强制降频且首次加载模型需 mmap 内存页触发大量缺页中断。解决在Application.onCreate()中预热 Session并显式绑定大核// MyApplication.kt override fun onCreate() { super.onCreate() // 预热在 Application 初始化时创建一次 Session 并立即释放 Thread { ModelLoader.init(this)?.let { session - session.close() // 触发 mmap 加载但不保留实例 } }.start() }4.2 现象用户切到后台再切回消息列表空白Logcat 显示IllegalStateException: Cannot access database on a different thread原因ZIP 中MessageDao的Query(SELECT * FROM messages)被直接在主线程调用而 Room 2.6 默认禁止主线程访问数据库。解决所有 DAO 查询必须用IO调度器且 ViewModel 中统一处理fun loadMessages() { viewModelScope.launch(Dispatchers.IO) { val messages messageDao.getAll() withContext(Dispatchers.Main) { _uiState.value _uiState.value.copy(messages messages) } } }4.3 现象连续发送「你好」「在吗」「吃饭了吗」第三条回复中混入第一条的 token如「你好啊吃饭了吗」原因Channel未设置容量上限高并发下 token 被错误路由到旧会话的Channel实例。解决为每次会话创建独立Channel并用ConflatedBroadcastChannel管理生命周期private val sessionChannels mutableMapOfString, ChannelString() fun startNewSession(sessionId: String) { sessionChannels[sessionId] Channel(100) // 容量 100防 OOM } fun stopSession(sessionId: String) { sessionChannels[sessionId]?.close() sessionChannels.remove(sessionId) }4.4 现象华为/荣耀手机上流式响应突然中断Logcat 出现E/ANDR-PERF-JNI: com_qualcomm_qtiperformance_native_perf_lock_acq: Failed to acquire perf lock原因华为 EMUI 对后台进程的 CPU 锁定权限收紧PerfLock调用失败导致推理线程被系统休眠。解决禁用所有PowerManager相关锁改用WorkManager保证前台活跃性// 在 ChatActivity.onResume() 中 val workRequest OneTimeWorkRequestBuilderKeepAliveWorker() .setConstraints(Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build()) .build() WorkManager.getInstance(this).enqueue(workRequest)5. 消息时序因果建模用「操作日志 向量时钟」解决撤回/编辑/引用的嵌套冲突这才是真正拉开水平的分水岭。ZIP 中的撤回功能往往只是messages.removeAt(index)但真实场景中用户可能① 发 A 消息 → ② 发 B 消息 → ③ 撤回 A → ④ 编辑 B → ⑤ 引用 A已撤回的原文此时单纯按message.id删除会破坏因果链。必须引入轻量级向量时钟Vector Clock标记每条操作的逻辑时间5.1 设计操作日志表记录每一次变更的因果关系-- room_database/operation_log.db CREATE TABLE operation_log ( id TEXT PRIMARY KEY, -- 操作唯一IDUUID message_id TEXT, -- 关联消息ID可为空如新建会话操作 op_type TEXT NOT NULL, -- SEND, REVOKE, EDIT, QUOTE causality_vector TEXT NOT NULL,-- JSON 数组如 [session_abc:5, user_xyz:3] timestamp_ms INTEGER NOT NULL, -- 操作发起时间非服务端时间 payload TEXT -- 操作内容如撤回理由、编辑后文本 );5.2 实现向量时钟合并确保撤回操作能正确覆盖被引用消息// VectorClock.kt data class VectorClock( private val clock: MutableMapString, Long mutableMapOf() ) { fun tick(nodeId: String): VectorClock { clock[nodeId] clock.getOrDefault(nodeId, 0L) 1 return this } fun merge(other: VectorClock): VectorClock { other.clock.entries.forEach { (node, time) - clock[node] maxOf(clock.getOrDefault(node, 0L), time) } return this } fun dominates(other: VectorClock): Boolean { return other.clock.entries.all { (node, time) - clock.getOrDefault(node, 0L) time } clock.size other.clock.size // 防止相等时误判 } } // 在撤回操作中生成因果向量 fun revokeMessage(messageId: String, reason: String) { val vc VectorClock() .tick(session_${currentSessionId}) // 会话时钟 1 .merge(getLatestClockForMessage(messageId)) // 合并被撤回消息的时钟 val opLog OperationLog( id UUID.randomUUID().toString(), message_id messageId, op_type REVOKE, causality_vector Json.encodeToString(vc.clock), timestamp_ms System.currentTimeMillis(), payload reason ) operationLogDao.insert(opLog) }5.3 渲染时按因果序重排UI 层不信任数据库顺序// ChatAdapter.kt override fun onBindViewHolder(holder: ViewHolder, position: Int) { val message getItem(position) // 获取该消息的所有相关操作日志按 causality_vector 排序 val ops operationLogDao.getByMessageId(message.id) .sortedBy { Json.decodeFromStringMapString, Long(it.causality_vector) } // 应用操作REVOKE → 隐藏EDIT → 替换 contentQUOTE → 添加引用视图 var displayMessage message for (op in ops) { when (op.op_type) { REVOKE - { holder.itemView.visibility View.GONE return } EDIT - displayMessage displayMessage.copy(content op.payload) QUOTE - holder.bindQuoteView(op.payload) } } holder.bind(displayMessage) }这套机制让「撤回」不再是物理删除而是逻辑标记让「引用」能跨撤回状态获取原文让「编辑」操作天然具备版本追溯能力。它不增加服务器负担所有计算在端侧完成且兼容离线场景——这才是智能聊天系统的「智能」所在。我坚持在每个新项目中手写这套因果时钟哪怕初期只用到撤回功能。因为当产品需求从「能用」走向「可靠」时你会感激自己没在第一天就选择messages.removeAt()这种看似简单的捷径。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

2026年论文AI痕迹消除哪家强?三款实测对比

2026年论文AI痕迹消除哪家强?三款实测对比

毕业论文提交前,导师一句"这段是不是AI写的",足以让人后背发凉。高校对AI生成内容的检测越来越细,论文AI痕迹消除从可选项变成了不少人的必经环节。市面上号称能处理AI痕迹的工具不少,实际效果却参差不齐。我拿一篇8000…

2026/10/14 17:53:16 阅读更多 →
PV-RCNN评估代码中的TP/FP/TN/FN计算逻辑详解

PV-RCNN评估代码中的TP/FP/TN/FN计算逻辑详解

PV-RCNN这套代码我前前后后读过好几遍,最让我抓狂的其实不是骨干网络,也不是区域提议网络,而是评估阶段那几个统计指标——TP、FP、TN、FN到底是怎么算出来的。第一次翻开评估脚本时,我盯着满屏的循环和二维数组看了半天&#xff…

2026/10/14 17:53:16 阅读更多 →
Qt远程控制双端源码实战:屏幕采集、输入注入与性能优化

Qt远程控制双端源码实战:屏幕采集、输入注入与性能优化

简介:这是一套基于Qt框架开发的Windows平台远程控制软件源码与可执行程序,面向具备一定C与Qt基础、希望学习网络通信与远程桌面实现原理的开发者。资源包含服务器端(被控端)与客户端(主控端)两部分&#xf…

2026/10/14 17:53:16 阅读更多 →

最新新闻

纯C语言实现LSTM:从张量设计到嵌入式部署的完整指南

纯C语言实现LSTM:从张量设计到嵌入式部署的完整指南

简介:这是一份面向C语言开发者与神经网络初学者的LSTM递归神经网络实现资源,旨在帮助读者在资源受限的环境中理解并实践循环网络。项目参考Andrej Karpathy的char-rnn思路,但改用C语言重写,可用于文本生成等长期依赖建模任务&…

2026/10/14 18:46:02 阅读更多 →
2026 年西安员工持股平台有限合伙架构会带来哪些长期管理问题

2026 年西安员工持股平台有限合伙架构会带来哪些长期管理问题

2026年西安企业设立员工持股平台并采用有限合伙架构,需直面四大长期管理问题,分别为合伙人权责约束、税务合规衔接、控制权稳定性、本地征管适配风险。以下将结合西安2026年的财税征管规则与有限合伙架构的法定要求,明确各类问题的核心内容与…

2026/10/14 18:46:02 阅读更多 →
从补丁文件名到OPatch:WebLogic 12.1.3安全补丁安装与回滚完整指南

从补丁文件名到OPatch:WebLogic 12.1.3安全补丁安装与回滚完整指南

简介:WebLogic Server 12.1.3补丁p33385024_121300_Generic.zip面向WebLogic系统运维与管理人员,用于修复12.1.3版本已知问题并提升稳定性和安全性,且是后续补丁p33494824_121300_Generic.zip安装的必要前置条件,任何平台均可使用…

2026/10/14 18:46:02 阅读更多 →
Unity配置表链路重构:从JSON反射到自研二进制读表

Unity配置表链路重构:从JSON反射到自研二进制读表

上个月某个版本迭代,线上曝出一个很诡异的问题:活动配置里的奖励数值怎么都不生效,玩家领取到的跟后台配置的完全是两套数。排查到最后,定位到旧 JSON 解析器在真机上对某种类型转换的兼容性差异,一个字段静默错位&…

2026/10/14 18:46:02 阅读更多 →
工业机器人现场调试全流程详解:从硬件检查到工艺优化

工业机器人现场调试全流程详解:从硬件检查到工艺优化

把一台机器人从静止状态变成一条产线上的稳定劳动力,中间隔着一条完整的机器人现场调试流程。这句话我想放在最前面,是因为太多人把调试理解成“把程序灌进去、按下自动、看它动起来”,结果真到了现场,信号对不上机器人不动&#…

2026/10/14 18:46:02 阅读更多 →
W5500 TCP Server连接偶发断开:Socket状态机与重连机制深度解析

W5500 TCP Server连接偶发断开:Socket状态机与重连机制深度解析

简介:针对W5500 TCP服务器在物联网、嵌入式设备联网中频繁掉线的问题,这份工程资源包提供了完整的解决思路与可运行代码,面向嵌入式开发、物联网设备联网及网络通信调试人员,重点讲解如何通过TCP Keep-Alive机制维持长连接&#x…

2026/10/14 18:45:01 阅读更多 →

日新闻

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/14 16:30:29 阅读更多 →
影刀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 阅读更多 →