Skip to content

Implementation Plan — Agent Document Editor + LLM Consolidation into the Chatbot Microservice

Status: plan only — do NOT implement yet (per direction 2026-06-21). Feature doc: docs/features/agent-document-editor.md Related: docs/backend_microservice_extraction_plan.md (monolith slimming — dovetails with Phase 0 here); docs/engineering/chatbot-agent-platform-migration.md (the full cards→agents picture; this plan is its editor/WS2 slice). Last updated: 2026-06-24 — added assignment doc type + Publish-to-Assignment, full visual block kinds, assignment as the Phase 1 pilot.

0. Two goals, kept separate

This plan covers two intertwined but distinct workstreams. Keep them separate so they can ship independently:

  1. LLM consolidation — move all LLM-calling generation into the chatbot microservice. The FastAPI backend becomes a thin proxy + persistence layer with no generation prompts.
  2. Agent Document Editor — give lesson_plan / question_bank / lesson-module outputs an editable, exportable two-pane editor (/documents/:id), reusing the existing TipTap NotionEditor.

Workstream 1 is the precondition for the refine ("regenerate this block") feature in workstream 2, but the editor's read/edit/export can ship before refine.


1. Current state (verified)

Concern Today Source
lesson_plan / question_bank chat modes Already proxied to external chatbot service apps/backend/src/services/chatbot_client.py:180 (stream_chat_sse); routed from ai_tutor.py:1041 _stream_chat_via_chatbot
ai_teacher generation (lesson/homework/quiz) LLM called locally in FastAPI via prompts apps/backend/src/core/ai/prompts.py (LESSON_GENERATION_PROMPT:109, HOMEWORK_GENERATION_PROMPT:178, KNOWLEDGE_CHECK_GENERATION_PROMPT:979); consumed by api/v1/ai_teacher.py:528,670
Output of lesson_plan/question_bank Plain streamed markdown into messages.content MessageBubble.tsx (no card; renders markdown)
Editable document store None — only conversations/messages models/communication.py:62,88
Editor infra TipTap NotionEditor + AIPanel already exist apps/web/src/components/content-editor/
Dedicated artifact route precedent /presentations/:id + Presentation table PresentationViewerPage, models/presentation.py:43
Export precedent doc-intelligence service does HTML→PDF/PPTX; research does DOCX/MD services/presentation.py:908, api/v1/deep_research.py

Key consequence: the consolidation is ~half done. lesson_plan/question_bank already live in the microservice; the ai_teacher path and all the prompts in prompts.py do not.


2. Target architecture

┌────────────┐    SSE/REST     ┌──────────────────────────┐
│  apps/web  │ ──────────────▶ │   FastAPI backend        │
│  (editor + │                 │  (proxy + persistence)   │
│   chat)    │ ◀────────────── │  - documents CRUD        │
└────────────┘                 │  - export proxy          │
                               │  - auth, quota, RAG ctx  │
                               └──────────┬───────────────┘
                                          │ gRPC/REST + SSE
                               ┌──────────────────────────┐
                               │   Chatbot microservice   │
                               │  OWNS ALL LLM GENERATION │
                               │  - chat (all modes)      │
                               │  - lesson_plan / qbank / │
                               │    module generation     │
                               │  - block-level refine    │
                               │  - ALL prompts live here │
                               └──────────────────────────┘

Boundary rules: - FastAPI holds zero generation prompts after Phase 0. prompts.py generation strings move to the microservice. - FastAPI still owns: auth, quota/entitlements, persistence (documents, conversations), RAG context assembly (it can pass curriculum context to the microservice, but does not call the LLM). - Export stays a service call (doc-intelligence), proxied by FastAPI — not in the chatbot service.


3. The document contract (microservice ⇄ backend ⇄ web)

For block-level refine to work, generation must return structured, addressable output, not an opaque markdown blob. Define a versioned contract owned by the microservice:

