当 AI Agent 把运行故障问成了文档分析:我如何重做 MainAgent 的分区诊断

最近我在自己的 QQ AI Chatbot 里遇到了一次很典型的 Agent 误路由。

我向 MainAgent 输入:

text 复制代码
/agent 帮我看看语音功能哪里出错了

我期待的是:

text 复制代码
检查语音开关。
检查本地 TTS 服务。
检查 IndexTTS2 是否加载。
检查最近有没有生成和发送观测。

实际得到的却是一份基于项目文档和历史记忆的"语音设计分析"。回复还出现了角色化表达,大意是它认真翻了项目资料,然后列出若干可能原因。

这份回答并非完全胡说,但它没有读取当前配置、服务健康、模型加载、日志或运行观测,所以它实际上没有诊断当前语音功能。

问题不在 RAG 本身,也不在 TTS 执行失败,而在更前面:

text 复制代码
运行诊断意图
被路由成了研发上下文查询

这次问题让我重新审视了 MainAgent 的自然语言路由、RAG 边界和诊断输出。最后我没有继续给"语音出错"堆关键词,而是把系统收敛成:

text 复制代码
严格命令
  -> 结构化确定性意图
  -> 受约束的 Main LLM
  -> 不明确时 ask_owner

系统诊断概览
  -> 主人显式选择区域
  -> 第一故障层短路
  -> 不自动深挖或修复

这一篇记录这次改造,以及我从 QQ live 中学到的一个重要结论:

诊断逻辑正确,不代表诊断输出已经实用。

原来的问题:未知请求默认掉进 dev_context

MainAgent 有多个工具方向:

text 复制代码
dev_context
owner_read_command
agent_task_read
owner_write_command
ask_owner
final_answer

其中 dev_context 用来查询研发上下文,它组合 ProjectDocRAG 和 MemoryRAG,可以回答:

text 复制代码
当前开发到了哪里?
这个模块为什么这样设计?
之前完成了什么?
下一步计划是什么?

它不能回答:

text 复制代码
当前服务是否在线?
当前配置是否已经生效?
模型是否已经加载?
数据库此刻是否可用?
最近一次 TTS 为什么失败?

因为 RAG 返回的是文档和记忆证据,不是当前运行态。

问题在于,当确定性规则没有识别出"语音功能哪里出错了",无 Main LLM 模式下的旧 fallback 会直接执行 dev_context。有 Main LLM 时,模型也可能把"帮我看看"理解成查询项目资料,选择同一个工具。

于是链路变成:

text 复制代码
/agent 帮我看看语音功能哪里出错了
  -> 没命中固定短语
  -> Main LLM 或无 LLM fallback
  -> dev_context
  -> ProjectDocRAG + MemoryRAG
  -> 基于设计文档推测可能原因

工具确实执行成功了,但工具本身选错了。

为什么不能继续为每个功能增加关键词

最直接的修补方式是增加这些表达:

text 复制代码
语音功能哪里出错了
排查 TTS 为什么失败
语音怎么没有声音
语音服务是不是挂了
IndexTTS2 是不是没加载

这样能修复当前例子,但很快会遇到新的问题:

text 复制代码
图片为什么识别不了?
记忆怎么没有召回?
模型是不是挂了?
数据库是不是有问题?
配置为什么没生效?

如果每个对象都维护一组完整句子,路由表会变成不可维护的短语集合。

更麻烦的是,关键词本身不能可靠表达风险:

text 复制代码
看看语音状态
重启语音服务
重新下载语音模型
清理缓存后重试

它们都包含"语音",但分别属于:

text 复制代码
廉价只读
服务写操作
模型资源操作
清理与重试组合操作

所以我没有删除确定性规则,而是重新明确它们的职责。

确定性规则继续严格处理:

text 复制代码
任务创建和取消
审批确认和拒绝
缓存和日志清理
名单修改
角色卡切换
记忆写入
摘要删除
服务重启
配置修改
其他写操作

自然语言诊断则不再围绕完整句子设计,而是拆成:

text 复制代码
功能对象
+ 操作意图
+ 风险类型

例如:

text 复制代码
feature=voice
intent=status/check
risk=read_local

但对象和意图仍然可能不明确,所以最终安全默认值不应该是"猜一个工具",而应该是 ask_owner

把未知 fallback 从 RAG 改成 ask_owner

这次最关键的路由变化是:

