Education Studio
Four-agent teaching workflow with concept extraction, quiz generation, lesson planning, and multi-format export via PDF, DOCX, PPTX, and Markdown.
What this demonstrates
The Education Studio example turns uploaded course material into quizzes, lesson plans, and export-ready teaching packages. Four specialized agents — content-analyzer, quiz-generator, lesson-builder, and format-exporter — collaborate through three cross-agent skills (generate_quiz, generate_lesson, generate_full_package). The quiz-generator uses mini-agents to draft questions from multiple source sections in parallel. All agents share RAG-scoped namespaces for source material, generated content, and exports. A cron-driven weekly-quiz schedule and a chat-bound education.generate signal trigger event-driven generation without manual intervention.
Run it
pip install -e ./orchid -e ./orchid-api
ORCHID_CONFIG=examples/education/orchid.yml uvicorn orchid_api.main:app --port 8000Or via the CLI:
pip install -e ./orchid -e ./orchid-cli
orchid chat interactive --config examples/education/orchid.ymlInstall extra dependencies for PDF and PPTX export:
pip install reportlab python-pptxConfiguration walkthrough
orchid.yml sets the runtime — LLM, Qdrant for RAG, file uploads, and identity resolution:
# orchid.yml (trimmed)
agents:
config_path: examples/education/agents.yaml
llm:
#model: ollama/llama3.2
#ollama_api_base: http://host.docker.internal:11434
model: gemini/gemini-flash-latest
gemini_api_key: ${GEMINI_API_KEY}
auth:
dev_bypass: true
identity_resolver_class: examples.education.identity.EducationIdentityResolver
rag:
vector_backend: qdrant
qdrant_url: http://qdrant:6333
#embedding_model: ollama/nomic-embed-text
embedding_model: gemini/gemini-embedding-001
gemini_api_key: ${GEMINI_API_KEY}
upload:
#vision_model: ollama/minicpm-v
namespace: education-uploads
max_size_mb: 20
chunk_size: 1000
chunk_overlap: 200
storage:
class: orchid_ai.persistence.sqlite.OrchidSQLiteChatStorage
dsn: ~/.orchid/education_chats.db
# ...truncatedAgent configs define the four agents, their tools, three cross-agent skills, and the event pipeline:
# agents.yaml (trimmed)
version: "1"
defaults:
llm:
model: "gemini/gemini-flash-latest"
temperature: 0.2
rag:
enabled: true
k: 8
max_context_chars: 4000
supervisor:
assistant_name: "Education Studio"
tools:
extract_concepts:
class: "examples.education.tools.content.extract_concepts.ExtractConceptsTool"
description: "Extract key concepts and headings from source material"
parallel_safe: true
generate_questions:
class: "examples.education.tools.content.generate_questions.GenerateQuestionsTool"
description: "Generate quiz question scaffolds from extracted concepts"
parallel_safe: true
validate_questions:
class: "examples.education.tools.content.validate.ValidateQuestionsTool"
description: "Validate questions for structure and duplication"
parallel_safe: true
build_lesson_structure:
class: "examples.education.tools.content.build_lesson.BuildLessonStructureTool"
description: "Build a timed lesson-plan scaffold"
parallel_safe: true
define_learning_objectives:
class: "examples.education.tools.content.format_lesson.DefineLearningObjectivesTool"
description: "Create Bloom-aligned learning objectives"
parallel_safe: true
format_lesson_section:
class: "examples.education.tools.content.format_lesson.FormatLessonSectionTool"
description: "Format one lesson section as Markdown"
parallel_safe: true
format_multiple_choice:
class: "examples.education.tools.content.format_quiz.FormatMultipleChoiceTool"
description: "Format multiple-choice questions as Markdown"
parallel_safe: true
format_true_false:
# ... additional format and export tools
skills:
generate_quiz:
description: "Analyze material, extract concepts, turn them into a quiz."
steps:
- agent: content-analyzer
instruction: "Extract key concepts from the source material."
- agent: quiz-generator
instruction: "Generate a quiz grounded in those concepts."
- agent: format-exporter
instruction: "Format the quiz for the requested file format."
generate_lesson:
description: "Analyze material and assemble a lesson plan."
steps:
- agent: content-analyzer
instruction: "Identify concepts for a coherent lesson."
- agent: lesson-builder
instruction: "Build objectives, sections, and assessment."
- agent: format-exporter
instruction: "Format the lesson for the requested output format."
generate_full_package:
description: "Produce quiz, lesson plan, and presentation exports."
steps:
- agent: content-analyzer
- agent: quiz-generator
- agent: lesson-builder
- agent: format-exporter
agents:
content-analyzer:
description: "Extracts headings and core concepts from uploaded material."
tools: [extract_concepts]
rag:
namespace: education-source
execution_hints:
parallel_safe: true
quiz-generator:
description: "Converts concepts into answerable questions."
tools: [generate_questions, validate_questions]
mini_agent:
enabled: true
max_count: 4
rag:
namespace: education
execution_hints:
parallel_safe: true
lesson-builder:
description: "Turns concepts into a paced lesson structure."
tools: [build_lesson_structure, define_learning_objectives, format_lesson_section]
rag:
namespace: education
execution_hints:
parallel_safe: true
format-exporter:
description: "Exports content as PDF, DOCX, PPTX, Markdown, or text."
tools: [format_multiple_choice, format_true_false, format_fill_blank, format_matching, generate_pdf, generate_docx, generate_pptx, generate_markdown, generate_txt]
rag:
enabled: false
execution_hints:
parallel_safe: false
events:
enabled: true
store:
class: orchid_ai.events.backends.sqlite.SQLiteEventStorage
scheduler:
class: orchid_ai.events.schedulers.apscheduler.APSchedulerBackend
schedules:
- id: weekly-quiz-cron
cron: "0 8 * * 1"
trigger_id: weekly-quiz
identity: { mode: service_account, name: quiz-bot }
triggers:
- id: weekly-quiz
"on": { signal: cron, cron: "0 8 * * 1" }
emits:
agent: quiz-generator
prompt_template: |
Generate a 10-question weekly review quiz.
identity: { mode: service_account, name: quiz-bot }
visibility: tenant
parallelism: unbounded
- id: chat-bound-generation
"on":
signal: education.generate
emits:
agent: content-analyzer
prompt_template: |
Extract key concepts from this source text.
Source: {{payload.source_text}}
identity: { mode: act_as_user, user_id_from: signal.user_id }
respect_chat_binding: true
parallelism: per_user
# ...truncatedWhat to look for
tools.<name>.class: "examples.education.tools.content.extract_concepts.ExtractConceptsTool"→ tools areOrchidToolsubclasses with declarativeparameters_schemaandparallel_safeflags; the framework resolves them by dotted import path at startup.tools.*.parallel_safe→ content-analysis tools areparallel_safe: true(stateless reads), while export tools that write files areparallel_safe: false(sequential I/O).mini_agent.enabled: trueonquiz-generator→ multi-section source material fans out into parallel mini-agents, each drafting questions from one section. Customdecomposer_promptandaggregator_promptcontrol the split/merge logic.- Three cross-agent skills (
generate_quiz,generate_lesson,generate_full_package) → chain agents sequentially; each step passes context forward. Thegenerate_full_packageskill demonstrates a four-agent pipeline. rag.namespace: education-source/education/education-exports→ three distinct RAG namespaces isolate source content from generated quizzes from export artifacts.events.schedules[].cron: "0 8 * * 1"→ the APScheduler backend fires a weekly cron that triggersquiz-generatorto produce a tenant-wide review quiz every Monday at 08:00.identity.mode: service_accounton the weekly quiz → the Bloom runs asquiz-bot, visible to all users in the tenant viavisibility: tenant.respect_chat_binding: trueonchat-bound-generation→ when a Bloom is triggered from within a chat context, its output is attached to that conversation rather than creating a standalone artifact.format-exporteragent hasrag.enabled: false→ export formatting doesn't need retrieval; saves embedding and context overhead.eywords→EducationIdentityResolverimplementsresolve_service_account()andmint_for_user(), enabling both cron-driven service-account flows and per-user event scoping.upload:section inorchid.yml→ file uploads target theeducation-uploadsQdrant namespace; the framework auto-chunks and indexes uploaded material at 1000-char chunks with 200-char overlap.