Signals: Trajectory Sampling and Triage for Agentic Interactions

Paper: arXiv:2604.00356 Code: katanemo/plano Code reference: main @ 554a3d1f (2026-05-26)

1. Motivation (研究动机)

Agentic application 的部署形态已经从单轮问答变成多步交互循环:LLM 需要规划、调用工具、观察环境反馈、再继续行动。论文关注的不是“如何让某个 agent 当场自我修正”,而是部署后如何从海量轨迹里找到最值得人类审查、最可能转化为偏好数据的样本。这个问题在生产环境里很具体:轨迹数量大、非确定性强、包含自然语言与工具调用两层行为;如果逐条做人审或逐条跑 LLM-as-a-judge,成本都会随流量线性爆炸。

当前方法的断点在于:生产系统能记录丰富 trajectory,RLHF/DPO 等 post-training 方法又需要 curated preference data,但两者之间缺少低成本的数据选择接口。离线 benchmark 只能覆盖已知场景,手工排查只适合少量 case,LLM judge 虽然能在结构化任务上接近人类偏好,但对每条生产轨迹调用模型并不现实。更麻烦的是,传统 dialogue quality indicators 只看对话层;agent 可能对用户说话很流畅,却在工具调用、API 状态、数据库变更上完全失败。

这篇论文要解决的目标因此是:设计一组不改 online agent 行为、无需额外模型调用、可直接挂在 live trace 上的 trajectory signals,用它们把“值得看”的轨迹从普通流量中 triage 出来。它的价值在于把 observability 变成 optimization 的入口:先用便宜信号筛选失败、卡住、用户不满、成功 exemplar,再把这些样本交给人审、counterfactual continuation 或 preference-pair construction,最终服务 DPO/RLHF/SFT 等训练流程。

2. Idea (核心思想)

核心洞察是:trajectory 是否“值得学习”不一定要先判断最终质量;许多可解释、可定位、低成本的行为信号本身就足以筛出高信息量样本。Signals 不是 reward model,也不是 correctness classifier,而是把用户修正、对话停滞、工具失败、执行循环、环境耗尽、满意反馈等模式标成结构化 metadata。

方法上的关键创新有三点:第一,把 agent trajectory 拆成 interaction、execution、environment 三层,避免只看文本对话或只看最终 reward;第二,用 deterministic lexical/sequence heuristics 检测信号,保证 always-on 部署成本远低于 LLM judge;第三,把 signal sampling 作为 preference-data construction 的上游 sampling infrastructure,而不是直接把 signal 当训练 reward。

与 Reflexion、Self-Refine、Voyager 这类在线自改进方法相比,Signals 处理的是已经完成的部署轨迹,不要求环境 reset、task retry 或 ground-truth reward。与 LLM-as-a-judge / Agent-as-a-Judge 相比,它不逐条调用模型做语义评判,而是先用 rule-based signals 降低审查候选集的熵;语义判断仍可留给后续人审或 selective judge。

3. Method (方法)

3.1 Overall framework:从 live trajectory 到 triage queue

论文的 arXiv source 中没有原始图片(source package 中 includegraphics=0,仅包含两张 LaTeX 表格),所以下图是根据论文 §3–§4 与 released code crates/brightstaff/src/signals/ 自绘的结构示意图,不是原论文图。

Figure 1 解读:系统从 live agent trajectory 中读取 user/assistant messages、function_callobservation 等结构化事件;SignalAnalyzer 先做 last- 截断与文本 normalization,然后分别运行 interaction、execution、environment detectors。Interaction 与 execution signals 是 learning-oriented,可进入人审队列与 preference data construction;environment exhaustion 更适合系统诊断,论文实验中的 signal sampler 明确排除了 environment-only signals。

