2121import com .qlangtech .tis .aiagent .core .IAgentContext ;
2222import com .qlangtech .tis .aiagent .llm .LLMProvider ;
2323import com .qlangtech .tis .aiagent .llm .UserPrompt ;
24+ import com .qlangtech .tis .datax .TimeFormat ;
2425import com .qlangtech .tis .plugin .ontology .chatbi .prompt .PromptBuilder ;
2526import com .qlangtech .tis .plugin .ontology .chatbi .trace .TraceWriter ;
2627import com .qlangtech .tis .plugin .ontology .chatbi .validation .AstValidator ;
3435import org .slf4j .LoggerFactory ;
3536
3637import java .util .ArrayList ;
38+ import java .util .Date ;
3739import java .util .List ;
3840import java .util .Objects ;
41+ import java .util .UUID ;
3942
4043/**
4144 * ChatBI 服务默认实现(§5 T5:重试编排)。
@@ -51,6 +54,9 @@ public class DefaultChatBIService implements ChatBIService {
5154
5255 private static final Logger logger = LoggerFactory .getLogger (DefaultChatBIService .class );
5356
57+ // private static final DateTimeFormatter DATETIME_FORMATTER =
58+ // DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
59+
5460 private final GraphRAGService graphRAGService = DefaultGraphRAGService .getInstance ();
5561 private final KeywordWhitelistValidator keywordValidator = new KeywordWhitelistValidator ();
5662 private final AstValidator astValidator = new AstValidator ();
@@ -71,9 +77,9 @@ public void setLlmProvider(LLMProvider llmProvider) {
7177 }
7278
7379 public void setConfigs (com .qlangtech .tis .plugin .ontology .chatbi .config .RetryConfig retryConfig ,
74- com .qlangtech .tis .plugin .ontology .chatbi .config .ValidationConfig validationConfig ,
75- com .qlangtech .tis .plugin .ontology .chatbi .config .ExecutionConfig executionConfig ,
76- com .qlangtech .tis .plugin .ontology .chatbi .config .RetrievalConfig retrievalConfig ) {
80+ com .qlangtech .tis .plugin .ontology .chatbi .config .ValidationConfig validationConfig ,
81+ com .qlangtech .tis .plugin .ontology .chatbi .config .ExecutionConfig executionConfig ,
82+ com .qlangtech .tis .plugin .ontology .chatbi .config .RetrievalConfig retrievalConfig ) {
7783 this .retryConfig = Objects .requireNonNull (retryConfig , "retryConfig can not be null" );
7884 this .validationConfig = Objects .requireNonNull (validationConfig , "validationConfig can not be null" );
7985 this .executionConfig = Objects .requireNonNull (executionConfig , "executionConfig can not be null" );
@@ -82,32 +88,46 @@ public void setConfigs(com.qlangtech.tis.plugin.ontology.chatbi.config.RetryConf
8288
8389 @ Override
8490 public ChatBIResult ask (String domain , String nlq ) {
91+ return ask (domain , nlq , step -> {});
92+ }
93+
94+ @ Override
95+ public ChatBIResult ask (String domain , String nlq , java .util .function .Consumer <TraceStep > stepCallback ) {
96+ // 请求 ID:yyyyMMddHHmmss-{uuid32},贯穿整个流水线,用作 trace 文件名
97+ String createTime = TimeFormat .yyyyMMddHHmmss .format (new Date ());// LocalDateTime.now().format(DATETIME_FORMATTER);
98+ String reqId = createTime + "-" + UUID .randomUUID ().toString ().replace ("-" , "" );
99+
85100 List <TraceStep > trace = new ArrayList <>();
86- long startTime = System .currentTimeMillis ();
87101
88102 try {
89103 // Step 1: 检索
90104 long t1 = System .currentTimeMillis ();
91105 com .qlangtech .tis .plugin .ontology .graphrag .RetrievalOptions retrievalOptions =
92- Objects . requireNonNull ( retrievalConfig , "retrievalConfig can not be null" ) .toRetrievalOptions ();
93- // : com.qlangtech.tis.plugin.ontology.graphrag.RetrievalOptions.defaults();
106+ retrievalConfig != null ? retrievalConfig .toRetrievalOptions ()
107+ : com .qlangtech .tis .plugin .ontology .graphrag .RetrievalOptions .defaults ();
94108 RetrievalResult retrievalResult = graphRAGService .retrieve (domain , nlq , retrievalOptions );
95109 long t2 = System .currentTimeMillis ();
96- trace .add (TraceStep .retrieve (retrievalResult .objectTypes ().size (), retrievalResult .linkers ().size (), t2 - t1 ));
110+ TraceStep retrieveStep = TraceStep .retrieve (retrievalResult .objectTypes ().size (), retrievalResult .linkers ().size (), t2 - t1 );
111+ trace .add (retrieveStep );
112+ stepCallback .accept (retrieveStep );
97113
98114 if (retrievalResult .objectTypes ().isEmpty ()) {
99115 String error = "No relevant ontology found for query: " + nlq ;
100- trace .add (TraceStep .error ("retrieve" , error ));
101- TraceWriter .writeTrace (domain , nlq , trace );
102- return ChatBIResult .fail (error , trace );
116+ TraceStep errStep = TraceStep .error ("retrieve" , error );
117+ trace .add (errStep );
118+ stepCallback .accept (errStep );
119+ TraceWriter .writeTrace (domain , nlq , trace , reqId );
120+ return ChatBIResult .fail (error , trace , reqId );
103121 }
104122
105123 // Step 2: 拼装 Prompt
106124 String graphragContext = retrievalResult .promptContext ();
107125 List <String > systemPrompt = PromptBuilder .buildSystemPrompt (graphragContext );
108126 UserPrompt userPrompt = PromptBuilder .buildInitialPrompt (nlq , graphragContext );
109127 int tokens = PromptBuilder .estimateTokens (systemPrompt .get (0 ) + userPrompt .getPrompt ());
110- trace .add (TraceStep .prompt (tokens , systemPrompt .get (0 ), userPrompt .getPrompt ()));
128+ TraceStep promptStep = TraceStep .prompt (tokens , systemPrompt .get (0 ), userPrompt .getPrompt ());
129+ trace .add (promptStep );
130+ stepCallback .accept (promptStep );
111131
112132 // Step 3-5: LLM 调用 + 校验 + 重试循环
113133 String candidateSql = null ;
@@ -126,29 +146,39 @@ public ChatBIResult ask(String domain, String nlq) {
126146
127147 if (!llmResponse .isSuccess ()) {
128148 String error = "LLM invocation failed: " + llmResponse .getErrorMessage ();
129- trace .add (TraceStep .error ("llm" , error ));
130- TraceWriter .writeTrace (domain , nlq , trace );
131- return ChatBIResult .fail (error , trace );
149+ TraceStep llmErrStep = TraceStep .error ("llm" , error );
150+ trace .add (llmErrStep );
151+ stepCallback .accept (llmErrStep );
152+ TraceWriter .writeTrace (domain , nlq , trace , reqId );
153+ return ChatBIResult .fail (error , trace , reqId );
132154 }
133155
134- trace .add (TraceStep .llm (llmResponse .getModel (), llmResponse .getPromptTokens (),
135- llmResponse .getCompletionTokens (), llmResponse .getContent (), t4 - t3 ));
156+ TraceStep llmStep = TraceStep .llm (llmResponse .getModel (), llmResponse .getPromptTokens (),
157+ llmResponse .getCompletionTokens (), llmResponse .getContent (), t4 - t3 );
158+ trace .add (llmStep );
159+ stepCallback .accept (llmStep );
136160
137161 // 提取 SQL
138162 candidateSql = PromptBuilder .extractSqlFromCodeBlock (llmResponse .getContent ());
139163 if (candidateSql .isBlank ()) {
140164 String error = "Failed to extract SQL from LLM response" ;
141- trace .add (TraceStep .error ("extract" , error ));
142- TraceWriter .writeTrace (domain , nlq , trace );
143- return ChatBIResult .fail (error , trace );
165+ TraceStep extractErrStep = TraceStep .error ("extract" , error );
166+ trace .add (extractErrStep );
167+ stepCallback .accept (extractErrStep );
168+ TraceWriter .writeTrace (domain , nlq , trace , reqId );
169+ return ChatBIResult .fail (error , trace , reqId );
144170 }
145- trace .add (TraceStep .extract (candidateSql ));
171+ TraceStep extractStep = TraceStep .extract (candidateSql );
172+ trace .add (extractStep );
173+ stepCallback .accept (extractStep );
146174
147175 // Step 4: 静态校验
148176 validationResult = validateSql (domain , candidateSql , retrievalResult , enableExplain );
149177 JSONObject issues = new JSONObject ();
150178 issues .put ("issues" , validationResult .issues ());
151- trace .add (TraceStep .validate (validationResult .valid (), validationResult .reason (), issues ));
179+ TraceStep validateStep = TraceStep .validate (validationResult .valid (), validationResult .reason (), issues );
180+ trace .add (validateStep );
181+ stepCallback .accept (validateStep );
152182
153183 if (validationResult .valid ()) {
154184 break ; // 校验通过,退出循环
@@ -157,8 +187,8 @@ public ChatBIResult ask(String domain, String nlq) {
157187 // 校验失败:检查是否为关键字白名单失败(不重试)
158188 if (validationResult .reason () != null && validationResult .reason ().contains ("keyword" )) {
159189 logger .warn ("Keyword whitelist validation failed (no retry): {}" , validationResult .reason ());
160- TraceWriter .writeTrace (domain , nlq , trace );
161- return ChatBIResult .fail (validationResult .reasonAndIssue (), trace , validationResult .exception ());
190+ TraceWriter .writeTrace (domain , nlq , trace , reqId );
191+ return ChatBIResult .fail (validationResult .reasonAndIssue (), trace , reqId , validationResult .exception ());
162192 }
163193
164194 // 其它失败:重试
@@ -178,24 +208,27 @@ public ChatBIResult ask(String domain, String nlq) {
178208 long t5 = System .currentTimeMillis ();
179209 queryResult = executeQuery (domain , candidateSql );
180210 long t6 = System .currentTimeMillis ();
181- trace .add (TraceStep .execute (queryResult .rowCount (), t6 - t5 ));
211+ TraceStep execStep = TraceStep .execute (queryResult .rowCount (), t6 - t5 );
212+ trace .add (execStep );
213+ stepCallback .accept (execStep );
182214 }
183215
184216 // 写入 trace
185- TraceWriter .writeTrace (domain , nlq , trace );
217+ TraceWriter .writeTrace (domain , nlq , trace , reqId );
186218
187219 if (validationResult .valid ()) {
188- return ChatBIResult .success (candidateSql , queryResult , trace );
220+ return ChatBIResult .success (candidateSql , queryResult , trace , reqId );
189221 } else {
190- return ChatBIResult .fail (validationResult .reasonAndIssue (),
191- trace , validationResult .exception ());
222+ return ChatBIResult .fail (validationResult .reasonAndIssue (), trace , reqId , validationResult .exception ());
192223 }
193224
194225 } catch (Exception e ) {
195226 logger .error ("ChatBI ask failed" , e );
196- trace .add (TraceStep .error ("exception" , e .getMessage ()));
197- TraceWriter .writeTrace (domain , nlq , trace );
198- return ChatBIResult .fail ("Internal error: " + e .getMessage (), trace );
227+ TraceStep exceptionStep = TraceStep .error ("exception" , e .getMessage ());
228+ trace .add (exceptionStep );
229+ stepCallback .accept (exceptionStep );
230+ TraceWriter .writeTrace (domain , nlq , trace , reqId );
231+ return ChatBIResult .fail ("Internal error: " + e .getMessage (), trace , reqId );
199232 }
200233 }
201234
0 commit comments