Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions python/agent-framework/sample-agent/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Python
__pycache__/
*.py[cod]
*.pyo
.venv/
venv/
.pytest_cache/
.mypy_cache/
.ruff_cache/
build/
dist/
*.egg-info/

# IDE
.vscode/
.idea/

# Local files
.env
.env.local
*.log

Comment on lines 1 to 22
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding common Python build artifacts to .dockerignore (e.g., .pytest_cache/, .mypy_cache/, .ruff_cache/, build/, dist/, *.egg-info/). Excluding these helps avoid accidentally copying local build outputs into the image and improves build performance.

Copilot uses AI. Check for mistakes.
# Git
.git/
.gitignore

# Documentation
*.md
!README.md
49 changes: 49 additions & 0 deletions python/agent-framework/sample-agent/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

# Python Agent Framework - Container Deployment
# Related to: https://github.com/microsoft/Agent365-Samples/pull/187
FROM python:3.12-slim

WORKDIR /app

# Suppress apt-get warnings in Docker
ENV DEBIAN_FRONTEND=noninteractive

# Install system dependencies (gcc needed for some Python packages)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
curl \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean

# Upgrade pip
RUN pip install --no-cache-dir --upgrade pip

# Copy dependency metadata first for better layer caching
COPY pyproject.toml .

# Install Python dependencies from pyproject.toml
RUN pip install --no-cache-dir .

# Copy application code
COPY . .

# Create non-root user for security
RUN useradd --create-home --shell /bin/bash agentuser \
&& chown -R agentuser:agentuser /app
USER agentuser

Comment on lines 29 to 36
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For faster/cleaner image builds, consider copying only pyproject.toml (and any lock file) first, installing dependencies, then copying the rest of the source. The current “COPY . .” before dependency installation makes Docker layer caching ineffective and will reinstall deps on any source change.

Suggested change
# Copy application code
COPY . .
# Install Python dependencies from pyproject.toml
RUN pip install --no-cache-dir --root-user-action=ignore .
# Copy dependency metadata first for better caching
COPY pyproject.toml .
# Install Python dependencies from pyproject.toml
RUN pip install --no-cache-dir --root-user-action=ignore .
# Copy application code
COPY . .

Copilot uses AI. Check for mistakes.
# Expose port
EXPOSE 3978

# Set environment variables
ENV PORT=3978
ENV PYTHONUNBUFFERED=1

# Health check using PORT env var
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD curl --fail --silent http://127.0.0.1:${PORT}/api/health || exit 1

