Monthly PR Trend Agent with Azure AI
Build a Foundry Agent that queries Cosmos DB and generates monthly PR trend reports.
Introduction
30 Second Summary
Keeping up with PR coverage is one thing. Turning a month's worth of scattered news articles into a clear trend report is another challenge entirely.
In this project, you will build a Monthly PR Trend Agent that connects to your Cosmos DB, queries PR articles by date range, and synthesizes them into structured monthly reports using Azure AI Foundry's function-calling capabilities.
What You'll Build
You'll ask the agent "What were the PR trends for July 2026?" and watch it call your custom database tool, pull real articles with sentiment scores, and deliver an analyst-grade summary highlighting key themes and coverage patterns.
By the end of this project, you'll have:
- A Python tool function that queries your Cosmos DB for PR articles filtered by date range, returning structured data your agent can reason over.
- A Foundry Agent with a system prompt tuned for monthly PR trend analysis that automatically requests data when it needs it.
- A complete function-calling execution loop that detects when the agent needs data, runs your tool against live Cosmos DB records, and feeds results back for synthesis.
- Secret Mission: Configure Responsible AI guardrails via the Foundry portal to protect your agent against prompt injection and unsafe content.
Are there any prerequisites?
This project assumes you have enterprise Azure access with a deployed gpt-5.4-mini model, an existing Foundry Agent skeleton (YAML, project endpoint, and calling code), and a Cosmos DB NoSQL database populated with PR trend data.
You also need Azure CLI installed and authenticated against your subscription.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters for your PR reporting workflow.
Set Up the Python Environment
Your Foundry Agent skeleton is in place, but it has no way to reach your Cosmos DB or register tool functions. The missing piece is the Python SDKs that bridge your code to Azure services.
In this step, you'll install the three packages your agent needs, confirm your Azure CLI session is active, and prove you can connect to Cosmos DB directly.
In this step, get ready to:
- Install the azure-ai-projects, azure-cosmos, and azure-identity packages.
- Confirm Azure CLI authentication.
- Test Cosmos DB connectivity by listing your database containers.
Install the required Python packages
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type VS Code and press Enter to open it.
- Click File in the top menu bar.
- Click Open Folder and select the folder that contains your Foundry Agent skeleton files (the YAML and calling code).
- Open the integrated terminal by pressing Ctrl+` (macOS and Windows).
Your agent needs three SDKs: one for the Azure AI Foundry Agent Service, one for Cosmos DB, and one for authentication. You'll pin these in a requirements.txt file so the versions are reproducible.
- Create a new file in VS Code by clicking File then New File.
- Save it immediately as requirements.txt in your project folder by pressing Cmd+S (macOS) or Ctrl+S (Windows).
- Paste the following contents into requirements.txt:
azure-ai-projects>=2.0.0
azure-cosmos>=4.9.0
azure-identity>=1.19.0
What do these packages do?
- azure-ai-projects provides the AIProjectClient for creating agents, threads, and runs in Foundry.
- azure-cosmos provides the CosmosClient for querying your PR data.
- azure-identity provides DefaultAzureCredential for passwordless authentication with Azure services.
- Save requirements.txt.
- Install all three packages by running this command in your terminal:
pip install -r requirements.txt
You should see pip download and install the packages along with their dependencies. The output ends with a success message listing the installed packages.
Seeing permission errors or command not found?
- If pip is not recognised, try pip3 instead. Your system may use pip3 for Python 3.
- If you see a permissions error on macOS or Linux, run python -m pip install -r requirements.txt instead.
Help me troubleshoot pip install errors in my Python project.
Before you run the next command, what do you expect it to print if all three packages installed correctly?
- Verify the packages are importable by running:
python -c "from azure.ai.projects import AIProjectClient; from azure.cosmos import CosmosClient; print('All imports OK')"
You should see All imports OK printed. This confirms all three SDKs are installed and ready to use.
✔️ Awesome, I've got everything!
Great. Double check you've saved requirements.txt and move on to the next substep.
ⓧ I'd like to double check the full code
Your requirements.txt should contain exactly this:
azure-ai-projects>=2.0.0
azure-cosmos>=4.9.0
azure-identity>=1.19.0
Confirm Azure CLI authentication
Your agent code uses DefaultAzureCredential which relies on your Azure CLI session for local development. A quick check confirms the session is live and pointed at the right subscription.
- Confirm your Azure CLI session is active by running:
az account show
You should see JSON output containing your subscription name, subscription ID, and tenant ID. Confirm the subscription matches the one where your Foundry Agent and Cosmos DB live.
Seeing an error about expired tokens?
- Run az login to re-authenticate. A browser window opens for you to sign in.
- After signing in, run az account show again to verify the subscription.
Help me fix my Azure CLI authentication issue.
Test Cosmos DB connectivity
The final check is proving that your Python environment can actually reach your Cosmos DB account and read its containers. This requires your Cosmos DB endpoint and key set as environment variables.
- Export your Cosmos DB connection details by running the following (replace the placeholder values with your actual endpoint and key):
export COSMOS_ENDPOINT="https://your-account.documents.azure.com:443/"
export COSMOS_KEY="your-cosmos-key-here"
Where do I find these values?
In the Azure portal, navigate to your Cosmos DB account. Click Keys in the left sidebar under Settings. Copy the URI for your endpoint and the PRIMARY KEY for your key.
Before you run the connectivity test, what do you expect to see if the connection succeeds?
- Test the connection by listing your database containers:
python -c "
from azure.cosmos import CosmosClient
import os
client = CosmosClient(os.environ['COSMOS_ENDPOINT'], credential=os.environ['COSMOS_KEY'])
db = client.get_database_client('prdata')
containers = [c['id'] for c in db.list_containers()]
print(f'Connected! Found containers: {containers}')
"
You should see output like Connected! Found containers: ['articles']. This proves Python can reach your Cosmos DB and read the container where your PR data lives.
Seeing a connection or authorization error?
- Double check that your COSMOS_ENDPOINT value ends with a trailing slash and port (:443/).
- Verify your COSMOS_KEY is the primary key, not a connection string. The key is a long Base64-encoded string.
- If you see a database-not-found error, confirm your database is named prdata. If yours has a different name, replace 'prdata' in the test command with your actual database name.
Help me troubleshoot my Cosmos DB connection error.
Your environment is wired up and ready. Next, you'll write the Cosmos DB query function that your agent will call to fetch PR articles by month.
Build the Cosmos DB Query Tool
Your Foundry Agent exists, but it has no way to look up information on its own. When it needs PR data from your Cosmos DB, it has to call a function you provide.
In this step, you'll write that function. It queries your database by date range and returns PR articles as JSON. This becomes the bridge between the agent and your data.
In this step, get ready to:
- Write a Cosmos DB query function that retrieves PR articles by month.
- Test the function against your live database to confirm it returns real data.
- Format the function for automatic tool schema generation by the Foundry Agent SDK.
Set up the configuration file
Your function needs connection details for Cosmos DB and the Foundry project. You'll store these as environment variables so credentials stay out of your code.
- Export the required environment variables in your terminal by running these commands:
macOS
export PROJECT_ENDPOINT=your-foundry-project-endpoint
export COSMOS_ENDPOINT=your-cosmos-db-endpoint
export COSMOS_KEY=your-cosmos-db-key
export COSMOS_DATABASE=prdata
export COSMOS_CONTAINER=articles
Windows
$env:PROJECT_ENDPOINT="your-foundry-project-endpoint"
$env:COSMOS_ENDPOINT="your-cosmos-db-endpoint"
$env:COSMOS_KEY="your-cosmos-db-key"
$env:COSMOS_DATABASE="prdata"
$env:COSMOS_CONTAINER="articles"
What are these variables?
PROJECT_ENDPOINT is your Foundry Agent project connection string (from the agent YAML you already have). COSMOS_ENDPOINT and COSMOS_KEY are the same credentials you verified connectivity with in the previous step.
Replace the placeholder values with your real credentials. The database and container names default to prdata and articles respectively. Adjust them if your Cosmos DB uses different names.
- Create a new file called monthly_reporter.py in your project directory and add the imports and configuration:
import os
import time
import json
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient
from azure.ai.agents.models import FunctionTool
from azure.cosmos import CosmosClient
# Configuration
PROJECT_ENDPOINT = os.environ["PROJECT_ENDPOINT"]
MODEL_DEPLOYMENT_NAME = os.environ.get("MODEL_DEPLOYMENT_NAME", "gpt-5.4-mini")
COSMOS_ENDPOINT = os.environ["COSMOS_ENDPOINT"]
COSMOS_KEY = os.environ["COSMOS_KEY"]
COSMOS_DATABASE = os.environ.get("COSMOS_DATABASE", "prdata")
COSMOS_CONTAINER = os.environ.get("COSMOS_CONTAINER", "articles")
What does this code do?
- The imports bring in the Azure SDKs you installed in Step 1: CosmosClient for database access, AIProjectClient for the Foundry Agent, and FunctionTool for registering functions with the agent.
- Required variables (os.environ["KEY"]) crash immediately if missing, which is intentional. You want a clear error rather than a silent misconfiguration.
- Optional variables (os.environ.get("KEY", "default")) fall back to sensible defaults so you only need to export them if your setup differs.
- Save monthly_reporter.py.
- Verify the imports and configuration load correctly by running:
python monthly_reporter.py
The script should exit silently with no output. That means all imports resolved and all environment variables loaded successfully.
Seeing a KeyError?
A KeyError: 'PROJECT_ENDPOINT' (or similar) means the environment variable is not set in your current terminal session.
Re-run the export commands above. Make sure you're in the same terminal window where you exported them.
Help me debug my environment variable setup for the Foundry Agent project.
Write the query function
The FunctionTool class in the Azure AI SDK auto-generates a tool schema from your Python function's signature and docstring. This means type annotations and a well-structured docstring are not optional. They tell the agent exactly what parameters your function accepts and what it returns.
- Add the function definition below the configuration section in monthly_reporter.py. This first part sets up the Cosmos DB client and calculates the date range:
# --- Cosmos DB Query Tool ---
def get_pr_articles_by_month(year: str, month: str) -> str:
"""
Retrieve PR articles from Cosmos DB for a given year and month.
:param year: The year to query, e.g. "2026".
:param month: The month to query as a two-digit string, e.g. "07" for July.
:return: A JSON string containing a list of PR articles with fields: source, sentiment_score, summary, date.
"""
cosmos_client = CosmosClient(COSMOS_ENDPOINT, credential=COSMOS_KEY)
database = cosmos_client.get_database_client(COSMOS_DATABASE)
container = database.get_container_client(COSMOS_CONTAINER)
start_date = f"{year}-{month}-01"
end_month = int(month)
end_year = int(year)
if end_month == 12:
end_date = f"{end_year + 1}-01-01"
else:
end_date = f"{end_year}-{end_month + 1:02d}-01"
What does this code do?
- The type annotations (year: str, month: str) and the docstring are what FunctionTool reads to build the JSON schema the agent sees. Without them, the agent would not know what arguments to pass.
- The date calculation converts a month like "07" into a start/end date range: 2026-07-01 to 2026-08-01. The December edge case rolls over to January of the next year.
- Still inside the get_pr_articles_by_month function, add the query logic directly below the date calculation:
query = """
SELECT c.source, c.sentiment_score, c.summary, c.date
FROM c
WHERE c.date >= @start_date AND c.date < @end_date
ORDER BY c.date DESC
"""
parameters = [
{"name": "@start_date", "value": start_date},
{"name": "@end_date", "value": end_date},
]
items = list(
container.query_items(
query=query,
parameters=parameters,
enable_cross_partition_query=True,
)
)
return json.dumps(items, indent=2)
What does this code do?
- The SQL query uses parameterized values (@start_date, @end_date) instead of string interpolation to prevent injection and let Cosmos DB optimize the query plan.
- enable_cross_partition_query=True allows the query to scan across all partitions. This is needed because your articles may be partitioned by source or date.
- The result is returned as a JSON string because the Foundry Agent SDK passes tool outputs as strings between the agent and your code.
- Save monthly_reporter.py.
Test the function against your live database
Before wiring this function into the agent, you need to confirm it actually returns data from your Cosmos DB. A standalone test proves the query logic and credentials work independently.
- Before you run this, what do you expect to see? If your Cosmos DB has PR articles for July 2026, you should get a JSON array of articles with source, sentiment_score, summary, and date fields.
- Test the function by running:
python -c "from monthly_reporter import get_pr_articles_by_month; print(get_pr_articles_by_month('2026', '07'))"
You should see a JSON array of PR articles printed to your terminal, each containing a news source, sentiment score, summary, and date.
What if I see an empty array?
An empty [] means the query ran successfully but found no articles in that date range. Try a different month that you know has data in your Cosmos DB. Adjust '2026' and '07' in the test command to match a month where you have articles stored.
Seeing a connection error?
If you get a ServiceRequestError or timeout, double-check that your COSMOS_ENDPOINT includes the full URL (e.g. https://your-account.documents.azure.com:443/).
If you see an authorization error, verify your COSMOS_KEY is the primary key from the Cosmos DB **Keys** page in the Azure portal.
Help me troubleshoot my Cosmos DB connection in Python.
✔️ Awesome, I've got everything!
Great. Double check you've saved monthly_reporter.py and confirmed the function returns real data.
ⓧ I'd like to double check the full code
Your monthly_reporter.py should have three sections in this order. Scroll up and confirm each is present:
- Imports and Configuration (7 import lines followed by 6 configuration constants starting with PROJECT_ENDPOINT).
- Function signature and date calculation (def get_pr_articles_by_month(year: str, month: str) -> str: with the docstring, Cosmos client setup, and start/end date logic).
- Query and return (the SQL SELECT query with parameterized @start_date and @end_date, query_items call with enable_cross_partition_query=True, and return json.dumps(items, indent=2)).
Your query tool is built and tested. In the next step, you'll register this function with your Foundry Agent and give it a system prompt that knows how to turn raw PR data into monthly trend reports.
Create the Agent Version with Tool and System Prompt
You have a tested tool function that can pull PR data from Cosmos DB. The missing piece is the agent itself.
In this step, you will wire your get_pr_articles_by_month function into a Foundry Agent as a callable tool, give it a system prompt that knows how to analyze PR trends, and run it to prove it reaches for your data.
In this step, get ready to:
- Wrap your Cosmos DB function in a FunctionTool and connect to your Foundry project.
- Write a system prompt that instructs the agent on PR trend analysis and report structure.
- Send a message to the agent and observe it request your tool.
Wrap the query function in a FunctionTool
The FunctionTool class reads your function's type annotations and docstring to auto-generate the tool schema that the agent uses to understand what arguments it can pass.
- In monthly_reporter.py, scroll to the bottom of the file (below the get_pr_articles_by_month function) and add the following code:
# --- Agent Setup and Execution ---
def main():
credential = DefaultAzureCredential()
project_client = AIProjectClient(
endpoint=PROJECT_ENDPOINT,
credential=credential,
)
# Define the function tool
user_functions = {get_pr_articles_by_month}
functions = FunctionTool(functions=user_functions)
What does this code do?
DefaultAzureCredential picks up the identity you authenticated with az login earlier. AIProjectClient connects to your Foundry project using the endpoint from your environment variables.
Passing your function inside a set ({get_pr_articles_by_month}) to FunctionTool tells the SDK to inspect the function's signature and docstring. It generates a JSON schema describing the year and month parameters so the agent knows exactly how to call it.
Craft the system prompt for PR trend analysis
The system prompt is where you define what the agent does with the data it retrieves. A well-structured prompt produces consistent, well-organized reports every time.
- Directly below the functions = FunctionTool(...) line you just added, paste this system prompt definition:
system_prompt = """You are a Monthly PR Trend Analyst agent. Your role is to analyze PR coverage data retrieved from the database and produce structured monthly trend reports.
When asked about monthly trends:
1. Use the get_pr_articles_by_month tool to fetch PR articles for the requested month.
2. Analyze the returned data to identify:
- Key themes and topics that dominated coverage
- Overall sentiment trends (positive, negative, neutral distribution)
- Notable sources and their sentiment patterns
- Any significant shifts compared to typical coverage
3. Structure your report with clear sections:
- Executive Summary (2-3 sentences)
- Key Themes (bullet points with supporting article counts)
- Sentiment Overview (distribution and notable outliers)
- Notable Coverage (specific high-impact articles)
- Observations (patterns, gaps in coverage, or days without data)
If some days have no data or data points lack a monthly conclusion, note this transparently. If news is spread across multiple days, group by theme rather than by date.
Keep the tone professional and analytical. Ground every claim in the data returned by the tool."""
What does this system prompt do?
The prompt gives the agent a clear identity and a repeatable workflow. Step 1 tells it to call your tool. Steps 2 and 3 define how to analyze and structure the output.
The final instructions handle edge cases in your data. Some days may have no articles, and coverage often spans multiple days on the same theme. Grouping by theme rather than date produces more useful summaries.
Tune the prompt to your needs
This prompt is a starting point. After you see the first report, you can adjust section headings, add industry-specific terminology, or change the sentiment thresholds to match your PR team's preferences.
Create the agent, send a message, and observe the tool request
Now you will create the agent on the Foundry service, open a conversation thread, and ask for a monthly report. The agent will read your system prompt, realize it needs PR data, and request your tool function.
- Still inside the main() function, directly below the system_prompt variable, add the following code to create the agent and start a run:
with project_client:
# Create the agent with the function tool
agent = project_client.agents.create_agent(
model=MODEL_DEPLOYMENT_NAME,
name="monthly-reporter",
instructions=system_prompt,
tools=functions.definitions,
)
print(f"Created agent: {agent.id}")
What does create_agent do?
This registers a new agent on the Foundry service with your model (gpt-5.4-mini), your system prompt as instructions, and the auto-generated tool schema from functions.definitions. The agent now knows it has a get_pr_articles_by_month tool available.
- Directly below the print(f"Created agent: {agent.id}") line (still inside the with project_client: block), add the thread, message, run, and polling code:
# Create a thread and send a message
thread = project_client.agents.create_thread()
print(f"Created thread: {thread.id}")
message = project_client.agents.create_message(
thread_id=thread.id,
role="user",
content="What were the PR trends for July 2026?",
)
print(f"Sent message: {message.id}")
# Create a run
run = project_client.agents.create_run(
thread_id=thread.id,
agent_id=agent.id,
)
print(f"Created run: {run.id}")
# Poll until requires_action
while run.status in ("queued", "in_progress"):
time.sleep(1)
run = project_client.agents.get_run(
thread_id=thread.id,
run_id=run.id,
)
print(f"Run status: {run.status}")
What is happening in this code?
- A thread is a conversation context. It holds the message history between you and the agent.
- The message sends your question to the thread. The agent reads this when the run starts.
- A run triggers the agent to process the thread. It reads your message, checks its instructions, and decides what to do.
- The polling loop checks the run status every second. It exits when the status leaves queued or in_progress. The expected next status is requires_action because the agent needs to call your tool.
- Save monthly_reporter.py.
Before you run this, what do you think will happen? The agent has your tool registered but there is no code to actually execute the tool and return results. Will it complete successfully, or will it stop and ask for something?
- Run your script by executing this command:
python -c "from monthly_reporter import main; main()"
You should see output similar to this:
Created agent: asst_abc123...
Created thread: thread_def456...
Sent message: msg_ghi789...
Created run: run_jkl012...
Run status: requires_action
The run stopped at requires_action. The agent read your question, recognized it needs PR data for July 2026, and is requesting a call to get_pr_articles_by_month. It is waiting for you to execute that function and send the results back.
Why does it stop here?
Function calling in Foundry agents is a two-step handshake. The agent decides it needs data and tells you which function to call with which arguments. Your code must execute the function locally, then submit the output back to the agent so it can continue.
Right now, you have no code to handle this handshake. The agent is stuck waiting. Runs expire after 10 minutes if no tool output is submitted.
Seeing a different status?
- If you see failed, check that your PROJECT_ENDPOINT environment variable is set correctly and that your gpt-5.4-mini deployment is active in your Foundry project.
- If the script hangs for more than 30 seconds, your network may be blocking the connection. Confirm az account show still returns your subscription.
- If you get an import error, make sure you are running the command from the same directory that contains monthly_reporter.py.
Still stuck? Help me debug why my agent run is not reaching requires_action status.
✔️ Awesome, I've got everything!
Great. Double check you have saved monthly_reporter.py with all three sections: the client setup, the system prompt, and the thread/run/poll code inside main().
ⓧ I'd like to double check the full code
Your monthly_reporter.py should now contain these sections in order. Scroll up and confirm each is present:
- Imports (os, time, json, DefaultAzureCredential, AIProjectClient, FunctionTool, CosmosClient).
- Configuration constants (PROJECT_ENDPOINT, MODEL_DEPLOYMENT_NAME, COSMOS_ENDPOINT, COSMOS_KEY, COSMOS_DATABASE, COSMOS_CONTAINER).
- The get_pr_articles_by_month function (from Step 2).
- The # --- Agent Setup and Execution --- comment and main() function with credential, client, FunctionTool, and system_prompt.
- Inside with project_client:: agent creation, thread, message, run, and polling loop ending with print(f"Run status: {run.status}").
Your agent is reaching for data but hitting a dead end. In the next step, you will build the execution bridge that catches the tool call, runs your function with the agent's arguments, and feeds real Cosmos DB data back into the conversation.
Complete the Tool Execution Loop
Your agent knows it needs data. In the previous step you saw the run enter requires_action status, proving the agent tried to call your get_pr_articles_by_month function. But nothing answered.
The agent is stuck in limbo because no execution bridge exists to intercept that request, run your function with the arguments the model chose, and feed the results back. This step builds that bridge so real Cosmos DB data flows into the agent and a complete monthly trend report comes out the other side.
In this step, get ready to:
- Handle the requires_action status by executing the tool call and submitting results back to the agent.
- Extract and print the final monthly PR trend report from the completed run.
- Run the full script end-to-end and verify a structured report appears.
Add the tool execution handler to the polling loop
Right now your polling loop exits as soon as the run leaves queued or in_progress. You need it to also stay alive when requires_action fires, so you can intercept the tool call and respond.
- In monthly_reporter.py, find the existing polling section at the bottom of main(). It starts with this comment and code:
# Poll until requires_action
while run.status in ("queued", "in_progress"):
time.sleep(1)
run = project_client.agents.get_run(
thread_id=thread.id,
run_id=run.id,
)
print(f"Run status: {run.status}")
- Delete that entire block (the comment, the while loop, and the print statement).
- Replace it with the following tool execution loop:
# Poll and handle function calls
while run.status in ("queued", "in_progress", "requires_action"):
time.sleep(1)
run = project_client.agents.get_run(
thread_id=thread.id,
run_id=run.id,
)
if run.status == "requires_action":
tool_calls = run.required_action.submit_tool_outputs.tool_calls
tool_outputs = []
for tool_call in tool_calls:
if tool_call.function.name == "get_pr_articles_by_month":
arguments = json.loads(tool_call.function.arguments)
result = get_pr_articles_by_month(
year=arguments["year"],
month=arguments["month"],
)
tool_outputs.append(
{
"tool_call_id": tool_call.id,
"output": result,
}
)
print(f"Executed tool: {tool_call.function.name}")
if tool_outputs:
run = project_client.agents.submit_tool_outputs_to_run(
thread_id=thread.id,
run_id=run.id,
tool_outputs=tool_outputs,
)
What does this code do?
The while loop now includes requires_action in its status check, so it keeps running instead of exiting when the agent requests a tool.
- run.required_action.submit_tool_outputs.tool_calls extracts the list of tool calls the agent wants executed.
- For each call to get_pr_articles_by_month, the code parses the JSON arguments the model chose (year and month), runs your function, and collects the output.
- submit_tool_outputs_to_run sends the results back to the agent so it can continue generating its response.
Why submit within 10 minutes?
Runs expire 10 minutes after creation. If your tool outputs are not submitted before that window closes, the run fails permanently. For a single Cosmos DB query this is never a problem, but keep it in mind for heavier workloads.
Extract the report and clean up the agent
Once the agent finishes processing the tool results, the run reaches completed status. You need to pull the assistant's final message out of the thread and print the trend report.
- Immediately after the while loop you just added, paste the following code (still inside the with project_client: block):
if run.status == "completed":
messages = project_client.agents.list_messages(thread_id=thread.id)
for msg in messages.data:
if msg.role == "assistant":
for content_block in msg.content:
if content_block.type == "text":
print("\n--- Monthly PR Trend Report ---\n")
print(content_block.text.value)
break
else:
print(f"Run ended with status: {run.status}")
# Cleanup
project_client.agents.delete_agent(agent.id)
print("\nAgent cleaned up.")
- After the closing of the main() function (outside the function body, at the module level), add the entry point:
if __name__ == "__main__":
main()
What does this code do?
- list_messages retrieves all messages in the thread. The code finds the first assistant message and prints its text content as the report.
- delete_agent removes the agent from your Foundry project after each run so you do not accumulate unused agents.
- The if __name__ guard ensures main() only runs when you execute the script directly.
- Save monthly_reporter.py.
Seeing a syntax error on save?
- Make sure the if run.status == "completed": block is at the same indentation level as the while loop (8 spaces inside the with block).
- Confirm that if __name__ == "__main__": is flush left with no indentation.
- Check that the old polling block is fully deleted. Leftover lines cause indentation conflicts.
Ask for help: I'm getting a syntax or indentation error in monthly_reporter.py after adding the tool execution loop. Can you help me debug?
✔️ Awesome, I've got everything!
Great. Double check you have saved monthly_reporter.py and you are ready to run the full script.
ⓧ I'd like to double check the full code
Your monthly_reporter.py is long, so confirm each section is present in order:
- Imports and configuration constants at the top.
- The get_pr_articles_by_month function with Cosmos DB query logic.
- The main() function: credential setup, FunctionTool definition, system prompt, agent creation, thread and message creation, run creation.
- The while loop with requires_action handling and submit_tool_outputs_to_run.
- The if run.status == "completed": message extraction and delete_agent cleanup.
- The if __name__ == "__main__": main() entry point at the bottom of the file.
Run the agent end-to-end
Everything is wired up. Your script creates the agent, asks for a monthly report, intercepts the tool call, queries Cosmos DB, and feeds results back. Time to see if the full loop produces a real report.
Before you run this, what do you expect to see? Will the agent print raw JSON from Cosmos DB, or will it synthesize the data into a structured report with themes and sentiment analysis?
- Run the full script by executing this command:
python monthly_reporter.py
You should see output that follows this progression:
- Created agent: (an agent ID).
- Created thread: (a thread ID).
- Sent message: (a message ID).
- Created run: (a run ID).
- Executed tool: get_pr_articles_by_month.
- --- Monthly PR Trend Report --- followed by a multi-paragraph analysis.
- Agent cleaned up. at the end.
The report itself is a structured analysis synthesized by the model. It references real article sources, sentiment scores, and dates from your Cosmos DB data. The agent did not dump raw JSON. It grouped coverage by theme, summarized sentiment distribution, and highlighted notable articles.
Seeing "Run ended with status: failed"?
- Check that your environment variables COSMOS_ENDPOINT and COSMOS_KEY are set correctly. The tool executes your function but cannot connect if credentials are missing.
- Confirm your PROJECT_ENDPOINT points to the correct Foundry project and your Azure CLI session is still authenticated (az account show).
- If the run timed out, verify you submitted tool outputs quickly. Runs expire 10 minutes after creation.
Ask for help: My monthly_reporter.py run is failing or not producing a report. Can you help me debug?
Secret mission
Configure Guardrails
Your agent ingests external PR data from web sources. That data could contain prompt injection attempts or harmful content embedded in article summaries. Configure Responsible AI guardrails to protect your agent at the points where risk enters the system.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs within your existing enterprise Azure subscription, so there are no additional ongoing costs.
Resources you used:
- Local project files (monthly_reporter.py and requirements.txt).
- Python packages (azure-ai-projects, azure-cosmos, azure-identity).
- Guardrails policy configured on your monthly-reporter agent in the Foundry portal.
Keep everything running
No action needed. Choose this if you plan to continue developing your Monthly PR Trend Agent or want it ready for production use.
- Your local files remain on your machine, ready for the next run.
- The guardrails policy stays active on your agent in the Foundry portal. It adds no cost and protects future runs.
- The agent itself is ephemeral. Your script creates it at the start of each run and deletes it at the end, so no persistent agent lingers between runs.
Pause - I'll come back to this later
No action needed. Foundry agents created by your script do not incur idle charges because the script deletes them at the end of each run.
- Your local files stay in place for when you return.
- The guardrails policy remains configured in the portal at no cost.
- Your Cosmos DB and Foundry project are pre-existing resources unaffected by this project.
Delete - I don't want to use this again
Remove all project files and undo configuration changes.
- Delete your local project files by running the appropriate command for your operating system.
macOS
rm monthly_reporter.py requirements.txt
Windows
Remove-Item monthly_reporter.py, requirements.txt
- Uninstall the Python packages by running:
pip uninstall azure-ai-projects azure-cosmos azure-identity -y
- Remove the guardrails policy from your agent in the Foundry portal.
- Navigate to Build > Agents > select your monthly-reporter agent.
- Click the Guardrails tab.
- Delete the active guardrail policy to remove the configured controls.
What about the agent itself?
Your script already calls project_client.agents.delete_agent(agent.id) at the end of each run. If your script crashed before reaching that line, you can delete any leftover agents from the Foundry portal under Build > Agents.
Nice Work!
Nice Work!
You just built a fully functional Monthly PR Trend Agent that pulls live data from your Cosmos DB and generates actionable monthly reports. Ask it about any month, and it queries your database, analyzes sentiment, and delivers a structured summary.
You've learned how to:
- Built a reusable Cosmos DB query tool with proper type annotations that auto-generates its own tool schema for the agent.
- Wired a custom FunctionTool into a Foundry Agent with a system prompt tuned for PR trend analysis.
- Implemented the complete function-calling execution loop that detects requires_action, executes your tool, and returns real data back to the agent to produce structured reports.
- Secret Mission: Configured Responsible AI guardrails to protect against prompt injection and unsafe content at the User input and Tool response intervention points.
Ready to quiz yourself?