Improve common third-party enrichment quality

Give the company-profile agent (Agent A) the read-only browser toolset
and build it per-run, so it can read footer, imprint, about, and legal
pages and follow a product domain to the corporate one to resolve the
legal name and headquarters address rather than failing cold.

Make the website the hard precondition: when Agent A cannot resolve a
canonical website, skip the compliance-docs agent and logo step instead
of running them blind, which previously produced inconsistent
cross-domain document URLs. Fall back to the catalog display name for
the legal name when nothing better is found, recorded with a distinct
provenance status so a later real find overwrites it.

Rewrite both enrichment prompts in the project's role/task/instructions
XML style, add a domain-consistency rule for document URLs and a
tool-budget directive, and document the prompt style as a rule and guide.

Fix the find_links_matching browser tool, which double-encoded its
pattern and made JSON.parse fail on every keyword, starving any agent
that used it until it hit the turn cap. Salvage output when an agent
exhausts its turn budget while still exploring with a pending structured
output by forcing one final synthesis turn instead of failing outright.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-06-11 16:53:45 +02:00
parent b36bbf9992
commit 8182c61fa8
10 changed files with 301 additions and 92 deletions

View File

@@ -30,10 +30,11 @@ const (
enrichmentSourceEnrichment = "enrichment"
enrichmentSourceExternal = "external"
enrichmentFieldStatusFound = "found"
enrichmentFieldStatusNotFound = "not_found"
enrichmentFieldStatusLowConfidence = "low_confidence"
enrichmentFieldStatusExternal = "exists_external"
enrichmentFieldStatusFound = "found"
enrichmentFieldStatusNotFound = "not_found"
enrichmentFieldStatusLowConfidence = "low_confidence"
enrichmentFieldStatusExternal = "exists_external"
enrichmentFieldStatusFallbackDisplayName = "fallback_display_name"
// Run-level status recorded at the top of the enrichment payload.
enrichmentStatusDone = "done"
@@ -230,6 +231,35 @@ func applyCertifications(
}
}
// applyLegalNameFallback fills legal_name with the catalog display name
// when the merge left the column empty, so it is never blank. A real
// value (agent-resolved or external) already on the row is left
// untouched. The fallback is recorded as enrichment-owned so a later run
// that resolves the real legal entity overwrites it.
func applyLegalNameFallback(
party *coredata.CommonThirdParty,
meta map[string]EnrichmentFieldMeta,
now time.Time,
) {
const name = "legal_name"
if party.LegalName != nil && strings.TrimSpace(*party.LegalName) != "" {
return
}
displayName := strings.TrimSpace(party.Name)
if displayName == "" {
return
}
party.LegalName = &displayName
meta[name] = EnrichmentFieldMeta{
Status: enrichmentFieldStatusFallbackDisplayName,
Source: enrichmentSourceEnrichment,
UpdatedAt: now,
}
}
// normalizeCertifications trims, drops blanks, and de-duplicates the
// certification names returned by the agent, preserving order.
func normalizeCertifications(values []string) []string {