All lessons › Function D — Agentic Evaluation and Red Teaming › One Red-Team Lifecycle, End to End
D1.4

Establishing telemetry and detecting the actor

D1
▶
Lightboard recording

Not recorded yet. The lesson below is complete and runnable today — the video is added later without touching this page.

Video placeholderpublishes automatically from recordings/D1.4.mp4

The lab

Score actors on behavioural signals to separate agents from people, and pick the threshold by expected cost rather than accuracy.

◉Use case relevance

To catch the actor you just played you have to see it, and an agent's tool calls arrive through a gateway that logs none of the fields telling an agent from a person. This is where the offensive finding becomes defensive telemetry.

At CyberTravels. redteam/actor.py reads the spans A2.1 already emits and scores four shape signals — rate, gap variance, tool breadth, burst. Your planner run scores above 0.7; a person's session scores below 0.4. Then the part that matters: pick_threshold() chooses by the cost of each mistake rather than by accuracy, and prints what accuracy would have picked. When those differ, accuracy was optimising something nobody asked for.

◴What this lesson is — Day 0, Day 1, Day 2

Separating an agent from a person using only the shape of the trace — rate, gap variance, tool breadth, burst — and choosing the threshold by the cost of each mistake rather than by accuracy, since the two mistakes are never equally expensive.

Day 0 — why

To catch the actor you have just played, you have to be able to see it — and the model gateway logs none of the fields that would distinguish an agent from a person.

Day 1 — how

Wrap the gateway trace with identity, tool and arguments, then score actors on behaviour rather than on what they claim to be.

Day 2 — measure

Threshold chosen by expected cost rather than accuracy, and the unregistered actors it surfaces.

Measure this

what to measuredenominator or target
threshold chosen by expected cost rather than accuracythe threshold, with the cost model behind it
unregistered actors surfacedcount

▦The framework, and how it works

   the model gateway, by default        wrapped for telemetry

   { } no per-call identity        ->    {"id": "svc-idx", "tool": "...",
   { } no tool arguments                  "args": {...}, "ts": ...}
   { } no acting principal                        |
                                                  v
                                    score actors on behaviour:
                                    regularity, rate, continuity
                                    threshold set by COST, not accuracy

The pivot of the whole function: an offensive finding becomes defensive telemetry. To catch the actor you have just played, you have to be able to see it at all — and an agent's tool calls arrive through a model gateway that, by default, logs none of the fields that would tell an agent apart from a person.

Establishing telemetry means JSON-wrapping the gateway trace — prompt, tool, arguments, identity — and then scoring actors on behaviour, because the ones you most need to find are the ones in no registry, acting under a human's credential at a machine's tempo.

2 · Demo — score actors from gateway timing alone

3 · Where it breaks — the agent that reads as its owner

4 · The control — a threshold set by cost, not by accuracy

5 · The procedure, as a skill

The skill looks at five CyberTravels accounts and works out which are people and which are software. It judges them on how they behave, not on what they call themselves — anyone can set a label.

Then it has to choose a cut-off. It chooses by cost, not by accuracy:

  • Wrongly flagging a person costs about half an hour of an analyst's time.
  • Missing an agent costs about forty hours.

Those numbers are eighty times apart. So the cut-off with the best accuracy score is not the cut-off you want.

▶Real time execution as skill

The skill — skills/detection/agent-versus-human-scoring/SKILL.md

name: agent-versus-human-scoring
description: >-
  Score actors on behavioural signals to separate agents from people, sweep the
  threshold, and pick it by expected cost rather than by accuracy. Use when
  deciding whether a session is automated, or when unregistered automation needs
  finding.
allowed-tools: Read, Grep, Glob

Pick the threshold by what each mistake costs

Separating agent from human is a scoring problem with two asymmetric errors: a flagged human costs an analyst half an hour, and a missed agent costs whatever an unmonitored automation does. Choosing the threshold by accuracy weights those equally, which is the one thing you know is wrong.

When to use this

Finding unregistered automation, deciding whether a session is a person, and before any control that treats agents differently from users.

Procedure

1 — Score on behaviour, not on the user agent string. Inter-action variance, rate, breadth, and the share of actions with no preceding read. Anything self-declared is a claim.

2 — Score a spread of real actors. A service indexer, an unknown token, a person, a person driving an IDE assistant, and an agent deliberately jittered to look human. The last two are the interesting middle.

3 — Sweep the threshold and record both errors. Humans flagged and agents missed, at each setting. They move in opposite directions and the crossing point is not the answer.

4 — Attach a cost to each error and minimise the total. Analyst hours for a false positive, expected hours of an unmonitored agent for a false negative. The chosen threshold now has a justification somebody can argue with.

