왜 만들었나
에이전트 프레임워크는 내부가 감춰져 있어 루프와 컨텍스트가 실제로 어떻게 움직이는지 알기 어렵고, 실습하려면 API 키와 로컬 환경이 필요합니다. 표준 라이브러리만 쓴 작은 하네스를 랩별로 직접 채우고 브라우저에서 바로 채점하게 했습니다.
주요 기능
- 11개 랩: 대화 배열, 도구, 에이전트 루프, 컨텍스트 예산, 서브에이전트, 조종, 가드레일, 디버깅, RAG, 온톨로지, 캡스톤
- Pyodide로 브라우저 안에서 unittest 채점, 실패 원인을 짧은 힌트로 요약
- API 키 없이 동작하는 FakeBrain·ScriptedBrain 두뇌
- 편집 중인 코드 자동 저장, 캡스톤에서 앞 랩의 내 코드를 조립
- 랩별 퀴즈와 바로 실행해 볼 수 있는 따라 해보기 예제
숫자로 보면
실습 랩 11개, 채점 테스트 56개
랩 퀴즈 22문항, 따라 해보기 예제 34개
RAG 실습용 구매 업무 문서 8개(BM25 검색)
어떻게 동작하나
사이트 빌드
build_site.py가 랩별 마크다운을 HTML로 바꾸고 실습 파이썬 파일 전체를 assets/files.json 하나로 묶습니다.
project/scripts/build_site.pyPyodide 준비
채점을 처음 누르면 Pyodide를 내려받고 files.json의 파일을 가상 파일시스템 /harness-lab 아래에 씁니다.
assets/app.js내 코드 주입
모든 랩의 exercise.py를 localStorage에 저장된 내 코드로 덮어쓰고, 캡스톤에서는 안 푼 랩을 정답으로 채웁니다.
assets/app.js환경 맞추기
브라우저에 없는 subprocess.run을 echo·ls·cat·pwd만 흉내 내는 함수로 바꾸고 이전 실행의 모듈 캐시를 지웁니다.
assets/runner.py테스트 실행
랩의 test_lab.py를 unittest로 돌려 테스트별 통과 여부와 오류 내용을 JSON으로 돌려줍니다.
assets/runner.py결과 표시
통과 수와 실패 힌트를 그리고, 모두 통과하면 그 랩을 완료로 표시합니다.
assets/app.js
핵심 코드
for _ in range(max_rounds):
response = brain({"system": system, "messages": messages, "tools": tools})
messages.append({"role": "assistant", "content": response["content"]})
for b in response["content"]:
if b["type"] == "text" and b["text"].strip():
emit("text", b["text"])
if response.get("stop_reason") != "tool_use":
return final_text(response)
results = []
for b in response["content"]:
if b["type"] == "tool_use":
emit("tool", b)
results.append({"type": "tool_result", "tool_use_id": b["id"],
"content": execute(b["name"], b["input"])})
if inject:
inject(results)
messages.append({"role": "user", "content": results})모델 호출 → tool_use 실행 → tool_result를 배열에 덧붙이기를 stop_reason이 바뀔 때까지 반복하는 에이전트 루프의 참고 구현입니다.
어려웠던 점
로컬 파이썬용으로 쓴 실습 코드를 브라우저(Pyodide)에서 그대로 채점하는 부분입니다. subprocess를 흉내 낸 함수로 바꾸고, 실행마다 sys.modules와 sys.path를 정리해 이전 코드가 남지 않게 했습니다.

