Illinois Faculty Retreat | January 15, 2026

A Day With Claude

Using and Teaching Coding Agents

"The hope is that, in not too many years, human brains and computing machines will be coupled together very tightly, and that the resulting partnership will think as no human brain has ever thought."

— J.C.R. Licklider, 1960

Link to These Slides

geoffreychallen.com/talks/2026-01-15

I Want These Tools to Work

I've always built software to make my courses more effective (cs124.org, learncs.online, backend infrastructure).

  • Democratization: Building software to solve problems should be available to everyone
  • More builders = more problems solved — break the "software priesthood"
  • For my students: AI is accelerating my ability to make courses better

"We Solve Problems First" — Siebel Center

Yesterday: The Day of Claude

903
prompts I sent (~1/min)

17
hours of work

63
commits shipped

16,266
lines of code added

5,801
lines deleted

$1,000+
API cost

I wrote ZERO lines of code by hand.

What I Built Yesterday

usingandunderstanding.ai

  • Next.js 16 + Tailwind v4 migration
  • Complete auth system rewrite
  • RAG-based AI chat
  • Welcome emails to 24 students
  • Terminal-inspired redesign

cs124.org

  • Staff hiring automation
  • Discourse integration
  • K8s log aggregation
  • AI chat improvements
  • E2E testing infrastructure

The Day: Hour by Hour

7am9am11am1pm3pm5pm7pm9pm11pm
cs124.org
E2E Testing7–8am • ~30 prompts
Student journey tests, reviews display fix
Deps Update8–9am • ~40 prompts
Dependencies update, e2e fixes, version bump
Hire Process9–10am • ~20 prompts
k8s:restart, hiring improvements, UI fixes
Tests Infra11am–1pm • ~85 prompts
data-testid refactor, student journey tests
Discourse1–2pm • ~25 prompts
Discourse migration fixes, WWC group management
Appts2–3pm • ~30 prompts
Staff hiring automation tool
📞 Jim Wentworth3–4:30pm
Call with Jim Wentworth
Appts (cont.)4–5pm • ~55 prompts
Continued appointments automation
Qdrant5–6pm • ~45 prompts
Sync fixes, version bumps, lesson dates
K8s Logs6–7pm • ~90 prompts
K8s log aggregation utility
Fix7–8pm • ~65 prompts
planprogress CPU issue fix
AI Chat11pm–12am • ~30 prompts
AI chat RAG improvements
usingandunderstanding.ai
LDAP/Graph8–9am
LDAP, Microsoft Graph, roster generation
Data Reorg9–10am
Semester format, course content migration
Emails (24)10–11am • ~35 prompts
Welcome emails via Outlook → 24 sent
Node 2410–11am
Node.js 24 upgrade, dependencies
Next.js 1611am–12pm • ~45 prompts
Next.js 16, Tailwind v4 migration
DB Setup12–1pm • ~50 prompts
Docker PostgreSQL, NextAuth config
Design Redesign1–2pm • ~30 prompts
Terminal-inspired aesthetic, typography
📞 Jim Wentworth3–4:30pm
Call with Jim Wentworth
RAG Setup2–3pm • ~10 prompts
Embedding tool, RAG setup
Agents5–6pm • ~10 prompts
Markdown-based agent definitions
Auth Migration6–9pm • ~140 prompts
NextAuth → Better Auth migration
RAG v211pm–12am • ~45 prompts
Hierarchical chunking, hybrid search
Testing Integrations Hiring Infrastructure AI/RAG Design

Hover over blocks for details, press ↓ to dive deeper

cs124.org: 26 Commits

7:15am E2E Testing Student journey tests, reviews display fix
8:15am Dependencies Deps update, e2e fixes, version bump
9:42am Hiring k8s:restart, hiring improvements, UI fixes
11:42am Test Infra data-testid refactor, student journey tests
1:06pm Discourse Migration fixes, WWC group management
2:03pm Appointments Staff hiring automation tool (2+ hours)
5:21pm Qdrant Sync fixes, version bumps, lesson dates
6:25pm K8s Logs Log aggregation utility
7:54pm Performance planprogress CPU issue fix
11:43pm AI Chat RAG improvements