直觉上,这个框架把“评价一个 agent 做得好不好”拆成更可操作的问题:“哪些轨迹里出现了可解释的摩擦或 exemplar?”这样做避免了两类误用:一是把用户满意度、工具错误、系统 outage 混成一个 reward;二是在每条轨迹上调用昂贵 judge,却仍难以解释为什么样本值得看。Signals 只负责提供可索引、可排序、可抽样的 evidence pointer,后续的人审或训练流程再决定如何转成偏好对。

3.2 Signal taxonomy:三层、七类、二十个 leaf signals

论文把 signals 定义为 recurring behavioral patterns 的 descriptive markers,不直接测 correctness、reward 或 quality。released code 中 SignalType 精确实现了三层 taxonomy:

LayerCategoryLeaf signals用途
InteractionMisalignmentcorrection, rephrase, clarification用户可见的 intent mismatch,可用于 learning
InteractionStagnationdragging, repetition对话继续但无进展,可用于 learning
InteractionDisengagementescalation, quit, negative_stance用户合作意图撤回,可用于 failure triage
InteractionSatisfactiongratitude, confirmation, success成功 exemplar sampling,不是 reward score
ExecutionFailureinvalid_args, bad_query, tool_not_found, auth_misuse, state_error工具使用失败,可用于 learning
ExecutionLoopsretry, parameter_drift, oscillation控制流不进展,可用于 learning
EnvironmentExhaustionapi_error, timeout, rate_limit, network, malformed_response, context_overflow外部系统/资源问题,主要用于 diagnosis

每个 detected signal 在 code 中被封装为 SignalInstance(signal_type, message_index, snippet, confidence, metadata);同一 category 聚合成 SignalGroup(category, count, signals, severity)。Severity 是 count-based bucket: 个为 个为 mild, 个为 moderate, 个为 severe。完整 SignalReport 同时包含 interaction、execution、environment 三层、turn_metrics、summary、以及一个产品化 quality_score

3.3 Interaction signals:自然语言层的 misalignment / stagnation / disengagement / satisfaction

Misalignment 检测用户是否在修正、重述或请求澄清。released code 中 misalignment.rs 先匹配 phrase-level patterns,例如 “no, I meant”、 “let me rephrase”、 “I don’t understand”;如果没有显式 pattern,则比较当前 user turn 与最近 user turn 的 meaningful-token overlap,要求两条消息都至少有 个非停用词、overlap ratio 、new-content ratio ,且相隔不超过 个 message index。这样能捕获“用户换种说法继续问同一件事”的隐式修复。

Stagnation 分两部分:dragging 根据 user turn 数相对 baseline 计算 efficiency,code 默认 baseline_turns=5,超过 baseline 后用 衰减;当 efficiency 时触发 dragging。repetition 在同一 speaker role 的最近 条消息里查近重复,exact threshold 是 ,near-duplicate threshold 是 ,并跳过 “yes/ok/thanks” 这类天然重复的 positive prefix 短回复。

Disengagement 抓取用户退出合作的迹象,包括“talk to a human / supervisor / support”等 escalation phrases、quit patterns、all-caps shouting、连续 ??? / !!! / mixed punctuation、profanity 和 generic negative stance。Satisfaction 则只在 user turn 中寻找 gratitude、confirmation、success patterns,例如 “that’s helpful”、 “that works”、 “it worked”。论文特别强调 satisfaction 不是给轨迹打高分,而是让系统也能抽到 successful exemplars。

