-
Notifications
You must be signed in to change notification settings - Fork 15
LMS AI Authoring
The mechanics of the three AI helpers in the course editor β how FreeITSM turns a knowledge article into a lesson, drafts a course outline from a topic, and writes quiz questions from a lesson you've written. Companion to LMS Authoring; the shared provider plumbing is on AI Providers.
All three live in one endpoint, api/lms/ai_author.php, gated by lms.manage. They share everything except the prompt: the same provider config, the same JSON contract, the same error handling. Each returns a draft β and that word carries the whole safety model.
Every helper returns a draft that lands in the editor for the author to read and change. Nothing is written to the database by the AI. When the author presses Save, the draft goes through the same validated endpoints a hand-typed lesson or question uses β so the AI cannot create a course nobody looked at, a lesson full of markup it shouldn't emit, or (critically) a question with no correct answer. The model is a drafting assistant behind the normal front door, not a privileged writer.
This is why, for example, AI-generated quiz questions are still subject to the "at least one correct answer, and a single-choice question can't have two" checks in api/lms/questions.php (see LMS Authoring β validation). A malformed generation is rejected exactly as a malformed hand edit would be.
$cfg = aiSettingsLoad($conn, 'lms_ai'); // provider, model, decrypted key
if (($cfg['api_key'] ?? '') === '') throw new Exception('LMS AI is not configuredβ¦');
$resp = aiProviderChat($cfg, ['system' => $system, 'user' => $user, 'max_tokens' => β¦]);
$draft = parseClaudeJson(trim((string)($resp['content'] ?? '')));-
lms_aiis the config namespace (provider + model + encrypted key), set on LMS β Settings. See AI Providers for how namespaces, encryption and theaiProviderChat()client work. Unconfigured β the helper throws a friendly error and the editor still works by hand. -
parseClaudeJson()(api/cmdb/_ai_helpers.php) is the shared, tolerant JSON extractor β it copes with a model that wraps its JSON in prose or fences. Every mode asks the model to "Respond ONLY as JSON" with an explicit shape, then parses it, then validates the shape before trusting it. - A shared house prompt sets the voice for all three: "an experienced instructional designer writing internal IT trainingβ¦ plainly and concretely, in British Englishβ¦ Never invent product features, policies or facts that were not given to you." That last clause is the spine of the whole feature.
Input: a topic and a lesson count (2β12). Output: {title, description, lessons: [{title, summary}]}.
The model is asked for exactly N lessons that build on each other in a sensible teaching order. Back in the editor (lms-editor.js), each returned lesson is created as a real but empty lesson whose body is just the AI's one-line summary wrapped in a <p> β a skeleton to write into, explicitly not finished content. The author then fills each lesson (by hand, or with Mode 2).
The outline is the one helper that does create rows β but only empty lesson shells, each saved through the normal lesson endpoint. It's scaffolding, not content.
Input: a knowledge article_id, and/or a lesson title. Output: {title, body} where body is simple HTML.
This mode has two genuinely different prompts, because the two jobs have different risk profiles:
| Source | Prompt stance | Why |
|---|---|---|
| A knowledge article (grounded) | "Rewrite the source material below as a lesson that TEACHES it⦠Keep every fact from the source; add none of your own." | The article is known-good content you wrote. Rewriting it into teaching material is safe and grounded. |
| Just a title (ungrounded) | "Because you have no source material, stay at the level of general good practice and do NOT invent company-specific policies, names, systems or figures." | With nothing to ground on, the model is explicitly fenced away from inventing specifics it can't know. |
When grounded, the article's HTML is reduced to plain text server-side (strip_tags β decode entities β collapse whitespace, capped at ~12k chars) before it's handed to the model β the same plain-text reduction the Knowledge "Ask AI" uses. The output body is constrained to a small HTML allowlist (<p>, <h3>, lists, <strong>/<em>, <code> β no <script>, no styles, no images) so it drops cleanly into TinyMCE. The draft appears in the editor; nothing is saved until the author presses Save.
Turning your knowledge base into training is the headline use. Your documented fixes become your onboarding, and the AI works from something true rather than hallucinating a course about nothing β which is the whole reason the grounded path exists.
On a multi-company install, the article source is shared articles only (knowledge_articles.tenant_id IS NULL) β enforced in includes/lms/knowledge_source.php and applied to both the picker list and the body read, since the picker only posts an id and ai_author.php is gated on lms.manage rather than any Knowledge permission. This is deliberately not "the author's own companies": nothing in the LMS carries a company (lms_courses, lms_lessons, lms_course_assignments, lms_learning_groups, lms_progress), so a lesson built from one client's article would be shown to every learner regardless β scoping the picker to the author would close the article leak only to open a lesson leak. Shared-only is the one rule coherent with an install-wide course; revisit if the LMS ever gains a company of its own.
Input: a saved lesson_id and a question count (1β10). Output: {questions: [{question_text, question_type, explanation, answers: [{answer_text, is_correct}]}]}.
The lesson's own text is reduced to plain text and put in the prompt, and the model is told every question must be answerable from the lesson text alone β "test whether someone has UNDERSTOOD the lesson, not whether they can remember a phrase from it", with plausible wrong options rather than obvious filler. Grounding in the lesson is the trick: a question the lesson doesn't answer is worse than no question.
Because a model can still return something malformed, the endpoint cleans and filters before handing the draft back:
- drop any question with fewer than two answers, or with no correct answer;
- if a question is marked
singlebut has several correct answers, relabel itmultiple(trust the key, fix the label); - keep only
single/multiple/truefalseas the type.
Anything that survives is offered to the author, who saves it through questions.php β where the same validation runs again, server-side, as a belt-and-braces final gate. A generated answer key that's still wrong never reaches a learner.
- One endpoint, three prompts β the modes differ only in the instruction and the JSON shape, so sharing the config/parse/error path keeps them consistent and cheap to maintain.
- Draft-not-write β the AI's output is always reviewable and always re-validated on save, so the blast radius of a bad generation is "the author sees a poor draft", never "a learner is graded against a broken quiz".
- Grounding over generation β the helpers are strongest when pointed at something true (an article, a written lesson) and are explicitly fenced when they aren't, which is what keeps the training trustworthy.
- LMS Authoring β the editor, the content model, and the save-side validation these helpers rely on
-
AI Providers β namespaces, encrypted keys, and the
aiProviderChat()client - Knowledge β the article source for grounded lessons
- LMS β the module hub
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- Theming & Dark Mode
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
- MobileβFriendly
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- Problem Management
- Tasks
- Assets
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
- Forms
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)