usingandunderstanding.ai: 37 Commits

8:15am Integrations LDAP, Microsoft Graph, roster generation
8:50am Data Reorg Semester format, course content migration
10:02am Welcome Emails Outlook MCP, email workflow → 24 sent
10:12am Upgrades Node.js 24, dependencies
11:22am Framework Next.js 16, Tailwind v4, dev server
12:20pm Database Docker PostgreSQL, NextAuth config
1:07pm Redesign Terminal aesthetic, typography
2:35pm RAG Setup Embedding tool, dev server
5:47pm Agents Markdown-based agent definitions
6:18pm Auth Migration NextAuth → Better Auth (2+ hours)
11:06pm RAG v2 Hierarchical chunking, hybrid search

Real Conversations

Actual exchanges from yesterday's work sessions:

  • Planning — Feature design and refinement
  • Iteration — Back-and-forth improvement
  • Course Correction — When things go wrong

Press ↓ to see examples

Planning: AI Chat for Students

"Now it's time to enable intelligent chat, allowing students to talk to an AI agent that can help answer their questions based on CS 124 content... The agent should not answer questions unrelated to CS 124, and should steer students to course materials."
The plan covers: Backend (servers/api): RAG search via Qdrant, Azure OpenAI chat completion, conversation persistence. Frontend: GPT-like centered chat UI with source citations. 8 files to create/modify.
"For RAG, should we do vector similarity and top K? Also, I would like citations and links to course materials."
Updated with hybrid search using Reciprocal Rank Fusion: semantic search via Qdrant finds conceptually related content; keyword search finds exact term matches. This is valuable for CS 124 because code/syntax questions need exact matching.

Press ↓ to see the actual plan

The Actual Plan: 457 Lines

