Using an AI agent to learn the perceptron algorithm — an annotated conversation

How can you steer an AI to teach you something new that you don’t know yet? AI agents are trained to follow your instructions, and they can be great teachers, but it can be a challenge to get them to teach you something you haven’t learned yet. Here is an annotated summary of a conversation you can try yourself, showing several techniques for doing it.

The conversation is about learning Rosenblatt’s perceptron algorithm, and how to prove that it converges. Even though the AI can give a proof in a single prompt, it took a few hours of conversation to get the AI to really explain it well. Along the way I am asking it to make a pretty visual explanation as an interactive JavaScript widget.

The centre column has the prompts, what came back, and the figure as it stood at that moment. The margin has what I was thinking that led to the next question.

Three challenges to overcome when learning from an AI agent

You do not understand it yet. That is the situation you are trying to get out of, so you cannot specify what you want up front. You can only react to what you are shown.

The agent will build what you asked for. Including when what you asked for is wrong. It is much better at satisfying a request than at telling you the request was malformed.

You can both be wrong at once. The failure that matters is not the agent inventing a fact. It is the two of you settling on a picture that looks right, reads well, and quietly misrepresents the theorem. Nothing in the conversation will flag this. Only a check will.

The transcript below shows ways to overcome all three of these problems.

Part one — getting the first picture

This half ran in an ordinary chat. The agent built a small interactive figure after each exchange, and I kept telling it what I still could not see.

You“I’m going to teach Rosenblatt’s algorithm today. What is the best way to teach it? Remind me exactly the algorithm.”

Agent“Teach it as geometry first, proof second, failure third. The algorithm is four lines; the value is all in what students see while running it by hand. … Augment each x with a constant 1 so the bias rides along in w. Labels y ∈ {+1, −1}.”

w ← 0
repeat until no mistakes (or max epochs):
    for each (x, y) in training set:
        if y (w · x) ≤ 0:          # mistake, including ties
            w ← w + η y x           # η = 1

“Updates happen only on mistakes. Correct points are untouched. … If some unit w* separates the data with margin γ = mini yi(w*·xi) > 0, and ∥xi∥ ≤ R, the algorithm makes at most (R/γ)² mistakes, regardless of presentation order.”

“The proof is a sandwich and fits on one board:”

  1. Every mistake grows the projection: w*·wk ≥ w*·wk−1 + γ, so w*·wk ≥ kγ.
  2. Every mistake barely grows the length: ∥wk∥² = ∥wk−1∥² + 2y(wk−1·x) + ∥x∥² ≤ ∥wk−1∥² + R², because the middle term is ≤ 0 exactly when it was a mistake. So ∥wk∥ ≤ √k·R.
  3. Cauchy–Schwarz: kγ ≤ w*·wk ≤ ∥wk∥ ≤ √k·R, which rearranges to k ≤ (R/γ)².

“No d, no n. Students should notice that and be bothered by it.”

Correct, complete, and delivered in one prompt — but too compact for any intuition.

You“Simplify the proof for me; make it common-sense for whiteboard argument.”

Agent“Whiteboard version: two gauges on the board, one counting real progress, one counting bloat. Progress climbs steadily. Bloat can't keep up. When they cross, you're out of mistakes.”

“Set the units first. Scale so the longest input has length 1. Then the margin γ is just how far the good separator keeps points off the fence, measured in the same units. The answer will be 1/γ² mistakes.”

Gauge 1: every mistake moves you toward the truth. Draw the good separator’s normal w* as a fixed arrow, true north. Track only the shadow of w on true north … Gauge 2: mistakes barely make you longer. Here’s the whole trick, and it’s Pythagoras. You only update when w got the sign wrong, which means w and the correction y x point at an obtuse angle. Draw it: the correction leans backward against w. … Collide the gauges. A shadow can never be longer than the arrow casting it.”

That vocabulary — gauges, true north, progress against bloat — is the agent’s own jargon. It thinks it is crystal clear, and it cannot tell that it is meaningless to a person.

You“Please draw the diagram here for me. Maybe with js/svg?”

Agent“Here’s the collision, with γ on a slider. Watch the crossing point run away as the margin shrinks.”

Everything in it is a function of k: no data, no w, no w*. Live: drag γ.

“The other picture worth putting on the board is why Gauge 2 holds: the correction always leans backward against w. … The angle exceeding 90° is exactly the mistake condition: w·x had the wrong sign, so the correction points against w.”

