Local Development
This page covers the practical local setup loop for the current v2 framework, not the early-v2 minimum-only setup.
The modern local dev workflow should help you do five things reliably:
- boot the engine with the right annotations
- run one full conversation locally
- inspect audit and trace output
- reload static config safely
- debug rule, prompt, MCP, and response behavior quickly
Local prerequisites
Use these as the baseline:
- Java 21
- a Spring Boot host application
- a local relational database with the current
ce_*schema - one working
LlmClientbean
Recommended for a productive setup:
- one seeded example intent
- audit enabled
- SSE enabled
@EnableConvEngineCaching@EnableConvEngineAsyncConversation
Minimal application wiring
@SpringBootApplication
@EnableConvEngine(stream = true)
@EnableConvEngineCaching
@EnableConvEngineAsyncConversation
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Add @EnableConvEngineAsyncAuditDispatch only if you want to test async audit dispatch behavior locally.
Required consumer-owned pieces
Your local app still owns:
LlmClient- datasource and JPA config
- any custom task executors
- any tool handlers or transport adapters
- any response/container transformers you use
If any of those are missing, the framework may start but your target flow will still be incomplete.
Recommended local config
Local database setup strategy
For local development, do not start by seeding everything.
A cleaner path is:
- Apply the current DDL for your database dialect.
- Seed one tiny end-to-end intent.
- Add one response.
- Add schema, rules, tools, or pending actions only when the base flow is stable.
The smallest useful local slice is:
- one
ce_intent - one
ce_intent_classifier - one
ce_prompt_template - one
ce_response
Then add:
ce_output_schemaif the flow collects structured fieldsce_ruleif the flow has transitionsce_verboseif you want readable progress feedbackce_mcp_tool/ce_mcp_planneronly when testing MCP
Core local test loop
This is the fastest stable loop for current v2:
- Call
POST /api/v1/conversation/message. - Inspect
GET /api/v1/conversation/audit/{conversationId}. - Inspect
GET /api/v1/conversation/audit/{conversationId}/trace. - Fix DML, prompt rows, or config.
- Refresh caches if needed.
- Re-run the same or a fresh
conversationId.
That loop is still the single most useful way to debug behavior.
Cache-aware local development
The current framework caches static control-plane data. That is good for runtime performance, but it changes how local iteration feels.
When you change seeded rows in tables like:
ce_rulece_responsece_output_schemace_prompt_templatece_pending_actionce_mcp_toolce_mcp_plannerce_verbose
you should refresh the static cache before assuming the runtime sees the new data.
Useful endpoints:
POST /api/v1/cache/refreshGET /api/v1/cache/analyze
Use cache analysis when you need to confirm whether the expected table cache is actually populated and active.
Debugging the most important runtime surfaces
1. Rule and state debugging
Check:
RULE_MATCHRULE_APPLIED- final
intent_codeandstate_code - trace step ordering
If the engine is "technically working" but answering incorrectly, this is often where the root cause lives.
2. Prompt and LLM debugging
Check:
DIALOGUE_ACT_LLM_*INTENT_*SCHEMA_EXTRACTION_LLM_*MCP_PLAN_LLM_*RESOLVE_RESPONSE_LLM_*
Modern v2 uses shared prompt rendering, so a prompt-variable issue can affect multiple steps.
3. MCP debugging
Check:
context.mcp.lifecycle.*context.mcp.toolExecution.*MCP_TOOL_CALLMCP_TOOL_RESULTMCP_TOOL_ERRORMCP_FINAL_ANSWER
Remember that current MCP scope is explicit. If a tool is not visible, verify intent_code, state_code, and whether the row is too narrow or too broad.
4. Direct tool debugging
Check:
tool_requesttool_resulttool_statusTOOL_ORCHESTRATION_REQUESTTOOL_ORCHESTRATION_RESULTTOOL_ORCHESTRATION_ERROR
When to enable optional local features
STOMP
Use STOMP locally only when you need to test websocket clients or broker behavior.
If you do not need bidirectional messaging yet, keep:
- SSE enabled
- STOMP disabled
Async audit dispatch
Use it when you need to test local behavior under heavier audit load. Keep it off when you want the simplest, most deterministic debugging path.
Experimental SQL generation
Enable only when you are explicitly testing the experimental SQL generator APIs:
POST /api/v1/conversation/experimental/generate-sqlPOST /api/v1/conversation/experimental/generate-sql/zip
These routes are gated behind convengine.experimental.enabled=true.
Most common local-development mistakes
- Editing database config and forgetting to refresh caches.
- Testing with multiple browser tabs against the same
conversationId. - Enabling too many features before one base flow is stable.
- Treating prompt rows as plain text instead of behavior contracts.
- Inspecting only the final response and not the trace.
Good local-development target
Your local environment is in good shape when you can do all of the following quickly:
- reproduce a good turn
- reproduce a bad turn
- explain the exact step where it went wrong
- fix the config
- rerun and verify the correction
For current v2, the fastest path is still: change config, refresh cache, call /message, inspect audit/trace, repeat. The framework is heavily data-driven, so a disciplined local trace loop is more valuable than ad hoc code debugging.