Using AI to Auto‑Generate Forms: Production‑Ready Low‑Code Form Generation with Spring AI Alibaba ReactAgent
The article presents a production‑grade solution that lets users describe a form in natural language, then uses a Spring AI Alibaba ReactAgent powered by a ReAct reasoning loop to retrieve templates, validate fields, generate layout, enforce governance, and finally emit a versioned JSON schema ready for deployment.
Problem Definition: Design, Not Drag‑And‑Drop, Is the Bottleneck
In enterprise low‑code platforms, creating a moderately complex form (20‑40 fields) typically consumes 0.5‑2 person‑days because requirements must be translated into structured fields, templates are hard to reuse, and naming, binding, and versioning inconsistencies arise.
Why an Agent Fits This Problem
Form creation is essentially a structured UI DSL generation task: the input is unstructured natural language, the output is a JSON schema, and the process can be broken into constrained tool steps. ReAct agents excel at such multi‑step reasoning and tool calling.
Goals: Not Just "Can Generate" but "Can Deploy"
Functional: natural‑language to standard form JSON, template reuse, field metadata constraints, automatic layout, human‑in‑the‑loop confirmation, versioning.
Engineering: support synchronous streaming and asynchronous long‑task modes, high concurrency, idempotency, audit, and security.
Governance: schema validation, version control, rollback, and permission checks.
Overall Architecture (Four‑Layer Decoupling)
┌──────────────── User Access Layer ────────────────┐
│ Low‑Code Designer OpenAPI Chat UI │
└───────────────────────────────────────────┘
│
▼
┌──────────────── Intelligent Orchestration Layer ────────────────┐
│ Form ReactAgent │
│ Prompt Router ReAct Loop HITL Gate │
│ Stream Output Task State Retry Guard│
└───────────────────────────────────────────────────────────────┘
│
▼
┌──────────────── Capability Tool Layer ────────────────┐
│ Schema Search Field Registry Validator│
│ Layout Engine Rule Builder Saver│
│ Dictionary API Binding Mapper Diff Tool│
└───────────────────────────────────────────────────────┘
│
▼
┌──────────────── Infrastructure Layer ────────────────┐
│ Redis Elasticsearch MySQL RocketMQ│
│ Nacos Micrometer OTel K8s│
└───────────────────────────────────────────────────────┘Core Design Principles
Agent does not own business truth : field legality, dictionary values, and bindings are resolved by the tool layer.
Model output must be a controlled DSL : final result is a versioned, verifiable JSON contract, not free‑form text.
High‑cost steps are async : LLM calls, vector search, and rule generation run asynchronously when needed.
Human‑in‑the‑loop is mandatory : saving, overwriting, or publishing a schema requires explicit confirmation.
ReAct Reasoning for Form Generation
The user request is broken into a sequence of thoughts, actions, and observations:
Thought → Action → Observation → Thought → … → FinalTypical steps include:
Identify business domain.
Retrieve similar historical templates.
Extract candidate fields.
Fetch standard field metadata and dictionaries.
Generate layout and interaction rules.
Validate the JSON against the DSL contract.
Await human confirmation before persisting.
Domain Model Design
Four core metadata records are defined as Java record s:
public record FieldMeta(
String fieldCode,
String fieldName,
String componentType,
String javaType,
boolean required,
Integer maxLength,
String dictCode,
String tableName,
String columnName,
List<String> tags) {}
public record FormTemplate(
String templateId,
String businessDomain,
String templateName,
Integer version,
String schemaJson,
List<String> keywords,
Double qualityScore) {}
public record FormSchema(
String schemaId,
String schemaName,
Integer version,
LayoutConfig layout,
List<FormControl> controls,
PublishConfig publishConfig) {}
public record FormGenerationTask(
String taskId,
String sessionId,
String tenantId,
String userId,
String requestText,
String status,
Integer retryCount,
String resultSchemaId) {}A field‑metadata center prevents three common hallucination problems: inconsistent naming, wrong component types, and binding errors.
JSON Schema Contract Example
{
"schemaId": "after_sale_workorder_v1",
"schemaName": "售后维修工单",
"version": 1,
"layout": {"type": "grid", "columns": 2, "labelAlign": "right", "gutter": 16},
"controls": [
{"id": "customer_name", "type": "input", "label": "客户姓名", "required": true, "placeholder": "请输入客户姓名", "validation": [{"rule": "required", "message": "客户姓名必填"}, {"rule": "maxLength", "value": 50}], "dataBinding": {"table": "after_sale_order", "column": "customer_name"}},
{"id": "priority", "type": "select", "label": "紧急程度", "required": true, "options": [{"label": "高", "value": "HIGH"}, {"label": "中", "value": "MEDIUM"}, {"label": "低", "value": "LOW"}], "dataBinding": {"table": "after_sale_order", "column": "priority"}},
{"id": "fault_images", "type": "upload", "label": "故障图片", "maxCount": 5, "accept": [".jpg", ".png"], "dataBinding": {"table": "after_sale_order", "column": "fault_images"}}
],
"rules": [{"when": "priority == 'HIGH'", "then": [{"action": "setRequired", "target": "visit_time", "value": true}]}]
}Tool Design
Tools are implemented as Spring @Component s that conform to a BiFunction signature. Example of a historical‑template search tool:
@Component
public class SchemaSearcher implements BiFunction<String, ToolContext, String> {
private final FormTemplateSearchService templateSearchService;
public SchemaSearcher(FormTemplateSearchService templateSearchService) {
this.templateSearchService = templateSearchService;
}
@Override
@Tool(description = "检索相似历史表单模板")
public String apply(String query, ToolContext context) {
List<FormTemplateCandidate> candidates = templateSearchService.hybridSearch(query, 3);
return JsonUtils.toJson(candidates);
}
}Other tools include fetch_field_metadata, validate_fields, compose_form_layout, build_form_rules, validate_schema, and save_form_schema. Retrieval and field‑metadata fetch are parallelizable.
State Machine for Long‑Running Generation
INIT → ANALYZING → TOOL_RUNNING → WAITING_CONFIRM → SAVING → COMPLETED → FAILED → CANCELEDThe state machine enables front‑end polling, back‑end compensation, audit replay, and operational metrics.
Conversation Memory and Context Management
In multi‑instance deployments, session memory must be externalized. Redis is used as the primary store with a 30‑minute TTL and trimming to the most recent 10‑30 messages.
@Component
public class RedisConversationMemory {
private static final String KEY_PREFIX = "agent:chat:";
private final RedisTemplate<String, Object> redisTemplate;
public RedisConversationMemory(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void append(String sessionId, ChatMessage chatMessage) {
String key = KEY_PREFIX + sessionId;
redisTemplate.opsForList().rightPush(key, chatMessage);
redisTemplate.expire(key, Duration.ofMinutes(30));
Long size = redisTemplate.opsForList().size(key);
if (size != null && size > 30) {
redisTemplate.opsForList().trim(key, size - 30, -1);
}
}
}High Concurrency & Scalability Strategies
Dual‑channel: lightweight requests use SSE streaming; heavy requests are queued as async tasks returning a taskId.
Queue throttling with RocketMQ to limit concurrent LLM calls.
Parallel execution of independent tools (template search, field metadata, dictionary lookup).
Multi‑level caching for field metadata, dictionaries, hot template results, and semantic embeddings.
Model‑based routing: simple forms use a lightweight model, complex forms use a higher‑capacity model.
Idempotency Controls
Task‑level idempotency via unique taskId.
Schema version uniqueness enforced by a DB unique constraint on schemaName + version.
Request‑level deduplication using a requestHash cache.
Human‑In‑The‑Loop Integration
Critical actions (save, overwrite, publish, cross‑domain field generation) are intercepted with a compiled graph that pauses before save_form_schema. The front‑end presents field previews, layout previews, diff against historical templates, and risk warnings for user confirmation.
Security, Permission, and Auditing
All tool calls carry a ToolContextMeta containing tenant ID, user ID, trace ID, and permission set. Permissions are fine‑grained (e.g., form.template.read, form.schema.save). Audit logs record who initiated the request, the natural‑language input, invoked tools, tool summaries, generated schema, and confirmation details.
Observability
Metrics are collected via Micrometer for agent request volume, success rate, iteration count, latency percentiles, model token usage, tool call counts, and failure rates. Cost monitoring tracks token consumption per tenant and cache hit ratios.
@Component
public class FormMetrics {
private final MeterRegistry meterRegistry;
public FormMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
public void recordToolLatency(String toolName, long millis) {
Timer.builder("form.agent.tool.latency")
.tag("tool", toolName)
.register(meterRegistry)
.record(Duration.ofMillis(millis));
}
}API Design
Synchronous streaming : POST /api/forms/generate/stream returns SSE events (THINKING, TOOL_RUNNING, WAITING_CONFIRM, FINAL_SCHEMA).
Asynchronous : POST /api/forms/generate/async returns a taskId; status queried via GET /api/forms/tasks/{taskId}; confirmation via POST /api/forms/tasks/{taskId}/confirm.
Real‑World Case Study: After‑Sale Repair Work Order
The operator inputs a request for a repair work order with fields such as customer name, phone, product model, SN, fault description, images, visit time, priority, warranty flag, and processing result. The agent executes the steps described above, retrieves the most similar template (Repair Work Order V3), pulls standard fields, generates layout (two‑column grid), validates rules, and finally presents a preview. After user confirmation, the schema after_sale_workorder_v1 is saved.
Time to go from request to deployable schema drops from 2‑4 hours in a manual process to 5‑15 minutes with the AI‑assisted pipeline.
Common Production Issues & Mitigations
Hallucinated fields : enforce validate_fields and reject non‑standard fields.
Infinite loops : set maxIterations in the ReAct agent and include explicit failure‑stop prompts.
Cost explosion on similar requests : use semantic cache to return cached templates when embedding similarity exceeds a threshold.
Post‑publish compatibility breaks : embed schema version and run compatibility checks before saving.
Deployment Evolution Path
Prototype: Spring Boot + in‑memory memory + local templates (demo).
Initial production: Redis, MySQL, Elasticsearch, SSE (10‑50 QPS).
Stable production: RocketMQ, Redis Cluster, Kubernetes multi‑replica (50‑200 QPS).
Large‑scale: multi‑model routing, distributed tool services, semantic cache (>200 QPS).
Conclusion
Form generation in low‑code platforms is not a simple "let the model write JSON" task; it is a tightly coupled system of Agent + metadata center + rule engine + stable DSL**. By letting the LLM handle understanding and planning while the surrounding services enforce constraints, governance, observability, and scalability, teams can turn a manual, days‑long activity into a fast, reliable, and auditable production workflow.
References
Spring AI Alibaba official site: https://java2ai.com
Spring AI Alibaba GitHub: https://github.com/alibaba/spring-ai-alibaba
Spring AI Alibaba examples: https://github.com/springaialibaba/spring-ai-alibaba-examples
ReAct: Synergizing Reasoning and Acting in Language Models – https://arxiv.org/abs/2210.03629
OpenTelemetry documentation – https://opentelemetry.io/docs/
Ray's Galactic Tech
Practice together, never alone. We cover programming languages, development tools, learning methods, and pitfall notes. We simplify complex topics, guiding you from beginner to advanced. Weekly practical content—let's grow together!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