// document generation result (v1)
{
  "doc_type": "lesson_plan" | "question_bank" | "module",
  "title": "Introduction to Right-Triangle Trigonometry",
  "schema_version": 1,
  "blocks": [
    { "id": "b1", "kind": "heading", "level": 1, "text": "Learning Objective" },
    { "id": "b2", "kind": "paragraph", "text": "Students will be able to ..." },
    { "id": "q1", "kind": "mcq", "stem": "...", "options": ["A","B","C","D"],
      "answer_index": 2, "explanation": "...", "difficulty": "medium", "bloom": "apply" },
    // visual block kinds (locked full scope — agents return text + visuals):
    { "id": "m1", "kind": "math", "latex": "\\cos 30^\\circ = \\frac{10}{hyp}" },
    { "id": "c1", "kind": "chart", "chart_type": "bar", "data": [/* {label,value} */] },
    { "id": "t1", "kind": "table", "headers": ["A","B"], "rows": [["1","2"]] },
    { "id": "i1", "kind": "image", "prompt": "right triangle labelled ...", "url": "gs://..." },
    { "id": "d1", "kind": "diagram", "engine": "mermaid", "source": "graph TD; A-->B" }
    // ...
  ],
  "meta": { "grade_level": "...", "subject": "...", "audience": "higher_ed", "language": "en" }
}
  • doc_type includes assignment (structured/gradeable — see §4.4) in addition to lesson_plan / question_bank / module.
  • Visual block kinds map to existing TipTap nodes: mathMathBlock, chartChartBlock (Recharts), table→TipTap table, imageImageBlock (GCS), diagramnew MermaidBlock (net-new; reuse the chat MermaidDiagram renderer + DOMPurify SVG sanitize). image blocks are produced by a chatbot agent image tool (relocated Imagen/Gemini/DALL·E), not the old in-process ai_teacher media path.

  • Web maps blocks[] → TipTap document JSON (and back) deterministically. Each block keeps a stable id so refine can target one block.

  • Refine request: { doc_id, block_id, instruction: "make harder" | "regenerate" | "shorten" | free-text } → microservice returns the replacement block(s) with the same id.
  • Streaming generation still streams text for the chat card; the structured payload arrives in the done event (mirrors how generatePresentationStream returns TPresentationDonePayload).

Decision needed (open): keep KaTeX as $...$ inside text/stem fields (web already renders KaTeX) — simplest. Documented in feature doc open questions.


4. Backend (FastAPI) changes — proxy + persistence only

4.1 New documents table (Alembic migration)

Model apps/backend/src/models/document.py (mirrors Presentation + content_blocks patterns):

Column Type Notes
id UUID PK
owner_id FK users
conversation_id FK conversations, nullable originating chat
doc_type enum(lesson_plan,question_bank,assignment,module) assignment publishes to the gradeable assignments table (§4.4)
published_assignment_id FK assignments, nullable set on Publish to Assignment (assignment docs only)
title str
body_json JSON TipTap doc JSON (source of truth for editing)
body_html Text rendered HTML (for export + preview)
source_blocks JSON the contract blocks[] (enables block-refine + re-derive)
meta JSON grade/subject/audience/language
status enum(draft,ready)
created_at / updated_at ts last-write-wins autosave

Defer a document_versions table to Phase 2 (history).

4.2 Endpoints (apps/backend/src/api/v1/documents.py)

Method Path Purpose
POST /documents create from generation done payload (stores contract + initial TipTap json/html)
GET /documents list (owner-scoped; filter by doc_type)
GET /documents/{id} fetch for editor
PATCH /documents/{id} autosave body_json/body_html/title
POST /documents/{id}/refine proxy block-refine to microservice; persist returned block
GET /documents/{id}/export?format=docx\|pdf\|markdown proxy to doc-intelligence export (reuse services/presentation.py export path generalized to arbitrary HTML)
POST /documents/{id}/publish-assignment assignment only — map question blocks → Assignment.questions + settings/targets; create the gradeable assignment via existing homework.py create path
DELETE /documents/{id} soft delete
  • /refine and generation calls go through chatbot_client (extend it with generate_document + refine_block methods). No prompts in FastAPI.
  • Quota/entitlement checks stay here (lesson_plan/question_bank are Pro-gated per chat-mode-surface.md; assignment gen uses AIFeatureType.CONTENT_GENERATION, the existing generate-homework gate).

4.4 Assignment document type (structured → gradeable)

assignment reuses the editor + contract but is not a free document. The card flow: enter details (topic / subject / type / count / difficulty / due date / targets) → agent generates (POST /v1/agents/assignment, replaces in-process /ai-teacher/generate-homework) → opens in /documents/:id → review/edit/refine → POST /documents/{id}/publish-assignment.

  • Publish maps the contract's question blocks (mcq/short/long/true_false/fill_blank) back to Assignment.questions (models/assessment.py:1182) via the existing homework.py create path — preserving auto-grade, attempts, submissions, grading.
  • The sidebar Assignments entry is unchanged (/homework/assignments, management surface). The 3-step CreateAssignmentPage wizard stays for fully-manual authoring; the card is the new AI-first entry. The current AIGenerateModal (raw api.post('/ai-teacher/generate-homework')) is superseded by the agent path through the services/ wrapper.