# CS 124 AI Chat Feature with RAG ## Overview Add an AI-powered chat assistant at `/chat` that helps students with CS 124 questions using RAG (Retrieval-Augmented Generation) to provide context-aware responses based on course materials. ## Key Decisions - Backend: `servers/api` (already has Azure OpenAI chat config) - Persistence: Store conversation history in MongoDB (7-day TTL) - Streaming: Non-streaming initially (simpler, can add SSE later) - Access: Current students and staff only (`ctx.hasRole` check) - Navigation: Direct URL access only (`/chat`) - no menu link yet ## Architecture Frontend (sites/cs124) Backend (servers/api) ┌──────────────────┐ ┌─────────────────────────────────┐ │ pages/chat.tsx │──POST────>│ server/chat.ts │ │ │ │ ├─ searchRAG() → Qdrant │ │ GPT-like UI │<─JSON─────│ ├─ chatCompletion() → Azure │ │ with sources │ │ └─ saveConversation() → Mongo │ └──────────────────┘ └─────────────────────────────────┘ ## Files to Create/Modify ### 0. `servers/shareable/server/rag.ts` (modify) Add sparse vector generation for hybrid search: // Add sparse vector generation function function generateSparseVector(text: string): { indices: number[], values: number[] } { // Tokenize, remove stopwords, compute TF weights // Use murmurhash for token → index mapping } // Update saveChunkToQdrant to store both vector types await qdrantClient.upsert(QDRANT_COLLECTION, { points: [{ id: formattedId, vector: { "dense": chunk.embedding, "sparse": generateSparseVector(chunk.text), }, payload: { ... } }] }) ### 1. `lib/collections/src/api.ts` (modify) Add collection constant: export const API_CHAT_CONVERSATIONS_COLLECTION = "api_chat_conversations" ### 2. `servers/api/client/chat.ts` (new) import { Array, Literal, Number, Optional, Record, Static, String } from "runtypes" export const ChatSource = Record({ contentId: String, title: String, timestamp: Number, author: String, type: Literal("walkthrough").Or(Literal("video")), url: String, }) export const ChatMessage = Record({ role: Literal("user").Or(Literal("assistant")), content: String, sources: Optional(Array(ChatSource)), timestamp: String, }) export const ChatRequest = Record({ message: String, conversationId: Optional(String), }) export const ChatResponse = Record({ response: String, sources: Array(ChatSource), conversationId: String, }) ### 3. `servers/api/client/index.ts` (modify) Add export: export * from "./chat" ### 4. `servers/api/server/chat.ts` (new) Key components: // Environment variables const AZURE_OPENAI_CHAT_ENDPOINT = String.check(process.env.AZURE_OPENAI_CHAT_ENDPOINT) const AZURE_OPENAI_CHAT_API_KEY = String.check(process.env.AZURE_OPENAI_CHAT_API_KEY) const QDRANT_URL = String.check(process.env.QDRANT_URL) const CHAT_RATE_LIMIT_PER_MINUTE = parseInt(process.env.CHAT_RATE_LIMIT_PER_MINUTE || "10") // System prompt const SYSTEM_PROMPT = `You are a helpful CS 124 teaching assistant... - Answer questions ONLY related to CS 124 (Java, Kotlin, introductory programming) - Direct students to relevant course materials with timestamps - Never provide complete homework/quiz solutions - Politely redirect off-topic questions` // Qdrant search function async function searchRAG(query: string, limit = 5): Promise<RAGResult[]> // Routes router.post("/", async (ctx) => { /* main chat endpoint */ }) router.get("/history/:conversationId", async (ctx) => { /* get history */ }) router.delete("/:conversationId", async (ctx) => { /* clear conversation */ }) ### 5. `servers/api/server/index.ts` (modify) Register chat router: import { router as chatRouter } from "./chat" .use("/chat", chatRouter.routes(), chatRouter.allowedMethods()) ### 6. `servers/api/package.json` (modify) Add dependencies: "@qdrant/js-client-rest": "^1.16.2", "openai": "^4.x.x" ### 7. `docker-compose.yml` (modify) Add env vars to api service: AZURE_OPENAI_CHAT_ENDPOINT: ${AZURE_OPENAI_CHAT_ENDPOINT} AZURE_OPENAI_CHAT_API_KEY: ${AZURE_OPENAI_CHAT_API_KEY} QDRANT_URL: ${QDRANT_URL} QDRANT_API_KEY: ${QDRANT_API_KEY:-} CHAT_RATE_LIMIT_PER_MINUTE: ${CHAT_RATE_LIMIT_PER_MINUTE:-10} ### 8. `sites/cs124/pages/chat.tsx` (new) GPT-like centered interface: - Message list with auto-scroll - User messages right-aligned (blue) - Assistant messages left-aligned with source citations - Input field at bottom with send button - Loading indicator during response - "New conversation" button to clear history Key patterns: const { headers, course } = usePersonable() // Require enrollment if (!course?.you) return <LoginRequired /> // Centered container <MainContainer maxWidth="md"> <Box sx={{ display: "flex", flexDirection: "column", height: "calc(100vh - 64px)" }}> {/* Scrollable message area */} <Box sx={{ flex: 1, overflow: "auto" }}> {messages.map(msg => <ChatBubble key={msg.id} message={msg} />)} </Box> {/* Sticky input */} <Paper sx={{ position: "sticky", bottom: 0 }}> <TextField ... /> <Button onClick={sendMessage}>Send</Button> </Paper> </Box> </MainContainer> ## API Contract ### POST /v1/chat Request: { "message": "What is a variable?", "conversationId": "uuid-optional" } Response: { "response": "A variable in Java is a named storage location...", "sources": [ { "contentId": "abc123", "title": "Variables and Types", "timestamp": 165, "author": "[email protected]", "type": "walkthrough", "url": "/play/abc123?t=165" } ], "conversationId": "uuid" } ### GET /v1/chat/history/:conversationId Returns conversation messages array. ### DELETE /v1/chat/:conversationId Clears conversation history. ## MongoDB Schema Collection: `api_chat_conversations` { conversationId: String, // UUID netID: String, email: String, semester: String, messages: [{ role: "user" | "assistant", content: String, sources: [ChatSource], timestamp: ISODate }], createdAt: ISODate, // TTL index: 7 days updatedAt: ISODate } ## Rate Limiting - 10 requests per minute per user (in-memory counter) - Returns 429 with retry-after header when exceeded - Use pattern from `servers/shareable/server/common.ts` ## System Prompt Strategy You are a CS 124 teaching assistant. Your role is to: 1. Answer questions ONLY about CS 124 content (Java, Kotlin, intro programming) 2. Reference course materials with timestamps when relevant 3. NEVER provide complete solutions to homework or quizzes 4. Guide students to understand concepts, don't just give answers 5. For off-topic questions, respond: "I can only help with CS 124 topics." When referencing content, cite: [Title by Author at MM:SS] ## RAG Retrieval Strategy Hybrid Search using Qdrant (Semantic + Keyword in one query): Qdrant natively supports hybrid search by storing both dense vectors (semantic) and sparse vectors (keyword/BM25) in the same collection. ### Setup (one-time, in shareable RAG indexing) // When indexing chunks, store both vector types await qdrantClient.upsert(QDRANT_COLLECTION, { points: [{ id: chunkId, vector: { "dense": embedding, // 3072-dim from text-embedding-3-large "sparse": sparseVector, // BM25-style sparse vector }, payload: { text, contentId, timestamp, author, ... } }] }) ### Query (in API server) async function searchRAG(query: string, topK = 5): Promise<RAGResult[]> { // Generate both dense and sparse vectors for query const [denseVector, sparseVector] = await Promise.all([ generateEmbedding(query), generateSparseVector(query), ]) // Qdrant hybrid search with RRF fusion built-in const results = await qdrantClient.query(QDRANT_COLLECTION, { prefetch: [ { query: denseVector, using: "dense", limit: topK * 2 }, { query: sparseVector, using: "sparse", limit: topK * 2 }, ], query: { fusion: "rrf" }, // Reciprocal Rank Fusion limit: topK, with_payload: true, }) return enrichWithMetadata(results) } Why Qdrant-native hybrid is better: - Single query instead of two separate systems - Built-in RRF fusion, no manual merging - Lower latency, simpler code Why hybrid search matters for CS 124: - "What is a variable?" → Semantic finds conceptually related content - "NullPointerException" → Keyword matches exact term - "public static void main" → Keyword matches code patterns ## RAG Context Format for LLM You have access to the following course materials to help answer the student's question. Reference these with [1], [2], etc. and I will include links for the student. [1] "Variables and Types" walkthrough by [email protected] [02:45] Variables in Java must be declared with a type. Unlike Python... [02:58] The primitive types include int, double, boolean, and char... [2] "Lesson 3: Data Types" video [05:30] When declaring a variable, think about what values it will hold... --- Student question: {question} ## Citations and Links The LLM response should include inline citations like [1], [2] which map to sources. Frontend renders citations as clickable links that open the video/walkthrough at the exact timestamp. ## Implementation Order ### Phase 1: Validate Hybrid Search Locally 1. Create migration script - `servers/shareable/bin/migrateQdrantHybrid.ts` - Reads all points from local Qdrant - Generates sparse vectors from payload text - Upserts back with both dense + sparse vectors 2. Test hybrid search - Write a simple test query to verify: - Semantic query: "how do variables work" - Keyword query: "NullPointerException" 3. Decision point: If hybrid works → continue. If not → fall back to full-text index. ### Phase 2: Build Chat Feature 4. Backend types - `lib/collections`, `servers/api/client/chat.ts` 5. Backend routes - `servers/api/server/chat.ts` with hybrid search 6. Docker config - Add env vars to docker-compose.yml 7. Frontend page - `sites/cs124/pages/chat.tsx` ### Phase 3: Update RAG Indexing 8. Update shareable RAG - Modify to generate sparse vectors 9. Update build script - Ensure lesson/MDX content also gets sparse vectors 10. Run checks - `rushx check` in all affected projects ### Fallback: Full-Text Index If sparse vectors don't work well, use Qdrant's payload text index instead: await qdrantClient.createPayloadIndex(QDRANT_COLLECTION, { field_name: "text", field_schema: "text", }) This requires no migration, just a one-time index creation. ## Verification 1. Start backend: `rushx backend` 2. Start frontend: `cd sites/cs124 && rushx start` 3. Navigate to `http://localhost:3000/chat` 4. Test scenarios: - Ask "What is a variable in Java?" → Should get response with sources - Ask "What's the weather?" → Should redirect to CS 124 topics - Send 11 messages quickly → Should get rate limited - Refresh page → Should see conversation history - Click "New conversation" → History should clear ## Future Enhancements (not in this PR) - SSE streaming for real-time response display - Navigation link in TopBar - Usage analytics dashboard - Feedback mechanism (thumbs up/down) - Context-aware follow-ups based on student's current lesson

