Raw log lines never match directly. Timestamps, seeds, and addresses differ on every run, so the agent replaces each with a placeholder before comparing.
Token load matters here. A common approximate rule is 3 to 4 characters per token for English prose with common tokenizers, fewer for hex and numbers; the exact ratio varies by tokenizer, so measure it before sizing a truncation budget.
Pattern | Replaced with | Why |
|---|---|---|
Hex addresses |
| Same bug fails at different addresses. |
Decimal numbers |
| Timestamps and cycle counts always differ. |
The rest | Kept as is | The message text carries the signature. |
Lines with equal normalized text share a signature. Runs sharing a signature form one group, and the group size counts the repeats.
Long inputs are less reliable than short ones. Studies report models use the start and end of long inputs better than the middle, a tendency rather than a law, so keep the error line and the tail when trimming logs.
The program below is a scripted stand-in for the clustering step, not a model. Six log lines form three groups with sizes 3, 2 and 1, and the checks verify exactly that.
import re
import sys
LOGS = [
"UVM_ERROR @ 12345 ns: axi_rd_data bad, addr 0x3f20",
"UVM_ERROR @ 67890 ns: axi_rd_data bad, addr 0x112c",
"TIMEOUT @ 99999 ns: waiting for irq, addr 0xabcd",
"UVM_ERROR @ 11111 ns: axi_rd_data bad, addr 0x00ff",
"TIMEOUT @ 22222 ns: waiting for irq, addr 0x4455",
"ASSERT @ 33333 ns: fifo full stuck",
]
def signature(line):
s = re.sub(r"0x[0-9a-fA-F]+", "#", line)
s = re.sub(r"[0-9]+", "#", s)
return s
def cluster(logs):
groups = {}
for i, line in enumerate(logs):
groups.setdefault(signature(line), []).append(i)
return groups
groups = cluster(LOGS)
sizes = sorted((len(v) for v in groups.values()), reverse=True)
if len(groups) != 3:
print("FAIL: expected 3 groups")
sys.exit(1)
if sizes != [3, 2, 1]:
print("FAIL: expected sizes 3, 2 and 1")
sys.exit(1)
if sorted(groups[signature(LOGS[0])]) != [0, 1, 3]:
print("FAIL: first group wrong")
sys.exit(1)
if sorted(groups[signature(LOGS[2])]) != [2, 4]:
print("FAIL: second group wrong")
sys.exit(1)
print("PASS: 6 runs form 3 groups with sizes 3, 2 and 1")Yes. Both patterns are replaced with placeholders, so the normalized texts match.
All three normalize to one UVM_ERROR line with placeholders. Run 2 differs in message text, so it starts its own group.