Reduce batch size to 10 and tighten AI price check prompt

Still seeing stubs despite MaxTokens=8192 — smaller batches and explicit
word limits in the prompt eliminate any remaining truncation risk.

- BatchSize: 15 → 10 (~1200 output tokens per batch vs. potential 3000+)
- Prompt: added 20-word cap on assumptions, 25-word cap on reasoning
- Prompt: strengthened "nothing before or after the '['" instruction
- Error log: now includes item IDs and first 300 chars of raw response
  so the next failure tells us exactly what Claude returned
- JS timing: updated batch divisor from 25 → 10 to match actual batch size

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-25 19:57:23 -04:00
parent 2c4c1a6846
commit 9370fcdd8f
2 changed files with 15 additions and 10 deletions
@@ -20,7 +20,7 @@ public class AiCatalogPriceCheckService : IAiCatalogPriceCheckService
private readonly ILogger<AiCatalogPriceCheckService> _logger;
private const string Model = "claude-sonnet-4-6";
private const int BatchSize = 15; // 25 items at ~250 tokens each exceeds 4096 output tokens
private const int BatchSize = 10; // keep responses predictably short; 10 items × ~120 tokens ≈ 1200 output tokens
private static readonly JsonSerializerOptions JsonOpts = new() { PropertyNameCaseInsensitive = true };
@@ -132,10 +132,11 @@ public class AiCatalogPriceCheckService : IAiCatalogPriceCheckService
}
};
var raw = string.Empty;
try
{
var response = await SendAsync(client, parameters);
var raw = response.Content.OfType<TextContent>().FirstOrDefault()?.Text ?? "[]";
raw = response.Content.OfType<TextContent>().FirstOrDefault()?.Text ?? "[]";
var json = ExtractJsonArray(raw);
var claudeItems = JsonSerializer.Deserialize<List<ClaudePriceCheckItem>>(json, JsonOpts) ?? new();
@@ -164,8 +165,10 @@ public class AiCatalogPriceCheckService : IAiCatalogPriceCheckService
}
catch (Exception ex)
{
_logger.LogError(ex, "AI catalog price check batch failed");
// Return stub verdicts so the rest of the report still renders
var preview = raw.Length > 300 ? raw[..300] + "…" : raw;
_logger.LogError(ex,
"AI price check batch failed for items [{ItemIds}]. Raw response preview: {RawPreview}",
string.Join(", ", batch.Select(b => b.Id)), preview);
return batch.Select(item => new CatalogItemPriceVerdict
{
CatalogItemId = item.Id,
@@ -220,10 +223,12 @@ public class AiCatalogPriceCheckService : IAiCatalogPriceCheckService
sb.AppendLine();
sb.AppendLine("If the item already has an ApproximateArea or EstimatedMinutes, use those instead of guessing.");
sb.AppendLine();
sb.AppendLine("Return ONLY a JSON array, no prose, no markdown fences. Use this exact schema for each element:");
sb.AppendLine("IMPORTANT: Keep responses concise to avoid truncation. Limit assumptions to 20 words max. Limit reasoning to 25 words max.");
sb.AppendLine();
sb.AppendLine("Return ONLY a JSON array — no prose, no markdown fences, nothing before or after the '['. Use this exact schema for each element:");
sb.AppendLine(@"{
""catalogItemId"": <int>,
""assumptions"": ""<what you assumed about size/complexity>"",
""assumptions"": ""<≤20 words>"",
""estimatedSqFtMin"": <decimal>,
""estimatedSqFtMax"": <decimal>,
""estimatedMinutesMin"": <int>,
@@ -233,7 +238,7 @@ public class AiCatalogPriceCheckService : IAiCatalogPriceCheckService
""suggestedPriceMin"": <decimal>,
""suggestedPriceMax"": <decimal>,
""confidence"": ""high|medium|low"",
""reasoning"": ""<1-2 sentence explanation>""
""reasoning"": ""<≤25 words>""
}");
return sb.ToString();
}
@@ -12,14 +12,14 @@
// Estimate total seconds based on item count (roughly 12s per batch of 25, min 15s).
function estimateDuration(itemCount) {
var batches = Math.max(1, Math.ceil(itemCount / 25));
return Math.max(15, batches * 12);
var batches = Math.max(1, Math.ceil(itemCount / 10));
return Math.max(15, batches * 10);
}
// Messages keyed to approximate progress milestones (0100).
function messageAt(pct, batchCount) {
if (pct < 10) return 'Loading catalog items…';
if (pct < 20) return 'Sending items to Claude…';
if (pct < 20) return 'Sending items for analysis…';
if (batchCount <= 1) {
if (pct < 75) return 'Analyzing your catalog with AI…';
if (pct < 92) return 'Reviewing pricing data…';