7. Two steps replaced by code
The delta
Step by step
| Step | What it decides | All agentic | Hybrid | In the hybrid |
|---|---|---|---|---|
s1_parse | Extract the machine variant from the request | 100.0% | 100.0% | agentic |
s2_history | Retrieve comparable ticket history | 100.0% | 100.0% | agentic |
s3_diagnose | Choose the fault code the symptom implies | 47.5% | 85.0% | deterministic code |
s4_part | Turn the fault plus variant into a part number | 55.0% | 85.0% | deterministic code |
s5_stock | Decide source-from-stock or order, on lead time | 85.0% | 85.0% | agentic |
s6_compose | Compose the work order | 45.0% | 85.0% | agentic |
Two steps changed. Step 3, symptom to fault code, became a nearest-centroid classifier fitted on the ticket corpus. Step 4, fault plus variant to part number, became a dictionary lookup. Steps 1, 2, 5 and 6 are identical in both configurations, and the agent still makes the tool calls.
Notice that steps 4, 5 and 6 in the hybrid column match step 3 exactly. That is not a coincidence or a bug: once the diagnosis is right, the rest is deterministic, so they cannot fail independently. The whole workflow's reliability collapses onto one number, and that number is now the only thing worth improving.
Why these two steps and not the others
- Step 4 is a lookup. One remedy part exists per fault and variant. There is no judgement in it, and a language model can only add a failure mode.
- Step 3 looked like judgement and is not. Ten classes, 360 labelled examples in the ticket history, fixed vocabulary. That is a text classification problem with abundant training data. The classifier scores 90.0% on the 10 held-out phrasings, against 47.5% for the agentic path.
- Steps 2 and 6 stay agentic because they involve choosing what to retrieve and how to say it, which is what the model is for.
The published arithmetic points the same way. Six steps at p = 0.90 is 53.1%; four at 0.90 plus two at 0.9995 — ordinary software reliability, not perfection — is 65.5%, a gain of 12.4 points. At p = 0.85 the same swap is worth 14.4 points. Those are computed, not measured, and are shown here as the reference the measured delta above should be read against.
This is also why field-service diagnosis sits on the Cognition side of the multi-agent argument rather than the Anthropic side. A repair recommendation is one coherent artefact with high dependency between steps: the part depends on the fault, which depends on the history. “Actions carry implicit decisions, and conflicting decisions carry bad results.” Adding a second agent to that adds a negotiation nobody can observe.
The held-out evaluation of the classifier
Ten symptom phrasings, one per fault, written in a reporter's words and present in no ticket. The margin column is the gap to the runner-up; below 0.02 the classifier declares the case ambiguous and refuses, which costs coverage and is why the workflow figure is lower than the raw accuracy.
| Expected | Predicted | Correct | Margin | Held-out phrasing |
|---|---|---|---|---|
| F-101 | F-101 | yes | 0.0035 | runs hot and drops out on a temperature fault once it has been working hard for a while |
| F-102 | F-102 | yes | 0.1281 | there is oil showing up where it should not be, downstream of the machine |
| F-103 | F-103 | yes | 0.0056 | it will not come up to pressure and the tools are starving |
| F-104 | F-104 | yes | 0.1781 | we are getting water through to the tools every shift |
| F-105 | F-105 | yes | 0.2323 | keeps kicking the overload out when we try to start it |
| F-106 | F-104 | no | 0.0035 | there is a bad shake at the drive end that gets worse through the day |
| F-107 | F-107 | yes | 0.2426 | output has dropped off and the service indicator is showing red |
| F-108 | F-108 | yes | 0.3235 | the controller will not hold the set point, it hunts up and down |
| F-109 | F-109 | yes | 0.1590 | there is fluid weeping out underneath near the shaft |
| F-110 | F-110 | yes | 0.1636 | the cooler is not doing its job, outlet is far too warm for the ambient |
The code that replaced the two steps
This is the running source of pipeline/deterministic.py,
read with inspect.getsource at render time.
def diagnose(symptom: str, *, top_k: int = 2) -> dict[str, Any]:
"""Symptom text to fault code. Deterministic, ~1 ms, no model call."""
m = _model()
df, n = m["df"], m["n"]
q: dict[str, float] = defaultdict(float)
for f in _features(symptom):
q[f] += 1.0
qv = {f: (1 + math.log(c)) * math.log((n + 1) / (df.get(f, 0) + 0.5))
for f, c in q.items()}
qn = math.sqrt(sum(v * v for v in qv.values())) or 1.0
scored = []
for code, cen in m["centroids"].items():
s = sum(v * cen.get(f, 0.0) for f, v in qv.items()) / qn
scored.append((s, code))
scored.sort(reverse=True)
best = scored[0]
runner = scored[1] if len(scored) > 1 else (0.0, None)
margin = best[0] - runner[0]
return {
"fault_code": best[1],
"score": round(best[0], 4),
"runner_up": runner[1],
"margin": round(margin, 4),
# The stop condition the Skill specifies. Two ways to abstain: the top
# two classes are effectively tied, or nothing scored high enough to be
# a diagnosis at all. Abstaining converts a wrong answer into a
# question, which is the correct trade in a field-service setting.
"ambiguous": margin < MIN_MARGIN or best[0] < MIN_SCORE,
"abstain_reason": ("tied with the runner-up" if margin < MIN_MARGIN else
("no fault scored above the floor" if best[0] < MIN_SCORE
else "")),
"top": [(c, round(s, 4)) for s, c in scored[:top_k]],
}
def select_part(fault_code: str, model_variant: str) -> str | None:
"""A catalogue lookup. One line, and it cannot be wrong given its inputs."""
return store.canonical_part(fault_code, model_variant)
def sourcing(part_no: str | None) -> str | None:
"""A threshold comparison. Three branches."""
p = store.PARTS_BY_NO.get(part_no or "")
if not p:
return None
if p["stock_qty"] > 0:
return "source from stock"
if p["lead_time_days"] <= LEAD_TIME_THRESHOLD_DAYS:
return "order, lead time within target"
return "order, escalate on lead time"
Demo4/pipeline/.