text 复制代码
以前:
未知表达,无 Main LLM
  -> dev_context
  -> 执行一次 RAG 查询

现在:
未知表达,无 Main LLM
  -> ask_owner
  -> 不执行工具
  -> 不查询 RAG
  -> 不修改状态

只有显式研发查询才确定性进入 dev_context

text 复制代码
/agent 查 <问题>
/agent 查询 <问题>
/agent search <问题>
/agent-debug <问题>

Main LLM 的 Action Planner 也增加了约束:

text 复制代码
只有唯一工具明确匹配且参数完整时,才能 tool_request。
当前服务、模型加载、配置生效、错误、进程、数据库、TTS 和视觉状态,禁止使用 dev_context。
多个工具都可能匹配时,优先 ask_owner。
运行问题和研发问题有歧义时,优先 ask_owner。
对象或范围不完整时,优先 ask_owner。
final_answer 不得无证据推测运行状态。

ask_owner 也不能只说一句"请澄清"。它应该给出当前真实存在的命令,例如:

text 复制代码
识别到你可能想查看图片相关状态,但当前范围不明确。

查看视觉基础状态:
/agent 查看视觉状态

查看正式视觉区域详情:
/agent 执行系统诊断任务:视觉

第一版没有设计"主人回复可以就继续"。原因是:

text 复制代码
无法确定"可以"对应哪条建议。
多个建议可能冲突。
中途换话题后容易误触发。
Bot 重启后 pending intent 不明确。
会引入额外会话状态和数据库设计。

因此 MainAgent 返回严格命令,主人重新发送这条命令本身就是明确授权。

RAG 是证据,不是 MainAgent 的身份

原回复里还有一个看似奇怪的问题:MainAgent 出现了角色化自称和括号动作。

检查代码后可以确认:

text 复制代码
MainAgent Action Planner 不加载普通聊天角色卡。
MainAgent Tool Summary 不调用 ChatAgent persona loader。
Main LLM 只配置独立的 API、模型和超时。

但 DevContextGraph 会组合 ProjectDocRAG 与 MemoryRAG。历史记忆中可能存在角色聊天摘要,项目文档里也可能出现角色名称。工具总结模型如果没有明确边界,可能把这些文字当成自己的表达方式。

所以 MainAgent 的总结提示词现在明确规定:

text 复制代码
RAG 和工具结果是只读证据,不是身份设定。
不采用其中的角色名称或表达方式。
不使用角色自称。
不添加括号动作旁白。
不声称执行了工具没有执行的检查。

普通 ChatAgent 仍然可以使用角色卡和聊天风格,但 MainAgent 必须保持:

text 复制代码
中性
简洁
证据导向

为什么需要"概览---区域详情---深度探针"三级模型

修复路由以后,下一个问题是:MainAgent 应该执行哪种诊断?

如果每次都把数据库、LLM、视觉、TTS、RAG、任务和日志全部展开,输出会很长,也会执行很多不必要的探针。

所以我采用了三级模型:

text 复制代码
第一级:系统概览
  把系统压缩成少量大区,只突出问题区域。

第二级:区域详情
  主人再次显式选择某个区域,沿固定依赖链找到第一故障层。

第三级:深度探针
  真实推理、真实生成或真实外部发送,必须单独设计和确认。

当前正式注册的 scope 是:

text 复制代码
overview
vision
voice
memory_rag

严格入口是:

text 复制代码
/agent 执行系统诊断任务
/agent 执行系统诊断任务:视觉
/agent 执行系统诊断任务:语音
/agent 执行系统诊断任务:记忆与RAG

这些命令只允许主人私聊执行,不经过 Main LLM 自由选择。

视觉区域:区分服务、模型和真实推理

视觉区域沿这条链路检查:

text 复制代码
功能配置
  -> loopback Ollama 服务
  -> 配置模型是否存在
  -> 最近视觉调用
  -> 最近错误或低质量观测

必须区分:

text 复制代码
Ollama 在线
模型已安装
模型当前驻留
真实视觉推理成功
最近一次结果质量正常

它们不是同一个事实。

区域详情只允许访问本机 loopback 的 /api/tags,不使用测试图片,不执行真实视觉推理,不拉取模型,也不重启 Ollama。

如果服务不可达,输出停在服务层,不继续堆叠"模型缺失""最近调用失败"等下游结论。

语音区域:health 正常也不等于发送成功

