Sunday, June 1, 2025

 Another form of the example cited in previous article for automatic query decomposition, parallel execution and reranking of merged results is done with this form of agentic retrieval:

#!/usr/bin/python

from dotenv import load_dotenv

from azure.identity import DefaultAzureCredential, get_bearer_token_provider

import os


load_dotenv(override=True)


project_endpoint = os.environ["PROJECT_ENDPOINT"]

agent_model = os.getenv("AGENT_MODEL", "gpt-4o-mini")

search_endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"]

api_version = os.getenv("AZURE_SEARCH_API_VERSION")

search_api_key = os.getenv("AZURE_SEARCH_ADMIN_KEY")

credential = DefaultAzureCredential()

token_provider = get_bearer_token_provider(credential, "https://search.azure.com/.default")

index_name = os.getenv("AZURE_SEARCH_NEW_INDEX_NAME", "index01")

azure_openai_endpoint = os.environ["AZURE_OPENAI_ENDPOINT"]

azure_openai_api_key = os.getenv("AZURE_OPENAI_API_KEY")

azure_openai_gpt_deployment = os.getenv("AZURE_OPENAI_GPT_DEPLOYMENT", "gpt-4o-mini")

azure_openai_gpt_model = os.getenv("AZURE_OPENAI_GPT_MODEL", "gpt-4o-mini")

azure_openai_embedding_deployment = os.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT", "text-embedding-3-large")

azure_openai_embedding_model = os.getenv("AZURE_OPENAI_EMBEDDING_MODEL", "text-embedding-3-large")

agent_name = os.getenv("AZURE_SEARCH_AGENT_NAME", "agentic-retrieval-drone-images")


from azure.search.documents.indexes.models import KnowledgeAgent, KnowledgeAgentAzureOpenAIModel, KnowledgeAgentTargetIndex, KnowledgeAgentRequestLimits, AzureOpenAIVectorizerParameters

from azure.search.documents.indexes import SearchIndexClient


agent = KnowledgeAgent(

    name=agent_name,

    models=[

        KnowledgeAgentAzureOpenAIModel(

            azure_open_ai_parameters=AzureOpenAIVectorizerParameters(

                resource_url=azure_openai_endpoint,

                deployment_name=azure_openai_gpt_deployment,

                model_name=azure_openai_gpt_model

            )

        )

    ],

    target_indexes=[

        KnowledgeAgentTargetIndex(

            index_name=index_name,

            default_reranker_threshold=2.5

        )

    ],

    request_limits=KnowledgeAgentRequestLimits(

        max_output_size=10000

    )

)


index_client = SearchIndexClient(endpoint=search_endpoint, credential=credential)

index_client.create_or_update_agent(agent)

print(f"Knowledge agent '{agent_name}' created or updated successfully")


from azure.ai.projects import AIProjectClient


project_client = AIProjectClient(endpoint=project_endpoint, credential=credential)


list(project_client.agents.list_agents())

instructions = """

A Q&A agent that can answer questions about the drone images stored in Azure AI Search.

Sources have a JSON description and vector format with a ref_id that must be cited in the answer.

If you do not have the answer, respond with "I don't know".

"""

agent = project_client.agents.create_agent(

    model=agent_model,

    name=agent_name,

    instructions=instructions

)


print(f"AI agent '{agent_name}' created or updated successfully")

from azure.ai.agents.models import FunctionTool, ToolSet, ListSortOrder


from azure.search.documents.agent import KnowledgeAgentRetrievalClient

from azure.search.documents.agent.models import KnowledgeAgentRetrievalRequest, KnowledgeAgentMessage, KnowledgeAgentMessageTextContent, KnowledgeAgentIndexParams


agent_client = KnowledgeAgentRetrievalClient(endpoint=search_endpoint, agent_name=agent_name, credential=credential)


thread = project_client.agents.threads.create()

retrieval_results = {}


def agentic_retrieval() -> str:

    """

        Searches drone images about objects detected and their facts.

        The returned string is in a JSON format that contains the reference id.

        Be sure to use the same format in your agent's response

        You must refer to references by id number

    """

    # Take the last 5 messages in the conversation

    messages = project_client.agents.messages.list(thread.id, limit=5, order=ListSortOrder.DESCENDING)

    # Reverse the order so the most recent message is last

    messages = list(messages)

    messages.reverse()

    retrieval_result = agent_client.retrieve(

        retrieval_request=KnowledgeAgentRetrievalRequest(

            messages=[KnowledgeAgentMessage(role=msg["role"], content=[KnowledgeAgentMessageTextContent(text=msg.content[0].text)]) for msg in messages if msg["role"] != "system"],

            target_index_params=[KnowledgeAgentIndexParams(index_name=index_name, reranker_threshold=2.5)]

        )

    )


    # Associate the retrieval results with the last message in the conversation

    last_message = messages[-1]

    retrieval_results[last_message.id] = retrieval_result


    # Return the grounding response to the agent

    return retrieval_result.response[0].content[0].text


# https://learn.microsoft.com/en-us/azure/ai-services/agents/how-to/tools/function-calling

functions = FunctionTool({ agentic_retrieval })

toolset = ToolSet()

toolset.add(functions)

project_client.agents.enable_auto_function_calls(toolset)


from azure.ai.agents.models import AgentsNamedToolChoice, AgentsNamedToolChoiceType, FunctionName


message = project_client.agents.messages.create(

    thread_id=thread.id,

    role="user",

    content="""

        How many parking lots are empty when compared to all the parking lots?

        How many red cars could be found as parked?

    """

)


run = project_client.agents.runs.create_and_process(

    thread_id=thread.id,

    agent_id=agent.id,

    tool_choice=AgentsNamedToolChoice(type=AgentsNamedToolChoiceType.FUNCTION, function=FunctionName(name="agentic_retrieval")),

    toolset=toolset)

if run.status == "failed":

    raise RuntimeError(f"Run failed: {run.last_error}")

output = project_client.agents.messages.get_last_message_text_by_role(thread_id=thread.id, role="assistant").text.value


print("Agent response:", output.replace(".", "\n"))


import json


retrieval_result = retrieval_results.get(message.id)

if retrieval_result is None:

    raise RuntimeError(f"No retrieval results found for message {message.id}")


print("Retrieval activity")

print(json.dumps([activity.as_dict() for activity in retrieval_result.activity], indent=2))

print("Retrieval results")

print(json.dumps([reference.as_dict() for reference in retrieval_result.references], indent=2))



No comments:

Post a Comment