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:
@@ -401,6 +401,12 @@ func coreLoop(ctx context.Context, startAgent *Agent, inputMessages []llm.Messag
|
||||
|
||||
emptyOutputRetries := 0
|
||||
|
||||
// forcedSynthesis records that the turn budget was exhausted while
|
||||
// the agent was still exploring with a pending structured output, so
|
||||
// the last turn was spent forcing the schema rather than failing
|
||||
// outright. It guards against looping past the cap more than once.
|
||||
forcedSynthesis := false
|
||||
|
||||
structuredFormat := resolveStructuredFormat(s.agent)
|
||||
|
||||
// When the agent has both tools and a structured output request,
|
||||
@@ -440,7 +446,33 @@ func coreLoop(ctx context.Context, startAgent *Agent, inputMessages []llm.Messag
|
||||
}
|
||||
|
||||
if s.turns >= s.agent.maxTurns {
|
||||
return s.finishRun(ctx, nil, &MaxTurnsExceededError{MaxTurns: s.agent.maxTurns})
|
||||
// The turn budget is exhausted. If the agent is still
|
||||
// exploring with tools and owes a structured output, spend
|
||||
// one final turn forcing the schema (ToolChoice=none) so it
|
||||
// emits the best answer it can from what it has gathered,
|
||||
// rather than failing with nothing. This runs at most once;
|
||||
// the next iteration falls through to the error below.
|
||||
if !exploring || forcedSynthesis || structuredFormat == nil || len(s.toolDefs) == 0 {
|
||||
return s.finishRun(ctx, nil, &MaxTurnsExceededError{MaxTurns: s.agent.maxTurns})
|
||||
}
|
||||
|
||||
forcedSynthesis = true
|
||||
exploring = false
|
||||
|
||||
s.messages = append(
|
||||
s.messages,
|
||||
llm.Message{
|
||||
Role: llm.RoleUser,
|
||||
Parts: []llm.Part{llm.TextPart{Text: synthesisNudge}},
|
||||
},
|
||||
)
|
||||
|
||||
s.logger.WarnCtx(
|
||||
ctx,
|
||||
"max turns reached while exploring: forcing final synthesis turn",
|
||||
log.Int("turn", s.turns),
|
||||
log.Int("max_turns", s.agent.maxTurns),
|
||||
)
|
||||
}
|
||||
|
||||
fullMessages := buildFullMessages(s.systemPrompt, s.messages)
|
||||
@@ -533,7 +565,7 @@ func coreLoop(ctx context.Context, startAgent *Agent, inputMessages []llm.Messag
|
||||
Parts: []llm.Part{llm.TextPart{Text: synthesisNudge}},
|
||||
},
|
||||
)
|
||||
s.logger.WarnCtx(
|
||||
s.logger.DebugCtx(
|
||||
ctx,
|
||||
"entering synthesis turn: forcing structured output with tool_choice=none",
|
||||
log.Int("turn", s.turns),
|
||||
|
||||
@@ -62,7 +62,7 @@ func FindLinksMatchingTool(b *Browser) agent.Tool {
|
||||
|
||||
js := fmt.Sprintf(
|
||||
`(() => {
|
||||
const pattern = JSON.parse(%s).toLowerCase();
|
||||
const pattern = (%s).toLowerCase();
|
||||
const normalize = s => s.replace(/[-_\s]+/g, "");
|
||||
const normalizedPattern = normalize(pattern);
|
||||
return Array.from(document.querySelectorAll("a[href]"))
|
||||
|
||||
Reference in New Issue
Block a user