The triangle for Cauchy–Schwarz, and again no data in it. The drawn result is shorter than w, which the bound allows — it caps growth rather than promising any. Live: drag the angle below 90° and the bound breaks.

You“The plot is not informative, but I think that the triangles could be. But you’ve plotted w without plotting w* and you are not showing the mass of other points. I think to explain it would be helpful to show the points.”

Agent“Fair. Here it is with the data, the true separator and its margin corridor, and w’s boundary rotating as you step through mistakes.”

You“I like the margin figure, but the ringed point you’ve drawn is not on the wrong side. We need some examples that are on the wrong side!”

One click in. The ringed point, labelled mistake, scores +0.82 against the amber boundary drawn beside it — because the same click applied the update.

Agent“Caught it: I was ringing the point and applying the update in the same click, so you only ever saw the boundary that had already fixed it. Now it’s two clicks. Find the violation, look at it, then apply.”

Running that build also shows the second defect, the one that surfaces in the next exchange: after a single update 0 of its 14 points are misclassified, so a second click has nothing to find.

Even though they are great at code, agents make plenty of bugs.

You“I liked the previous visualization better. Except that find a mistake didn’t work! I want a 2d scatterplot of data points where the initial model misclassifies many of them.”

Agent“The dataset was the problem: after one update w already separated everything, so the second click had nothing to find. Here’s a version starting from a badly wrong w, with 12 of 16 points misclassified out of the gate. Every currently-wrong point carries a red halo, so you can watch the count fall (and sometimes rise).”

12 of 16 points haloed at the start, with the first violation ringed and the shaded half-plane showing what w currently calls +.

Agents pick easy examples. An easy example makes the demo run, which is not the same as making it show anything.

“The halo count is the number to watch, and it does not fall monotonically. Somewhere in the middle a correction fixes one point and breaks two. That’s worth pausing on: nothing about the algorithm promises the error count goes down, and any student who assumes it does will misread the proof.”

Running the whole sequence, the count goes 12, 12, 12, 6, 2, 0 and never rises once. True of the algorithm, not true of this figure.

You“It’s great, except that it doesn’t show how w is updated from the error case. Is it a parallelogram?”

Agent“Yes: it’s the parallelogram rule, and the second side is the data point itself. The correction y·x is the point’s own position vector, drawn from the origin (flipped through the origin when the label is negative). So the update is w and the point as two sides, w_new as the diagonal.”

One update in. w before is the faded amber side, y·x runs from the origin, and the bold diagonal is w + y·x, with the boundary perpendicular to it.

“The correction has no free parameters. It is the data point, full stop: no gradient, no step size to choose, no loss to differentiate. Rosenblatt’s rule predates all of that machinery, and students who have already seen gradient descent tend to assume the update must be a derivative of something. It isn’t.”

You“You haven’t highlighted the narrowing solution space, but I’d like that.”

Agent“The narrowing region is drawn as a violet wedge closing onto the green one. … The violet wedge only ever narrows, and it narrows exactly by the constraints the algorithm has bumped into. It starts unbounded, since a w that has hit nothing is consistent with nothing being ruled out. Each mistake slices it.”

You“The cone on the right that you said is used to prove convergence doesn’t really make sense to me — e.g. the w isn’t in that blueish cone — what’s it bounding? Are we drawing the wrong vector?”

Agent“You’re right, and the fault is mine: that violet wedge bounds the solutions, not w. w is never promised to be inside it. It’s bookkeeping about which constraints have been ruled out, and it has no role in the convergence proof. I put it next to the proof talk and made it look load-bearing.”

The violet wedge is labelled consistent so far; the green one solves all 16. The amber arrow is w, and it is outside both.

“The cone that actually bounds w is a different one: centered on w*, half-angle arccos(√k·γ/R), and it contains w after k mistakes. … The check to make with students: the amber arrow should always sit inside the blue cone. That’s the theorem as a visual assertion, and it’s falsifiable on screen.”

Objecting got me more than a correction. It got me the right object, and the finished figure is built on it.

The replacement, at k = 0 with w starting at zero: a cone on w* labelled w must be in here, nearly the whole plane at the start and tightening with every mistake.

You“On the current data shown, why is it that after the second iteration the w vector is not in the blue cone? Is the blue cone more aggressive than the real bound?”

