How to get citation links from Azure AI Agents reposnse through MessageTextAnnotation

Jagan P 0 Reputation points
2025-04-25T17:19:57.47+00:00

We are using Semantic Kernel framework to integrate Azure agents with our application.

We have added Azure AI Search service to one of our agents and upon querying the agent it returned search response along with few citation links (in Azure AI Foundry).

Now I tried the same in C#, but I am unable to retrieve the citation links from Azure.AI.PRopjects.MessageTextAnnotation

I can only get citation quote (【3:2†source】)

Below is the C# code we use

private async Task<string> InvokeAgentThreadAsync(AzureAIAgent aIAgent, string query)
{
    Response<AgentThread> threadResponse = await _agentsClient.CreateThreadAsync();
    AgentThread thread = threadResponse.Value;
    await _agentsClient.CreateMessageAsync(
       thread.Id,
       MessageRole.User,
       query);
    Response<ThreadMessage> messageResponse = await _agentsClient.CreateMessageAsync(
        thread.Id,
        MessageRole.User,
        query);
    ThreadMessage message = messageResponse.Value;
    Response<ThreadRun> runResponse = await _agentsClient.CreateRunAsync(thread, aIAgent.Definition);
    do
    {
        await Task.Delay(TimeSpan.FromMilliseconds(500));
        runResponse = await _agentsClient.GetRunAsync(thread.Id, runResponse.Value.Id);
    }
    while (runResponse.Value.Status == RunStatus.Queued
        || runResponse.Value.Status == RunStatus.InProgress
        || runResponse.Value.Status == RunStatus.RequiresAction);
    Response<PageableList<ThreadMessage>> afterRunMessagesResponse
            = await _agentsClient.GetMessagesAsync(thread.Id);
    IReadOnlyList<ThreadMessage> messages = afterRunMessagesResponse.Value.Data;
    string chatResult = null;
    foreach (ThreadMessage threadMessage in messages)
    {
        Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
        foreach (MessageContent contentItem in threadMessage.ContentItems)
        {
            if (contentItem is MessageTextContent textItem)
            {
                Console.Write(textItem.Text);
                chatResult = textItem.Text;
                string annotatedText = textItem.Text;
                foreach (MessageTextAnnotation annotation in textItem.Annotations)
                {
                    if (annotation is MessageTextUrlCitationAnnotation textUrl) // here MessageTextUrlCitationAnnotation is undefined
                    {
                        annotatedText = annotatedText.Replace(
                            textUrl.Text,
                            $" [see {textUrl.UrlCitation.Title}] ({textUrl.UrlCitation.Url})");
                    }
                    if (annotation is MessageTextFileCitationAnnotation urlAnnotation)
                    {
                        annotatedText = annotatedText.Replace(
                            urlAnnotation.Text,
                            $" [see {urlAnnotation.Text}] ({urlAnnotation.Quote}) {urlAnnotation.FileId}"); // urlAnnotation is null
                    }
                }
            }
            Console.WriteLine();
        }
    }
    return chatResult;
}
Azure AI Search
Azure AI Search
An Azure search service with built-in artificial intelligence capabilities that enrich information to help identify and explore relevant content at scale.
1,283 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Sina Salam 19,616 Reputation points
    2025-04-30T12:29:32.9966667+00:00

    Hello Jagan P,

    Welcome to the Microsoft Q&A and thank you for posting your questions here.

    I understand that you would like to get citation links from Azure AI Agents reposnse through MessageTextAnnotation.

    Regarding your code and explanations:

    This is a prerequisite before you will implement the code below:

    1. Check your SDK and make sure you are using the correct SDK using bash command: dotnet add package Azure.AI.Projects --prerelease
    2. NOTE: MessageTextAnnotation only includes text (the "quote"). File/URL citation metadata is only available if Azure AI Agents used file-based retrieval (i.e., not just text from AI Search).

    There are two method you can use to achieve this, either you use reflection or JSON fallback as shown below:

    foreach (ThreadMessage threadMessage in messages)
    {
        foreach (MessageContent contentItem in threadMessage.ContentItems)
        {
            if (contentItem is MessageTextContent textItem)
            {
                string text = textItem.Text;
                foreach (MessageTextAnnotation annotation in textItem.Annotations)
                {
                    // File-based citation (from uploaded PDFs, etc.)
                    if (annotation is MessageTextFileCitationAnnotation fileCitation && fileCitation.FileId != null)
                    {
                        Console.WriteLine($"Quote: {fileCitation.Text}");
                        Console.WriteLine($"FileId: {fileCitation.FileId}");
                        Console.WriteLine($"Quote: {fileCitation.Quote}");
                    }
                    // Try URL-based citation (rare - mostly in web-grounded data)
                    if (annotation.GetType().Name == "MessageTextUrlCitationAnnotation")
                    {
                        var citationUrlProp = annotation.GetType().GetProperty("UrlCitation");
                        var urlCitation = citationUrlProp?.GetValue(annotation);
                        if (urlCitation != null)
                        {
                            var url = urlCitation.GetType().GetProperty("Url")?.GetValue(urlCitation)?.ToString();
                            var title = urlCitation.GetType().GetProperty("Title")?.GetValue(urlCitation)?.ToString();
                            Console.WriteLine($"URL Title: {title}, URL: {url}");
                        }
                    }
                }
            }
        }
    }
    

    Since MessageTextUrlCitationAnnotation might not be accessible, use reflection to access UrlCitation dynamically will be one of the best options. This also guards against runtime failures and works in most SDK versions.

    Secondly, to use raw JSON inspection, if the above fails (e.g., annotations are completely empty), do:

    string json = JsonSerializer.Serialize(threadMessage);
    Console.WriteLine(json); // Inspect for "urlCitation" or "fileCitation"
    using JsonDocument doc = JsonDocument.Parse(json);
    foreach (var msg in doc.RootElement.GetProperty("content_items").EnumerateArray())
    {
        if (msg.TryGetProperty("annotations", out var annotations))
        {
            foreach (var annotation in annotations.EnumerateArray())
            {
                if (annotation.TryGetProperty("urlCitation", out var urlCitation))
                {
                    Console.WriteLine($"Title: {urlCitation.GetProperty("title").GetString()}");
                    Console.WriteLine($"URL: {urlCitation.GetProperty("url").GetString()}");
                }
            }
        }
    }
    

    I hope this is helpful! Do not hesitate to let me know if you have any other questions or clarifications.


    Please don't forget to close up the thread here by upvoting and accept it as an answer if it is helpful.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.