Using Foundry-Created Agents in Engram
Status: Analysis & Implementation Guide
Last Updated: January 2026
Approach: Using Foundry Agent Service runtime instead of LangGraph
Overview
Yes, you can create agents in Azure AI Foundry and use them in Engram! This is a different approach than our current LangGraph-based agents, offering managed infrastructure and built-in capabilities.
Two Approaches Compared
Current Approach: LangGraph Agents
What We Have:
- Custom LangGraph agents (Elena, Marcus, Sage)
- Custom system prompts
- Custom tools (LangChain tools)
- Custom reasoning workflows
- Full control over agent logic
Pros:
- ✅ Maximum flexibility
- ✅ Custom workflows (reason → tool → respond)
- ✅ Full control over tool execution
- ✅ Can use any LangChain tool
- ✅ Complex multi-step reasoning
Cons:
- ❌ More infrastructure to manage
- ❌ Custom thread management (now using Foundry for this)
- ❌ More code to maintain
Alternative Approach: Foundry Agents
What Foundry Provides:
- Agent definitions (system prompt, tools, model)
- Built-in thread management
- Built-in tool orchestration
- Managed infrastructure
- Agent catalog
Pros:
- ✅ Managed infrastructure
- ✅ Built-in thread management
- ✅ Built-in tool orchestration
- ✅ Agent catalog for discovery
- ✅ Less code to maintain
Cons:
- ❌ Less flexibility than LangGraph
- ❌ Limited to Foundry’s agent model
- ❌ Tool execution model may differ
- ❌ May need to adapt existing tools
Creating Agents in Foundry
Option 1: Azure Portal
- Navigate to Foundry Portal:
- Go to Azure Portal → Azure AI Foundry
- Select your project
- Go to “Agents” section
- Create Agent:
- Click “Create Agent”
- Provide agent name (e.g., “Elena”, “Marcus”, “Sage”)
- Configure:
- System Prompt: Copy from Engram agent definition
- Model: Select deployment (e.g., gpt-4o, gpt-5.2-chat)
- Tools: Add function definitions
- Temperature: Set as needed
- Define Tools:
- Add function definitions (JSON schema)
- Configure tool endpoints (if using external tools)
- Set tool descriptions
- Save Agent:
- Agent is now available in Foundry catalog
- Note the agent ID for integration
Option 2: REST API
# Create agent via REST API
import httpx
from azure.identity import DefaultAzureCredential
async def create_foundry_agent(
agent_name: str,
system_prompt: str,
model_deployment: str,
tools: list[dict]
):
endpoint = "https://<account>.services.ai.azure.com"
project = "<project-name>"
credential = DefaultAzureCredential()
token = credential.get_token("https://cognitiveservices.azure.com/.default")
headers = {
"Authorization": f"Bearer {token.token}",
"Content-Type": "application/json",
}
url = f"{endpoint}/api/projects/{project}/agents"
payload = {
"name": agent_name,
"instructions": system_prompt,
"model": model_deployment,
"tools": tools,
"temperature": 0.7,
}
async with httpx.AsyncClient() as client:
response = await client.post(
url,
headers=headers,
json=payload,
params={"api-version": "2024-10-01-preview"},
)
response.raise_for_status()
return response.json()
Option 3: SDK (if available)
from azure.ai.foundry import FoundryClient
client = FoundryClient(
endpoint="https://<account>.services.ai.azure.com",
project="<project-name>",
credential=DefaultAzureCredential(),
)
agent = await client.agents.create(
name="Elena",
instructions=elena_system_prompt,
model="gpt-5.2-chat",
tools=[...],
)
Integrating Foundry Agents into Engram
Approach 1: Hybrid - Use Foundry Agent Runtime
Create a wrapper that uses Foundry’s agent runtime while maintaining Engram’s interface:
# backend/agents/foundry_agent_wrapper.py
from backend.agents.foundry_client import FoundryAgentServiceClient
from backend.core import EnterpriseContext
class FoundryAgentWrapper:
"""
Wrapper for Foundry-created agents.
Provides Engram-compatible interface.
"""
def __init__(self, agent_id: str, foundry_agent_id: str):
self.agent_id = agent_id # Engram agent ID (elena, marcus, sage)
self.foundry_agent_id = foundry_agent_id # Foundry agent ID
self.foundry_client = get_foundry_client()
async def run(
self,
user_message: str,
context: EnterpriseContext,
thread_id: Optional[str] = None
) -> tuple[str, EnterpriseContext]:
"""
Execute Foundry agent with user message.
Returns:
Tuple of (response_text, updated_context)
"""
# Get or create thread
if not thread_id:
thread_id = await self.foundry_client.create_thread(
user_id=context.security.user_id,
agent_id=self.agent_id,
project_id=context.security.project_id,
)
# Add user message to thread
await self.foundry_client.add_message(
thread_id=thread_id,
role="user",
content=user_message,
)
# Run Foundry agent
# Note: This requires Foundry's agent execution API
response = await self._run_foundry_agent(thread_id)
# Add assistant response to thread
await self.foundry_client.add_message(
thread_id=thread_id,
role="assistant",
content=response,
)
# Update context
context.episodic.conversation_id = thread_id
context.episodic.metadata["foundry_thread_id"] = thread_id
context.episodic.metadata["foundry_agent_id"] = self.foundry_agent_id
return response, context
async def _run_foundry_agent(self, thread_id: str) -> str:
"""
Execute Foundry agent on thread.
This uses Foundry's agent execution API.
"""
# Foundry agent execution endpoint
url = f"{self.foundry_client.base_url}/agents/{self.foundry_agent_id}/runs"
headers = await self.foundry_client._get_headers()
payload = {
"thread_id": thread_id,
"assistant_id": self.foundry_agent_id,
}
async with httpx.AsyncClient(timeout=120.0) as client:
response = await client.post(
url,
headers=headers,
json=payload,
params={"api-version": self.foundry_client.api_version},
)
response.raise_for_status()
data = response.json()
# Poll for completion or wait for streaming
run_id = data.get("id")
return await self._wait_for_completion(run_id, thread_id)
async def _wait_for_completion(self, run_id: str, thread_id: str) -> str:
"""Wait for agent run to complete and get response."""
# Poll run status
# When complete, get latest assistant message from thread
messages = await self.foundry_client.list_messages(thread_id, limit=1)
if messages and messages[0].get("role") == "assistant":
return messages[0].get("content", "")
return "Agent response not available"
Approach 2: Replace LangGraph Agents
Update backend/agents/router.py to use Foundry agents:
# backend/agents/router.py
from backend.agents.foundry_agent_wrapper import FoundryAgentWrapper
class AgentRouter:
def __init__(self):
settings = get_settings()
if settings.use_foundry_agents:
# Use Foundry-created agents
self.agents = {
"elena": FoundryAgentWrapper("elena", "foundry-elena-id"),
"marcus": FoundryAgentWrapper("marcus", "foundry-marcus-id"),
"sage": FoundryAgentWrapper("sage", "foundry-sage-id"),
}
else:
# Use existing LangGraph agents
self.agents = {
"elena": elena,
"marcus": marcus,
"sage": sage,
}
Tool Integration
Converting LangChain Tools to Foundry Tools
Foundry agents use function definitions (OpenAI function calling format):
# LangChain tool
@tool("search_memory")
async def search_memory_tool(query: str, limit: int = 5) -> str:
"""Search memory for relevant content."""
# ... implementation
return results
# Foundry function definition
foundry_tool = {
"type": "function",
"function": {
"name": "search_memory",
"description": "Search memory for relevant content using tri-search (keyword, vector, graph).",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query"
},
"limit": {
"type": "integer",
"description": "Maximum number of results",
"default": 5
}
},
"required": ["query"]
}
}
}
Tool Execution
Foundry agents call tools via function calling. You need to:
- Register Tool Endpoints:
- Create API endpoints for each tool
- Foundry will call these endpoints when agent uses tool
- Handle Tool Calls:
@router.post("/api/v1/tools/search_memory") async def search_memory_tool_endpoint(request: ToolRequest): """Tool endpoint called by Foundry agent.""" result = await search_memory_tool( query=request.arguments["query"], limit=request.arguments.get("limit", 5) ) return {"result": result}
Migration Strategy
Phase 1: Create Agents in Foundry
- Export Agent Definitions:
- Extract system prompts from Engram agents
- List all tools used by each agent
- Document tool schemas
- Create Foundry Agents:
- Create Elena agent in Foundry
- Create Marcus agent in Foundry
- Create Sage agent in Foundry
- Register Tools:
- Create tool endpoints in Engram
- Register tools with Foundry agents
- Test tool execution
Phase 2: Hybrid Mode
- Add Feature Flag:
use_foundry_agents: bool = Field(False, alias="USE_FOUNDRY_AGENTS") - Update Router:
- Support both LangGraph and Foundry agents
- Switch based on feature flag
- Test in Development:
- Enable flag for one agent (e.g., Elena)
- Compare behavior with LangGraph version
- Fix any issues
Phase 3: Full Migration (Optional)
- Migrate All Agents:
- Move all agents to Foundry
- Update router to use Foundry only
- Remove LangGraph Code (if desired):
- Remove
BaseAgentclass - Remove LangGraph workflows
- Simplify codebase
- Remove
Trade-offs
When to Use Foundry Agents
✅ Use Foundry Agents If:
- You want managed infrastructure
- You want built-in thread management
- You want agent catalog/discovery
- You’re okay with Foundry’s agent model
- You want less code to maintain
When to Keep LangGraph Agents
✅ Keep LangGraph Agents If:
- You need complex multi-step reasoning
- You need custom workflows
- You need full control over tool execution
- You want to use advanced LangGraph features
- You have complex state management needs
Hybrid Approach (Recommended)
✅ Use Both:
- Use Foundry for thread management (already implemented)
- Use LangGraph for agent logic (current approach)
- Get benefits of both worlds
Implementation Example
Creating Elena in Foundry
# scripts/create_foundry_agents.py
import asyncio
from backend.agents.elena.agent import ElenaAgent
from backend.agents.foundry_client import FoundryAgentServiceClient
async def create_elena_in_foundry():
"""Create Elena agent in Foundry."""
# Get Elena's system prompt
elena = ElenaAgent()
system_prompt = elena.system_prompt
# Convert Elena's tools to Foundry format
tools = []
for tool in elena.tools:
foundry_tool = {
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.args_schema.schema() if hasattr(tool, 'args_schema') else {},
}
}
tools.append(foundry_tool)
# Create agent in Foundry
foundry_client = FoundryAgentServiceClient()
# Use Foundry agent creation API
agent = await foundry_client.create_agent(
name="Elena",
instructions=system_prompt,
model="gpt-5.2-chat",
tools=tools,
)
print(f"Created Elena agent in Foundry: {agent['id']}")
return agent['id']
if __name__ == "__main__":
asyncio.run(create_elena_in_foundry())
Next Steps
- Create Agents in Foundry:
- Use Azure Portal or REST API
- Export system prompts from Engram agents
- Register tools
- Implement Wrapper:
- Create
FoundryAgentWrapperclass - Implement Engram-compatible interface
- Handle tool execution
- Create
- Add Feature Flag:
- Add
USE_FOUNDRY_AGENTSflag - Update router to support both modes
- Test in development
- Add
- Evaluate:
- Compare behavior with LangGraph agents
- Measure performance
- Decide on migration path
Summary
✅ Yes, you can create agents in Foundry and use them in Engram!
Options:
- Hybrid: Use Foundry for threads (current) + LangGraph for logic
- Foundry Agents: Use Foundry’s agent runtime (requires migration)
- Both: Support both modes with feature flags
Recommendation: Start with hybrid approach (Foundry threads + LangGraph agents), then evaluate Foundry agents for specific use cases.
Last Updated: January 2026