mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
b05fc9e849
* Initial plan * Add load_dotenv() to 303 Python samples for environment variable loading Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Update SAMPLE_GUIDELINES.md to document load_dotenv() requirement Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Update samples README.md to document .env file usage Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Run ruff format on all changed sample files Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Clarify load_dotenv() usage in README - local dev vs production Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Remove deprecated getting_started folder as requested Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Document env_file_path parameter for per-client configuration Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Merge main branch to resolve conflicts Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Fix run_evaluation.py file that was empty in merge commit Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Remove dotnet changes from merge - out of scope for this PR Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Remove package and test changes from merge - only sample changes needed Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Remove test_func_utils.py - only sample changes needed Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Revert sample files not in original changeset - keep only load_dotenv additions Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Move load_dotenv() outside snippet tag in 06_host_your_agent.py Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Fix comment placement - move load_dotenv before code comments Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Fix load_dotenv() placement across all samples - after docstring, before code comments Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Merge latest main branch with load_dotenv changes Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Remove non-sample changes from merge - keep only load_dotenv additions Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Revert non-load_dotenv sample changes from merge Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Fix run_evaluation.py - use main's improved version (file already had load_dotenv) Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> * Manual update * Manual update 2 * Fix Role usage and load_dotenv placement per PR review feedback Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * Fix Role usage - use string literals not enum attributes Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * Fix SAMPLE_GUIDELINES.md example - load_dotenv before docstring per guidance Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * Move load_dotenv() before docstrings in all samples per SAMPLE_GUIDELINES ordering Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * Address PR review: rename files, fix placement, add session usage, remove note Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * Update Redis README to reference renamed file redis_history_provider.py Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> Co-authored-by: Tao Chen <taochen@microsoft.com> Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
124 lines
4.6 KiB
Python
124 lines
4.6 KiB
Python
# Copyright (c) Microsoft. All rights reserved.
|
|
|
|
import asyncio
|
|
import os
|
|
from contextlib import suppress
|
|
from typing import Any
|
|
|
|
from agent_framework import Agent, AgentSession, BaseContextProvider, SessionContext, SupportsChatGetResponse
|
|
from agent_framework.azure import AzureOpenAIResponsesClient
|
|
from azure.identity import AzureCliCredential
|
|
from dotenv import load_dotenv
|
|
from pydantic import BaseModel
|
|
|
|
# Load environment variables from .env file
|
|
load_dotenv()
|
|
|
|
|
|
class UserInfo(BaseModel):
|
|
name: str | None = None
|
|
age: int | None = None
|
|
|
|
|
|
class UserInfoMemory(BaseContextProvider):
|
|
DEFAULT_SOURCE_ID = "user_info_memory"
|
|
|
|
def __init__(self, source_id: str = DEFAULT_SOURCE_ID, *, client: SupportsChatGetResponse, **kwargs: Any):
|
|
"""Create the memory.
|
|
|
|
If you pass in kwargs, they will be attempted to be used to create a UserInfo object.
|
|
"""
|
|
super().__init__(source_id)
|
|
self._chat_client = client
|
|
|
|
async def after_run(
|
|
self,
|
|
*,
|
|
agent: Any,
|
|
session: AgentSession | None,
|
|
context: SessionContext,
|
|
state: dict[str, Any],
|
|
) -> None:
|
|
"""Extract user information from messages after each agent call."""
|
|
# ensure you get all the messages you want to parse from, including the input in this case.
|
|
request_messages = context.get_messages(include_input=True, include_response=True)
|
|
# Check if we need to extract user info from user messages
|
|
user_messages = [msg for msg in request_messages if hasattr(msg, "role") and msg.role == "user"] # type: ignore
|
|
|
|
if (state["user_info"].name is None or state["user_info"].age is None) and user_messages:
|
|
with suppress(Exception):
|
|
# Use the chat client to extract structured information
|
|
result = await self._chat_client.get_response(
|
|
messages=request_messages, # type: ignore
|
|
instructions="Extract the user's name and age from the message if present. "
|
|
"If not present return nulls.",
|
|
options={"response_format": UserInfo},
|
|
)
|
|
|
|
# Update user info with extracted data
|
|
with suppress(Exception):
|
|
extracted = result.value
|
|
if state["user_info"].name is None and extracted.name:
|
|
state["user_info"].name = extracted.name
|
|
if state["user_info"].age is None and extracted.age:
|
|
state["user_info"].age = extracted.age
|
|
|
|
async def before_run(
|
|
self,
|
|
*,
|
|
agent: Any,
|
|
session: AgentSession | None,
|
|
context: SessionContext,
|
|
state: dict[str, Any],
|
|
) -> None:
|
|
"""Provide user information context before each agent call."""
|
|
state.setdefault("user_info", UserInfo())
|
|
|
|
context.extend_instructions(
|
|
self.source_id,
|
|
"Ask the user for their name and politely decline to answer any questions until they provide it."
|
|
if state["user_info"].name is None
|
|
else f"The user's name is {state['user_info'].name}.",
|
|
)
|
|
context.extend_instructions(
|
|
self.source_id,
|
|
"Ask the user for their age and politely decline to answer any questions until they provide it."
|
|
if state["user_info"].age is None
|
|
else f"The user's age is {state['user_info'].age}.",
|
|
)
|
|
|
|
|
|
async def main():
|
|
client = AzureOpenAIResponsesClient(
|
|
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
|
deployment_name=os.environ["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"],
|
|
credential=AzureCliCredential(),
|
|
)
|
|
|
|
context_name = UserInfoMemory.DEFAULT_SOURCE_ID
|
|
|
|
# Create the memory provider
|
|
memory_provider = UserInfoMemory(context_name, client=client)
|
|
|
|
# Create the agent with memory
|
|
async with Agent(
|
|
client=client,
|
|
instructions="You are a friendly assistant. Always address the user by their name.",
|
|
context_providers=[memory_provider],
|
|
) as agent:
|
|
# Create a new session for the conversation
|
|
session = agent.create_session()
|
|
|
|
for msg in ["Hello, what is the square root of 9?", "My name is RuaidhrĂ", "I am 20 years old"]:
|
|
print(f"User: {msg}")
|
|
print(f"Assistant: {await agent.run(msg, session=session)}")
|
|
|
|
# Access the memory component and inspect the memories
|
|
print()
|
|
print(f"MEMORY - User Name: {session.state[context_name]['user_info'].name}")
|
|
print(f"MEMORY - User Age: {session.state[context_name]['user_info'].age}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|