def detect_interaction_signals(messages, cfg):
    normalized = [normalize(m.text, max_len=cfg.max_message_length)
                  for m in messages if m.role in {"human", "gpt"}]
 
    misalignment = []
    for user_turn in normalized_user_turns(normalized):
        if fuzzy_match(user_turn, CORRECTION_PATTERNS):
            misalignment.append(signal("interaction.misalignment.correction", user_turn))
        elif fuzzy_match(user_turn, REPHRASE_PATTERNS):
            misalignment.append(signal("interaction.misalignment.rephrase", user_turn))
        elif fuzzy_match(user_turn, CLARIFICATION_PATTERNS):
            misalignment.append(signal("interaction.misalignment.clarification", user_turn))
        elif similar_to_recent_user_turn(user_turn, overlap=0.75,
                                         min_tokens=4, max_new_ratio=0.5,
                                         max_distance=3):
            misalignment.append(signal("interaction.misalignment.rephrase", user_turn,
                                       confidence=0.8))
 
    stagnation = detect_dragging(messages, baseline_turns=5, threshold=0.5)
    stagnation += detect_repetition(normalized, lookback=2,
                                    exact_threshold=0.95,
                                    near_duplicate_threshold=0.85)
    disengagement = detect_escalation_quit_negative_stance(normalized_user_turns(normalized))
    satisfaction = detect_gratitude_confirmation_success(normalized_user_turns(normalized))
    return interaction_report(misalignment, stagnation, disengagement, satisfaction)

3.4 Execution signals:工具调用失败与控制流 loop

Execution signals 来自 structured runtime events,而不是对话文本本身。Failure detector 在 function_call 后面的 observation 中查错误模式,并把错误绑定回最近一次 tool call 的 tool_namecall_index。它覆盖 invalid arguments、bad query、unknown tool/function、authorization misuse、state/order error 等。与 environment exhaustion 的区别是 attribution:如果错误主要是 agent 的选择、参数、调用顺序导致,属于 execution;如果主要由外部服务、额度、网络、context cap 导致,属于 environment。

Loop detector 先把 function_call 解析为 (index, name, canonical_args),然后检测三种 sequence pattern:同一个 tool + identical args 连续至少 次是 retry;同一个 tool 连续至少 次但 args 至少有 种变化是 parameter drift;包含至少两个 tool 的 pattern 重复至少 个 cycles 是 oscillation。这个检测补上了单次 tool error 无法覆盖的情况:agent 可能没有报错,但一直在同一片状态空间里绕圈。

def detect_execution_signals(messages):
    failures = []
    last_call = None
    for i, msg in enumerate(messages):
        if msg.role == "function_call":
            last_call = parse_tool_call(msg.value)  # name + canonical args
        elif msg.role == "observation":
            if regex_match(msg.value, INVALID_ARGS_PATTERNS):
                failures.append(signal("execution.failure.invalid_args", i, tool=last_call.name))
            elif regex_match(msg.value, TOOL_NOT_FOUND_PATTERNS):
                failures.append(signal("execution.failure.tool_not_found", i, tool=last_call.name))
            elif regex_match(msg.value, AUTH_MISUSE_PATTERNS):
                failures.append(signal("execution.failure.auth_misuse", i, tool=last_call.name))
            elif regex_match(msg.value, STATE_ERROR_PATTERNS):
                failures.append(signal("execution.failure.state_error", i, tool=last_call.name))
            elif regex_match(msg.value, BAD_QUERY_PATTERNS):
                failures.append(signal("execution.failure.bad_query", i, tool=last_call.name))
 
    calls = [parse_tool_call(i, m.value) for i, m in enumerate(messages) if m.role == "function_call"]
    loops = []
    loops += find_runs(calls, same_name=True, same_args=True, min_len=3,
                       signal_type="execution.loops.retry")
    loops += find_runs(calls, same_name=True, unique_args_at_least=2, min_len=3,
                       signal_type="execution.loops.parameter_drift")
    loops += find_repeated_tool_patterns(calls, min_pattern_len=2, min_cycles=3,
                                         signal_type="execution.loops.oscillation")
    return execution_report(failures, loops)

3.5 Environment signals:诊断用 exhaustion,不进入 learning sampler

Environment exhaustion detector 只扫描 observation,匹配 rate limit、5xx/API error、timeout、network error、malformed response、context overflow 等外部或边界条件。论文把它单独列出,是为了避免把系统 outage、quota、context limit 误当成 agent policy 的学习信号。如果把这些直接喂给偏好优化,模型可能学到 spurious correlation:例如某类任务经常遇到 API outage,并不代表 agent 在这类任务上决策差。

