ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Serial Studio 智能助手上下文健康检测与复合工作流:Spec 0008「弱模型优先」方案的设计与实现

Serial Studio 智能助手上下文健康检测与复合工作流:Spec 0008「弱模型优先」方案的设计与实现 Serial Studio 智能助手上下文健康检测与复合工作流Spec 0008「弱模型优先」方案的设计与实现【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio本文以doc/claude/specs/0008-assistant-auto-canary/plan.md为主体骨架结合该 Spec 在仓库中的落地实现core/Ui/AI/下的SentinelProbe、MemoryStore、SkillRouter、ContextBuilder、Conversation等源码系统讲解 Serial Studio 内置 AI 助手如何在不信任模型自组织能力的前提下用应用层确定性机制解决两个核心问题上下文退化检测Context Health Probe与跨对话/跨回合的工作流复合Compounding Workflows包括持久记忆、聊天交接、技能路由、自动验证。读完本文你将理解这套「弱模型优先」架构的五大机制、关键数据结构、与 prompt 缓存/热路径的关系以及对应的测试与验收方法。一、背景为什么需要「应用层承担纪律」1.1 问题的一半长对话静默退化长 AI 助手的对话会静默退化随着聊天增长模型对系统提示中的指令把握变弱——开始忽略规则、对早前陈述的事实漂移、产生细微错误的工具调用——而用户直到输出明显错误时才收到信号。对于遥测工作这可能意味着一次糟糕的仪表盘编辑或一次带着自信发出的破坏性命令。仓库本身已经做过同样的实验CLAUDE.md中的 Context Canary每回合要求从记忆中复现一行固定的关键常量证明哨兵行的变异或缺失是上下文退化的领先指标先于可见故障出现。但仓库 canary 依赖人眼发现漂移应用内探针则可以让应用自己检查。1.2 问题的另一半跨回合/跨聊天的能力依赖模型自律助手的单回合机制已经很完善——渐进式工具披露、按需技能加载、四层命令安全Safe/Confirm/Device-gated 等、dry-run 预检、滚动检查点——但一切跨越回合或聊天的能力都依赖模型自身的纪律系统提示要求模型在某个领域的第一次工具调用前加载技能但弱模型会忘记导致工具编排错误系统提示要求模型验证工作但弱模型会跳过验证在解析器损坏时报「完成」每个聊天都从冷启动开始用户在一个聊天里教会的设备配置、脚本语言偏好、一次痛苦的纠正下个会话全部重来。1.3 一个根因一个修复两个半问题共享一个根因不能指望模型自我编排。Haiku 级别的默认模型以及大多数非 Anthropic 提供商的主流小模型flash/mini/small/local都会「一边正常工作一边丢掉尾部的格式样板」——naive 探针会在用户为了省钱而选择的小模型上疯狂报警训练所有人无视它。因此应用必须确定性地承担纪律应用自己探测健康、自己路由知识、自己验证工作、自己携带上下文前进——探针对从未合规的模型静默by construction便宜到不会惩罚在乎 token 的用户只在携带真实信号时发声。检测到退化时的恢复也不能让用户损失工作状态一个忘记一切的新聊天是惩罚而非修复所以探针与复合机制作为同一个功能发布。二、总体方案五个小机制 三条设计约束plan.md的核心结论是五个由应用驱动的小机制全部挂在助手既有的单写入者接缝single-writer seams上没有一个是让模型自己编排自己机制作用对应源码SentinelProbe向缓存的角色块追加哨兵指令在两个渲染出口剥离/校验尾部哨兵并按 providermodel 持久化合规状态SentinelProbe.h、SentinelProbe.cppMemoryStore侧车 JSON仿照ChatStore保存需用户同意的事实封顶索引以未缓存尾部系统块随请求携带MemoryStore.h、MemoryStore.cppHandoff digest应用侧确定性计算交接摘要增量写入聊天快照Conversation.cppSkillRouter将用户文本与内置触发词表匹配把技能体注入为合成的meta.loadSkilltool_use/tool_result 对由既有历史老化机制管理成本SkillRouter.h、skill_triggers.jsonAuto-verify 表在Conversation中于 apply 类变更后运行匹配的 dry-run/读回校验把结果折叠进 tool result 与工具调用卡片AutoVerifier.cpp三条贯穿始终的设计约束源自spec.md的 Constraints Invariants探针的裁决约束在从未合规的模型上不能产生误报噪音——一个用户学会无视的探针比没有探针更糟对应 R5复合机制的裁决约束假设模型从不自我编排——每个机制都必须能在一个从不主动调用 meta 工具的模型上正常工作提示词可以强化行为但绝不能成为机制本身对应 R17禁用时零成本不是「隐藏」而是从请求中彻底缺席对应 R6/R18。全部机制都在Conversation/ContextBuilder层完成与具体 provider 无关全部可通过既有 Settings 菜单开关全部在serialStudioAI日志分类下记录。三、上下文健康探针SentinelProbe3.1 哨兵行与指令块SentinelProbe的哨兵行由固定标记与一组「行为锚点」token 拼接而成源码见 SentinelProbe.cpp// 标记 static const QString kMarker QStringLiteral([[SS-CHECK); // 行为锚点 token不是构建常量而是助手运行规则依赖的事实 static const QStringList kTokens { toolsapproved, // 工具使用纪律 untrusteddata, // 不可信内容边界 idsfresh, // ID 新鲜度 batchbulk, // 批量操作纪律 saveauto, // 自动保存预期 };拼接出的完整哨兵行为[[SS-CHECK toolsapproved untrusteddata idsfresh batchbulk saveauto]]。设计要点是哨兵编码的是一组助手自身规则依赖的行为锚点因此当值漂移时可以定位是「哪个锚点漂移了」而不只是二值报警——这正是 spec R1 的要求。指令块instructionBlock()向模型要求「每条可见回复以该行逐字结束」并明确说明应用会在显示前剥离它、用户永远看不到它、不得提及/翻译/重排/改动任何值、无法逐字复现时干脆省略「verbatim-or-nothing」——省略是退化信号但模型必须正确区分。3.2 流式安全剥离stripForDisplay哨兵绝不能闪现给用户R2包括流式渲染中途。stripForDisplay()的实现要点SentinelProbe.cpp作用于累积文本而非单个 chunk与rewriteHelpLinks的先例一致在 flush 时刻对m_assistantText整体操作只在尾部剥离用lastIndexOf定位标记如果标记后跟有完整]]但后面还有非空白内容说明模型在正文里引用了标记不剥离如果]]未闭合但标记后出现换行说明后面还有内容同样不剥离尾部保持holdback当最后一段部分行仍可能是哨兵前缀[[SS-CHECK的不完整前缀时暂不渲染直到确认或排除——所以流式过程中既无闪现也不会在流中途做出变异判定剥离会顺带修剪行尾空白避免留下多余换行。Conversation.cpp的渲染路径在flushPendingStreamUpdate处执行SentinelProbe::stripForDisplay(rewriteHelpLinks(m_assistantText))见 Conversation.cpp复制/导出读取的也是剥离后的 UI 文本因此干净无哨兵AC2。3.3 回复校验与状态机evaluateReply()SentinelProbe.cpp对完成的回复仅最终回复即没有挂起工具调用时由onReplyFinished调用分类分类判定Healthy哨兵逐字存在Mutated标记存在但内容被改动——classify()还会找出第一个缺失的 token如toolsapproved或(format)/(truncated)提示作为driftedSegment()输出Missing完全没有标记Muted该 providermodel 从未合规探针静默关键的状态机逻辑R5 的落地模型本次对话内或历史上QSettings 持久化至少复现过一次哨兵 → 视为已知合规known_compliant此后一旦失败立即置m_degraded true触发 UI 横幅并记录失败类型与漂移段未知模型在前 N 条回复kComplianceWindow 3即前 3 条可见回复内从未复现哨兵 → 持久化标记为不合规之后永久静默Outcome::Muted跨会话、跨新聊天都不会再报警AC4健康回复会即时把合规状态写回QSettingspersistCompliance(true)。合规键为providerKey / currentModel()由于 QSettings 把/当组分隔符、而 OpenRouter 的模型 ID 含/sanitizeKey()会把/、\替换为_SentinelProbe.cpp。切换模型 ID 自然重置分类。3.4 合规持久化与降级 UI合规状态存于QSettings的ai/compliance/key组值即布尔合规标记——与既有ai/model/*一致体积极小、无需加载器代码。降级状态degraded、lastFailure、driftedSegment按聊天快照持久化restoreLatch()支持快照往返——只有新聊天才能清除降级重新打开已降级的聊天会再次显示横幅。Assistant暴露contextDegraded与degradationDetail属性见 Assistant.hQML 侧在输入框上方渲染持久非模态横幅复用dropToast视觉、作为workingStripe的兄弟节点展开可显示「缺失 vs 变异 漂移段」动作按钮「Start fresh chat」调用newChatFromHandoff(activeChatId)R4 的恢复路径。四、持久记忆MemoryStore4.1 数据模型与容量边界MemoryStore是位于AppLocalDataLocation/ai/memory.json的侧车文件仿照ChatStore先例结构为{schema:1, facts:[...]}每条 fact 含见 MemoryStore.hstruct Fact { QString id; // 唯一 ID QString category; // 分类见 categories() QString text; // 事实文本 QString projectPath; // 仅 project 类事实携带所属项目路径 qint64 createdAt; qint64 lastUsedAt; // LRU 驱逐依据 };分类上user preference、correction/feedback、external reference类事实安装级生效project fact类事实限定在来源项目内R11。容量硬上限MemoryStore.h上限值说明kMaxFactChars400 字符单条事实文本上限kMaxFacts100 条事实总数上限超出按lastUsedAtLRU 驱逐kMaxIndexBytes4096 字节每次请求携带的索引上限约 ≤1.5KB/≤~400 tokenskMaxFileBytes256 KB侧车文件上限4.2 索引注入未缓存尾部块ContextBuilder::memoryIndexBlock()ContextBuilder.cpp构建紧凑索引按kMaxIndexBytes截断并以不可信信封包裹后追加到系统数组untrusted sourceassistant_memory ...capped index... /untrusted信封内的文本经过neutralizeUntrustedDelimiter()把untrusted//untrusted替换为带空格形式防伪造分隔符注入。为什么放在未缓存的尾部而不是缓存前缀内因为 prompt 缓存是前缀匹配记忆每次编辑都会变如果放在缓存前缀里会让整个缓存前缀失效封顶的小索引≤~400 tokens每次以未缓存代价携带远比「每次记忆编辑都废掉整个缓存前缀」划算。这正是buildSystemArray()的布局逻辑ContextBuilder.cpp[role block可缓存含探针指令] [docs block可缓存开启时] [live state block每请求变化] [memory index block变化时] [handoff block变化时]4.3 写入门禁只经用户动作 强制脱敏写入只允许四种路径手动「Remember this」、记忆管理器编辑/新增、确认模型提案assistant.memory.propose的确认 chip、以及用户对提案的确认。每次写入都经过Redactor::scrubMemoryStore.cpp如果脱敏后文本为空则拒绝存储——AC14试图记住含 API key 的字符串会被拒绝或脱敏存储。记忆永远不会进入.ssproj项目文件R13 的隐私边界记忆只存在本机不共享、不上报、不进入可分发的项目文件。五、聊天交接Handoff5.1 确定性摘要而非模型摘要persistActiveChat()时应用侧重新计算交接摘要buildHandoffDigest()见 Conversation.cpp聊天标题、最近 ≤3 条用户提问、成功的变更类工具调用名目标、检查点路径总长约 ≤600 字节作为snapshot[handoff]增量键写入聊天快照快照保持schema: 1loadSnapshot()忽略未知键旧聊天照常加载。选择「确定性应用构建」而非「模型写摘要」的理由plan 的 tradeoff 表零 token、任何模型可用、且契合「假设模型从不自我编排」——模型写摘要恰恰是弱模型最做不到的纪律。5.2 播种与注入newChatFromHandoff(id)创建携带种子摘要的新聊天m_handoffSeed截断至kMaxDigestCharsContextBuilder::handoffBlock()ContextBuilder.cpp把种子作为未缓存尾部块注入包裹为untrusted sourceprevious_chat ...handoff seed... /untrusted由于交接文本引用了用户/工具内容属于不可信数据同样走neutralizeUntrustedDelimiter与untrusted信封与项目状态块同一信任边界。R4 的恢复动作自动携带交接摘要——退化聊天 → 一键新聊天 → 新聊天自动继承旧聊天的工作上下文AC3/AC12。六、确定性技能路由SkillRouter6.1 触发词表SkillRouter加载:/ai/skill_triggers.json即 skill_triggers.json已注册进 rcc.qrc该文件是精选的、可审计的、离线的逐技能触发词表v1 不做模糊匹配。文件头注释明确规则对用户消息做大小写不敏感匹配最长匹配短语胜出每条消息至多注入一个技能宁可漏报——漏报即维持现状模型仍可自己调用meta.loadSkill误报则在小模型上浪费 token。示例片段{ schema: 1, skills: { tool_discovery: [what can you do, what commands, list commands, available tools, which tools], behavioral: [undo, roll back, revert, restore a checkpoint, go back to before], project_basics: [new project, create a project, add a group, add a source, add a dataset], frame_parsers: [frame parser, parse frames, csv frames, frame start, frame end, nmea, mavlink], transforms: [transform, ...] } }6.2 合成工具对Conversation::start()在.trimmed()之后、issueRequest()之前调用SkillRouter::match(text, loadedSkills)见 Conversation.cpp。命中后SkillRouter::buildInjectionPair(skillId, byteBudget)构建合成的 Anthropic 消息形状工具对追加进m_historyassistant 消息一个tool_use工具名为meta.loadSkill{name}应用生成 ID标记_synthetic:trueuser 消息对应的tool_result负载技能体。这个形状与合规模型自己产生的完全一致因此既有的ageHistoryToolResults()与reconcileHistoryToolPairs()可以零新增机制地管理其成本技能体随工具结果老化budgetedHistory在新用户回合处裁剪。用户消息行上会显示「skill loaded: X」的小 chip去重集合持久化在快照loadedSkills键中。对needsSmallToolSurface的小模型注入体有 byte budget 限制最大的技能体dashboard_layout691 行其成本与模型自己加载它相当。6.3 为什么不用 BM25 模糊匹配plan 的权衡是触发源选择精选词表而非复用捆绑文档的 BM25 索引做模糊匹配——词表小、可审计、离线、误报可控模糊匹配的误报率需要实测留待后续。每次匹配都在serialStudioAI日志记录R9/AC 级可观测性便于调优词表。七、强制自动验证AutoVerifier7.1 映射表apply 类命令 → 只读读回AutoVerifier::verify()AutoVerifier.cpp维护一张静态映射表readBackCommandFor同文件 L48-L84apply 类命令读回校验命令project.frameParser.setCodeproject.frameParser.dryCompile同参project.dataset.setTransformCodeproject.dataset.transform.dryRun缺省补values:[0.0]project.painter.setCodeproject.painter.dryRun同参project.outputWidget.update含transmitFunctionproject.outputWidget.dryRun取code参数assistant.project.bulkApply扫描自身failureCount无额外调用assistant.script.apply读取自身steps[]无额外调用7.2 执行与呈现recordToolResult在成功的结果之后运行验证命令经工具分发器全部 Safe 层只读把verification:{ok, detail}折叠进同一个 tool_result 负载模型同回合看到无需额外往返——这对 Haiku 尤其重要并写入工具卡片 map用户看到徽章。之所以选择「折叠进同一 tool_result」而非「单独后续消息」后者要花费一整次完整请求。失败状态✗ 徽章展开显示详情提供「Restore checkpoint…」按钮预置既有的assistant.restore确认流alwaysConfirm 层仍然生效用户仍要点实际批准。7.3 安全保证验证表只含 Safe 层只读 dry-run表加载时用Q_ASSERT registry 检查强制该性质防「未来某次重命名把验证目标挪进 Confirm 层导致用户被莫名弹确认」这类静默破坏。Device-gated 命令永远不会出现在表中R16验证步骤绝不自动执行本身需要确认的操作。八、热路径与线程影响plan.md明确声明Hotpath threading impact触碰热路径没有。所有改动都在core/Ui/AI/ QML 一个 API handlerFrameReader/CircularBuffer/FrameBuilder/Dashboard一律未动。唯一与仪表盘相邻的调用是验证器复用的既有 Safe 层只读命令新增跨线程信号/槽没有。全部在主线程运行网络回复本就经 Qt 事件循环到达主线程新增对缓存热路径标志的输入没有。时间戳所有权未动不涉及帧路径代码。九、数据模型与持久化汇总存储位置内容备注聊天快照schema: 1新增增量键handoff、loadedSkills、probe:{state, sawSentinel}、handoffSeed旧聊天照常加载旧构建忽略新键AppLocalDataLocation/ai/memory.json{schema:1, facts:[...]}硬上限 LRU 驱逐QSettings五个ai/*开关键 ai/compliance/*组无需迁移.ssproj无任何改动记忆刻意不进入可分发的项目文件五个开关键均以Q_PROPERTY暴露见 Assistant.h遵循allowDeviceControl模式ai/contextProbe探针默认开、ai/memory、ai/handoffSeeding、ai/skillRouting、ai/autoVerify。任一关闭即从请求中零字节缺席R6/R18。十、API / SDK 面新增唯一命令assistant.memory.propose {category, text}注册于 AssistantHandler.cpp与既有assistant.*轨道并列AssistantHandler声明同步增加归类safe层command_safety.json返回{queued:true}——从不直接持久化即使模型刷屏调用也不可能违反 R13写入只发生在用户确认 chip 之后工具分发的第三个腿ToolDispatcher在审查中补充了assistant.*三元注册——经executeAssistantTool的runCommandfallthrough 与目录项路由否则该命令从 AI 路径不可路由即「双重注册」陷阱工具广告仅当记忆启用时assistant.memory.propose才进入essentialToolNames()对needsSmallToolSurface模型仍包含它很小且是 R13 想要的模型侧唯一让渡点。SDK 面由 sanitize 流水线的generate-sdk步骤在命令注册后自动重新生成scripts/sanitize-commit.py产物为app/rcc/api/*无需手工编辑。十一、UI 呈现QML降级横幅输入框上方持久非模态条带workingStripe兄弟节点、dropToast视觉文案取自degradationDetail缺失 vs 变异 漂移段可展开按钮「Start fresh chat」→newChatFromHandoff永不阻塞输入框spec 约束仅提示与提供绝不拦截发送Settings 菜单五个可勾选项沿用autoApproveEditsMenuItem 模式记忆消息上下文区出现「Remember this」memory_proposal行/标志到达时渲染确认 chipMemoryManagerDialog.qml 从 Settings 菜单打开浏览/编辑/删除 分类过滤删除事实在下一次请求生效索引每请求重建ToolCallCardverification字段渲染 ✓/✗ 徽章✗ 展开详情 「Restore checkpoint…」预置assistant.restore确认流ChatSidebar携带交接摘要的聊天显示「New chat from handoff」动作。所有新字符串经qsTr()本特性不重新生成.ts/.qmspec 约束。十二、关键权衡与备选方案摘自 plan 的决策表决策点备选选择与理由记忆存储位置侧车memory.json/ QSettings blob / 嵌入.ssproj侧车——可整体浏览编辑镜像 ChatStore 先例.ssproj会把私密事实泄漏进可分享文件淘汰记忆与交接注入点未缓存尾部块 / 缓存块断点前未缓存尾部——索引封顶小≤~400 tokens每回合支付未缓存代价好过每次记忆编辑失效整个缓存前缀ContextBuilder.cpp:821的缓存前缀匹配语义技能注入形状合成meta.loadSkill工具对 / 用户回合附加文本块合成对——与合规模型产物一致ageHistoryToolResultsreconcileHistoryToolPairs免费管理成本用户回合文本块永不老化交接生成器确定性应用构建摘要 / 模型写摘要确定性摘要——零 token、任何模型可用、契合「模型从不自我编排」模型摘要恰是弱模型失败之处记忆提案路径仅手动 / 仅模型工具 /手动 safe 层 propose 工具两者——手动是确定性基线R13 在零模型配合下成立工具是优雅增强最坏情况只是「没有提案」哨兵剥离范围仅渲染文本/ 渲染 历史仅渲染——审查中反转从历史剥离先前哨兵会少样本教模型省略哨兵制造虚假的「Missing」警报违反探针裁决约束。历史保留原始回复显示/复制/导出读剥离后的 UI 文本满足 R2每回合约 20 token 的成本反而强化弱模型的合规合规记录位置QSettings ai/compliance/*/ 侧车 JSONQSettings——镜像ai/model/*微型定长记录无加载器代码验证结果投递折叠进同一 tool_result / 单独后续消息折叠进——Haiku 同回合看到通过/失败无需额外往返单独消息要一整次请求十三、风险与缓解Prompt 缓存回归所有每聊天/每回合动态内容记忆索引、交接、实时状态都在缓存断点之后探针指令在角色块内、随设置状态稳定。用既有cacheReadTokens/cacheCreatedTokens计数器前后对比验证哨兵横跨流 chunk剥离在 flush 时刻对累积的m_assistantText操作rewriteHelpLinks先例 尾部保持窗口绝不对单个 chunk 操作校验只在onReplyFinished技能体撑爆小上下文每聊天去重注入一次、经既有工具结果老化、budgetedHistory在新用户回合裁剪触发词表必须高精度v1 精选无模糊匹配技能路由误命中精选触发词宁缺毋滥漏报 维持现状模型仍可meta.loadSkill每次匹配都记录日志便于调优合成工具对迷惑 provider 翻译层对既有的真实meta.loadSkill对OpenAI/Gemini 等翻译器已处理脚本化端点 AC3/AC10 专门演练此路径自动验证误触发确认弹窗表只含 Safe 层只读 dry-run表加载时Q_ASSERT registry 检查强制记忆无限增长/存储机密MemoryStore硬上限 LRU每次写入Redactor::scrub脱敏后为空则拒绝信任边界交接文本与记忆事实引用用户/工具内容两块都用untrusted source...信封 neutralizeUntrustedDelimiter与项目状态块同级编码规范暴露面新 Assistant 属性全部走 setter-guard 模式新翻译串不混用%n.arg()对话关闭槽不做重活记忆对话框直接改 store不走文件对话框。十四、测试与验收计划spec.md的 15 条验收标准AC1–AC15全部标记完成验证方式分三类集成测试维护者运行应用 API server 于localhost:7777新增tests/integration/test_assistant_memory.py——assistant.memory.propose返回{queued:true}且不创建事实R13命令出现在meta/schema既有assistant.*测试保持绿色checkpoint/restore 未动维护者观察脚本化本地 provider 端点AC1/AC2健康聊天无哨兵闪现、复制导出干净用托管前沿模型AC3/AC4变异/缺失 → 横幅从不合规模型保持静默含跨新聊天用脚本化端点重放罐头回复AC5/AC9探针关 → 请求无指令记忆关 → 零记忆字节用QT_LOGGING_RULESserialStudioAI.debugtrue的请求组包日志issueRequest中新增的块名字节数行绝不记录含机密的负载AC11验证徽章 恢复能力用脚本化端点重放一条刻意写坏的project.frameParser.setCodeAC14 尝试记忆形如 API key 的字符串静态检查每个触碰文件过python scripts/code-verify.py --check交接前qt-cpp-reviewqt-qml-review提交前python scripts/sanitize-commit.py同时为新命令重生成 SDK。关键源码索引探针实现SentinelProbe.h、SentinelProbe.cpp记忆存储MemoryStore.h、MemoryStore.cpp技能路由SkillRouter.h、触发词表 skill_triggers.json上下文组装ContextBuilder.cppbuildSystemArray/memoryIndexBlock/handoffBlock会话编排与交接摘要Conversation.cpp、Conversation.h自动验证AutoVerifier.cpp开关与状态属性Assistant.h命令注册AssistantHandler.cpp规范与验收标准spec.md、tasks.md结语Spec 0008 的核心方法论可概括为一句话把「模型纪律」从提示词祈祷改为应用层确定性执行。探针自己测健康哨兵逐字校验 合规状态机防误报、自己路由知识记忆索引 交接摘要以未缓存尾部块注入尊重 prompt 缓存前缀匹配、自己验证工作apply → dry-run 读回折叠进同回合 tool result、自己恢复现场降级横幅一键新聊天且自动携带交接。这套机制对 provider 中立、对弱模型友好、对热路径零接触且每个开关关闭即从请求中彻底消失——它是 Serial Studio 助手在「小模型省钱、但不想牺牲可靠性」这一现实约束下的完整工程答案。【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表