The New Workflow

Before

Iterate over
implementations

write → test → fix → repeat

Now

Iterate over
plans

design → refine → approve → agent implements

Human time shifts from writing code to designing systems

Source code is the new assembly code — do you read compiler output?

Iteration: Refining Together

"Let's not have ncu use -u by default. By default it should just show which versions would be updated."
Good point — safer to preview first. Let me restructure: `bun run ncu` previews updates, `bun run ncu:apply` actually applies them.
"The first attempt failed. Is there a way to add screenshots when an appointment doesn't submit properly so we can debug?"
Done. Screenshots captured at: form-completed, after-submit, submit-button-not-found, student-not-found. Saved with timestamps to screenshots/ directory.

Course Correction

"No no no. You need to fix the script. Stop trying to do this manually. The script has to work."
You're right. Let me fix the script to handle this case properly instead of working around it...

Key insight: Clear, direct feedback keeps agents on track.
They respond well to correction — no ego, just adjustment.

The Deletion Story

5,801 lines deleted = fearless pivoting

  • Complete frontend redesign (didn't like the first one)
  • Entire auth system rewrite (NextAuth → Better Auth)
  • Multiple RAG implementation iterations
When implementation is fast, you spend more time thinking and designing.
Bad decisions are cheap to throw away.

Agents Do Get Off Track

My actual messages from yesterday:

8:01am: "No no no. This needs to work."

11:04am: "NO NO NO NO NO NO! You need to shut down the servers cleanly!"

9:19pm: "No no no no no. This is totally wrong."

Why frustration is rising: Expectations are rising.
The baseline is so good that mistakes feel jarring.

How I Work WITH Claude

"Working with agents requires understanding source code, but differently than before. I supervise and approve edits, but the amount of code I write by hand has declined to near zero."
  • Bypass permissions mode — not reviewing most code
  • Testing + manual verification — not line-by-line review
  • 17 planning cycles — Claude proposes, I approve
  • 41 clarification questions — Claude asks when unsure

Testing: The Secret Weapon

Give agents good targets and boundaries — they excel.

  • Tests define success — clear pass/fail criteria
  • Agents write great tests — thorough and systematic
  • Tests replace code review — verify behavior, not syntax

Yesterday's example:

"Add comprehensive student journey e2e test with language selection flow"
+ data-testid attributes, timing fixes, test refactoring

7 testing-related commits in cs124.org alone

The Power of Integrations

These are NOT coding tasks — they're course management tasks:

  • Email: Outlook integration for welcome emails
  • LDAP: University roster pulls, student data enrichment
  • Discourse: Forum management, section enrollment
  • PDF: Read applicant responses, extract data
  • Spreadsheets: Generate staff override sheets
  • Course content: Review slides, assignments, handouts, online materials

Yesterday was infrastructure-heavy, but I regularly use these for content review too.

CS 124: Responding to AI

The problem: Claude completes traditional assignments easily.

Traditional: Instructor idea → Spec → Student writes code
Spring 2026: Student idea → Student spec → AI writes code

"My Project" — each student builds their own Android app

First-semester students who have never programmed building real Android apps. That's amazing.

Press ↓ for more details

Student Feedback: Fall 2025

We tried AI on traditional scaffolded projects (no creativity). Students had... feelings.

"I didn't build it, AI did."
"I just completely rely on the coding agent and I don't know how to complete the code without it. That makes me feel pretty bad."
"The focus on AI in an intro level course is extremely misplaced... it is blocking a lot of important learning."

My take: We asked them to do the wrong thing. Students are scared and looking to us for guidance. We need to show more leadership.

Why Traditional Assignments Broke

The old anti-cheating approach:

  • Rotate assignments every semester
  • Students can't find old solutions online
  • No tool existed to translate spec → code
We've built that tool. It's called a coding agent.

If the agent can do the entire assignment...
...the student is doing little to nothing.

The Impossible Tension

Accurate specification

AI can do it all

Vague specification

Student frustration

This is almost impossible to resolve with traditional assignments.

My Project: First Half (NEW Spring 2026)

Idea Development (in discussion sections)

  • Brainstorming — what problem do you want to solve?
  • Writing about the idea — who is it for? why?
  • App design — screen mockups, user flows
  • Specification — what should it actually do?

This is the uniquely human part — AI can't come up with your idea.

My Project: Second Half

Build with Claude

  • Students use Claude to build their Android app
  • Must be Java/Kotlin (course ecosystem)
  • Every student has their own unique project
  • Staff help with debugging, design decisions

The key insight: AI enables beginners to build real apps.
Personal investment → deeper learning.

Classical Programming Remains

CBTF quizzes: 80% of grade

Why keep it?

  • Clear thinking — decompose problems precisely
  • Accurate expression — say exactly what you mean
  • Reading comprehension — understand complex systems

Like a crossword puzzle for the mind — mental training that transfers.

The New Reality

  • Students do: Idea → Specification (uniquely human)
  • AI does: Specification → Code (what it excels at)
  • Classical programming: Mental training (80% via CBTF)

We need to teach students to work WITH AI, not against it.

Key Takeaways

  1. AI coding is production-ready — I use it for real work
  2. Zero hand-written code — 16,266 lines, all AI-generated
  3. Deletions = freedom — fast pivoting, better design
  4. Not just coding — course management: email, LDAP, PDFs, rosters
  5. Agents aren't perfect — but expectations are rising
  6. Education must adapt — teach WITH AI

The Future of CS?

A model to consider: Mathematics

  • Many students learn math at the university
  • Very few major in mathematics
  • What math professors do is unrecognizable to most

Perhaps everyone will learn "programming" — meaning prompting agents to build things.
But CS majors will do something... different.

Am I teaching a dead skill? We need to adapt together.

Get Familiar With These Tools

A previous speaker suggested AI can only perform knowledge work of
"limited scope and complexity".

I hope today's examples challenge that assumption.

We can't teach these tools if we don't know how to use them.
We don't know how to use them unless we are using them.

Even if AI coding agents stopped improving today,
software development has changed forever.

Start small. Try Claude for your next task—writing, coding, analysis, anything.
The learning curve is surprisingly gentle when you just... start.

Questions + Live Demo

Let's talk while Claude works in the background.

(What should we build?)

Geoffrey Challen
geoffreychallen.com
[email protected]

cs124.orgusingandunderstanding.ai