def detect_environment_exhaustion(messages):
    exhaustion = []
    for i, msg in enumerate(messages):
        if msg.role != "observation":
            continue
        if regex_match(msg.value, RATE_LIMIT_PATTERNS):
            exhaustion.append(signal("environment.exhaustion.rate_limit", i, confidence=0.95))
        elif regex_match(msg.value, API_ERROR_PATTERNS):
            exhaustion.append(signal("environment.exhaustion.api_error", i, confidence=0.90))
        elif regex_match(msg.value, TIMEOUT_PATTERNS):
            exhaustion.append(signal("environment.exhaustion.timeout", i, confidence=0.90))
        elif regex_match(msg.value, NETWORK_PATTERNS):
            exhaustion.append(signal("environment.exhaustion.network", i, confidence=0.90))
        elif regex_match(msg.value, MALFORMED_PATTERNS):
            exhaustion.append(signal("environment.exhaustion.malformed_response", i, confidence=0.85))
        elif regex_match(msg.value, CONTEXT_OVERFLOW_PATTERNS):
            exhaustion.append(signal("environment.exhaustion.context_overflow", i, confidence=0.90))
    return signal_group("exhaustion", exhaustion)

3.6 Sampling and metrics:triage 不是 quality scoring

论文实验比较三种固定预算 sampling:random 从全池均匀抽样;heuristic 只抽至少 个 user messages 的长对话;signal sampler 抽取触发 interaction 或 execution signals 的轨迹,排除 environment-only diagnosis signals。论文没有公开 composite triage score 的精确公式,只说明对 full interaction signals 与 execution signals 聚合后 prioritizes trajectories exhibiting one or more activations。因此可以把实验逻辑理解为:signals 先提供候选与优先级,人类 annotation 再给 “developer-informative?” 标签。

论文核心 metric 是 majority-vote informativeness rate: 为排除“只是在过采失败样本”的解释,论文又用 random sampler 的 reward distribution 做 counterfactual standardization。Random 中 failed/successful 比例是 37%/63%,所以标准化率可写成: 在 released code 中还有一个产品化 quality_score,从 分开始:satisfaction 加分,disengagement、high misalignment rate、stagnation、execution failure、execution loops、environment exhaustion 扣分,然后 clamp 到 并映射成 excellent/good/neutral/poor/severe。论文公式与 released code 实现差异:论文正文强调 signals are not quality scores,也没有给出 quality-score 或 exact triage-score 公式;planoquality_score/OTEL attributes 是产品化 observability 输出,不应被理解成论文实验已验证的训练 reward 或偏好标签。

def sample_for_review(trajectories, budget=100):
    scored = []
    for traj in trajectories:
        report = analyze_signals(traj)
        learning_count = (report.interaction.misalignment.count
                          + report.interaction.stagnation.count
                          + report.interaction.disengagement.count
                          + report.interaction.satisfaction.count
                          + report.execution.failure.count
                          + report.execution.loops.count)
        # Environment-only exhaustion is retained for diagnosis but not learning sampling.
        if learning_count > 0:
            priority = severity_weighted_count(report.interaction, report.execution)
            scored.append((priority, traj, report))
    return top_k(scored, k=budget)

3.7 Code-to-paper mapping

Code reference: main @ 554a3d1f (2026-05-26) — pseudocode and mapping based on this commit