4.3 LLM consolidation (the "move to microservice" work)

  • Migrate generation prompts out of apps/backend/src/core/ai/prompts.py into the chatbot microservice: LESSON_GENERATION_PROMPT, HOMEWORK_GENERATION_PROMPT, KNOWLEDGE_CHECK_GENERATION_PROMPT, and the essay/project/case-study/etc. variants.
  • Replace api/v1/ai_teacher.py LLM calls (generate_lesson:528, generate_homework:670) with chatbot_client proxy calls returning the document contract.
  • The AIPanel (content-editor/ai/AIPanel.tsx) currently calls ai_teacher — repoint it through the proxy; it becomes a thin client of the same microservice generation endpoint.
  • After migration, prompts.py retains only non-generation strings (if any) or is deleted; verify with grep -r "from .*prompts import".
  • Dovetails with backend_microservice_extraction_plan.md Phase 0 (dependency slimming) — moving generation out lets us drop langchain-style LLM deps from the backend image.

5. Frontend (apps/web) changes — Phase 1 surface

5.1 Route + page

  • Add /documents/:idDocumentEditorPage (lazy, in routes/lazy-pages.ts + SharedRoutes.tsx, mirroring PresentationViewerPage wiring).
  • Two-pane layout: left = prompt/chat + refine; right = NotionEditor.

5.2 Reuse the editor

  • Configure NotionEditor with a new EditorMode = 'document' (content-editor/NotionEditor.tsx:77).
  • Map document contract blocks[] ↔ TipTap JSON. MCQ blocks can reuse KnowledgeCheckBlock extension; math via existing MathBlock.
  • Autosave: debounce onSave(json, html)PATCH /documents/{id} (same pattern as LessonEditorPage autosave).

5.3 AI panel (conversational, block-scoped)

  • Adapt content-editor/ai/AIPanel.tsx from form-driven to a small chat that targets the document: full-doc refine + selected-block refine ("regenerate this question", "make harder", "add 5 more MCQs").
  • On block selection in the editor, panel shows block-scoped actions → POST /documents/{id}/refine.

5.4 Chat integration ("Open in editor")

  • Add a DocumentCard inline artifact (model on PresentationCard.tsx) rendered in MessageBubble.tsx for lesson_plan/question_bank/module done payloads.
  • Card shows title + "Open in editor" → navigates to /documents/:id (document is created on done via POST /documents).
  • Also fixes the flagged gap: toolResult rendering path (currently stored, never rendered).

5.5 Documents list + export

  • /documents list page (filter by type), empty-state CTA "Generate from chat".
  • Export buttons (DOCX/PDF/MD) → GET /documents/{id}/export; gate downloads for B2C free via existing renderDownload/DownloadLockButton.

6. Improved agent prompts (deliverables for the microservice)

These live in the chatbot microservice after consolidation. Drafted here so the microservice team has the spec. Goal: industry-standard pedagogy + structured, editor-ready output matching §3 contract. Replaces the thin placeholder behavior noted in memory (lesson-plan-generator).

6.1 Lesson plan — system prompt (draft)

You are an expert instructional designer for higher-ed, corporate, and K-12 educators.
Produce a lesson plan using BACKWARDS DESIGN (objective → assessment → activities).

Always include, as ordered sections:
1. Title (concise, names the topic + level)
2. Learning Objectives — measurable, Bloom's-tagged verbs (apply/analyze/evaluate), 2–4 items.
3. Prerequisites — what learners must already know.
4. Assessments — Formative (in-class checks) AND Summative (graded), each tied to an objective.
   For any quiz problems, include a worked answer key.
5. Lesson Flow — timed segments (e.g., 0–10 min hook, 10–30 min direct instruction, ...),
   with instructor actions and learner actions per segment.
6. Materials & Resources.
7. Differentiation — support for struggling learners + extension for advanced.
8. Closure / Exit Ticket.

Rules:
- Match vocabulary to AUDIENCE ({audience}): K-12 → "students/class"; higher_ed → "students/semester";
  corporate → "participants/session".
- Use the learner's curriculum context if provided ({rag_context}); cite it, don't invent standards.
- Write math in KaTeX ($...$).
- Output MUST conform to the document contract v1 (blocks with stable ids). No prose outside blocks.
- Be specific and classroom-ready; avoid filler like "engage students meaningfully".

6.2 Question bank — system prompt (draft)

You are an assessment specialist. Generate a question bank that is rigorous, unambiguous,
and ready to edit.

Inputs: topic, count, difficulty mix, question types, audience, language, optional {rag_context}.

