看不见的 Agent 很难被信任

一个任务运行了十分钟,界面上只有“思考中”。用户不知道它是在搜索资料、修改文件、等待外部接口,还是已经陷入循环。

即使最后成功,开发者也很难回答:时间花在哪里?哪个工具最常失败?哪条策略阻止了操作?为什么同类任务昨天能完成,今天却超时?

这就是 Harness 第七个核心部分要解决的问题:可观察性与用户控制

可观察性让系统内部发生的事情能够被理解,用户控制则让人可以在正确的时机暂停、取消、批准或纠正。两者合在一起,Agent 才不是一个只能等待结果的黑盒。

本文是《把 Harness 讲清楚:Agent 背后的运行系统》八个核心部分的第七篇。

日志、指标和追踪有什么区别

三者都重要,但解决的问题不同。

日志

记录离散事件,适合回答“具体发生了什么”。例如工具参数校验失败、审批被拒绝、模型请求返回限流。

指标

记录可聚合数字,适合回答“整体表现如何”。例如任务成功率、P95 延迟、平均工具调用数和每天 token 成本。

追踪

把一次任务中的多个阶段串成因果链,适合回答“时间花在哪里、上下游怎样关联”。

一个 Trace 可以包含:

Task Trace:修复登录 Bug,18.4 秒
├─ Context Retrieval,1.2 秒
├─ Model Step 1,3.8 秒
├─ Tool:search_files,0.3 秒
├─ Tool:read_file,0.1 秒
├─ Model Step 2,4.6 秒
├─ Tool:edit_file,0.2 秒
├─ Tool:run_tests,6.9 秒
└─ Final Evaluation,1.3 秒

只有日志,能看到事件却难以理解整体时序;只有指标,能看到异常趋势却不知道具体样本;追踪把两者连接起来。

应该观测哪些阶段

Harness 至少应为这些对象建立稳定标识:

  • task_id:一个用户目标;
  • turn_id:一次被输入唤醒的运行;
  • step_id:一次模型请求及其工具执行;
  • tool_call_id:一次具体工具调用;
  • approval_id:一次用户决策;
  • trace_id:贯穿相关服务的追踪标识。

每个阶段记录开始、结束、耗时、状态和错误分类。对模型调用,还可以记录模型名称、输入输出 token、缓存命中和停止原因;对工具调用,记录工具名、风险等级、执行环境和结果大小。

敏感参数和模型正文不应默认完整进入遥测系统。可观察性不能以泄露隐私为代价。

一次任务的事件流

task/accepted
    ↓
turn/started
    ↓
context/assembled
    ↓
model/requested → model/completed
    ↓
tool/authorized
    ├─ approval/requested → approval/decided
    ↓
tool/started → tool/completed
    ↓
evaluation/completed
    ↓
turn/completed
    ↓
task/completed

如果任务失败,应该能从事件中看到它停在哪一层,而不是所有失败都变成一个 task_failed

面向开发者和面向用户的信息不同

内部观测可以非常细,但不应把所有调试信息倾倒给用户。

开发者关心:

  • 请求与响应时间;
  • 重试、限流和错误码;
  • 工具参数的安全摘要;
  • 状态 revision 和事件序号;
  • token、费用和缓存;
  • 调度队列与并发情况。

用户更关心:

  • 当前目标和阶段;
  • 已完成的重要动作;
  • 正在等待什么;
  • 是否需要自己做决定;
  • 是否可以停止;
  • 最终结果与证据。

好的 UI 会把内部事件翻译成简洁进度,例如“已定位到 3 个相关文件,正在运行回归测试”,而不是持续显示模型的私有推理文本。

为什么不应该展示完整思维过程

用户需要的是行动依据和可验证事实,不是未经整理的内部推理流。

完整思维过程可能包含错误猜测、敏感上下文和大量噪声,也会让界面难以阅读。更合适的做法是展示:

  • 当前计划;
  • 关键决策及简短理由;
  • 工具调用与结果摘要;
  • 风险和审批信息;
  • 可以复核的证据。

这既保留透明度,又不会把“模型想到的每一个词”误当成稳定解释。

用户应该能怎样控制任务

取消

立即停止启动新工作,并向正在执行的操作传播取消。已经开始的副作用要等待结算或标记为结果未知。

暂停与恢复

在安全边界保存状态,稍后继续。恢复时重新检查权限、凭据和外部环境。

审批与拒绝