Paper ConceptSource FileKey Class/Function
三层 taxonomy 与 leaf signal identifierscrates/brightstaff/src/signals/schemas.rsSignalType, SignalLayer, SignalInstance, SignalGroup, SignalReport
Top-level analyzer pipelinecrates/brightstaff/src/signals/analyzer.rsSignalAnalyzer::analyze_sharegpt, assess_quality, generate_summary
Interaction: misalignmentcrates/brightstaff/src/signals/interaction/misalignment.rsanalyze_misalignment, is_similar_rephrase
Interaction: stagnationcrates/brightstaff/src/signals/interaction/stagnation.rsanalyze_dragging, analyze_repetition, analyze_stagnation
Interaction: disengagementcrates/brightstaff/src/signals/interaction/disengagement.rsanalyze_disengagement
Interaction: satisfaction/exemplarscrates/brightstaff/src/signals/interaction/satisfaction.rsanalyze_satisfaction
Execution: tool failurescrates/brightstaff/src/signals/execution/failure.rsextract_tool_info, analyze_failure
Execution: loopscrates/brightstaff/src/signals/execution/loops.rsparse_tool_call, detect_retry, detect_parameter_drift, detect_oscillation, analyze_loops
Environment: exhaustioncrates/brightstaff/src/signals/environment/exhaustion.rsanalyze_exhaustion
Trace/export integrationcrates/brightstaff/src/signals/otel.rsemit_signals_to_span, emit_layered_attributes, emit_signal_events, is_concerning
Docs and detector contractdocs/source/concepts/signals.rst; tests/parity/signals/README.mdPaper citation, OTEL schema, Rust/Python parity harness

4. Experimental Setup (实验设置)

Dataset and scale

实验使用 -bench 作为 testbed。它模拟 tool-equipped agent 与 LLM-simulated user 的 multi-turn conversation,覆盖 airline 与 retail 两个 domain。轨迹同时包含 discourse-level interaction(user-agent dialogue)和 structured execution events(tool calls、API responses、database mutations),因此能触发 interaction、execution、environment 三类 signals。论文说明 trajectory pool 来自 -bench 公开 historical trajectories,由多个 agent configurations 生成,包含不同 model backbones 与 prompting strategies,覆盖 benchmark 的所有 tasks;总 pool size 记作 ,但论文没有披露具体

固定 annotation budget 是每种 sampling strategy 抽 条轨迹,三种策略合计 条。每条轨迹由 位熟悉 agentic systems 与 tool-use patterns 的 expert annotators 独立标注;轨迹混排并 blind to sampling condition。

Baselines and sampling strategies

  • Random:从全 trajectory pool 均匀抽样,是无偏 baseline。
  • Heuristic:抽至少 个 user messages 的轨迹,代表 practitioner 可能使用的“长对话更复杂/更可能出错”的一阶过滤器。
  • Signal:抽触发 interaction signals(misalignment、stagnation、disengagement、satisfaction)或 execution signals(failure、loop)的轨迹;environment signals 因为 diagnosis-only 被排除。

Annotation labels and metrics

每条轨迹有两个 annotation 问题。第一是 Developer-informative?,二分类 YES/NO;当轨迹含有足够具体 evidence,使 developer 至少能形成一个 plausible improvement hypothesis 时标 YES,包括 failure 和 strong success。第二是 Main reason,单选类别包括 action/tool-use behavior issue、conversation issue、external system issue、success exemplar、none/unclear、other。多问题轨迹用 priority rules:如果外部依赖是 dominant driver 才选 external system;execution 是关键失败模式时 action/tool-use 优先于 conversation。

Primary metric 是 majority-vote informativeness rate,即 3 个 annotators 中至少 2 人标 YES 的比例。置信区间使用 Clopper–Pearson 95% interval;两两比较使用 two-sided Fisher’s exact test。Reliability 使用 Gwet’s AC1 与 Fleiss’ :developer-informative binary question 的三人 Gwet’s AC1 是 0.477;在三人都同意 informative 的 N=130 条轨迹上,main-reason category 的 Fleiss’ 、Gwet’s AC1 0.829。

Training / implementation config

这篇论文没有训练新模型,也没有报告 GPU、learning rate、batch size 或 training steps;它验证的是一个 data selection / triage framework。论文只说明 -bench historical trajectories 由多个 model backbones 与 prompting strategies 的 agent configurations 生成,但未列出具体 backbone 名单。released plano repo 公开了 Signals detectors 与 OTEL integration,但没有包含论文中 -bench sampling/annotation experiment 的 launch script 或 full reproduction config。因此训练配置应记为 not applicable / not reported,不能从 plano 的产品默认配置推断论文实验超参。

