Skip to content

fix(trace): include instruction content in system_instruction span key - #1974

Merged
looplj merged 1 commit into
looplj:unstablefrom
ChancenJ:fix/trace-system-instruction-dedup
Jul 7, 2026
Merged

looplj merged 1 commit into
looplj:unstablefrom
ChancenJ:fix/trace-system-instruction-dedup

Conversation

@ChancenJ

@ChancenJ ChancenJ commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

问题描述

在追踪 OpenCode、Claude Code 等 agent 会话时,发现追踪页面只显示一个系统提示词,即使多个请求都有不同的系统提示词(如生成标题的系统提示词和主 agent 的系统提示词)。

根因分析

spanToKey 函数为每个 span 生成唯一 key 用于去重比较。但该函数缺少对 system_instruction 类型的处理,导致所有系统提示词 span 都返回相同的空字符串 key,无法区分不同内容,被错误去重。

修复方案

spanToKey 中添加 system_instruction 的处理,将指令内容纳入 key 的生成。

这样:

  • 不同内容的系统提示词会生成不同的 key,不会被错误去重
  • 相同内容的系统提示词仍然会生成相同的 key,保持正确的去重行为

影响范围

  • 仅修改 spanToKey 函数,不影响其他去重逻辑
  • 修复后所有使用 OpenAI/Anthropic 格式的 agent 追踪都能正确显示多个系统提示词

@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes a deduplication bug in the spanToKey function where all system_instruction spans were incorrectly assigned the same key ("system_instruction:"), causing distinct system prompts to be treated as duplicates and only the first one to be displayed in trace views.

  • Adds a case "system_instruction": branch to spanToKey that incorporates the instruction content into the key, matching the existing pattern used by user_query, text, thinking, and other content-bearing span types.
  • When span.Value.SystemInstruction is nil, the function correctly falls through to the default "system_instruction:" key, preserving existing nil-safety behaviour.

Confidence Score: 5/5

Safe to merge — the change is a minimal, targeted addition that fixes a clear deduplication bug without touching any other logic.

The fix adds a single 4-line case block that exactly mirrors the established pattern used by every other content-bearing span type in the same switch. The SpanSystemInstruction.Instruction field is a plain string, the nil guard is correct, and the fallback to 'system_instruction:' when the field is nil matches the function's existing default behaviour. No other code paths are affected.

No files require special attention.

Important Files Changed

Filename Overview
internal/server/biz/trace.go Adds a system_instruction case to spanToKey so different system prompts produce distinct deduplication keys; follows the identical pattern used by all other content-bearing span types in the same switch statement.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[spanToKey called] --> B{span.Value == nil?}
    B -- yes --> C["return 'type:'"]
    B -- no --> D{switch span.Type}
    D -- system_instruction --> E{SystemInstruction != nil?}
    E -- yes --> F["return 'system_instruction:<instruction>'"]
    E -- no --> G["fall-through → return 'system_instruction:'"]
    D -- user_query --> H["return 'user_query:<text>'"]
    D -- text --> I["return 'text:<text>'"]
    D -- other types --> J["return type-specific key"]
    D -- no match --> G
    F --> K[Dedup comparison]
    G --> K
    H --> K
    I --> K
    J --> K
    K --> L{currentKey == parentKey?}
    L -- yes --> M[Span skipped - duplicate]
    L -- no --> N[Span kept - unique]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[spanToKey called] --> B{span.Value == nil?}
    B -- yes --> C["return 'type:'"]
    B -- no --> D{switch span.Type}
    D -- system_instruction --> E{SystemInstruction != nil?}
    E -- yes --> F["return 'system_instruction:<instruction>'"]
    E -- no --> G["fall-through → return 'system_instruction:'"]
    D -- user_query --> H["return 'user_query:<text>'"]
    D -- text --> I["return 'text:<text>'"]
    D -- other types --> J["return type-specific key"]
    D -- no match --> G
    F --> K[Dedup comparison]
    G --> K
    H --> K
    I --> K
    J --> K
    K --> L{currentKey == parentKey?}
    L -- yes --> M[Span skipped - duplicate]
    L -- no --> N[Span kept - unique]
Loading

Reviews (2): Last reviewed commit: "fix(trace): include instruction content ..." | Re-trigger Greptile

spanToKey was missing a case for "system_instruction", causing all
system instruction spans to share the same key regardless of content.
This led to different system prompts being incorrectly deduplicated
when building the segment tree.
@ChancenJ
ChancenJ force-pushed the fix/trace-system-instruction-dedup branch from fbacf9c to 3a9f267 Compare July 7, 2026 07:27
@looplj
looplj merged commit 35a8e5b into looplj:unstable Jul 7, 2026
5 checks passed
@ChancenJ
ChancenJ deleted the fix/trace-system-instruction-dedup branch July 7, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants