位置:首页 > 进阶教程 > Owner控制台手动诊断:从只读页面到可控探针

Owner控制台手动诊断:从只读页面到可控探针

时间:2026-07-24  |  作者:深海捕梦者  |  阅读:0

从只读页面到可控探针:为Owner Console增加手动诊断

前几轮开发中,我为自己的QQ AI Chatbot搭建了一个本地Web Owner Console。最初的设计非常克制,只做了一件事:让主人看到系统内部状态——任务、审批、配置、运行观测,全部只读。前端请求也被限制在GET白名单里,不执行工具,不修改配置,不碰业务数据库。

从只读页面到可控真实探针:我如何给 Owner Console 加手动诊断

这套只读边界解决了“主人如何看见系统”的问题。
但很快,另一个问题暴露出来:

  • 看见配置开启,不等于功能真的可用。
  • 看见索引计数,不等于一次真实检索能够完成。
  • 看见服务地址,不等于依赖链已经跑通。

尤其是Diagnostics页面,即便把中英文状态、原始code和区块层级整理得清清楚楚,它仍然更像一份配置目录,而不是一个能真正帮你确认系统健康的诊断工具。

因此,接下来需要做的是:允许主人在页面上显式触发少量、后端固定、可证明边界的手动诊断。这篇记录的就是从只读页面走到真实探针的完整过程,以及如何避免“加一个诊断按钮”演变成任意操作入口。

可读性优化没有解决真实性问题

在开放手动动作之前,我先重做了Reliability和Diagnostics页面的信息层级。

Reliability页面保留英文原始证据,中文只负责解释,不覆盖这些值。确定性总结只根据结构化数据生成,不调用LLM,也不会在缺少等长对比窗口时武断地说“故障上升”或“故障下降”。Diagnostics页面也采用同样原则:中文含义解释值,原始英文证据。

页面变得好读以后,主人给了很准确的反馈:它仍然主要在展示开关和快照,没有真正执行诊断,也不能判断某条实际链路是否工作。

这个反馈帮助我们重新区分了两个概念:

  • read snapshot:读取已有状态,适合自动刷新。
  • manual diagnostic action:主人显式批准后,执行一次有真实成本或真实外部调用的固定动作。

第二类动作不能伪装成普通GET,也不能因为名字叫“诊断”就默认安全。

手动诊断不是自由工具面板

我没有给页面增加自由输入框——自由prompt、模型名称、Base URL、SQL工具名、top_k、超时、重试次数。

为何不这么做?因为一旦接受这些字段,Owner Console就不再是“运行已注册诊断”,而是在提供一个通用执行器。

最终采用的方案是:固定工作流注册模型

  • 前端只知道工作流名称和说明。
  • 后端拥有完整输入、预算和执行逻辑。
  • 请求体只携带固定confirmation。
  • 每次动作只执行一个已注册工作流。

首批开放的是:project_doc_rag_fixed_retrievalmemory_rag_index_consistency。它们都属于RAG,但安全边界完全不同,所以没有强行塞进一个“通用RAG测试”接口。

一个按钮背后需要多层授权

本地控制台只监听loopback,并不意味着POST可以随便开放。
每个手动诊断都需要经过这些条件:

  • 服务启动时显式启用对应工作流。
  • 页面第一次点击只展开风险说明。
  • 主人第二次点击才真正确认。
  • 请求必须来自允许的同源Origin。
  • 必须携带进程内动作Cookie。
  • 必须使用固定动作Header。
  • Content-Type必须正确。
  • JSON body必须精确匹配固定confirmation,多余字段也会被拒绝。

动作Cookie通过同源GET获得,设置为HttpOnly和SameSite=Strict。服务每次启动生成新的进程内动作session,仅知道POST路径还不够,旧进程的Cookie也不能拿到新进程继续使用。

前端原有guard也从“只允许GET”升级为精确allowlist:允许已有只读GET,只额外允许已注册的固定POST,未知POST、动态路径和自由请求仍然失败。这不是把整个控制台改成可写,而是在只读控制台上切出几个非常窄的动作孔径。

所有真实探针共享一把全局锁

手动动作还有一个容易被忽略的问题:并发

如果主人连续点击两个工作流,系统可能同时执行embedding、SQLite扫描或LLM请求。即使每个动作单独看都安全,并发后也会让结果、资源消耗和最近运行状态变得难以解释。

因此,这些工作流共享同一个进程内全局运行锁:

  • 正在运行一个工作流时,其他工作流直接返回busy。
  • 不排队,不自动等待,不自动重试。
  • 完成后也不自动串行启动下一个工作流。

最近结果只存在Owner Console进程内,重启后自然清空,不写入聊天历史、Agent task、approval、reliability event或业务诊断表。对这种主人临时确认的本机探针来说,进程内状态反而让边界更清楚。

ProjectDocRAG:一次真实embedding,但数据库必须只读

第一个工作流要回答的问题很朴素:当前ProjectDocRAG能否对后端固定问题生成向量,并从现有项目文档索引中返回固定前五项?

审计现有检索入口后,我没有直接复用它。原因是生产检索链会经过数据库初始化和普通可提交连接,无法证明database_write=false。因此这个工作流使用专用路径:

  • 固定问题只存在后端局部内存。
  • 当前embedding provider只调用一次。
  • 不做legacy fallback,不自动重试。
  • SQLite使用URI mode=ro,只查询source_id、chunk_index和embedding。
  • 本地计算相似度并取top 5。

