Address Copilot comments

This commit is contained in:
Tao Chen
2026-05-13 17:37:23 -07:00
Unverified
parent c3522ac8c6
commit 6daa941cbd
5 changed files with 35 additions and 25 deletions
@@ -116,7 +116,7 @@ curl -X POST "$SEARCH_ENDPOINT/indexes/$INDEX_NAME/docs/index?api-version=2024-0
}'
```
You can also point the sample at any existing index that exposes the four fields above; the provider reads `content`, `sourceName`, and `sourceLink` from the search results.
You can also point the sample at any existing index that exposes a retrievable text field such as `content`.
## Running the Agent Host
@@ -7,7 +7,7 @@ from agent_framework import Agent
from agent_framework.azure import AzureAISearchContextProvider
from agent_framework.foundry import FoundryChatClient
from agent_framework_foundry_hosting import ResponsesHostServer
from azure.identity.aio import DefaultAzureCredential
from azure.identity import DefaultAzureCredential
from dotenv import load_dotenv
# Load environment variables from .env file
@@ -37,9 +37,8 @@ async def main():
credential=credential,
)
async with (
search_provider,
Agent(
async with search_provider:
agent = Agent(
client=client,
instructions=(
"You are a helpful support specialist for Contoso Outdoors. "
@@ -51,8 +50,7 @@ async def main():
# is no need to store history by the service. Learn more at:
# https://developers.openai.com/api/reference/resources/responses/methods/create
default_options={"store": False},
) as agent,
):
)
server = ResponsesHostServer(agent)
await server.run_async()
@@ -0,0 +1 @@
downloaded_skills/
@@ -130,7 +130,7 @@ When deploying, make sure `SKILL_NAMES` is set in your `azd` environment so it g
azd env set SKILL_NAMES "support-style,escalation-policy"
```
If it is not set, running `azd ai agent init -m <agent-manifest.yaml>` will prompt you to enter it interactively.
If it is not set, running `azd ai agent init -m <agent.manifest.yaml>` will prompt you to enter it interactively.
The deployed agent's Managed Identity needs **Azure AI User** on the Foundry project to download skills at startup. Make sure you have run `provision_skills.py` against the same Foundry project before deploying — otherwise the agent will fail to start with HTTP 404 on the skill download.
@@ -39,6 +39,16 @@ DOWNLOADED_SKILLS_DIR: Final = Path(__file__).parent / "downloaded_skills"
logger = logging.getLogger(__name__)
def _safe_extract_zip(zf: zipfile.ZipFile, dest_dir: Path) -> None:
"""Extract ``zf`` into ``dest_dir``, rejecting entries that escape it (zip-slip guard)."""
dest_root = dest_dir.resolve()
for member in zf.infolist():
member_path = (dest_root / member.filename).resolve()
if dest_root != member_path and dest_root not in member_path.parents:
raise RuntimeError(f"Refusing to extract unsafe path '{member.filename}' outside of '{dest_root}'.")
zf.extractall(dest_dir)
async def _bootstrap_skills(endpoint: str, skill_names: list[str], target_dir: Path) -> None:
"""Download each named skill via ``project.beta.skills`` and unpack it as ``<target_dir>/<name>/SKILL.md``."""
if target_dir.exists(): # noqa: ASYNC240
@@ -56,7 +66,7 @@ async def _bootstrap_skills(endpoint: str, skill_names: list[str], target_dir: P
skill_dir = target_dir / name
skill_dir.mkdir()
with zipfile.ZipFile(io.BytesIO(zip_bytes)) as zf:
zf.extractall(skill_dir)
_safe_extract_zip(zf, skill_dir)
if not (skill_dir / "SKILL.md").is_file():
raise RuntimeError(f"Downloaded archive for '{name}' did not contain a SKILL.md at the root.")
@@ -77,23 +87,24 @@ async def main() -> None:
# instruction-only.
skills_provider = SkillsProvider.from_paths(skill_paths=str(DOWNLOADED_SKILLS_DIR))
client = FoundryChatClient(
project_endpoint=project_endpoint,
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=DefaultAzureCredential(),
)
async with DefaultAzureCredential() as credential:
client = FoundryChatClient(
project_endpoint=project_endpoint,
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=credential,
)
agent = Agent(
client=client,
instructions="You are a customer-support assistant for Contoso Outdoors.",
context_providers=[skills_provider],
# History will be managed by the hosting infrastructure, thus there
# is no need to store history by the service. Learn more at:
# https://developers.openai.com/api/reference/resources/responses/methods/create
default_options={"store": False},
)
server = ResponsesHostServer(agent)
await server.run_async()
agent = Agent(
client=client,
instructions="You are a customer-support assistant for Contoso Outdoors.",
context_providers=[skills_provider],
# History will be managed by the hosting infrastructure, thus there
# is no need to store history by the service. Learn more at:
# https://developers.openai.com/api/reference/resources/responses/methods/create
default_options={"store": False},
)
server = ResponsesHostServer(agent)
await server.run_async()
if __name__ == "__main__":