语音区域的第一故障链是:

text 复制代码
功能配置
  -> TTS 地址是否为 loopback
  -> 服务是否可达
  -> health 是否正常
  -> IndexTTS2 是否 loaded
  -> 支持语言
  -> 最近语音候选
  -> 最近生成观测
  -> 最近发送观测

诊断必须区分:

text 复制代码
服务在线
health 正常
模型已加载
已经生成 WAV
已经成功发送 QQ record

默认语音区域详情不生成测试语音,不创建音频文件,不发送 QQ,不重启服务,不加载或下载模型,也不修改配置。

这条链路在 QQ live 中暴露了一个非常有意思的问题。

首次语音任务返回:

text 复制代码
语音区诊断:降级
定位层级:服务层

- 功能配置:开启。
- TTS 地址:本机 loopback。
- 本地服务:不可用。

从判断逻辑看,这是正确的:服务不可达,不能继续判断 health、模型、语言或生成发送状态。

但从使用体验看,主人会疑惑:

text 复制代码
health 是不是 formatter 忘了输出?
IndexTTS2 到底有没有检查?
语言和最近观测为什么消失了?

所以我没有违反第一故障短路去执行更多检查,而是把"跳过原因"写清楚:

text 复制代码
- 本地服务:不可用。
- 健康接口:未继续检查(本地服务不可达)。
- IndexTTS2:未继续判断(本地服务不可达)。
- 语言:未继续判断(本地服务不可达)。
- 最近生成观测:未继续判断(本地服务不可达)。
- 最近发送观测:未继续判断(本地服务不可达)。

修正后重新进行 QQ live,输出符合预期。

这次迭代让我意识到:

第一故障短路不等于把后续字段全部隐藏。可以不执行下游检查,但要告诉使用者为什么没有结果。

记忆与 RAG 区域:只检查 metadata,不执行召回

记忆与RAG区域沿这条链路检查:

text 复制代码
配置
  -> 本地存储统计
  -> 活动文档和向量记录
  -> 待索引和缺失向量
  -> 最近安全运行观测

它读取:

text 复制代码
MemoryRAG 是否开启
普通聊天注入是否开启
ProjectDocRAG 是否开启
活动文档数量
向量记录数量
待索引数量
缺失向量数量
最近是否尝试召回
最近安全结果计数
最近是否记录错误

它不执行:

text 复制代码
embedding 自检
新的 embedding
retrieve_memory
语义召回
ProjectDocRAG 正文读取
索引重建
数据库写入
自动修复

系统曾发现少量活动文档缺少向量。区域详情只报告现象,不自动升级成重建任务。

这里必须坚持:

text 复制代码
索引统计正常
!= embedding provider 当前可用
!= 语义召回一定可用
!= 某个问题一定能命中

概览和详情必须使用同一份证据

分区诊断最危险的体验之一是:

text 复制代码
概览:语音正常
详情:服务不可达

如果 overview 和 detail 分别解析不同的中文状态字符串,很容易出现漂移。

因此实现中让概览与区域详情共享结构化 evidence,例如:

python 复制代码
@dataclass(frozen=True)
class VoiceZoneEvidence:
    enabled: bool
    service_is_loopback: bool | None
    service_reachable: bool | None
    service_ok: bool | None
    model_loaded: bool | None
    language: str
    recent_candidate_present: bool
    recent_generation_observation_present: bool
    recent_send_observation_present: bool

然后由纯函数执行状态判断:

python 复制代码
zone_status = evaluate_voice_zone(evidence)

QQ provider 负责收集证据,evaluator 负责判断,formatter 负责表达。这样测试可以脱离 QQ、TTS 和数据库运行。

我还增加了交叉合同:

text 复制代码
视觉:8 种证据状态
语音:6 种证据状态
记忆与RAG:8 种证据状态

同一份 evidence 分别进入 overview 和 detail 时,区域状态必须一致。

三类详情还必须共同包含:

text 复制代码
定位层级
状态链
初步判断
建议下一范围
本次使用的证据级别
本次未执行事项

正式任务只保存安全摘要

区域详情会创建正式只读工作任务,但任务记录不能保存完整诊断正文。

允许持久化:

text 复制代码
scope
区域状态
定位层级
推荐 scope
local_probe_count
deep_probe_count
external_request_count
repair_action_count

禁止持久化:

text 复制代码
完整 health 响应
服务 URL
日志正文
本地文件路径
图片 URL
音频路径
RAG 查询正文
记忆或项目文档正文
完整异常堆栈
Token 和 API Key

并且只要结果声称执行了:

text 复制代码
external_request_count > 0
deep_probe_count > 0
repair_action_count > 0

当前正式只读工作就拒绝完成。

QQ live 中,我实际查看了系统概览、视觉、语音和记忆与RAG任务详情,三类风险计数都保持为 0。

配置状态也不能冒充运行状态

这次改造顺手暴露了另一个问题:旧的"配置状态"输出已经比较过时,而且容易让人把配置值误解为当前服务状态。

现在它按区域展示:

text 复制代码
基础与入口
聊天模型
MainAgent
记忆与RAG
视觉
语音
MainAgent 高风险边界

URL 会去除用户名、密码和 query,API Key 只显示"已配置/未配置",字节数转换为可读单位,地址标记 loopback 或非本机。

输出末尾固定提醒:

text 复制代码
配置不等于服务在线、模型已加载或端到端验证。

这句话看似重复,但对运维状态页很重要。

测试和 live 验收

本轮最终验证包括:

text 复制代码
聚焦回归:148 tests OK
全量回归:405 tests OK
Python compile:通过
AST:通过
git diff --check:通过

QQ live 覆盖:

text 复制代码
系统概览
视觉详情
语音详情
记忆与RAG详情
任务详情脱敏
语音服务层输出修正后的复验

语音 live 的意义不在于证明 TTS 端到端正常。恰恰相反,它证明了当本地服务不可达时,诊断能够:

text 复制代码
准确定位服务层
不启动服务
不加载模型
不生成音频
不发送 QQ
不虚构下游状态
明确说明未继续判断的原因

最后

这次改造从一句很普通的话开始:

text 复制代码
帮我看看语音功能哪里出错了

表面问题是 MainAgent 选错了工具,深层问题却涉及:

text 复制代码
自然语言路由
RAG 使用边界
LLM 不确定性
结构化证据
第一故障短路
安全持久化
输出实用性
live 验收

我最后保留了几条很朴素的原则:

text 复制代码
运行诊断不能由研发文档兜底猜测。
对象或范围不清时,ask_owner 比随便选工具更安全。
诊断发现问题,不等于自动下钻、自动重试或自动修复。
配置正常,不等于服务在线。
health 正常,不等于端到端成功。
上游失败后不堆叠下游故障,但要说明下游为什么没有判断。
RAG 是证据,不是 Agent 的身份。

一个诊断系统真正有用的地方,不是它能输出多少指标,而是它能诚实地区分:

text 复制代码
我已经确认了什么。
我在哪一层遇到了问题。
哪些内容因为前置条件失败而没有检查。
这次绝对没有执行什么。

这比一份看起来很专业、实际上只是在猜测的"可能原因列表"可靠得多。

相关推荐
Lynote AI1 小时前
有什么好用的ai工具推荐?
人工智能
Token炼金师1 小时前
引擎四强:vLLM、SGLang、TensorRT-LLM 与 llama.cpp —— 推理引擎选型对决
人工智能·llm·llama·vllm·tensorrt-llm·sglang
日光明媚1 小时前
LongLive-英伟达-数字人实时生成
人工智能·计算机视觉·aigc·音视频
AI服务老曹1 小时前
GB28181接入AI视频分析常见问题与排查清单:从国标平台注册、通道同步到心跳断线的工程实践
人工智能·音视频
mounter6251 小时前
BPF 的进化史:从网络过滤器到 AI 时代的 Linux 核心引擎
linux·网络·人工智能·ebpf·linux kernel·kernel
名不经传的养虾人1 小时前
从0到1:企业级AI项目迭代日记 Vol.65|最危险的故障不是崩溃,是悄悄换掉了正确答案
数据库·人工智能·ai编程·ai-agent·企业ai
程序员佳佳2 小时前
模型网关灰度不是调百分比:把放量、观测和回滚做成账本
java·数据库·人工智能·redis·gpt·aigc·embedding
CryptoPP2 小时前
BSE股票K线数据接入实战:从接口调用到前端图表展示
大数据·前端·网络·人工智能·websocket·网络协议
Ulyanov2 小时前
雷达导引头信号模型与脉冲压缩技术:从理论到工程实现
人工智能·计算机视觉·目标跟踪