对具体高风险动作做一次性决策,并保留审计记录。

补充与纠正

用户可以添加新信息、缩小范围或指出方向错误。Harness 要明确这条消息进入当前步骤、下一步骤还是新一轮。

接管

Agent 无法可靠继续时,用户可以手动完成某一步,再把新状态交回系统。

这些操作都不应只是前端按钮。它们需要进入持久状态,并由 Agent Loop 和工具调度器真正执行。

进度如何做到既真实又简洁

不要让模型自由编造“完成了 70%”。更可靠的进度来自真实阶段和证据:

def user_progress(task_state):
    return {
        "phase": task_state.current_phase,
        "completed": [
            step.title
            for step in task_state.steps
            if step.has_verified_result
        ],
        "running": task_state.running_action.safe_summary,
        "blocked_by": task_state.pending_approval,
        "can_cancel": task_state.phase == "running",
    }

对于开放式探索,不必给出虚假的百分比。展示“已检查什么、正在做什么、还需要什么”通常更诚实。

指标应该围绕任务结果

模型请求次数和 token 是基础指标,却不能代表产品效果。

建议分四层观察:

层级 代表指标
结果 完成率、验证通过率、人工返工率
行为 工具成功率、重复调用率、越权阻止率
体验 总延迟、首次有效进度时间、接管率
成本 token、外部 API、计算和存储成本

“策略阻止率高”既可能说明攻击很多,也可能说明规则误伤严重。指标必须结合具体 Trace 和用户反馈解释。

观测代码应该怎样接入

业务代码不应该到处手写互不一致的日志字符串。可以通过统一事件和 span 包装:

async def run_tool(call, trace):
    with trace.span("tool.call") as span:
        span.set("tool.name", call.name)
        span.set("tool.risk", call.risk)

        started_at = clock.now()
        try:
            result = await tool_gateway.execute(call)
            span.set("result.status", result.status)
            metrics.tool_latency.observe(clock.now() - started_at)
            return result
        except Exception as error:
            span.record_error(classify(error))
            raise

参数只记录经过工具定义生成的安全摘要,禁止直接把完整命令、邮件正文或访问令牌塞进 span。

告警要指向可行动的问题

“Agent 错误数增加”过于宽泛。更有效的告警包括:

  • 某个模型提供方 P95 延迟连续 15 分钟超标;
  • 发布工具的结果未知比例高于阈值;
  • 同一任务重复工具调用次数异常;
  • 策略拒绝突然集中在某个新版本;
  • 用户取消后仍有新工具调用启动;
  • 完成率下降,但模型请求成功率正常。

最后一种情况尤其重要:基础设施都成功,并不代表 Agent 完成了用户目标。

常见的失败方式

只有一堆字符串日志

没有 task、step 和 call 关联标识,跨服务后无法重建完整过程。

为了调试记录全部内容

模型输入、工具参数和结果可能包含秘密与个人数据。遥测需要最小化、脱敏和访问控制。

UI 只显示“思考中”

用户无法判断是否有进展,也不知道何时该取消或介入。

只看 token,不看完成率

降低 token 可能只是让 Agent 更早放弃。成本指标必须和验证结果一起看。

取消按钮只改变前端状态

界面显示已停止,但后台仍在调用工具。控制信号必须贯穿 Loop、调度器和具体运行时。

可观察性与用户控制检查清单

  • Task、Turn、Step、Tool Call 是否有稳定关联 ID?
  • 日志、指标和 Trace 是否各司其职?
  • 是否记录阶段状态、耗时、错误类型和资源成本?
  • 遥测是否默认隐藏凭据、正文和敏感参数?
  • 用户是否能看到真实阶段、重要动作和阻塞原因?
  • 是否用计划、证据和简短理由替代完整思维倾倒?
  • 取消、暂停、审批和纠正是否真正进入运行时?
  • 告警是否能定位具体模块和可行动原因?
  • 运营指标是否同时覆盖结果、行为、体验和成本?
  • 能否从一次失败任务快速还原完整链路?

最后理解可观察性与用户控制

可观察性不是上线后才补的运维功能,用户控制也不是给界面加几个按钮。它们必须建立在 Harness 的事件、状态和取消语义之上。

当系统既能说明自己正在做什么,又能在用户介入时真正改变执行,Agent 才会从黑盒自动化变成可协作的工具。

一句话总结:让过程看得见,让风险停得下,让用户始终保有最终控制权。