Module 7: The Data and Feedback Loop
4. The feedback loop as a design decision
Overview
By the end of this lesson you'll understand that feedback isn't a single thing, but three distinct signals, and that capturing them is an architecture decision you make before needing them —because you can't recover the feedback you didn't capture—. In lesson 3 you set up the observability and saw that you need a "quality signal"; this lesson opens that signal and discovers that inside there are three very different sources, each measuring something the others don't see: the explicit thumbs up/down the user gives on purpose, the correction they make when they edit the model's proposal, and the action they take after the suggestion —whether they sent it as is, rewrote it, or escalated to another person—. All three are feedback, but they don't coincide with each other, and the most valuable one is usually the least instrumented.
This matters because the point where you capture the feedback is a design decision with irreversible consequences. If you didn't put the thumbs button, you have no thumbs. If you didn't record the text the human agent ended up sending, you can't know what they corrected. If you didn't save which result the user clicked, the action signal is lost forever. Unlike a bug, which you can fix when you discover it, uncaptured feedback isn't recovered: the user is already gone, the interaction already happened, and no future change gives you back the datum you didn't save. That's why feedback capture isn't a feature you add "when there's time"; it's a decision the architecture has to make from the design, just as you decide from the design what fields your database saves.
Connection with the module: this lesson assembles the loop's raw material. Lesson 2 gave you the motivation (the flywheel), lesson 3 the instrument to see the quality (the observability); here you build the mechanism that produces that quality signal —the feedback_loop that captures what users say and do—. It's the piece that feeds the next one: lesson 5 takes this captured feedback and closes it by turning it into eval-set cases, and lesson 7 routes it to the correct lever. Without a well-designed capture, there's nothing to close nor to route. And a connection with module 6 (the deterministic shell): the point where the human reviews and corrects the model's proposal before sending it is, at once, M6's containment layer and this module's feedback capture point —the same human review that stops the model from sending a bad response generates the correction that improves the model—.
Analogy: the waiter who observes three different things
Go back to lesson 1's restaurant, but look more closely at how the good waiter reads their diners. They don't settle for a single signal; they observe three, and they know they say different things.
The first is what the diner says explicitly: if at the end they ask "everything good?" and the diner answers "excellent" or "the dish was salty". It's direct and clear, but it has a problem: many people say "everything's fine" out of politeness even if they weren't satisfied. It's the thumbs up/down: the most explicit signal, but also the most contaminated by politeness and the bias of whoever bothers to give it.
The second is what the diner corrects: asks for the meat more cooked, sets aside the onion, adds salt. They didn't say "the dish is wrong", but their correction reveals exactly what it was missing. It's the correction: more specific than the thumbs, because it doesn't just say "I didn't like it" but "this is how it should have been". When a diner asks for something different from what they were served, they're handing you the correct answer on a plate.
The third —and the most honest— is what the diner does: whether they ate the whole dish or left it half-finished, whether they came back the following week or never returned, whether they recommended the place or didn't mention it. The action doesn't depend on the diner bothering to opine nor on their politeness: it's what really happened. A diner can say "everything's fine" (thumbs up) and leave half the dish (negative action); the action is the one that doesn't lie. The best waiter weighs all three, but when the thumbs and the action contradict each other, they believe the action.
Your AI component has the same three signals. The thumbs up/down is what the user says explicitly. The correction is when the user (or a human agent) edits the model's proposal —they hand you the correct answer—. And the action is what the user does: sends the response as is, rewrites it, clicks result 5 instead of 1, escalates to a human. Designing the feedback loop is deciding which of these three you capture and where you capture them —and this class's lesson is that all three matter, that they don't coincide, and that the action, the hardest to instrument, is the most honest—.
Worked example: the three signals that don't coincide
We're not going to say the three signals differ: we're going to see it. We model the feedback_loop of Mercado's support agent, where a human agent reviews each model proposal before sending it to the customer. For each interaction we capture the three signals —the customer's thumbs, whether the human corrected the text, and what action the human took— and derive the metrics of each one. Notice how the three numbers come out different, and what that difference reveals.
# Lesson 04 (M7) — the FEEDBACK LOOP as a design decision. SIMULATED.
# Zero network, zero API, zero keys. Deterministic.
#
# Feedback isn't one thing: it's THREE distinct signals, and each has to be
# DESIGNED at the capture point (you can't recover what you didn't capture):
# 1) EXPLICIT : the user gives thumbs up/down.
# 2) CORRECTION : the human edits the model's proposal before using it.
# 3) ACTION : what the human did after the suggestion (sent it as is,
# rewrote it, or escalated). The most honest feedback: it doesn't opine, it acts.
# The feedback_loop: one row per support-agent interaction. The human
# (a Mercado agent) reviews the model's proposal before sending it to the customer.
# Fields: input, model proposal, customer thumbs, final text sent,
# and the human's action (sent_as_is / edited / rewrote / escalated).
FEEDBACK_LOG = [
# (id, input, proposal, customer_thumbs, final_sent, human_action)
("i1", "where is my order", "You can see the tracking in your profile.",
"up", "You can see the tracking in your profile.", "sent_as_is"),
("i2", "how long does shipping take", "Shipping takes 3 to 5 business days.",
"up", "Shipping takes 3 to 5 business days.", "sent_as_is"),
("i3", "the product arrived broken", "Sorry, I don't have information about that.",
"down", "We're sorry about that. You can request a refund from the order.", "rewrote"),
("i4", "my coupon doesn't work", "Coupons always work.",
"down", "Check the coupon's expiration; it may have already expired.", "rewrote"),
("i5", "can I pay in installments", "Yes, you can pay in installments.",
"up", "Yes, you can pay in installments with no interest by card.", "edited"),
("i6", "I didn't receive my invoice", "We don't handle invoices.",
"down", None, "escalated"),
("i7", "how do I return a product", "Go to your order and press Return.",
"up", "Go to your order and press Return.", "sent_as_is"),
("i8", "I want to cancel", "You can cancel if it hasn't shipped yet.",
"up", "You can cancel the order if it hasn't shipped yet.", "edited"),
]
N = len(FEEDBACK_LOG)
# --- Signal 1: EXPLICIT (approval_rate from the customer's thumbs). ---
ups = sum(1 for r in FEEDBACK_LOG if r[3] == "up")
approval_rate = ups / N
# --- Signal 2: CORRECTION (what fraction the human had to touch the text). ---
corrected = sum(1 for r in FEEDBACK_LOG if r[5] in ("edited", "rewrote"))
correction_rate = corrected / N
# --- Signal 3: ACTION (what the human did: the signal that doesn't lie). ---
from collections import Counter
actions = Counter(r[5] for r in FEEDBACK_LOG)
accepted = actions["sent_as_is"]
acceptance_rate = accepted / N
print("=== The feedback_loop: 3 signals captured per interaction ===")
print(f"{'id':<4}{'thumbs':<8}{'human_action':<14}input")
for r in FEEDBACK_LOG:
print(f"{r[0]:<4}{r[3]:<8}{r[5]:<14}{r[1]}")
print()
print("=== The 3 derived signals (each measures something different) ===")
print(f" 1) EXPLICIT approval_rate : {ups}/{N} = {approval_rate:.0%} (customer's thumbs up)")
print(f" 2) CORRECTION correction_rate : {corrected}/{N} = {correction_rate:.0%} "
f"(the human edited or rewrote)")
print(f" 3) ACTION acceptance_rate : {accepted}/{N} = {acceptance_rate:.0%} "
f"(sent the proposal as is)")
print()
print(" breakdown of the human's actions:")
for act, cnt in actions.most_common():
print(f" {act:<12}: {cnt}")
print()
# The signals do NOT coincide, and that's where the value is: a thumbs_up with an edit
# hides a correction (i5, i8). The action reveals what the thumbs doesn't say.
edited_but_up = [r[0] for r in FEEDBACK_LOG if r[3] == "up" and r[5] in ("edited", "rewrote")]
print(f" cases with thumbs_up BUT that the human had to correct: {edited_but_up}")
print(" -> the approval_rate alone would look 'fine', but the action exposes hidden human work.")
What to expect. When you run it, the output is exactly this:
=== The feedback_loop: 3 signals captured per interaction ===
id thumbs human_action input
i1 up sent_as_is where is my order
i2 up sent_as_is how long does shipping take
i3 down rewrote the product arrived broken
i4 down rewrote my coupon doesn't work
i5 up edited can I pay in installments
i6 down escalated I didn't receive my invoice
i7 up sent_as_is how do I return a product
i8 up edited I want to cancel
=== The 3 derived signals (each measures something different) ===
1) EXPLICIT approval_rate : 5/8 = 62% (customer's thumbs up)
2) CORRECTION correction_rate : 4/8 = 50% (the human edited or rewrote)
3) ACTION acceptance_rate : 3/8 = 38% (sent the proposal as is)
breakdown of the human's actions:
sent_as_is : 3
rewrote : 2
edited : 2
escalated : 1
cases with thumbs_up BUT that the human had to correct: ['i5', 'i8']
-> the approval_rate alone would look 'fine', but the action exposes hidden human work.
Read the three numbers —62%, 50%, 38%— because their disagreement is the heart of the lesson.
The three signals give three different numbers, and all are true. The approval_rate (explicit signal) is 62%: five of eight customers marked thumbs_up. The correction_rate (correction signal) is 50%: in four of eight, the human agent had to edit or rewrite the model's proposal. The acceptance_rate (action signal) is 38%: only in three of eight did the human send the proposal as is, without touching it. There's no contradiction among them; each measures something different. The approval_rate measures the final customer's satisfaction; the correction_rate measures how much work the human had to do on top of the model; the acceptance_rate measures what fraction of the proposals were directly useful. Three different questions, three different answers.
The action is the most honest signal, and it's the hardest. Notice the order: 62% (thumbs) → 50% (correction) → 38% (action). The action signal gives the lowest number, and that's no coincidence: it's the most demanding because it doesn't depend on opinions or politeness, but on what really happened. The customer's thumbs can be generous ("it responded, I'll give it up") even if the human agent had to rewrite the entire response before sending it. The human's action —did they send it as is?— doesn't have that generosity: if they edited it, they edited it, and that's recorded. That's why, when you want to know how useful your component really is, the action gives you the most sincere answer.
The revealing case: thumbs_up that hid a correction. Look at the last two lines of the output. Cases i5 and i8 have thumbs_up from the customer —the customer was happy— but the human agent had to edit the proposal before sending it ("can I pay in installments" → the model said half, the human completed "with no interest by card"). If you only looked at the approval_rate, these cases would count as clean successes. But the action signal exposes them: there was hidden human work that the thumbs doesn't capture. This has an enormous practical consequence —if you're measuring the savings the support agent gives you, the approval_rate would make you believe the model resolved i5 and i8 on its own, when in reality a human had to intervene—. The action signal is the one that tells you how much human work you really saved, and it's exactly the one most teams don't instrument.
The lesson in one sentence: capture the three signals, because each answers a different question, and when they contradict each other, believe the action.
The three signals, and why the capture point is an architecture decision
The example showed the three signals; it's worth understanding what makes each one useful and why capturing them is a decision made in the design, not after.
The explicit signal (thumbs up/down): cheap to ask, expensive to get. It's the easiest to instrument —a button— and the most direct —the user tells you yes or no—. Its weakness is twofold: first, the bias of whoever responds —the people who bother to mark are usually the very happy or the very angry, so the approval_rate doesn't represent the silent majority—; second, politeness and fatigue —many people mark up out of inertia, or mark nothing—. It's a valuable signal but you have to read it knowing where it comes from. Architecture decision: where you put the button, whether it's mandatory or optional, and how you keep the bias from dominating.
The correction signal: the correct answer, served. When the user or a human agent edits the model's proposal, they don't just tell you it was wrong —they give you the correct version—. This is the richest signal for closing the loop, because the correction becomes almost directly an eval case (input → the correction is the criterion; lesson 5) or a prompt example. Its capture requirement is demanding: you have to record both the model's proposal and the final text that was used, to be able to compare and detect that there was a correction. If you only save the final text, you don't know whether the model generated it or the human rewrote it. Architecture decision: save the original proposal alongside the final result —many systems save only the final one and lose the correction signal forever—.
The action signal: what really happened, free but hard to instrument. The action —sent as is, edited, rewrote, escalated, clicked here, reformulated the search— is the most honest signal because it doesn't require the user to opine: it arises from their natural behavior. And often it's free in the sense that the user already produces it (they already click, already edit, already escalate) with no extra work. Its difficulty is instrumentation: you have to design the system to capture the action, and that action sometimes happens at a point in the flow you weren't recording (do you save which result was clicked?, do you record that the human escalated?). Architecture decision: identify the actions that are a signal and make sure you capture them at the point where they happen.
And the principle that unites them, which is the lesson's thesis: you can't recover the feedback you didn't capture. This makes the capture different from almost any other software decision. An algorithm you can change tomorrow; a database schema you can migrate; but the thumbs the user would have given yesterday, if you didn't put the button, doesn't exist and won't exist. That's why the design of the feedback loop is an early and deliberate decision: when you design the AI feature, you decide which signals you'll capture and where, knowing that the ones you don't capture are lost. It's the same kind of decision as "what fields does this table save?": what you don't save today, you won't have tomorrow.
The capture point, in the support agent's flow:
customer ──question──► [ model ] ──proposal──► [ human reviews ] ──response──► customer
│ │ │
(saves tokens, (saves the action: (saves the customer's
cost, latency) sent_as_is/edited/ thumbs up/down)
│ rewrote/escalated, │
│ AND the final text) │
▼ ▼ ▼
┌──────────────────────── feedback_loop ───────────────────────────┐
│ each signal is captured AT ITS POINT. What isn't captured there, │
│ is lost: there's no way to recover it afterward. │
└───────────────────────────────────────────────────────────────────┘
Common mistakes
Capturing only the thumbs and believing it's "the feedback" (of scope). What happens: the team puts a thumbs up/down button, measures the approval_rate, and takes for granted that it already has the feedback loop. It misses the other two —richer— signals: it doesn't know how much the humans correct (because it doesn't save the original proposal) nor what actions they take (because it doesn't instrument them). Its approval_rate, moreover, is biased by who bothers to mark. Result: a partial and optimistic snapshot of the quality, as in the example, where the thumbs said 62% but the action said 38%. Why it happens: the thumbs is the easiest to add and the most obvious, so it's taken as "the feedback" plain and simple. How to detect it: if your only quality signal is the thumbs, you don't see the hidden human work nor do you have the corrections to close the loop. How to fix it: capture the three signals —thumbs, correction, and action—; each answers a question the others don't.
Saving only the final result and losing the correction (of irreversible omission). What happens: the system records the response sent to the customer, but not the model's original proposal. When a human agent rewrites the response, the system saves the rewritten version as if the model had generated it. Two things are lost: the signal that there was a correction (you can't compute the correction_rate) and —worse— the correction itself, which was the correct answer ready to become an eval case. Why it happens: saving only the final result seems enough ("it's what the customer saw") and saves storage. How to detect it: if you can't compare what the model proposed with what was really used, you can't see the corrections. How to fix it: save the model's proposal alongside the final result; the difference between the two is the correction, and the correction is gold for closing the loop (lesson 5). And remember: this is irreversible —the corrections you didn't save this month aren't recovered—.
Instrumenting the capture "later", when it's already needed (of process). What happens: the team launches the feature without feedback capture ("first let's make it work, we'll add the feedback later"), and when months later it wants to improve the feature with usage data, it discovers it has none —all that usage passed without leaving a trace—. It has to start capturing from scratch and wait months more to accumulate data, having wasted all the previous traffic. Why it happens: the capture isn't visible to the user (it's not a sellable feature) and competes for priority with things that are seen. How to detect it: if your AI feature has been in production for a while and you have no feedback history, you already lost that data. How to fix it: design the capture from the start, as part of the feature's architecture —it's as fundamental as deciding what your database saves, because uncaptured feedback isn't recovered—.
Exercises
Exercise 1 — The waiter and the three signals. For each of the three things the good waiter observes, say which AI feedback signal it represents, what makes it valuable, and what its weakness is. Then explain why, when the thumbs and the action contradict each other, you have to believe the action.
See solution
- What the diner says ("everything's fine" / "it was salty") → the explicit signal (thumbs up/down). Valuable because it's direct and clear. Weakness: contaminated by politeness (many people say "everything's fine" out of courtesy) and by the bias of who bothers to respond (the very happy or the very angry).
- What the diner corrects (asks for the meat more cooked, sets aside the onion) → the correction signal. Valuable because it doesn't just say "it was wrong" but "this is how it should have been" —it hands you the correct answer—. Weakness: it requires capturing both what was served (the proposal) and what was asked to be corrected (the result), and it only appears when the user takes the trouble to correct.
- What the diner does (ate everything or left half the dish, came back or didn't) → the action signal. Valuable because it's what really happened, without depending on opinions or politeness —the most honest—. Weakness: the hardest to instrument, because you have to design the system to capture the action at the point where it happens.
When the thumbs and the action contradict each other, you have to believe the action because the thumbs is what the user says and the action is what they do, and words can be given out of politeness or inertia while the action reflects reality. In the example, i5 and i8 had thumbs_up but the human had to edit the proposal: the thumbs said "fine", the action said "I had to correct it". The action revealed the human work the thumbs hid. A diner who says "excellent" and leaves half the dish isn't lying out of malice; their politeness and their behavior simply don't coincide, and the behavior is what counts.
Exercise 2 — The savings that weren't. Mercado's team reports: "the support agent has an approval_rate of 62%, so it resolves most tickets on its own and saves us a lot of human work". With the example's data (approval_rate 62%, correction_rate 50%, acceptance_rate 38%), explain why that conclusion overestimates the savings, and what number they should use to measure the human work actually saved.
See solution
The conclusion overestimates the savings because it uses the approval_rate (62%) to measure something the approval_rate doesn't measure. The approval_rate says what fraction of customers were happy, not what fraction of responses the model resolved without human help. And those two things differ a lot: in the example, cases like i5 and i8 have a positive approval_rate (happy customer) but required a human agent to edit the proposal. The customer was happy because the human intervened, not because the model resolved it on its own.
The number that measures the human work actually saved is the acceptance_rate: 38% —the fraction of proposals the human sent as is, without touching—. Only in those cases did the model do the complete work and the human didn't have to intervene. In the other 62% (edited, rewrote, or escalated), there was human work: the model gave a draft that helped, but didn't save the human completely. So the real savings is closer to 38% than to 62%.
The architecture moral: to measure the savings of an assistant a human supervises, the correct signal is the action one (how much did they send without touching?), not the explicit one (how much did the final customer like it?). Using the approval_rate to estimate savings is the example's error, and it's only detected if you captured the action signal. A team that only measures thumbs can't correct this error because it doesn't have the datum.
Exercise 3 — Design the capture for the semantic search. Mercado's semantic search has no human agent to review, nor an obvious thumbs button. Design its feedback loop: (a) what action signal can you capture from the user's natural behavior?, (b) what would you have to save at the capture point to not lose that signal?, and (c) why is the action signal especially suitable for this feature?
See solution
- (a) The search's action signal is the click: at which ranking position the user clicked (and related signals: whether they reformulated the query without clicking, whether they didn't click anything, whether they searched the same thing again). A click in the top-3 = the search put the relevant thing up top; a click at position 5, or a reformulation without a click, = the search buried the relevant thing. The user's behavior IS the signal; there's no need to ask them to opine.
- (b) At the capture point you have to save, for each search: the query, the complete ranking that was shown, and at which position (and over which product) the user clicked —or that they didn't click—. If you only save the query and the result, you lose the click position, which is the signal. You need the triple (query, ranking shown, click) to know whether the ranking got it right. And like all capture, it's irreversible: the clicks of the searches you didn't instrument are lost.
- (c) The action signal is especially suitable here because the search has no human review nor a natural moment to ask for a thumbs —no one is going to mark up/down on every search—. The click, on the other hand, happens anyway: the user already clicks as part of using the search, so the signal is free (it doesn't require extra work from the user) and abundant (every search produces it). It's high-volume implicit feedback, ideal for a high-traffic feature. This is exactly the signal the lesson 8 project uses.
Summary and next step
In this lesson you built the loop's raw material: the feedback_loop that captures the quality signal. You saw that feedback isn't a single thing but three distinct signals —the explicit one (thumbs up/down), the correction (the human edits the proposal), and the action (what the human does: sends it as is, rewrites it, escalates)— with the analogy of the waiter who observes all three. And you measured them: 62% approval, 50% correction, 38% acceptance —three different numbers, all true, each answering a different question—, with the revealing detail that the action is the most honest (it exposed cases with thumbs_up that actually required human correction). You understood the architecture thesis: the capture point is an early and irreversible design decision, because you can't recover the feedback you didn't capture —saving only the final result loses the correction forever—.
Before moving on you should be able to: name the three feedback signals and what each measures; explain why the action is the most honest and when to believe it over the thumbs; recognize that capturing only the thumbs leaves most of the loop blind; and justify why the capture point is designed from the start (uncaptured feedback isn't recovered).
What follows is the module's heart: closing the loop. You already know why the flywheel matters (lesson 2), how to see the quality (lesson 3), and how to capture the feedback (this lesson). In lesson 5 you'll make that feedback return to something that improves the system: the thumbs_down cases and their correction will become new cases of the eval-set —module 3's—. You'll execute the conversion and see how the augmented eval-set catches a regression the old one let pass. It's the step from "I captured the feedback" to "the captured feedback made the system better, and I can prove it".
Resources
- Chip Huyen — Designing Machine Learning Systems (O'Reilly) — the chapter on feedback loops distinguishes explicit feedback from implicit (the action signal) and treats the capture-point design as an architecture decision; the conceptual source of this lesson.
- Chip Huyen — AI Engineering (O'Reilly) — the treatment of how user feedback (thumbs, corrections, behavior) feeds the improvement of an AI application, and the biases of each type of signal.
- martinfowler.com — "Emerging Patterns in Building GenAI Applications", Bharani Subramaniam and Martin Fowler — places the feedback capture in the data-loop pattern of an LLM app; the architectural frame. In English.
- Anthropic — Claude documentation — the conceptual reference on how to instrument an application with an LLM (record inputs, outputs, and usage signals) so you can evaluate and improve; without pinning a model version. In English.