step21 · Foundry Web Search (hosted tool + citations)

This lesson teaches how to attach the hosted web-search tool and read the citation annotations the service returns with its answer.

What this lesson demonstrates

A function tool is code you run when the model asks. A hosted tool is different: it’s a marker you attach to the agent that tells the Foundry service “you’re allowed to do this yourself.” &hostedtool.WebSearch{} carries no logic — the service performs the search, grounds its answer on the results, and returns the answer alongside CitationAnnotations pointing at the sources it used. This lesson attaches the tool and adds a pure function to pull those citations out of the response.

Reading citations off the response

The citation reader is a pure function of the response — no network, no globals — which is exactly what makes it directly unit-testable:

func collectCitations(resp *agent.Response) []citation {
    var out []citation
    if resp == nil {
        return out
    }
    for content := range resp.Contents() {
        for _, annotation := range content.Header().Annotations {
            c, ok := annotation.(*message.CitationAnnotation)
            if !ok || c.URL == "" {
                continue
            }
            out = append(out, citation{Title: c.Title, URL: c.URL, Snippet: c.Snippet})
        }
    }
    return out
}

What to notice / the gotcha

How it maps to the Agent Framework

hostedtool.WebSearch mirrors hostedtool.CodeInterpreter (step14): both are server-side capabilities the agent merely declares. What’s new here is the return path — the Go SDK surfaces grounding sources as CitationAnnotations on content headers, giving you attributable, auditable answers rather than an opaque blob of text.

Run it

go run ./tutorial/02-agents/providers/foundry/step21_web_search

The live run needs az login, FOUNDRY_PROJECT_ENDPOINT, and a web-search-capable deployment. Offline tests feed a hand-built response (one annotation with a URL, one without) to collectCitations and assert it keeps the URL’d one; the live call is gated behind AF_LIVE=1.


Next: step23 · Local MCP (wrapping remote tools)

Sources & References

The Go SDK this lesson exercises
Hosted web-search tool and grounding citations