Agent“Good catch, and the answer is yes: the cone I was drawing was more aggressive than the real bound for this run. Both gauges assume w starts at zero. … The honest bound is cosθk ≥ (w*·w₀ + kγ) / √(∥w₀∥² + kR²). Your default w₀ points away from w*, so w*·w₀ is negative and the numerator stays negative for the first couple of steps: the true cone is the entire plane there, constraining nothing. The zero-start formula was drawing a tight cone that w had no obligation to be in.”

You“Certainly when k=0 the blue cone should be the full 360, right?”

Agent“Right, and that’s the correct reading of the formula: with no mistakes yet the shadow bound hasn’t paid off anything, so the cone constrains nothing. The slider just didn’t go down to 0. … That opening frame is worth pausing on: before any mistake the theorem has nothing to say, and the entire proof is the story of that disc closing to a sliver.”

You“To explain the bounds cone, I think we should show a hypothetical worst-case rollout of the worst possible zig-zag of updates, starting from the worst case and updating up to step t, showing how it stays in the cone.”

Agent“Good instinct, and the worst case has a clean shape: every step must add exactly γ of shadow and exactly R² of length-squared, which pins the tip to horizontally and √k·R in radius. So the extremal walk marches right in equal γ steps while zigzagging across the axis, and its tip rides exactly on the cone edge at every k. … The cone is not a loose over-estimate. It is achieved.”

At k = 5 with γ = 0.30. Shadow 1.50 = kγ exactly, |w| 2.24 = √k·R exactly, so the tip sits on the cone edge at every step. The walk reaches the axis and dies at k = R²/γ² = 11.

“One caveat worth giving the class rather than hiding: staying perpendicular to w at every step needs a fresh orthogonal direction each time, so this trajectory requires dimension at least k. In two dimensions it dies after a couple of steps, which is why the runs in the earlier figure sit comfortably inside the cone rather than on its edge. The bound is tight in high dimensions and loose in low ones.”

You“How hard is it to construct data points and an unlucky ordering that actually exercises the worst case?”

Agent“Trivially easy in high dimensions, and apparently impossible in two. The lever is dimension, not ordering. … Take the standard basis in Rn: points xi = ei, every one labeled +1. … Exactly n mistakes against a bound of n. And each step saturates all three inequalities at once. … It’s the pink zigzag, realized.”

“So the honest reading of Novikoff: it’s tight, but only in the regime where dimension is at least as large as the mistake count. … What the worst case actually is in fixed small d, as a function of γ, I don’t know off the top of my head, and I’d want to check rather than guess.”

You“The blue cone is nice now, but the example you’ve shown has an initialization that is too favourable and doesn’t iterate enough. Give me a more prototypical example with more iterations.”

Agent“More prototypical: margin narrowed to about 0.19 of the data radius, with two large-norm points near the corridor edges that keep knocking w past each other. Bound is 28 mistakes, and the run takes a real while. … Each one is large, so correcting it swings w hard, past the other one, which then needs correcting back.”

γ/R = 0.187, wedge half-angle 10.7°, bound k ≤ 28. The corridor on the left is a sliver and the wedge on the right is a slit.

Part two — rebuilding what it meant

The second conversation moved the figure into a repository, where each change is one commit and each claim about it can be measured rather than argued.

What part one produced

A working demo of the perceptron mistake bound. It ran, it was correct, and it looked finished. Most of its elements changed meaning over the rounds that follow.

Data space on the left, weight space on the right, the bound drawn as a blue cone. Live: step it, or press New data.

You“When it finds a mistake, don’t take the first one it happens to scan. Sort the mistakes by which produces the worst updated w — the maximum angle with w* — and pick among the worst two.”

Agent“Found a real problem: each point appears twice in P (as +x and as −x with label −1), and both produce the identical update. So ‘the worst two’ were almost always the same update, making the random choice a no-op. Deduplicating by update vector:”

“166 picks across 12 datasets, split 98/68 between the worst and second-worst distinct update, never outside the top two. No run exceeded its bound.”

After the fix. The chosen mistake is ringed in red; the parallelogram shows where w goes next. Live: step it, or press New data.

You“Make the positive and negative cases unsymmetric when generating the points. And pick the initial w within 15° of −w*.”

Agent“One thing worth knowing, since it changed the first fix: the old generator built points in antipodal pairs, so x and −x with opposite labels both produced the same y·x. ‘The two worst’ were therefore usually one update wearing two hats, and the random choice between them did nothing. … Your asymmetry request removed the duplicates at the source.”

