You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I’ve set up a multi-agent architecture using Semantic Kernel where:
A Main Router Agent is responsible for routing queries.
Several specialized agents (screening, fundflow, counter, pivot, irregularities, file_chat, general) are wrapped with their own logic (token tracking, memory, validation).
All agents share a single Kernel, and the router is initialized with these wrappers as plugins.
The issue I’m observing is that when a user sends a query, the router agent sometimes directly calls kernel_function tools exposed by the wrappers, instead of delegating through the full specialized agent logic. This bypasses my wrappers’ additional responsibilities (like per-agent token cost tracking and memory handling).
My questions for the community:
Is this expected behavior when adding wrapper plugins to a ChatCompletionAgent?
Or is the recommended approach to give each specialized agent its own Kernel and keep them isolated?
I’d appreciate guidance on the best practice for building multi-agent flows in Semantic Kernel, especially for ensuring all requests go through the full wrapper logic (token tracking, mem0 memory, etc.), rather than being shortcut by direct tool calls.
My architecture is
User Question -> get the past history -> pass it to orch agent (invoke/get_response) -> wrote a wrapper class for specialized agent -> orch agent should invoke the wrapper class (tool) to call that specialized agent -> the specialized agent has a list of kernel_function (tools) in it -> It will call the tool accordingly based on the question
But what is happening is
User Question -> get the past history -> pass it to orch agent (invoke/get_response) -> directly calling the kerenl function tool
Instead of calling the specialized agent, this is what happening
orchestrator_agent.py
from semantic_kernel.agents import ChatCompletionAgent
from dotenv import load_dotenv
load_dotenv()
def create_main_router_agent(general_wrapper, screening_wrapper, fundflow_wrapper, counter_wrapper, irregularities_wrapper, pivot_wrapper, file_chat_wrapper,kernel):
"""Create Level 1 Main Router Agent that routes between different analysis agents."""
main_router_instructions = """You are an intelligent financial transaction"""
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise ValueError("Missing OPENAI_API_KEY environment variable")
print("🔄 Creating Main Router Agent...")
main_router_agent = ChatCompletionAgent(
id="MainRouterAgent",
name="MainRouterAgent",
instructions=main_router_instructions,
description="Main router that routes between different financial analysis agents.",
plugins=[general_wrapper, screening_wrapper, counter_wrapper, fundflow_wrapper, pivot_wrapper, irregularities_wrapper, file_chat_wrapper],
kernel=kernel
)
return main_router_agent
general_wrapper.py
from typing import Annotated
from semantic_kernel.agents import ChatCompletionAgent
from semantic_kernel.functions import kernel_function
from plugins.general_plugin import GeneralPlugin
from dotenv import load_dotenv
load_dotenv()
async def create_general_agent(kernel):
"""Create the general agent for handling general queries about features and capabilities."""
try:
print(" Creating General Agent with OpenAI...")
general_instructions = """
You are a Financial Intelligence Analyst specializing in advanced analytics, pattern recognition, and complex computational analysis for financial investigation and compliance.
"""
general_agent = ChatCompletionAgent(
kernel=kernel,
name="general-agent",
instructions=general_instructions,
plugins=[GeneralPlugin()]
)
return {
'general_agent': general_agent,
'kernel': kernel,
}
except Exception as e:
print(f" Error creating general agent: {e}")
return None
class GeneralWrapper:
"""Wrapper plugin that calls the General Agent."""
def __init__(self, general_agent):
self.general_agent = general_agent
@kernel_function(description="Handle feature explanations, suspicion analysis, and transaction pattern queries and all other general queries.",
name="handle_general_request")
async def handle_general_request(
self,
query: Annotated[str, "The general query about system features or capabilities"]
) -> Annotated[str, "General information and guidance"]:
"""Handle general support requests."""
try:
print(" Processing general support request...")
response = await self.general_agent.get_response(
messages=query
)
return f"General Support: {response.content}"
except Exception as e:
print(f" General support handling error: {str(e)}")
return "I apologize for the error. Please try again or contact support."
shared_kernel.py
# shared_kernel.py
import os
from dotenv import load_dotenv
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
load_dotenv()
def create_shared_kernel():
"""Create simple shared kernel with OpenAI service"""
api_key = os.getenv("OPENAI_API_KEY")
model_id = os.getenv("OPENAI_CHAT_MODEL_ID", "gpt-4o-mini")
if not api_key:
raise ValueError("Missing OPENAI_API_KEY")
print("🔧 Creating simple shared kernel with OpenAI service...")
kernel = Kernel()
kernel.add_service(OpenAIChatCompletion(ai_model_id=model_id, api_key=api_key))
print("✅ Simple shared kernel created")
return kernel```
main.py
```
async def initialize_agents():
"""Initialize all agents once at startup."""
kernel = create_shared_kernel()
general_data = await create_general_agent(kernel)
screening_data = await create_screen_agent(kernel)
Fundflow_data = await create_fundflow_agent(kernel)
counter_data = await create_counter_agent(kernel)
irregularities_data = await create_irregualrties_agent(kernel)
pivot_data = await create_pivot_agent(kernel)
file_chat_data = await create_file_chat_agent(kernel)
if not general_data or not screening_data or not Fundflow_data or not counter_data or not irregularities_data or not pivot_data or not file_chat_data:
raise Exception("Failed to initialize specialized agents")
general_wrapper = GeneralWrapper(
general_data['general_agent'])
screening_wrapper = ScreeningWrapper(
screening_data['screening_agent'])
fundflow_wrapper = FundflowWrapper(
Fundflow_data['fundflow_agent'])
counter_wrapper = CounterWrapper(
counter_data['counter_agent'])
irregularities_wrapper = IrregulartiesWrapper(
irregularities_data['irregularities_agent'])
pivot_wrapper = PivotWrapper(
pivot_data['pivot_agent'])
file_chat_wrapper = FileChatWrapper(
file_chat_data['file_chat_agent'])
router_agent = create_main_router_agent(general_wrapper, screening_wrapper, fundflow_wrapper, counter_wrapper, irregularities_wrapper, pivot_wrapper, file_chat_wrapper,kernel)
GLOBAL_AGENTS.update({
'router': router_agent,
})
print("✅ All agents initialized successfully")
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I’ve set up a multi-agent architecture using Semantic Kernel where:
The issue I’m observing is that when a user sends a query, the router agent sometimes directly calls kernel_function tools exposed by the wrappers, instead of delegating through the full specialized agent logic. This bypasses my wrappers’ additional responsibilities (like per-agent token cost tracking and memory handling).
My questions for the community:
I’d appreciate guidance on the best practice for building multi-agent flows in Semantic Kernel, especially for ensuring all requests go through the full wrapper logic (token tracking, mem0 memory, etc.), rather than being shortcut by direct tool calls.
My architecture is
User Question -> get the past history -> pass it to orch agent (invoke/get_response) -> wrote a wrapper class for specialized agent -> orch agent should invoke the wrapper class (tool) to call that specialized agent -> the specialized agent has a list of kernel_function (tools) in it -> It will call the tool accordingly based on the question
But what is happening is
User Question -> get the past history -> pass it to orch agent (invoke/get_response) -> directly calling the kerenl function tool
Instead of calling the specialized agent, this is what happening
orchestrator_agent.py
general_wrapper.py
shared_kernel.py
we are invoking the main agent like this
Beta Was this translation helpful? Give feedback.
All reactions