5. Experimental Results (实验结果)

Main informativeness results

Figure 2 解读:根据论文 Table 1 重绘。Signal sampling 在固定 100 条 annotation budget 下达到 82.0% informativeness rate,Random 只有 54.0%,Heuristic 为 74.0%。Signal vs Random 显著(论文正文给出 p<0.001;表注为 p<0.05),Signal vs Heuristic 未达到显著(p=0.232)。但分层后可以看到 Signal 不只是过采 failed trajectories:在 failed stratum 达到 96.2%,在 successful stratum 仍达到 66.7%。

最重要的结论是:signals 真的提高了“每审一条轨迹得到有用信息”的概率。随机抽样的 条 informative / 100 条说明 pool 里本来就有很多有用轨迹,但 Signal 把这个比例提高到 条 / 100 条;相对 random,annotation effort 的浪费大幅减少。

Reward-stratified and standardized analysis

Heuristic 的高总体率部分来自 failed trajectories 的过采:它抽到 70% reward 的失败轨迹,而 Random 的失败比例是 37%。Signal 的失败比例是 52%,比 Random 高但比 Heuristic 更均衡。论文用 Random 的 37% failed / 63% success 分布做标准化后得到:Signal 77.6%,Heuristic 62.7%,Random 54.0%。

这说明 Heuristic 的优势在去掉 failure-heavy composition 后下降 percentage points,而 Signal 只下降 points。换言之,Signal 能在 successful trajectories 中发现仍值得改进的模式,例如 policy violations 或 inefficient tool use;这类轨迹最终 reward 成功,但对 post-deployment improvement 仍有价值。

Annotation efficiency

论文把结果换算为 label cost:Signal 每得到 条 informative trajectory 需要 个 labels,Heuristic 需要 ,Random 需要 。相对 Random,Signal 的效率增益是 。这个数字来自相同 100 条 annotation budget 下 vs 个 informative samples 的差异,也就是:

Reason distribution

Figure 3 解读:根据论文 Table 2 重绘。在 informative subset 中,三种 sampling 的 reason distribution 很接近:action/tool-use issues 约 57–60%,conversation issues 约 38–43%,success exemplars 数量较少。这个结果支持作者的解释:Signal sampling 不是把样本类型偏向某个特殊错误类别,而是在相似的问题结构下更有效地找到更多 informative trajectories。

Domain robustness

论文额外比较 airline 与 retail domain。Airline domain 中三种策略都很高,informativeness rate 在 86%–96%,因此区分空间有限。Retail domain 更复杂、base informativeness 更低,分离最明显:Signal 达到 78%,Heuristic 66%,Random 35%。这说明当轨迹更 heterogeneous、无信息样本更多时,signal-based triage 的边际价值最大。

Limitations and caveats

作者明确指出三类限制。第一,实验只覆盖 -bench 的 airline 与 retail,并且使用 LLM-simulated users;真实用户的 disengagement、satisfaction、abandonment pattern 可能更复杂。第二,taxonomy 是 coarse-grained behavioral signals,不评估 semantic correctness 或 domain-specific policy violations;一个回答可能行为上很顺畅但事实错误,这种轨迹不会被当前 signals 自动 surfacing。第三,detectors 依赖 deterministic rules 和 lexical heuristics,轻量但 recall 有边界;更细微的 misalignment 或 implicit frustration 可能需要 selective model-based detectors,代价是失去完全 model-free 的 always-on 优势。

总体上,论文证明的是一个 upstream sampling claim:Signals 能在固定人审预算下更高效地发现 developer-informative trajectories,并为后续 preference data construction 提供更干净的候选集。它还没有证明完整闭环训练(triage → preference pair → DPO/RLHF → redeploy)会带来线上 agent 质量提升;这一步被作者留作 future work。