# Run the agent
CMD ["python", "start_with_generic_host.py"]
82 changes: 82 additions & 0 deletions python/agent-framework/sample-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,88 @@ To set up and test this agent, refer to the [Configure Agent Testing](https://le

For a detailed explanation of the agent code and implementation, see the [Agent Code Walkthrough](AGENT-CODE-WALKTHROUGH.md).

## Container Deployment

Container deployment is the recommended approach for production Agent 365 workloads. Here's why:

### Why Container Deployment?

**Production Readiness**
- **Consistency**: Containers ensure identical behavior across development, staging, and production environments, eliminating "works on my machine" issues
- **Isolation**: Each agent runs in its own isolated environment with explicit dependencies, preventing conflicts with other services
- **Scalability**: Container orchestrators (Kubernetes, Azure Container Apps) can automatically scale agent instances based on demand

**Azure Integration**
- **Azure Container Apps**: Purpose-built for microservices and agents with built-in autoscaling, managed certificates, and seamless Azure service integration
- **Azure Kubernetes Service (AKS)**: Enterprise-grade orchestration for complex multi-agent deployments
- **Azure Container Registry**: Private registry for secure image storage with geo-replication

**Operational Benefits**
- **Health Checks**: Container runtimes monitor `/api/health` to automatically restart unhealthy agents
- **Rolling Updates**: Deploy new versions with zero downtime using blue-green or canary strategies
- **Resource Limits**: Define CPU/memory boundaries to prevent runaway processes

### Network Binding Fix

This sample binds to `0.0.0.0` (all network interfaces) instead of `localhost`. This is **required** for container deployments because:

- `localhost` (127.0.0.1) only accepts connections from inside the container
- External traffic (Bot Framework messages, health checks) comes from outside the container network
- Binding to `0.0.0.0` allows the agent to receive requests on any network interface

**Symptoms if using localhost in containers:**
- Container starts but health checks fail with "Connection refused"
- Agent works locally but fails when deployed to Docker/Kubernetes/Container Apps
- Bot Framework cannot reach the `/api/messages` endpoint

### Build and run with Docker

```bash
# Navigate to the sample directory first
cd python/agent-framework/sample-agent

# Build the container image
docker build -t python-agent .

# Run with required environment variables
docker run -p 3978:3978 \
-e AZURE_OPENAI_ENDPOINT=https://your-endpoint.openai.azure.com/ \
-e AZURE_OPENAI_API_KEY=your-key \
-e AZURE_OPENAI_DEPLOYMENT=gpt-4o \
python-agent
Comment on lines 63 to 75
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docker build/run snippet assumes the current working directory is the sample-agent folder. If a reader runs this from the repo root, it will either fail or pick up the wrong context. Consider clarifying the working directory (cd into python/agent-framework/sample-agent) or using an explicit -f path + build context in the command.

Copilot uses AI. Check for mistakes.
```

### Azure Container Apps

```bash
# Navigate to the sample directory
cd python/agent-framework/sample-agent

# Ensure you're logged in to Azure CLI
az login
az account set --subscription <your-subscription-id>

# Build and push to Azure Container Registry
az acr build --registry <your-acr> --image python-agent:latest .

# Create Container App with required environment variables
az containerapp create \
--name python-agent \
--resource-group <your-rg> \
--environment <your-env> \
--image <your-acr>.azurecr.io/python-agent:latest \
--target-port 3978 \
--ingress external \
--env-vars \
AZURE_OPENAI_ENDPOINT=https://your-endpoint.openai.azure.com/ \
AZURE_OPENAI_API_KEY=secretref:openai-key \
AZURE_OPENAI_DEPLOYMENT=gpt-4o

# Note: For production, use Azure Container Apps secrets for API keys:
# az containerapp secret set --name python-agent --resource-group <your-rg> \
# --secrets openai-key=<your-actual-api-key>
```

## Support

For issues, questions, or feedback:
Expand Down
12 changes: 7 additions & 5 deletions python/agent-framework/sample-agent/host_agent_server.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

"""Generic Agent Host Server - Hosts agents implementing AgentInterface"""

Expand Down Expand Up @@ -332,12 +333,13 @@ async def anonymous_claims(request, handler):
print(f"🏢 {self.agent_class.__name__}")
print("=" * 80)
print(f"🔒 Auth: {'Enabled' if auth_configuration else 'Anonymous'}")
print(f"🚀 Server: localhost:{port}")
print(f"📚 Endpoint: http://localhost:{port}/api/messages")
print(f"❤️ Health: http://localhost:{port}/api/health\n")
print(f"🚀 Server: 0.0.0.0:{port}")
print(f"📚 Endpoint: http://0.0.0.0:{port}/api/messages")
print(f"❤️ Health: http://0.0.0.0:{port}/api/health")
print(" (Use localhost:PORT for local testing)\n")

try:
run_app(app, host="localhost", port=port, handle_signals=True)
run_app(app, host="0.0.0.0", port=port, handle_signals=True)
Comment on lines 341 to +342
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file’s header doesn’t follow the repo’s standard Microsoft MIT header format (it’s currently a single-line “Copyright (c) Microsoft. All rights reserved.”). Since this file is being modified, please update the header to the standard two-line “Copyright (c) Microsoft Corporation.” / “Licensed under the MIT License.” at the top of the file.

Copilot uses AI. Check for mistakes.
except KeyboardInterrupt:
print("\n👋 Server stopped")

Expand Down
Loading