Task: Implement a custom AI dialog engine with dynamic scenarios
Implement a custom AI dialog engine with dynamic scenarios
Build a multi-user dialog runtime from scratch without n8n or similar workflow solutions, featuring externally defined complex scenarios and independent state for each dialog.
Implement a custom AI dialog engine with dynamic scenarios
Context
The "AI Agent Center" project must have its own dialog subsystem. It cannot be built on n8n, Make, ready-made chatbot/workflow builders, or similar systems where the core dialog logic is executed by an external engine.
We need to implement our own core that accepts an external description of a complex scenario, guides the user through it dynamically, and returns the result. Each user dialog must exist and be computed as a separate, independent entity with its own state, history, and result.
Goal
Create a universal AI dialog execution service that can be used by various websites and agents without hardcoding specific scenarios into the application code.
The service must be able to:
- accept a dialog scenario externally;
- create a separate session/dialog for a specific user;
- determine the next step dynamically on each message based on the scenario, accumulated state, and user response;
- call the LLM where required by the logic;
- store dialog state and history;
- complete the dialog with a formalized result;
- concurrently serve many users and many independent dialogs.
Key Architectural Principle
The scenario is data, not the code of a specific chat.
The dialog engine must be universal. An external system passes a declarative scenario description to it, and the runtime interprets it and manages transitions.
It should not be necessary to program a new backend flow for each new scenario.
What to Implement
1. Scenario Model
Design a scenario format that can be transmitted via API and stored with versioning.
At a minimum, it must include:
- scenario identifier and version;
- starting point;
- steps/nodes;
- system instructions for the LLM;
- expected user data;
- conditions and branching;
- transitions between steps;
- intermediate variables/context;
- computed values;
- pre/post-step actions;
- completion conditions;
- final result schema.
The scenario must support not only linear question chains, but also dynamic branching.
2. Dialog Runtime
Implement a custom scenario execution machine.
On each incoming message, the runtime must:
- load the state of the specific dialog;
- determine the current scenario node;
- process user input;
- call the LLM if necessary;
- validate/normalize the received data;
- compute transition conditions;
- update the state;
- generate the next response;
- save the history and new state atomically;
- return the response and execution technical state externally.
Transition logic must be executed inside our service, not within an external workflow engine.
3. Dialog State
Store each dialog separately.
At a minimum, the following must be provided:
dialogId;- tenant/site/agent/user identifiers;
- scenarioId + scenarioVersion;
- current node;
- accumulated state / variables;
- message history;
- LLM call results required for reproducibility;
- status: active / completed / failed / cancelled;
- timestamps;
- final structured result.
A single user can have multiple independent dialogs.
4. Multi-user Mode
The service must function correctly under concurrent dialogs from different users and sites.
It is mandatory to provide:
- state isolation per dialog;
- absence of global mutable state between users;
- protection against simultaneous processing of two messages in the same dialog;
- idempotency for message redelivery;
- horizontal scalability across multiple runtime instances;
- tenant/site isolation at the API and data storage levels.
5. External API
A clear programmatic contract is needed for external systems.
Minimum set of operations:
- register/update a scenario version;
- create a dialog based on a scenario;
- send a message to a specific dialog;
- get current state/status;
- get history;
- get the final result;
- cancel/close the dialog.
The response to sendMessage must contain not only the text for the user, but also a machine-readable part when necessary:
- status;
- current/next step;
- extracted values;
- result upon completion;
- technical execution metadata.
6. Working with LLMs
The LLM is part of the runtime, but should not itself be the sole flow control source.
It is necessary to separate:
- deterministic transition logic;
- data extraction/classification via LLM;
- response text generation;
- validation of structured model responses.
For structured operations, use schema-constrained output and mandatory model result validation.
Handle the following:
- timeouts;
- malformed output;
- retries;
- fallbacks;
- rate limits;
- provider errors.
7. Versioning and Reproducibility
A running dialog must continue to execute on the scenario version it was created with, even if a new version is published later.
It is forbidden to change the semantics of an ongoing dialog via silent scenario updates.
8. Observability
Store a technical trace for each step:
- input;
- selected node;
- decision made;
- transition;
- model calls;
- latency;
- errors;
- token/cost usage, if available;
- step outcome.
This should allow investigating why a specific dialog took a certain branch.
9. Scenario Execution Security
An external scenario must not allow the execution of arbitrary code on the server.
Conditions, expressions, and actions must be executed via a restricted DSL/set of allowed operations.
eval or arbitrary JavaScript from the submitted scenario must not be used.
What is Out of Scope for This Task
- visual scenario builder;
- operator/manager UI;
- chat widget on a specific website;
- CRM integrations as a separate large layer;
- analytical BI dashboard;
- workflow building on n8n/Make and similar products.
These parts may use the dialog runtime later, but must not dictate its architecture.
Minimum Verification Scenario
For acceptance, implement at least one scenario that confirms the dynamic operation of the engine:
- multiple questions;
- at least one conditional branch;
- extraction of a structured value via LLM;
- returning to a clarifying question when data is insufficient;
- completion with a machine-readable result.
Simultaneously launch multiple dialogs using the same scenario with different responses and confirm that states and results do not mix.
Definition of Done
- The dialog runtime is written by us and does not depend on an external workflow engine.
- A new scenario can be passed via the external contract without modifying the runtime code.
- Non-linear branching and dynamic transitions are supported.
- Each dialog has an independent persistent state.
- A single user can have multiple dialogs, and the service can concurrently serve many users/sites.
- Protection against race conditions and message redelivery is implemented.
- The scenario version is fixed for the lifetime of the dialog.
- The final result is available in a structured format.
- Traces make it possible to understand how and why each step was executed.
- The verification scenario passes end-to-end and confirms the isolation of parallel dialogs.
Ворклоги
Runtime Foundation: Streaming Execution and Cancellation
A low-level execution mechanism has already been implemented for the upcoming proprietary dialogue runtime:
- a shared
ExecutionContextwith a run identifier, abort signal, and event delivery; - a streaming NDJSON contract
POST /api/chat; - correct started/delta/done/error/cancelled states;
- explicit execution cancellation as well as cancellation on disconnect/timeout/shutdown;
- independence of parallel runs;
- input and response size limits.
The HTTP layer has been re-verified with 8/8 tests.
This does not mean the scenario engine is ready: the declarative scenario model, nodes/branching, scenario versioning, persistent dialogue state, message idempotency, and atomic transitions are not yet implemented.
Minimum Agent Event Model and LLM
A basic agent reaction layer has been implemented, suitable as a building block for the future runtime:
Agentclass andPOST /api/agentendpoint;- basic chain
message.received → Agent.react → message.publish; - connection between the action and the source event via
stimulusIdand its ownactionId; - JSON/NDJSON and a general cancellation mechanism;
Agent.reactnow calls the realrunChat/LLM instead of a mock;- successfully re-passed 2/2 agent tests for fragments/action identity, cancellation, and provider error.
The current implementation handles a single incoming event type and the chat channel; the model receives only the current message. There is currently no conversation memory, accumulation of multiple events, deduplication, general decision-making system, or scenario transition machine.