页面不显示固定问题、文档正文、片段内容、文件路径或原始异常,只展示完成判断所需的有限证据:活动片段数、当前provider/model的有效向量数、返回数量、固定目标是否命中、最高相似度、workflow/stage/code等零副作用字段。

还有一个实际踩到的坑:生产配置里的enable_project_doc_rag=false,最初被我当成手动探针的硬阻断。但这两个开关语义不同:生产功能开关控制普通运行链是否使用ProjectDocRAG,而手动探针开关是主人是否在本次Owner Console启动中批准固定诊断。最终实现把生产开关保留为runtime_feature_enabled证据,但不让它覆盖主人对固定动作的独立授权。

真实页面验收中,这个工作流完成了:一次真实embedding、一次SQLite mode=ro检索、top 5返回、固定目标命中、数据库大小和mtime不变。这才是“真实检索通过”,而不只是“索引看起来存在”。

MemoryRAG:只审计一致性,不读取私人正文

第二个工作流的设计思路完全不同,没有做真实私人记忆检索。它先回答一个更基础、也更容易控制暴露面的事实问题:活动MemoryRAG文档与当前有效向量是否一致?

专用SQL仍然使用SQLite mode=ro,并且不选择content、summary或其他私人正文列。它只统计:

  • 活动文档数量。
  • 当前provider/model/content_hash的有效向量数量。
  • manual_fact、preference、session_summary各自缺失数量。
  • 来源映射缺口。
  • 软删除历史文档所带向量数量。

这个工作流不会生成查询向量、调用embedding、执行retrieve_memory、读取私人记忆正文,也不会调用ProjectDocRAG、DevContext或CombinedRAG,更不会重建索引、补写缺失向量或自动创建修复任务。

真实结果发现:活动文档37,有效向量35,缺失向量2,两条缺失都属于manual_fact,来源缺失0,软删除历史向量5。页面给出的结论是:outcome=attention,stage=result_validation,code=memory_rag_active_embedding_gap。

注意,这不是工作流失败。工作流成功完成了只读一致性检查,并发现了一个真实缺口。因此它需要关注,但不应该标成“执行失败”。

succeeded、attention和failed必须分开

诊断结果最容易混淆两类问题:诊断程序有没有正常完成?被诊断对象有没有完全满足期望?

最终采用三类结果:

  • succeeded:工作流完成,目标合同满足。
  • attention:工作流完成,但发现兼容性、性能或一致性问题。
  • failed:工作流本身没有完成,例如配置缺失、连接失败、超时或内部错误。

并且每个结果必须保留英文定位证据:workflow、stage、code。中文可以写“需要关注”,但不能把memory_rag_active_embedding_gap藏掉。否则后续排障只能依赖页面文案,接口合同会重新退化成不可搜索的自然语言。

为什么发现缺口后不顺手修复

看到两条缺失向量时,最诱人的念头当然是“自动补一下”。但“知道缺少两条向量”和“批准读取相应正文并调用embedding写回数据库”是两种完全不同的授权。

自动修复至少会扩大这些边界:

  • 读取私人内容。
  • 调用外部或本地模型。
  • 产生新的向量数据。
  • 修改数据库。
  • 处理部分成功和失败恢复。
  • 决定是否重试。

因此,本轮只记录事实,不创建修复任务,也不在页面放一个看似方便的“立即修复”按钮。诊断的职责是让问题变得可验证,不是替主人扩大授权。

自动化必须证明“没有做什么”

这类功能的测试不能只验证响应码是200。我给它补了几类负向合同:

  • 错误Origin、Cookie、Header、Content-Type或confirmation必须被拒绝。
  • 多余JSON字段必须被拒绝。
  • 页面一次确认只能产生一个POST。
  • 两个工作流不能并发。
  • SQLite查询前后文件大小和mtime不变。
  • MemoryRAG查询不选择正文列。
  • MemoryRAG provider不能调用embedding。
  • ProjectDocRAG embedding只能调用一次。
  • 前端guard只能放行注册过的固定POST。

最终回归覆盖了后端、前端、TypeScript、生产构建、固定POST guard、Python AST、依赖一致性和git diff --check。真实页面验收时,还单独核对了访问日志中的POST次数、最近运行ID、数据库文件大小与mtime,以及后端日志中的ERROR、Traceback和Exception。

最后

这次改造让我对“只读控制台”有了更细的理解。一个控制台可以默认只读,同时允许少量主人批准的真实诊断。

关键不在于HTTP方法是不是POST,而在于动作是否满足这些条件:

  • 输入后端固定。
  • 能力启动时显式启用。
  • 主人二次确认。
  • 接口精确allowlist。
  • 执行预算有上限。
  • 不同工作流共享并发锁。
  • 结果区分succeeded、attention和failed。
  • 原始英文证据可见。
  • 不自动重试、修复或启动后续动作。

ProjectDocRAG的价值是证明一次真实embedding和只读检索能跑通。
MemoryRAG一致性检查的价值是发现两条真实缺口,同时证明私人正文没有被读取、向量没有被补建、数据库没有被修改。

好的诊断不只告诉我系统做了什么,也要让我能够确认:它这次没有越过哪些边界。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多