“Each point gets its own margin, offset, and label. … Initial angle to w* 166.4°–179.2° across 20 datasets, point-symmetric pairs 0, duplicate updates 0, label splits 17/15, 20/12, 14/18 … runs exceeding bound 0. … Runs got longer, as you’d expect from a hostile start plus adversarial picks: 12–22 mistakes where it used to be 7–17, against bounds in the 60–130 range.”

Independent classes, and w starting almost exactly backwards. Live: step it, or press New data.

You“Is worst angle to w* the right heuristic, or should we be sorting by worst dot product?”

Agent“Good question, and it’s measurable rather than a matter of taste. Let me settle it empirically — first the deterministic pick, then an A/B on the two orderings.” The two orderings picked different points on 56% of steps; angle forced a mean 19.3 mistakes against 17.1, winning 27 of 40 datasets.

You“You can reason about it from the structure of the proof.”

Agent“Angle is right, and the proof says why without needing the experiment. The bound is a statement about cosθk = (w*·wk)/|wk|, a ratio. … Sorting by dot product attacks only the first. Since w*·(w + y·x) = w*·w + m and w*·w is common to every candidate, that ordering collapses to argmin over margins — it picks the smallest-margin available mistake and is otherwise blind to the point. It saturates the shadow floor perfectly and lets the length ceiling go slack.”

“There’s a sign flip that makes this sharper. While the shadow is negative, minimizing S/|w| wants |w| small; once the shadow goes positive it wants |w| large. The angle criterion switches automatically at the crossover. The dot criterion never sees the denominator at all, so it can’t.”

“One honest caveat: greedy minimization of cosθ one step at a time isn’t provably the optimal adversary over a whole sequence. It’s the right local proxy, and it has the property that matters for the demo — the selection rule and the pink cone are the same quantity.”

You“The worst-case cone should have another colour so we can talk about it out loud in class unambiguously. Yellow or pink?”

Agent“Pink — and the reason is that yellow is already spoken for. Amber #BA7517 is w itself and its separating plane, the two things you point at most. A yellow cone would sit directly underneath the amber w arrow in the same panel, so ‘the yellow arrow inside the yellow cone’ is exactly the ambiguity you’re trying to kill.”

Recoloured. Blue now means the model’s half-plane and its positive points. Live: step it, or press New data.

You“Why does it say cone: every direction?”

Agent“The old label was written when pink meant only the cone, so it now contradicts the picture: it announces ‘every direction’ over a visibly bounded blob. Both facts are true — direction unconstrained, length constrained — but the region is the thing on screen.”

You“Would it be informative for the pink cone to also show the bounds for the length of w?”

Agent“Yes, and it would upgrade the picture from asserting the bound to showing why it holds. What the proof actually pins down after k mistakes isn’t a cone. It’s the intersection of a half-plane and a disc … That region is a circular cap, and the pink cone is exactly its angular hull — the narrowest wedge that contains it. So the length bounds aren’t an addition to the cone; the cone is a lossy projection of them.”

“The chord marches outward linearly in k while the arc grows as √k. That race is the entire proof, and right now it’s only in the caption’s algebra. … Why the cone is vacuous early: when Sk ≤ 0 the chord sits behind the origin, so the cap contains the origin, and a region containing the origin admits every direction. … Why it terminates: as Sk → Rk the chord closes on the arc and the cap shrinks to a single point.”

The cap: arc = length ceiling, chord = shadow floor, dashed rays = the cone it spans. Live: step it, or press New data.

You“When showing the worst-case path, maybe it will touch the corner of this arc in a way we can understand?”

Agent“That identity is exact, and it’s the best reason to draw the cap. The worst-case tip at step j is placed at radius Rj and angle ±arccos(Sj/Rj) from w*. Those are precisely the coordinates of the point where the chord meets the arc. So the pink path is the sequence of cap corners, alternating between the two, and the zigzag stops being a drawing affectation: there are two corners, and it bounces between them.”

“Checking that against the current code turned up a real defect, though.”

wcTip places the tip at coneAngle(j), which is clamped to π while the shadow is negative. A point at angle π and radius Rj has w*·w = −Rj, which is below the shadow floor … true at every step from the start. So today’s worst-case path begins outside the region the proof permits. Using the cap’s own half-angle puts the tip exactly on the corner at every step, and at j=0 that corner is w₀.” 615 tips then landed on the corner to 3×10−15.

