LESSON 73 · ADVANCED4 min read

Project: A human-in-the-loop reply assistant

Parse a chat transcript, redact sensitive data, draft replies through a replaceable model adapter, and require human approval before anything is sent.

Python 3.12+Free to readModule 16 of 19
YOUR NEXT SMALL WINS

By the end, you’ll be able to…

  • Define and parse a small transcript format.
  • Redact personal and secret data before sending text to a model.
  • Hide the model behind an interface with timeouts and a fake test double.
  • Keep a human approval step and an audit trail.

01Define the transcript contract

A useful assistant starts with a predictable transcript format. For example, each line contains a time, a speaker, and a message: [09:14] Ada: Can we move the review? Parse the line into a small record rather than passing raw text around. Reject malformed lines with a clear message so the user can fix the export.

Keep the parser independent of any model. The parser decides what a message is; the model only drafts a response. That separation lets you test the parser with fixtures and swap providers without changing the input rules.

parse_transcript.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

02Redact before you send

A chat transcript can contain email addresses, phone numbers, tokens, and names that should not leave the user's machine. Redaction is a policy, not a magic filter: decide which patterns to replace and test them. Replace with stable placeholders such as [EMAIL] so the model can still understand the sentence.

Do not rely on the model to ignore secrets. Remove them before the request is built, and keep the original transcript local unless the user explicitly approves sending it. Log the redaction count, not the removed values.

redact.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

03Put the model behind an interface

Define a ReplyGenerator protocol with one method: generate(transcript, instruction) returns text. A real adapter can call an HTTP API; a fake adapter can return a deterministic draft. The assistant should not know which one it is using.

Set a timeout, handle rate limits, and avoid retrying a request that may already have succeeded unless the operation is idempotent. Treat model output as untrusted text: never execute it, never treat it as a shell command, and never send it without review.

generator_adapter.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

04Keep a human in the loop

A draft is not a sent message. Show the generated text, let the user edit or reject it, and require an explicit approval before sending. Record who approved it, when, and which draft version was sent. This audit trail is more valuable than an automatic reply that cannot be explained later.

The safest default is no automatic sending at all. If you add an auto-send mode, scope it to a test channel, require an allowlist of recipients, and cap the number of messages. A reply assistant should reduce effort, not remove responsibility.

approval_flow.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
LET IT CLICK

The ideas worth keeping.

  • Parse and validate the transcript before any model call.
  • Redact personal data before it leaves the local boundary.
  • Draft, review, approve, and log; never auto-send by default.
TURN UNDERSTANDING INTO SOMETHING YOU CAN DO

Make it yours.

Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.

01

Parse a transcript

Write parse_transcript(text) that returns a list of Message records. Raise ValueError with the line number for malformed input. Demonstrate it on two valid lines.

your_turn_1.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.pyPython
from dataclasses import dataclass@dataclass(frozen=True)class Message:    time: str    speaker: str    text: strdef parse_transcript(text):    messages = []    for number, line in enumerate(text.splitlines(), start=1):        if not line.strip():            continue        try:            stamp, rest = line.split("] ", 1)            speaker, message = rest.split(": ", 1)        except ValueError as exc:            raise ValueError(f"malformed line {number}") from exc        messages.append(Message(stamp.removeprefix("["), speaker, message))    return messagestranscript = "[09:14] Ada: Review?\n[09:15] Sam: After lunch."print(parse_transcript(transcript))

Skipping blank lines makes exported chat files easier to handle. The line number in the error points to the original input, which is more useful than an error from the middle of a split.

02

Redact secrets before drafting

Write redact(text) that replaces email addresses and long digit sequences with [EMAIL] and [NUMBER]. Test it on a sentence containing both.

your_turn_2.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.pyPython
import reEMAIL = re.compile(r"\b[\w.+-]+@[\w-]+\.[\w.]+\b")NUMBER = re.compile(r"\b\d{6,}\b")def redact(text):    return NUMBER.sub("[NUMBER]", EMAIL.sub("[EMAIL]", text))print(redact("Write to ada@example.com or use token 123456789"))

The output is 'Write to [EMAIL] or use token [NUMBER]'. Replacing the email first prevents the number pattern from touching digits inside the address.

A SMALL QUESTION. A USEFUL NEXT STEP.

What stayed with you?

Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.

A LITTLE ACTIVE RECALL0 / 3 answered
01Why parse the transcript before calling a model?
02What should be sent to the model?
03What is the safest default for sending replies?
⚡CODING CHALLENGE
🔒Required to Mark Lesson Complete

Parse a transcript

Write parse_transcript(text) that returns a list of Message records. Raise ValueError with the line number for malformed input. Demonstrate it on two valid lines.

EXPECTED OUTPUT
parse_transcript(transcript)
main.pyPython 3
Ctrl + ↵
Execution Console
Click "Run & Verify" (or press Ctrl + Enter) to test your code against the challenge.

Keep exploring.

The official references behind this lesson, when you want a second angle or the full details.

One lesson closer.

Reading is a start. Trying it yourself makes the connection.

Solve the hands-on coding challenge in the knowledge section to verify your code and unlock lesson completion.