-
Add a deterministic §5 intent stage before ReAct: extract {intent, form, entities, missing slots} via constrained JSON, then expose only the 3-5 tools relevant to that intent instead of all 12. New: - agent/Intent, agent/ToolScope: intent + visible-tool-set model - service/IntentService: constrained-JSON intent/entity extraction - service/OllamaJsonClient: JSON-mode Ollama calls - service/SlotFillService: slot extraction/prefill of known values Wire through AgentFactory, SystemPromptService, QueryTool, ProposeWriteTool, ErpClient, AgentChatController, OpController, chat.html.
-
…at UI (question/form_collect/token) - QueryTool: enforce table allowlist (viw_* + form data-sources + field-dict tables) via jsqlparser TablesNamesFinder -> blocks NL2SQL reading gdslogininfo/sysjurisdiction/ai_op_queue etc; single-table tenant predicate injection (sBrandsId); brand hint in prompt - TracingChatModelListener: config-gated Langfuse ingestion export (generation span) via JDK HttpClient, no new deps; docker-compose.langfuse.yml self-host - chat.html: send identity+token in chat req; render question(options) + form_collect(rich fields); forward Authorization on confirm/cancel - application-saaslocal.yml: langfuse config (off by default)
-
…ormCollect + Skills + KgSearch - AgentIdentity + AgentFactory: build agent per request with token+form-allowlist carried in tool instances (per-call context; robust vs LC4j callback threading) - ErpClient: token-aware read/write/examine; user-token expiry does NOT silently re-login as dev-admin (no privilege escalation) - AuthzService: devIdentity()/userIdentity() resolve granted-form set (sAuthsId) - proposeExamine tool + OpController examine dispatch + forward user Authorization on confirm - InteractionTool.askUser (structured options), FormCollectTool.collectForm (real gdsconfigformslave schema) - SkillService + SkillTool.loadSkill + ai_skill digest in system prompt - KgQueryTool.kgSearch: L2 neighbors/flow + L3 field->table - tools ErpReadTool/ProposeWriteTool/QueryTool/FormCollectTool now per-request (not @Component)
-
…i_op_queue staging cols + ai_skill registry - proposeCreate tool -> ai_op_queue draft(payload) -> confirm executes addBusinessData - ai_op_queue: add sPayload/sSourceRef/bAutoExecute/sResultBillId/sErrorMsg/tExecutedDate/tExpireAt (create/examine/auto-flow ready) - ai_skill registry table + seed playbooks (Skills §6) - QueryTool: self-repair retry (feed SQL error back to model, up to 3x) - TracingChatModelListener: per-call LLM latency/token/error trace (Langfuse hook point)
-
- FormResolverService: shared KG resolution (entity->master table excl. viw_*, field 中文->column, name field, labels) - ErpReadTool.lookupRecord(entity, record): reliably resolves the master table + returns a single record's full labelled fields — fixes the 'specific field of a specific record' case (e.g. 必胜客的销售员 -> 马艺祖) - AuditService + ai_audit_log: immutable audit of propose/confirm/cancel/query (who/when/action/target/result) - QueryTool.queryData(question): read-only SQL escape hatch — coder-model NL2SQL grounded on 字段字典, jsqlparser single-SELECT guard, blocks OUTFILE/LOAD_FILE/information_schema/SLEEP, forced LIMIT, audited. SAFE by construction; NL2SQL table/join accuracy for complex questions is limited (architecture-flagged, deferred). Verified: lookupRecord works; audit rows written; Query guards + LIMIT + audit work (NL2SQL grounding still mis-picks entity master on multi-table questions).