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_call、observation 等结构化事件;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:
| Layer | Category | Leaf signals | 用途 |
|---|---|---|---|
| Interaction | Misalignment | correction, rephrase, clarification | 用户可见的 intent mismatch,可用于 learning |
| Interaction | Stagnation | dragging, repetition | 对话继续但无进展,可用于 learning |
| Interaction | Disengagement | escalation, quit, negative_stance | 用户合作意图撤回,可用于 failure triage |
| Interaction | Satisfaction | gratitude, confirmation, success | 成功 exemplar sampling,不是 reward score |
| Execution | Failure | invalid_args, bad_query, tool_not_found, auth_misuse, state_error | 工具使用失败,可用于 learning |
| Execution | Loops | retry, parameter_drift, oscillation | 控制流不进展,可用于 learning |
| Environment | Exhaustion | api_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_name 与 call_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 公式;plano 的 quality_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 Concept | Source File | Key Class/Function |
|---|---|---|
| 三层 taxonomy 与 leaf signal identifiers | crates/brightstaff/src/signals/schemas.rs | SignalType, SignalLayer, SignalInstance, SignalGroup, SignalReport |
| Top-level analyzer pipeline | crates/brightstaff/src/signals/analyzer.rs | SignalAnalyzer::analyze_sharegpt, assess_quality, generate_summary |
| Interaction: misalignment | crates/brightstaff/src/signals/interaction/misalignment.rs | analyze_misalignment, is_similar_rephrase |
| Interaction: stagnation | crates/brightstaff/src/signals/interaction/stagnation.rs | analyze_dragging, analyze_repetition, analyze_stagnation |
| Interaction: disengagement | crates/brightstaff/src/signals/interaction/disengagement.rs | analyze_disengagement |
| Interaction: satisfaction/exemplars | crates/brightstaff/src/signals/interaction/satisfaction.rs | analyze_satisfaction |
| Execution: tool failures | crates/brightstaff/src/signals/execution/failure.rs | extract_tool_info, analyze_failure |
| Execution: loops | crates/brightstaff/src/signals/execution/loops.rs | parse_tool_call, detect_retry, detect_parameter_drift, detect_oscillation, analyze_loops |
| Environment: exhaustion | crates/brightstaff/src/signals/environment/exhaustion.rs | analyze_exhaustion |
| Trace/export integration | crates/brightstaff/src/signals/otel.rs | emit_signals_to_span, emit_layered_attributes, emit_signal_events, is_concerning |
| Docs and detector contract | docs/source/concepts/signals.rst; tests/parity/signals/README.md | Paper 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。