feat(harness,rag): dual LLM audit fields, run conclusion, and hybrid quality

Persist provider reasoning and assistant text separately on agent_reasoning_audit
(DeepSeekAssistantMessage path), extract diagnosis_run.conclusion, enrich RAG
tool audit (step_id/query/qualityScore), gate empty mysql tools, drop devtools,
and align MVP docs after live E2E verification.
This commit is contained in:
zhuyongxin
2026-07-28 19:43:13 +08:00
parent 2f40536248
commit 7ae9707a3b
116 changed files with 8364 additions and 1141 deletions
@@ -6,18 +6,21 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.superbiz.agent.domain.entity.ChatSession;
import com.superbiz.agent.domain.entity.DiagnosisRun;
import com.superbiz.agent.harness.audit.AgentStepAuditTracker;
import com.superbiz.agent.harness.audit.ModelCallComponent;
import com.superbiz.agent.harness.audit.RunConclusionExtractor;
import com.superbiz.agent.harness.contract.IntentType;
import com.superbiz.agent.harness.contract.PreviousTurn;
import com.superbiz.agent.harness.contract.PublishedResult;
import com.superbiz.agent.harness.contract.ReleaseOutcome;
import com.superbiz.agent.harness.core.RunBudgetUsage;
import com.superbiz.agent.harness.core.RunContext;
import com.superbiz.agent.harness.audit.ModelCallComponent;
import com.superbiz.agent.repository.ChatSessionRepository;
import com.superbiz.agent.repository.DiagnosisRunRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import java.time.LocalDateTime;
import java.util.List;
@@ -32,19 +35,28 @@ public class JpaChatRunStore implements ChatRunStore {
private final ObjectMapper objectMapper;
private final ObjectReader publishedReader;
private final PublishedResultPolicy publishedPolicy;
private final AgentStepAuditTracker stepTracker;
public JpaChatRunStore(ChatSessionRepository chatSessions,
DiagnosisRunRepository runs,
ObjectMapper objectMapper) {
this(chatSessions, runs, objectMapper,
new PublishedResultPolicy(PreviousTurnLimits.defaults()));
new PublishedResultPolicy(PreviousTurnLimits.defaults()), null);
}
public JpaChatRunStore(ChatSessionRepository chatSessions,
DiagnosisRunRepository runs,
ObjectMapper objectMapper,
PublishedResultPolicy publishedPolicy) {
this(chatSessions, runs, objectMapper, publishedPolicy, null);
}
@Autowired
public JpaChatRunStore(ChatSessionRepository chatSessions,
DiagnosisRunRepository runs,
ObjectMapper objectMapper,
PublishedResultPolicy publishedPolicy) {
PublishedResultPolicy publishedPolicy,
@Nullable AgentStepAuditTracker stepTracker) {
this.chatSessions = Objects.requireNonNull(chatSessions, "chatSessions must not be null");
this.runs = Objects.requireNonNull(runs, "runs must not be null");
this.objectMapper = Objects.requireNonNull(objectMapper, "objectMapper must not be null");
@@ -52,6 +64,7 @@ public class JpaChatRunStore implements ChatRunStore {
.with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.with(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
this.publishedPolicy = Objects.requireNonNull(publishedPolicy, "publishedPolicy must not be null");
this.stepTracker = stepTracker;
}
@Override
@@ -110,27 +123,34 @@ public class JpaChatRunStore implements ChatRunStore {
String safeContentJson, PublishedResult publishedResult, int durationMs) {
Objects.requireNonNull(context, "context must not be null");
Objects.requireNonNull(outcome, "outcome must not be null");
DiagnosisRun run = requiredRun(context.runId());
run.setIntent(intent);
run.setReleaseOutcome(outcome);
run.setStatus(status(outcome));
run.setAnswer(safeContentJson);
run.setPublishedResult(outcome == ReleaseOutcome.SUCCESS && intent == IntentType.DIAGNOSIS
&& publishedResult != null ? write(publishedResult) : null);
run.setTotalDurationMs(Math.max(0, durationMs));
RunBudgetUsage usage = context.budget().snapshot();
run.setTotalTokenCount(saturatingInt(usage.totalTokens()));
run.setStepCount(context.modelCalls().componentCallCount(
ModelCallComponent.DIAGNOSIS_AGENT));
run.setToolCallCount(saturatingInt(usage.toolCalls()));
runs.save(run);
if (outcome == ReleaseOutcome.SUCCESS || outcome == ReleaseOutcome.FALLBACK) {
chatSessions.findBySessionId(context.sessionId()).ifPresent(session -> {
session.setLastActiveAt(LocalDateTime.now());
session.setMessagePairCount(
session.getMessagePairCount() == null ? 1 : session.getMessagePairCount() + 1);
chatSessions.save(session);
});
try {
DiagnosisRun run = requiredRun(context.runId());
run.setIntent(intent);
run.setReleaseOutcome(outcome);
run.setStatus(status(outcome));
run.setAnswer(safeContentJson);
run.setConclusion(RunConclusionExtractor.extract(objectMapper, safeContentJson));
run.setPublishedResult(outcome == ReleaseOutcome.SUCCESS && intent == IntentType.DIAGNOSIS
&& publishedResult != null ? write(publishedResult) : null);
run.setTotalDurationMs(Math.max(0, durationMs));
RunBudgetUsage usage = context.budget().snapshot();
run.setTotalTokenCount(saturatingInt(usage.totalTokens()));
run.setStepCount(context.modelCalls().componentCallCount(
ModelCallComponent.DIAGNOSIS_AGENT));
run.setToolCallCount(saturatingInt(usage.toolCalls()));
runs.save(run);
if (outcome == ReleaseOutcome.SUCCESS || outcome == ReleaseOutcome.FALLBACK) {
chatSessions.findBySessionId(context.sessionId()).ifPresent(session -> {
session.setLastActiveAt(LocalDateTime.now());
session.setMessagePairCount(
session.getMessagePairCount() == null ? 1 : session.getMessagePairCount() + 1);
chatSessions.save(session);
});
}
} finally {
if (stepTracker != null) {
stepTracker.clear(context.runId());
}
}
}