For each question produce a contract block with: stem, type (mcq/short/long/true_false/fill_blank),
options (for mcq, exactly one unambiguously correct), answer, explanation, difficulty, Bloom level,
and the objective/topic it assesses.

Rules:
- Vary Bloom levels across the set unless the user requests one level.
- MCQ distractors must be plausible and represent common misconceptions — never "none of the above"
  as filler.
- No trick questions; one defensible correct answer each.
- Math/chemistry in KaTeX ($...$).
- Group by topic/difficulty if the set is large; emit a final "Answer Key" section.
- Ground in {rag_context} when provided; do not fabricate standards or page numbers.
- Output MUST conform to the document contract v1. Each question is one addressable block (stable id)
  so individual questions can be regenerated.

6.3 Lesson / module creation — system prompt (draft)

You are a curriculum author. Generate a self-contained learning MODULE (a teach-ready unit),
not just an outline.

Sections: Overview & learning outcomes; Concept explanations (with examples + analogies);
Worked examples; Knowledge-check questions (contract mcq blocks); Summary; Further reading.

Rules:
- Depth appropriate to {audience} and {duration}.
- Prefer concrete worked examples over abstract definitions.
- KaTeX for math; fenced code for programming topics.
- Ground in {rag_context} when provided.
- Output MUST conform to the document contract v1 with stable block ids.

6.4 Refine prompt (block-level, draft)

You are editing ONE block of an existing {doc_type}. Apply the instruction to the target block
ONLY; keep the surrounding document's style, difficulty band, and audience.
Return a single replacement block (same id, same kind unless the instruction changes type).
Instruction: {instruction}. Target block: {block_json}. Document meta: {meta}.

7. Phasing

Phasing here mirrors the unified roadmap in ../engineering/chatbot-agent-platform-migration.md §8 (this plan = the editor/WS2 view; the architecture doc = the full picture incl. presentation, research, visuals, and supporting agents).

Phase Scope Depends on
0 Consolidation: shared service_client; agent contract POST /v1/agents/<name>; document contract v1 incl. visual block kinds; extend chatbot_client with generate_document + refine_block. Backend holds no prompts. microservice contract sign-off
1 (pilot) Assignment card: POST /v1/agents/assignment; documents table + CRUD + export proxy; /documents/:id editor reusing NotionEditor; details → generate → editor → review/edit → Publish to Assignment. Sidebar management unchanged. Trainer-only. Phase 0 contract
1b Block-level refine via AI panel (regenerate question / make harder / add 5 MCQs). Phase 0 refine endpoint
2 Lesson plan + question bank as document agents; DocumentCard "Open in editor"; read/edit/export. Phase 1 + validation study
3 Visuals: Mermaid MermaidBlock node + diagram blocks; agent→ChartBlock mapping; AI image agent tool → image blocks; SVG/chart export fidelity. Phase 2
2′ (later) Learner read-only published view; version history; mobile parity. (Presentations stay on their own viewer; not folded in.) Phase 2 validation study

Validation gate: before broadening past the pilot (Phase 1 → 2), run the deferred 3-trainer "generate → refine → export" study (carried over from chat-first-ai-workspace.md).


8. Risks

  • Contract drift — if the microservice returns inconsistent block structure, web↔TipTap mapping and refine break. Mitigate: version the contract, validate server-side, add a fallback "treat as single markdown block" path.
  • Export fidelity for math — reuse doc-intelligence HTML→PDF; validate KaTeX-in-HTML export early in Phase 1.
  • Prompt-migration regressions — moving ai_teacher prompts may change output shape for existing lesson/homework generation. Mitigate: snapshot current outputs before migration; diff after.
  • Quota/entitlement leak — keep all gating in FastAPI proxy; microservice must not be reachable directly from web.
  • Autosave data loss — debounced PATCH + explicit "saved" status + server timestamp; no silent catch.

9. Open questions (also in feature doc)

  • [ ] Document route name: /documents/:id vs /artifacts/:id.
  • [ ] Does module write back to course content_blocks on "Publish", or stay standalone?
  • [ ] Version history in Phase 1 or Phase 2?
  • [ ] Where does the chatbot microservice live + how is the contract versioned/deployed? (blocks Phase 0)
  • [ ] Math representation in contract: inline KaTeX strings (proposed) vs. dedicated math blocks.

10. Explicitly NOT in this plan

  • No code is written in this round (plan only, per 2026-06-21 direction).
  • No real-time collaboration (CRDT/Yjs).
  • No presentation migration into the generic editor.
  • No mobile (React Native) editor.
  • No learner authoring.