You have two working local agents: the order desk from Week 5 and the course-question agent from Week 7. Both live in a terminal. Today they become chat apps you can open in a browser and on your phone, a supervisor puts them behind one window, and you find out what sharing a link does to your trust boundary. Nothing about the agents changes. Only the harness does.
Gradio is the Python library that turns a function into a web page. It is one install, but a big one, and the classroom network will not get thirty laptops through it at once.
Check it worked (prints a version number, nothing else):
Also bring: your laptop with Ollama and your model, your week5_ollama_starter folder, and the Week 7 index (course_index.json, if you never built it, run 7_rag_local.py index once at home, 20 seconds). Then download the add-on below and unzip it into that same folder.
8_app_rag.py, 8_app_orders.py, 8_app_router.py. They import your Week 5 and Week 7 scripts rather than copying them, so they must sit next to 5_demo_order_agent.py and 7_rag_local.py.Open that address in your browser. You get a chat window titled Ask CIS 4394. Ask it the Quiz 3 question. The answer arrives with the retrieved passages underneath: the same three chunks you read in the terminal two weeks ago.
Now open 8_app_rag.py in an editor and count what is new. The agent (retrieval, prompt, model call) is imported from 7_rag_local.py. The entire "app" is one function, answer(message, history), handed to gr.ChatInterface. input() became a text box; print() became a chat bubble. That is all an app is: a different harness around the same loop. Leave it running and open a second terminal for the next step (or press Ctrl+C to stop it first).
input()Type Refund order ORD-004412, it arrived damaged. In Week 5 the terminal froze on Approve? [y/N] and waited for you. A web server cannot freeze. It must answer the request and let go. So the human gate cannot be a blocking input() any more. It becomes state: the app remembers the pending refund and treats your next message as the decision.
issue_refundLook at the amount before you answer. The order is $19.99; on the instructor's laptop the model proposed $50, and on other runs $5.50, $100 and $200. The gate is doing exactly the job Week 5 gave it, and it now lives in the UI, which is where it lives in every real product.
if name in NEEDS_APPROVAL line plus input(). In the app it is the same if, plus a pending field in session state and a check at the top of every turn. A human gate is a state machine, and the UI is just how the state gets its input. Open 8_app_orders.py and find state["pending"]. That is the whole mechanism.One chat window, Front desk. Ask When is Quiz 3? and the first line of the reply says supervisor → course agent. Ask Where is my order ORD-004411? and it says supervisor → orders agent. Two specialists, each with its own tools and its own context, and a supervisor that decides who gets the message: the pattern from page 01, running on your laptop.
Ask the front desk to refund an order and tell you about Quiz 3 in one message. Whichever agent gets it cannot do the other half. The course agent has no refund tool to be talked into using, and the order agent has no index to read. That is the real reason production systems split into agents: each one can only reach what it needs. Least privilege (Week 5), enforced by construction.
Open 8_app_router.py and read route(): an order-number pattern and a few keywords. No model. That makes the supervisor deterministic and testable. The day you replace it with a model that "understands intent," the first thing you owe is its pass^k, because a router that is wrong 1 time in 10 sends 100 customers a day to the wrong desk.
Both start from an app you already ran. Method A reaches phones on your own Wi-Fi only. Method B gives you a public link that any phone on any network can open. Each one adds a question about who can now reach your refund tool.
The printed address is the one to give anyone on your network. Ignore the 0.0.0.0 line that follows; it means “listening on every interface”, not an address to open.
http:// and the :7862; there is no https here.Older copies of the scripts do not print the address. Find it yourself: Mac ipconfig getifaddr en0; Windows ipconfig, then the IPv4 Address line. The phone opens http://<that address>:7862.
Built your agent in Fleet instead of Python? Fleet has no public-link option, but the same SHARE=1 trick works through a small bridge page: see the Fleet box in the Week 10 summary.
Run 8_app_orders.py with SHARE=1 and read the link out loud. Three things are now true that were not true a minute ago:
issue_refund.1 · List everything that changed between 7_rag_local.py and 8_app_rag.py, and everything that did not.
print). The app is a harness: the same word we used in Week 5 for the code around the model that validates and executes. Every product you have ever used that "has AI in it" is one of these: a loop you already know, wearing an interface.2 · Why could the human gate not stay as input() in the web version? What replaced it, and what new failure did that create?
pending action stored per session, resolved by the next message. The new failure: the next message is whatever the user types. In the router demo, when no refund was pending, "yes" was just a word, and it got routed to the wrong agent. State-based gates need to know what they are waiting for and to reject anything that is not an answer: exactly the schema-validation habit from Week 5, applied to the human's input this time.3 · Page 02 says most multi-agent systems fail on coordination, not capability. Point to the line in 8_app_router.py where a coordination failure would originate, and say why a bigger model would not fix it.
route(), and specifically the fact that the order agent's pending state is only consulted there if state["pending"] was set. If the specialist fails to set it (the 0.5B model answered in prose instead of calling the tool), the supervisor has no way to know a decision is outstanding, and the handoff breaks on the next turn. A bigger model would call the tool more reliably, which hides the bug more often; it does not remove it. The fix is structural: the specialist must return an explicit "I am waiting for a decision" signal that the supervisor checks, independent of whether a model happened to behave. Coordination must be designed; it cannot be prompted.Wrap your own GA2 agent the same way: copy 8_app_orders.py, replace the import with your agent's module, keep the pending gate. Get it open on one teammate's phone (LAN at home; in class, the share link). Then, with the app running, answer in writing:
Produce: a screenshot of the app on a phone, and the three answers. Stop the script when you are done.
issue_refund and send_email should not be reachable from a public link; we would put them behind a login and keep the gate, or remove them from the shared build entirely." For question 3 they either name a specific second agent with a specific reason ("a separate returns-policy agent, because it needs the policy document in context and the order agent should not see customer PII") or, just as strong, they say the honest thing: "we do not have a reason yet, so we stay with one agent and good tools," which is exactly the Milestone I rule.A 2–3 minute recording of your agent running inside a chat window, with one visible human gate, is a better Milestone I demo than any terminal output, and it is forty lines on top of code you already have. Keep the three habits from this page: the gate is state, the router is a rule you can test, and a shared link is a security decision, not a convenience. If your capstone later needs a second agent, you now know what the justification must look like.
| You see | What it means | Fix |
|---|---|---|
No module named gradio | The install did not happen, or happened for a different Python. | Run the install line from the top of this page with the same python3 / python you use to run the scripts. |
No module named '7_rag_local' or '5_demo_order_agent' | The add-on was unzipped somewhere else. | Move the three 8_app_*.py files into week5_ollama_starter, next to the Week 5 and 7 scripts. |
course_index.json not found | You never built the Week 7 index. | python3 7_rag_local.py index (needs nomic-embed-text, 20 seconds). |
address already in use | Another app is on that port, probably your own, still running. | Stop it with Ctrl+C, or run with a different port: PORT=7870 python3 8_app_rag.py (PowerShell: $env:PORT="7870"). |
Could not create share link | The network blocks Gradio's tunnel, or the first-time download of its helper failed. | Use the LAN method, or demo on the laptop screen. Not required for the group task. |
| First answer takes 20+ seconds | Ollama is loading the model into memory. | Normal once per model. The second question is fast. |