feat(t208): close candidate decision feedback loop

This commit is contained in:
QiuSW
2026-07-28 11:57:25 +08:00
parent 2a5ded42b5
commit e7f4c3e114
35 changed files with 2854 additions and 203 deletions
@@ -418,6 +418,11 @@ func executionReportResponse(report *domain.ExecutionReport) gin.H {
"received_after_execution_expiry": batch.ReceivedAfterExecutionExpiry,
}
}
if dataset := report.DecisionDataset; dataset != nil {
response["candidate_decision_dataset"] = candidateDecisionDatasetResponse(
dataset,
)
}
if outcome := report.Outcome; outcome != nil {
response["outcome"] = gin.H{
"result_type": outcome.ResultType,
@@ -439,6 +444,85 @@ func executionReportResponse(report *domain.ExecutionReport) gin.H {
return response
}
func candidateDecisionDatasetResponse(
dataset *domain.CandidateDecisionDataset,
) gin.H {
observations := make([]gin.H, 0, len(dataset.Observations))
for _, observation := range dataset.Observations {
observations = append(observations, gin.H{
"ordinal": observation.Ordinal,
"title": observation.Title,
"sku_text": observation.SKUText,
"price_text": observation.PriceText,
"product_url": observation.ProductURL,
"image_url": observation.ImageURL,
"evidence_asset_ids": observation.EvidenceAssetIDs,
"collection_status": observation.CollectionStatus,
"observed_at": formatTime(observation.ObservedAt),
})
}
evaluations := make([]gin.H, 0, len(dataset.Evaluations))
for _, evaluation := range dataset.Evaluations {
evaluations = append(evaluations, gin.H{
"candidate_ordinal": evaluation.CandidateOrdinal,
"decision": evaluation.Decision,
"score": evaluation.Score,
"confidence": evaluation.Confidence,
"matched": decodedAuditJSON(&evaluation.MatchedJSON),
"missing_or_uncertain": decodedAuditJSON(&evaluation.MissingOrUncertainJSON),
"rejection_reasons": decodedAuditJSON(&evaluation.RejectionReasonsJSON),
"hard_constraints": decodedAuditJSON(&evaluation.HardConstraintsJSON),
"created_at": formatTime(evaluation.CreatedAt),
})
}
reviews := make([]gin.H, 0, len(dataset.HumanReviews))
for _, review := range dataset.HumanReviews {
reviews = append(reviews, candidateHumanReviewResponse(review))
}
response := gin.H{
"observations": observations,
"evaluations": evaluations,
"human_reviews": reviews,
}
if run := dataset.SearchRun; run != nil {
response["search_run"] = gin.H{
"task_content_sha256": run.TaskContentSHA256,
"execution_mode": run.ExecutionMode,
"search_query": run.SearchQuery,
"app_version": run.AppVersion,
"android_version": run.AndroidVersion,
"pdd_version": run.PDDVersion,
"started_at": formatTime(run.StartedAt),
"received_at": formatTime(run.ReceivedAt),
"observation_count": run.ObservationCount,
"collection_complete": run.CollectionComplete,
"received_after_execution_expiry": run.ReceivedAfterExecutionExpiry,
}
}
if model := dataset.ModelRun; model != nil {
response["model_run"] = gin.H{
"provider_id": model.ProviderID,
"model": model.Model,
"prompt_version": model.PromptVersion,
"schema_version": model.SchemaVersion,
"recommendation_threshold": model.RecommendationThreshold,
"request_sha256": model.RequestSHA256,
"result_sha256": model.ResultSHA256,
"created_at": formatTime(model.CreatedAt),
}
}
if recommendation := dataset.Recommendation; recommendation != nil {
response["recommendation"] = gin.H{
"candidate_ordinal": recommendation.CandidateOrdinal,
"conclusion": recommendation.Conclusion,
"policy_version": recommendation.PolicyVersion,
"reasons": decodedAuditJSON(&recommendation.ReasonsJSON),
"created_at": formatTime(recommendation.CreatedAt),
}
}
return response
}
func decodedAuditJSON(value *string) any {
if value == nil {
return nil