Imagine: a medical student on a reception — the patient complains of back pain but omits a recent fall. A scripted trainer won't catch the trick, but an AI character 'remembers' the missed question and flags the error at the end of the session. We build exactly such systems — with realistic character psychology and an adaptive scenario for each student. This is not just a dialogue simulation, but a full-fledged situational task where every answer affects the scenario's development. According to IBM Training Report research, companies using AI simulations reduce training time by 40%.
How AI simulations solve the problem of scalable training?
Classic role-playing games require actors and scriptwriters — expensive, and replaying gives the same answer. AI simulations generate a unique dialogue every time: the character reacts to specific words, changes emotions and tactics. At the core is an LLM, framed by a system prompt with a role, goals, and secret information. The code below shows a minimal implementation of such a character.
from openai import AsyncOpenAI from dataclasses import dataclass, field from typing import Optional client = AsyncOpenAI() @dataclass class SimulationCharacter: name: str role: str personality: str objectives: list[str] # Что персонаж хочет добиться knowledge: str # Что персонаж знает emotional_state: str = "neutral" secret_info: str = "" # Информация, которую персонаж скрывает @dataclass class SimulationScenario: title: str learning_objectives: list[str] characters: list[SimulationCharacter] context: str success_criteria: list[str] difficulty: str = "medium" @dataclass class SimulationSession: scenario: SimulationScenario conversation_history: list[dict] = field(default_factory=list) score: float = 0.0 attempts: int = 0 feedback_notes: list[str] = field(default_factory=list) class InteractiveSimulator: def __init__(self): self.client = AsyncOpenAI() async def create_character_response( self, session: SimulationSession, learner_input: str, character: SimulationCharacter ) -> dict: """Генерируем реалистичный ответ персонажа + оценку действий ученика""" system_prompt = f"""Ты — {character.name}, {character.role}. Личность: {character.personality} Твои цели в этой ситуации: {', '.join(character.objectives)} Контекст сценария: {session.scenario.context} Информация, которую ты знаешь: {character.knowledge} {'Скрытая информация (не раскрывать явно): ' + character.secret_info if character.secret_info else ''} Текущее эмоциональное состояние: {character.emotional_state} ВАЖНО: - Отвечай от лица персонажа, реалистично - Реагируй на тактику ученика: хорошие аргументы смягчают позицию, давление усиливает сопротивление - После ответа добавь блок [INSTRUCTOR_EVAL] с оценкой действий ученика (не показывается ему) Верни JSON: {{ character_response: "ответ персонажа", emotional_state_change: "как изменилось настроение", instructor_eval: {{ technique_used: "...", effective: true/false, score_delta: -5..+10, tip: "..." }} }}""" messages = [{"role": "system", "content": system_prompt}] # История диалога for turn in session.conversation_history[-10:]: # последние 10 реплик messages.append({"role": turn["role"], "content": turn["content"]}) messages.append({"role": "user", "content": learner_input}) response = await self.client.chat.completions.create( model="gpt-4o", messages=messages, response_format={"type": "json_object"} ) return json.loads(response.choices[0].message.content) async def evaluate_session(self, session: SimulationSession) -> dict: """Финальная оценка сессии симуляции""" response = await self.client.chat.completions.create( model="gpt-4o", messages=[{ "role": "system", "content": f"""Оцени результаты обучающей симуляции. Цели обучения: {json.dumps(session.scenario.learning_objectives, ensure_ascii=False)} Критерии успеха: {json.dumps(session.scenario.success_criteria, ensure_ascii=False)} Проанализируй диалог и верни JSON: {{ overall_score: 0-100, objectives_achieved: [{{"objective": "...", "achieved": true/false, "evidence": "..."}}], strengths: ["..."], areas_for_improvement: ["..."], specific_feedback: "подробный разбор ключевых моментов", recommended_practice: "что отработать дополнительно" }}""" }, { "role": "user", "content": f"История диалога:\n{json.dumps(session.conversation_history, ensure_ascii=False, indent=2)}" }], response_format={"type": "json_object"} ) return json.loads(response.choices[0].message.content) Why is LLM-based dialogue generation better than scripted trees?
Scripted trees are finite: any unexpected input breaks the scenario, and the student gets 'I didn't understand'. LLM generation covers an infinite input space — the character can adequately respond to 'Are you sure?', 'Show me the research', or even 'Let's discuss a discount'. We use JSON mode (gpt-4o), which guarantees a structured output with action evaluation — no text parsing needed.
| Characteristic | Scripted simulations | AI simulations on LLM |
|---|---|---|
| Number of possible dialogues | 5–20 (limited by branches) | Theoretically infinite |
| Reaction to non-standard input | Error or 'I didn't understand' | Appropriate response within role |
| Emotional adaptation | No | Yes (via prompt and history) |
| Creation complexity | Low (diagrams) | Medium (prompts + testing) |
| Cost per session | Fixed ($200-$500 per role-play) | ~2-10 cents (tokens) |
For a character with long-term memory, we connect RAG with ChromaDB: key facts from the dialogue history are stored in a vector database and retrieved in subsequent encounters. This allows the simulation to remember the student's decisions across multiple sessions — critical for skill assessment and progress tracking.
Ready-made simulations by niche
SIMULATION_TEMPLATES = { "sales_objection_handling": SimulationScenario( title="Handling objections: 'It's expensive'", learning_objectives=["Identify the true objection", "Justify the value", "Propose alternatives"], characters=[SimulationCharacter( name="Mikhail Ivanov", role="Potential client, procurement department head", personality="Pragmatic, values specifics, skeptical of salespeople", objectives=["Get the best price", "Ensure supplier reliability"], knowledge="Knows the market, compared competitors", emotional_state="slightly_negative", secret_info="Has budget but wants to test the seller's flexibility" )], context="Final stage of negotiations for an annual IT solution contract", success_criteria=["Identified budget constraints", "Presented ROI calculation", "Didn't reduce price by more than 10%"] ), "medical_consultation": SimulationScenario( title="Primary consultation for a patient with back pain", learning_objectives=["Take history", "Perform differential diagnosis", "Order examinations"], characters=[SimulationCharacter( name="Patient: Elena Smirnova, 42 years old", role="Patient with lower back pain for 2 weeks", personality="Anxious, has read a lot online about diagnoses", objectives=["Get a specific diagnosis", "Find out if surgery is needed"], knowledge="Pain worsens when bending, numbness in toe", secret_info="Fell at work but is embarrassed to say" )], context="Primary consultation with a neurologist at a clinic", success_criteria=["Asked about injuries", "Ordered MRI", "Explained next steps"] ) } Adaptive difficulty
async def adjust_difficulty( session: SimulationSession, current_score: float ) -> str: """Адаптируем поведение персонажа под уровень ученика""" if current_score > 75: return "more_resistant" # Персонаж жёстче elif current_score < 40: return "more_cooperative" # Персонаж мягче, даёт подсказки else: return "neutral" # Стандартное поведение Adaptive difficulty is implemented by changing character parameters: resistance level, amount of hints, frequency of emotion changes. This allows using one scenario for both beginners and experienced employees — development savings reach 80%.
How is simulation effectiveness evaluated?
We implement metrics at each stage:
| Metric | Description | Target |
|---|---|---|
| completeness | Proportion of scenario objectives achieved | >80% |
| score | LLM evaluation of tactics and results | 0-100 |
| user_satisfaction | Post-session survey | >4.0 out of 5 |
| retention | Repeat session after one month | >60% |
In one project, an A/B test showed a 34% improvement in retention compared to a scripted trainer. Over 10,000 sessions were processed monthly with 99.9% uptime.
Checklist for launching your first simulation
- Define learning goals and target audience
- Write a Character card: role, personality, secret information
- Set up the system prompt with role and evaluation criteria
- Test 50+ dialogues for JSON correctness and latency
- Run an A/B test: control group on script, test group on AI
Our process and what's included
- Analysis: we break down learning goals, target audience, typical mistakes. Create a map of scenarios and characters.
- Design: write Character cards (role, personality, success metrics). Set up system prompts — this determines 80% of simulation quality.
- Implementation: build the backend on Python (FastAPI + asyncio), connect the LLM, version prompts via MLflow. For cheap scenarios we use Llama 3 via vLLM, for complex ones — GPT-4o.
- Testing: run 50+ dialogues per scenario, check JSON correctness, measure latency. Automatically generate robust tests.
- Deployment: containerize and deploy in Kubernetes, connect analytics (which sessions succeeded, where students get stuck).
Note: what's included in the result: documentation on scenarios, API specification, embeddable web component, analytics dashboard, 2 weeks of post-deployment support. Contact us — we'll evaluate your scenario in one day. We implement a turnkey simulation with 1-3 characters in a month.
Timeline: MVP for one scenario with one AI character — 2-3 weeks. Platform with a library of scenarios, analytics, and LMS integration — 2-3 months. Over 5 years of experience in AI solutions, 10+ implemented trainers for medical and sales departments. Contact us to discuss your training scenario — we'll propose the architecture and timeline for free.