5 — Join to the registry. An actor scoring as an agent and absent from the registry is the finding worth routing; a registered agent scoring as an agent is working correctly.

Example

Input — the fixture committed at the top of scripts/agent_versus_human_scoring.py. Edit it and re-run: the buckets, counts and verdicts below are derived from it, not hard-coded.

Output — the opening lines of a real run:

actor                   score     cv   rate/s   span_h  truth
--------------------------------------------------------------
svc-indexer             0.800    0.0    20.04     0.01  agent
dana@corp               0.028   1.55      0.0     1.11  human
unknown-token-7f3c      0.563    0.0      1.0     0.11  agent
sam@corp-ide            0.533    0.0      0.5      0.1  human
polite-agent            0.021   1.26      0.0     0.83  agent
 threshold  humans flagged   agents MISSED

The run continues past this. test_skills.py executes the script on every build, with no model configured — so what CI proves is that it runs and refuses legibly, not that it prints these lines. The output above is a recorded run against a real model, and nothing re-diffs it: a model's answer is not reproducible, and this repository does not claim otherwise anywhere it can be checked.

Output contract

{
  "actors": [{"name": "str", "score": 0.0, "truth": "agent|human|unknown"}],
  "sweep": [{"threshold": 0.0, "humans_flagged": 0, "agents_missed": 0, "expected_cost": 0.0}],
  "costs": {"false_positive_hours": 0.0, "false_negative_hours": 0.0},
  "chosen": {"threshold": 0.0, "why": "str"},
  "registry": {"scored_agent_unregistered": ["str"]}
}

Failure modes

  • Scoring the user agent string. It is self-declared.
  • Optimising accuracy. It assumes the two errors cost the same.
  • Flagging registered agents. They are supposed to look like agents.

Three ways to do this lesson. Pick one. They run the same procedure against the same code — what changes is who drives it, and how much you see.

1In your AI agent — you are taught

The agent reads the lesson, explains the idea, fetches the code, runs the check and answers your questions as you go. This is the one to take if you are here to learn.

Install it, once. From the folder you cloned. --all does every agent it finds on your machine:

python3 scripts/install_skills.py --all --lessons D1.4

Then restart your agent and open it in that folder. On Windows, if python3 says Python was not found, use python instead.

Then pick d1-4-establishing-telemetry-and-detecting-the:

agentinstall just for this agentstart it
Claude Codepython3 scripts/install_skills.py --tool claude --lessons D1.4type /d1-4-establishing-telemetry-and-detecting-the
GitHub Copilot (VS Code)python3 scripts/install_skills.py --tool copilot --lessons D1.4type /d1-4-establishing-telemetry-and-detecting-the in Chat
Cursorpython3 scripts/install_skills.py --tool cursor --lessons D1.4type / in Agent chat and search for it
Codexpython3 scripts/install_skills.py --tool agents --lessons D1.4type $d1-4-establishing-telemetry-and-detecting-the, or run /skills

2As one Python command — you just run it

No agent, no conversation. The same lesson executes and prints the same readback. Take this one if you have no assistant, if you are reviewing rather than learning, or if you want the result without the teaching around it.

python3 scripts/lesson.py D1.4

3Read the code, and the difference — nothing runs

Neither of the above, and no model needed. The first command writes CyberTravels exactly as it stood at the end of this lesson; the second prints the diff — the lines this lesson added or changed, and nothing from any later one.

python3 scripts/checkpoint.py --at D1.4 --out work/cybertravels
python3 scripts/checkpoint.py --at D1.4 --diff

On Windows, type python wherever these say python3 — and use \ instead of / in the paths. Everything else is identical. A0.0 step 2 explains why.

Ways 1 and 2 need a model; way 3 does not. A signed-in Claude Code CLI needs no API key, and any OpenAI-compatible endpoint works too. With neither, the audit exits 2 and says so rather than inventing an answer. A0.0 sets this up.

Ways 1 and 2 both end with the same readback: What I did, What changed, The number and Read this next. A step that was skipped or refused is reported as exactly that, never as a pass.

If you did the last lesson’s challenge, expect this to stop. Most challenges ask you to edit the code, and ways 1 and 2 will not overwrite your edits: you get stopped, not a failure, with two options — --out for a fresh folder that keeps your work, or --force to discard it and start this lesson clean.

✓What you just proved

The service accounts and unregistered token score highest, the human lowest, and cost-weighting selects a low threshold that finds the shadow agents at the price of a few analyst-hours.

✎Your turn

Check whether your model gateway logs the acting identity per call. If it logs only the API key, every agent is anonymous and this scoring is the only actor you have.

Tools used

OpenTelemetryOpenSearch

Skill source: skills/detection/agent-versus-human-scoring

← PreviousD1.3 · Cognitive vulnerability and elicitation scaling