|
| 1 | +""" |
| 2 | +DESCRIPTION: |
| 3 | + This sample demonstrates how to use agent operations with the |
| 4 | + Morningstar OpenAPI tool, from the Azure Agents service using a synchronous client. |
| 5 | + To learn more about OpenAPI specs, visit https://learn.microsoft.com/openapi |
| 6 | + Link to the Morningstar Agent OpenAPI spec: https://developer.morningstar.com/direct-web-services/documentation/intelligence-engine/apps/morningstar-agent-api |
| 7 | + Download the OpenAPI spec and store it as morningstar.json |
| 8 | +USAGE: |
| 9 | + python sample_agents_openapi_morningstar.py |
| 10 | + Before running the sample: |
| 11 | + pip install azure-ai-projects azure-identity jsonref |
| 12 | + Set these environment variables with your own values: |
| 13 | + 1) PROJECT_ENDPOINT - The project endpoint in the format |
| 14 | + "https://<your-ai-services-resource-name>.services.ai.azure.com/api/projects/<your-project-name>" |
| 15 | + 2) MODEL - The model deployment name |
| 16 | + 3) CONNECTION_ID - The connection id in the format |
| 17 | + "/subscriptions/<sub-id>/resourceGroups/<your-rg-name>/providers/Microsoft.CognitiveServices/accounts/<your-ai-services-name>/projects/<your-project-name>/connections/<your-connection-name>" |
| 18 | + You'll also need a JWT token to authorize with the Morningstar service. |
| 19 | + You can fetch this JWT token by using the below code: |
| 20 | + import requests |
| 21 | + from requests.auth import HTTPBasicAuth |
| 22 | + url = "https://www.us-api.morningstar.com/token/oauth" |
| 23 | + response = requests.post(url, auth=auth) |
| 24 | + print(response.text) |
| 25 | + Note: This token expires, please remember to refresh your credentials. |
| 26 | +""" |
| 27 | + |
| 28 | +import os |
| 29 | +import jsonref |
| 30 | +from azure.ai.projects import AIProjectClient |
| 31 | +from azure.ai.agents.models import ( |
| 32 | + OpenApiConnectionAuthDetails, |
| 33 | + OpenApiConnectionSecurityScheme, |
| 34 | + OpenApiTool, |
| 35 | +) |
| 36 | +from azure.identity import DefaultAzureCredential |
| 37 | + |
| 38 | +# Initialize the project client using the endpoint and default credentials |
| 39 | +project_client = AIProjectClient( |
| 40 | + endpoint=os.environ["PROJECT_ENDPOINT"], |
| 41 | + credential=DefaultAzureCredential(exclude_interactive_browser_credential=False), |
| 42 | +) |
| 43 | + |
| 44 | +with open("./morningstar.json", "r") as f: |
| 45 | + openapi_spec = jsonref.loads(f.read()) |
| 46 | + |
| 47 | +# Create Auth object for the OpenApiTool (note that connection or managed identity auth setup requires additional setup in Azure) |
| 48 | +connection_id = os.environ["CONNECTION_ID"] |
| 49 | +auth = OpenApiConnectionAuthDetails( |
| 50 | + security_scheme=OpenApiConnectionSecurityScheme(connection_id=connection_id) |
| 51 | +) |
| 52 | + |
| 53 | +# Create the OpenAPI tool |
| 54 | +openapi = OpenApiTool( |
| 55 | + name="morningstar", |
| 56 | + spec=openapi_spec, |
| 57 | + description="Retrieves information from Morningstar.", |
| 58 | + auth=auth, |
| 59 | +) |
| 60 | + |
| 61 | +INSTRUCTIONS = """ |
| 62 | +You are a helpful assistant that answers questions using Morningstar's data. Only refer to the information found in the **"answer"** and **"sources"** fields of provided responses to generate your answers. If the user asks a complex question, break it down into simpler, more manageable sub-queries to guide your process effectively. |
| 63 | +# Guidelines |
| 64 | +- **Data Usage**: Strictly rely on the "answer" and "sources" fields for information. Do not infer or assume beyond what is explicitly provided. |
| 65 | +- **Complex Questions**: If the question is challenging or multi-faceted, first decompose it into smaller, simpler queries, ensuring each step aligns with the provided data. |
| 66 | +- **Clarity in Explanation**: When delivering the final answer, clearly articulate the reasoning used to arrive at the conclusion based on the Morningstar data provided. |
| 67 | +- **Consistency**: Ensure the terminology and phrasing used aligns with the financial and investment language standard for Morningstar. |
| 68 | +# Steps |
| 69 | +1. Determine if the question is straightforward or complex: |
| 70 | + - If straightforward, proceed directly to formulating an answer based on the "answer" and "sources" fields. |
| 71 | + - If complex, break it into simpler sub-queries, each directly addressable via data from the "answer" and "sources" fields. |
| 72 | +2. Reference only the "answer" and "sources" fields to gather information for responding. |
| 73 | +3. Construct a clear, concise answer or explanation using the gathered data. |
| 74 | +4. If necessary, list the sources referenced to provide users with transparency. |
| 75 | +# Output Format |
| 76 | +The response should be in plain text, clearly structured, and concise. |
| 77 | +For simple queries: |
| 78 | +- A direct, succinct answer based on the given data. |
| 79 | +- Include a reference to the sources field if needed. |
| 80 | +For complex queries: |
| 81 | +- Briefly outline the parallel or sequential reasoning steps taken to address the question. |
| 82 | +- Conclude with the final, comprehensive answer. |
| 83 | +- Optionally: Cite sources derived from the provided data. |
| 84 | +# Example |
| 85 | +### Input: |
| 86 | +"What are the key financial metrics of XYZ Company? What is my best course of action for diversification?" |
| 87 | +### Process: |
| 88 | +1. **Simplify the queries**: |
| 89 | + - What are the key financial metrics of XYZ Company? |
| 90 | + - What strategies can be considered for diversification? |
| 91 | +2. Consult the "answer" and "sources" fields for data relevant to each sub-query. |
| 92 | +### Output: |
| 93 | +- XYZ Company's key financial metrics are as follows: |
| 94 | + - Revenue: [Insert data from answer field] |
| 95 | + - Net Income: [Insert data from answer field] |
| 96 | + - P/E Ratio: [Insert data from answer field] |
| 97 | + (Source: [Insert relevant sources here]) |
| 98 | +- Regarding diversification: |
| 99 | + - Based on provided data, one approach might be to [Insert strategy]. |
| 100 | + (Source: [Insert relevant sources here]) |
| 101 | +# Notes |
| 102 | +- Do not speculate or provide opinions—use only the supplied "answer" and "sources" fields. |
| 103 | +- When data from Morningstar is insufficient to form a complete answer, explicitly communicate the limitation in the response.""" |
| 104 | + |
| 105 | +# Create agent with OpenApi tool and process assistant run |
| 106 | +with project_client: |
| 107 | + agent = project_client.agents.create_agent( |
| 108 | + model=os.environ["MODEL"], |
| 109 | + name="morningstar-agent", |
| 110 | + instructions=INSTRUCTIONS, |
| 111 | + tools=openapi.definitions, |
| 112 | + ) |
| 113 | + |
| 114 | + print(f"Created agent, ID: {agent.id}") |
| 115 | + |
| 116 | + # Create thread for communication |
| 117 | + thread = project_client.agents.threads.create() |
| 118 | + print(f"Created thread, ID: {thread.id}") |
| 119 | + |
| 120 | + # Create message to thread |
| 121 | + message = project_client.agents.messages.create( |
| 122 | + thread_id=thread.id, |
| 123 | + role="user", |
| 124 | + content="What is value investing according to Morningstar?", |
| 125 | + ) |
| 126 | + print(f"Created message, ID: {message.id}") |
| 127 | + |
| 128 | + # Create and process agent run in thread with tools |
| 129 | + run = project_client.agents.runs.create_and_process( |
| 130 | + thread_id=thread.id, |
| 131 | + agent_id=agent.id, |
| 132 | + ) |
| 133 | + print(f"Run finished with status: {run.status}") |
| 134 | + if run.status == "failed": |
| 135 | + print(f"Run failed: {run.last_error}") |
| 136 | + |
| 137 | + run_steps = project_client.agents.run_steps.list(thread_id=thread.id, run_id=run.id) |
| 138 | + |
| 139 | + # Loop through each step |
| 140 | + for step in run_steps: |
| 141 | + print(f"Step {step['id']} status: {step['status']}") |
| 142 | + |
| 143 | + # Check if there are tool calls in the step details |
| 144 | + step_details = step.get("step_details", {}) |
| 145 | + tool_calls = step_details.get("tool_calls", []) |
| 146 | + |
| 147 | + if tool_calls: |
| 148 | + print(" Tool calls:") |
| 149 | + for call in tool_calls: |
| 150 | + print(f" Tool Call ID: {call.get('id')}") |
| 151 | + print(f" Type: {call.get('type')}") |
| 152 | + |
| 153 | + function_details = call.get("function", {}) |
| 154 | + if function_details: |
| 155 | + print(f" Function name: {function_details.get('name')}") |
| 156 | + print() # add an extra newline between steps |
| 157 | + |
| 158 | + # Fetch and log all messages |
| 159 | + messages = project_client.agents.messages.list(thread_id=thread.id) |
| 160 | + for message in messages: |
| 161 | + print( |
| 162 | + f"Message ID: {message.id}, Role: {message.role}, Content: {message.content}" |
| 163 | + ) |
| 164 | + |
| 165 | + # Delete the agent when done |
| 166 | + project_client.agents.delete_agent(agent.id) |
| 167 | + print("Deleted agent") |
0 commit comments