The corrected worst case. Each faded dot is a corner where the chord meets the arc, and the run alternates between the two of them. Live: step it, or press New data.

You“Why wouldn’t the spray be such that all the blue rays go on one side and all the white rays on the other? I guess that won’t happen unless we already apply the separating plane rule to know which direction to draw the ray, right?”

Agent“Because the ray is y·x, not x — the label is already folded in. Multiplying by y is exactly the operation that maps both classes into one fan, so blue and grey interleave by construction. Draw the raw x instead and you get precisely the picture you describe: blues fanning one way, whites fanning the exact opposite way, two antipodal cones. And that needs no knowledge of the separating plane either — just the labels, which are given.”

“But your instinct is onto something real, one step over: the fact that the y·x fan fits inside a half-plane at all is exactly linear separability. A w* exists iff some direction has positive dot product with every y·x. Non-separable data would spread the fan past 180° and the green wedge would vanish.” Measured: 640 rays, max angle to w* 83.9°.

Rays along y·x. Colours interleave, because the label is already applied. Live: step it, or press New data.

You“Switch to raw x.”

Agent“One consequence to expect when you look: for a point the arrow and the dot now point opposite ways, so the thing being added to w is on the far side from the point it came from. That’s the sign flip made literal, and it’s the price of the separated colors — for a + they still coincide.”

Rays along raw x. Blue reaches 84.6° from w*, grey starts at 95.7°. Live: step it, or press New data.

You“Now that you’ve drawn it, I see. Mathematically the right thing in weight space is the dual hyperplanes — the line orthogonal to each point. Let’s go back to the dual lines, coloured by class.”

Agent“Right — the dual of a point is a hyperplane, not a direction, and that’s what makes the green wedge an intersection of half-planes again. … The wedge gets its construction back too: it’s the overlap of the half-planes these lines bound, and its two edges lie along two of the drawn lines — confirmed to 1.2×10−3 across 20 datasets, which is precisely the 0.0012 rad step of the wedge search, so exact up to that resolution.”

The duals. The y·x arrow is perpendicular to the chosen point’s line. Live: step it, or press New data.

Agent“Measured over 20 datasets, and it’s an exact identity: fan span + wedge width = 180° — coming out 179.87–179.98, the residual being the 0.0012 rad step of the wedge search.”

“The reason is direct: w must sit within 90° of every ray, so within 90° of both extremes, and the set within 90° of two directions that are S apart is an interval of width 180° − S. A tight fan leaves a wide wedge; a fan spreading toward 180° squeezes the wedge to nothing, which is separability failing.”

You“When we choose a mistake, should we put a green dot in the right place in weight space? It would show up on the corresponding spray line.”

Agent“A data point doesn’t map to a point in weight space — it maps to the line. … But there is a canonical dot that does land on the line, and it’s the one worth having: where the update crosses it.” Built, then measured: “Both measurements were right — the behaviour is step-dependent. Median t is 3.1 at step 0, 2.6 at step 1, 1.4 at step 2, then 0.3–0.45 from step 3 on.”

You“Maybe you are imagining a different dot than I am?”

Agent“That’s a different dot, and your instinct points at a better picture than the one that’s there.” Three candidates existed — the tip of y·x, the tip of w+y·x, and the crossing on the line — and the request separated none of them. Listing all three led to a fourth answer, a few rounds later: “The green dot is gone, since with the dual drawn a point has no position in weight space to mark.”

You“Maybe label it pink: Cauchy–Schwarz limit, or pink: bounded region for worst-case w?”

Agent“Neither quite, and the distinction is worth being fussy about in a proof demo. ‘Cauchy–Schwarz limit’ misnames it: the pink region is the raw conjunction of the two inductive bounds. Cauchy–Schwarz is the next step — what you apply to that region to extract the angle, producing the cone. Labeling the cap with it would attach the name to the input rather than the output.”

“‘Bounded region for worst-case w’ overstates in the other direction: the cap holds every w the proof still permits, not the worst one. The worst case is specifically its corner — which is the point you’ll be making when the zigzag touches there. … The accurate name is the one already in your legend: where w must lie after k mistakes. Necessary, not sufficient.”

The label in place, on the version the class sees. Live: step it, or press New data.

Try it yourself

The visualization we ended up with is here. You can play with it to learn a bit about the algorithm and its proof.

But my recommendation is to try having this conversation yourself, and to build your own visualization. You will find that you learn a great deal from making your own.