评估与可观测性
观测事实与评估判断不能合并成一个分数
可观测性回答“这次运行发生了什么”,评估回答“结果是否满足目标”。前者应尽量忠实、可重放;后者依赖验收标准,允许规则、模型和人工给出不同判断。aibuddy 先保存任务事实,再从这些事实生成面向排障、成本和质量的不同视图,而不是把一个综合分数写成系统真相。
一次工具调用返回成功,只能证明动作执行完毕;一次 Agent 运行自然停止,也只能证明运行结束。文件内容、测试结果、页面状态、引用证据或用户确认,才是任务是否成立的验收依据。
任务终态独立于模型的结束原因
模型步骤的 finishReason 描述一次生成为何停止,例如 stop、tool-calls、length 或 error;任务生命周期则需要表达用户此刻还能否继续推进。aibuddy 在任务上分别保存 isActive、awaitingUserInputAt 和 lastFinishReason,避免用一个布尔值推断所有结果。
管理侧据此生成六种状态:
| 状态 | 判定依据 |
|---|---|
running | 任务仍 active,且没有等待用户输入 |
awaiting_input | 任务仍 active,存在尚未完成的 HITL 交互 |
completed | 任务自然结束并写入 completed |
failed | 模型或执行错误使任务终止 |
aborted | 用户或管理操作主动停止 |
interrupted | 部署关闭等系统事件打断运行 |
HITL 暂停不会被计为完成;服务关闭也不会污染用户主动取消指标。旧数据没有 lastFinishReason 时才按兼容规则映射为完成状态。
逐步遥测把执行计划与实际结果配对
task_step_info 为每个模型步骤保存一行过程指标。prepareStep 侧记录预计输入、当时可见的工具数量、压缩落点和提醒是否触发;onStepEnd 侧补充实际 token、缓存读写、推理 token、工具调用、模型、耗时、警告和结束原因。两侧数据通过同一个步骤记录配对。
这种设计可以直接回答具体问题:预计上下文与实际输入相差多少、哪一步触发压缩、工具结果使下一步增加了多少上下文、缓存是否生效,以及异常成本从哪一步开始。逐步记录只保存指标和名称,不复制提示词与模型回复。
任务预算使用另一份 append-on-change 快照。只有上下文窗口、系统指令、工具 schema、模型或实际 loadout 发生变化时才新增记录;稳定运行不会每步重复保存相同配置。管理页再按需读取步骤、子 Agent、loadout 和压缩快照,长任务的基础详情不必一次载入全部轨迹。
模型计量与运维 trace 使用两条通道
所有模型入口最终经过 @aibuddy/ai 调用边界或 Agent Loop 的步骤钩子,并产生统一的 ModelInteraction。事件包含调用来源、用户与任务归属、provider、模型、BYOK、token 明细、结果和耗时。UsageSource 显式区分 task、chat、team、subagent、memory、compaction、knowledge 和 eval 等来源;缺少归属的调用仍会记录为 unknown 并产生告警,而不是静默消失。
服务端安装的 usage sink 将事件异步写入追加式 model_usage 表。写入失败只记录日志,不反向破坏模型调用;表也不通过外键依赖可能被清理的任务或用户记录。计量因此完整保留,计费策略可以在另一个边界决定哪些调用消耗额度,两者不会因为“未计费”而同时失去可见性。
运维 trace 服务不同目的。服务端可以把模型与 Agent 运行写入 OpenTelemetry,并通过同一个 trace ID 关联日志;Desktop 不注册该集成。aiTelemetry 默认关闭输入和输出内容采集,避免完整提示词和回复进入 trace 后端。只有显式设置 OTEL_CAPTURE_CONTENT=true 的诊断进程才会开启内容采集。
| 通道 | 主要问题 | 默认内容边界 |
|---|---|---|
| 任务记录 | 用户得到了什么,任务如何结束 | 消息、产物、状态与必要元数据 |
| 步骤遥测 | 哪一步出现上下文、工具或成本异常 | 指标,不保存完整提示词 |
| 模型用量 | 哪类调用使用了多少模型资源 | 归属、模型、token、结果和耗时 |
| 日志与 trace | 跨进程调用在哪里变慢或失败 | 结构化事件;模型内容默认关闭 |
同一证据可以生成不同检查视图
管理端的单任务评估页直接读取真实任务状态和步骤遥测,生成完成、预算、效率、控制、证据和风险步骤等视图。当前这些质量分由确定性规则根据状态、工具数量、结束原因、token 和压缩信息计算,适合快速定位异常,但它们不是 LLM-as-judge,也不是用户验收结论。
用户反馈采用更直接的粒度:每条 assistant 消息可以保存 up、down 或空值。反馈绑定具体回复,而不是笼统绑定整个用户或整个 Agent,因而可以回溯当时的任务状态、步骤和模型用量。它仍只表示用户偏好,不能单独证明事实正确或安全合规。
评估总览页面目前使用 mock 数据展示成功率、版本比较、稳定性、工具正确率和接管率等目标形态,尚未连接统一的线上评估仓储。文档和产品展示都不应把这些数字称为生产实测指标。
压缩评估已经形成一条定向 Judge 闭环
上下文压缩是当前已经接入真实 Judge 的窄场景。管理端可选择一个压缩后的 part 或 rolling summary,系统从原始任务消息重建压缩前内容,从持久快照读取压缩结果,再交给独立 Judge 判断。
结构化结果包含:
| 字段 | 含义 |
|---|---|
verdict | ok、minor 或 major 信息损失等级 |
faithful | 压缩结果是否存在歪曲或编造 |
lostItems | 丢失或被模糊的重要事实 |
reasoning | 对判定的简短依据 |
promptFindings | 仅对 summary 生成指令提出可归因的最小修改 |
Judge 还会区分可通过 view_tool_call 恢复的内容和不可恢复内容,避免把合法移除的大段可恢复输出误判为严重丢失。输入过长时分别保留头尾,并明确评估只覆盖可见部分。
判定以结构化流返回。任务已经结束时,结果写入对应压缩快照的独立 eval 列;任务仍 active 时只保留当前会话结果,防止运行时重新压缩同一个 key 后,异步 Judge 把旧判定写回新内容。这一限制保证“评估对象”和“被评快照”保持一致。
故障归因先定位层级,再决定改动位置
| 失败层级 | 可见证据 | 优先修复位置 |
|---|---|---|
| 目标与验收 | 最终产物不满足用户标准 | 任务契约、验收规则或人工关口 |
| 模型判断 | 选择了错误计划或结论 | 指令、示例、模型或定向 eval |
| 上下文 | 约束未装配、召回失败或压缩丢失 | 上下文装配、记忆、知识与压缩 |
| 工具执行 | 参数、权限、超时或部分副作用异常 | schema、宿主、授权与恢复路径 |
| 运行平台 | 队列、连接、存储、锁或部署中断 | 平台状态、日志与跨服务 trace |
| 资源效率 | 结果可用但步骤、token 或时延异常 | loadout、缓存、调度与停止条件 |
没有这层归因时,团队容易把所有失败都归结为 prompt。可观测数据的价值不在于字段数量,而在于能把一次失败定位到应当修改的系统层。
当前能力边界
aibuddy 已经具备持久任务终态、逐步过程遥测、预算与 loadout 快照、统一模型计量、消息级反馈、OpenTelemetry 接入,以及压缩结果的按需 Judge。当前仍缺少把线上样本、用户反馈、人工标注、自动 Judge 和版本化 eval-set 汇入同一评估后端的完整闭环。
因此,现阶段可以可靠回答“任务如何运行、在哪里异常、消耗了多少资源”,并能对压缩进行定向质量审计;系统级成功率、跨版本质量提升和稳定性仍需建立可重复数据集与统一判分口径后才能作为发布依据。
实现锚点
| 设计职责 | 对应模块 |
|---|---|
| 任务终态与 HITL 判定 | task-service.finalizeTurn |
| 逐步过程指标 | task_step_info / createOnStepEnd |
| 预算与 loadout 快照 | task_budget / saveBudgetSnapshot |
| 统一模型调用事件与来源归属 | ModelInteraction / UsageSource |
| 产品用量持久化 | usage-sink / model_usage |
| 运维 trace 内容边界 | aiTelemetry / registerTelemetry |
| 压缩审计与结构化 Judge | streamCompactionEval / compactionEvalSchema |