Skipping Semantic Kernel Response¶
Overview¶
In some scenarios, you may want to prevent Semantic Kernel from automatically sending its default response to the user. This can be useful when your capability needs to handle the response manually, send custom formatted messages, or when no response is needed at all.
The Xians AI platform provides a way to skip the automatic Semantic Kernel response by setting the SkipResponse
property on the MessageThread
instance.
When to Skip Responses¶
You might want to skip the automatic response in the following scenarios:
- Silent Handoffs: When transferring a conversation to another agent without the current agent sending a response
- Custom Response Handling: When you need to send a specially formatted response using
SendChat
orSendData
- Silent Operations: When performing background tasks that don't require user feedback
- Multi-step Processes: When the capability is part of a larger workflow that will respond later
- Error Handling: When you want to handle errors gracefully without confusing automatic responses
- Data Processing: When processing data that doesn't require immediate user notification
Implementation¶
To skip the Semantic Kernel response, set the SkipResponse
property to true
in your capability class:
using XiansAi.Flow.Router.Plugins;
using XiansAi.Messaging;
public class ProcessingCapabilities
{
private readonly MessageThread _messageThread;
public ProcessingCapabilities(MessageThread messageThread)
{
_messageThread = messageThread;
}
[Capability("Process data without sending automatic response")]
[Parameter("data", "The data to process")]
[Returns("Processing result")]
public async Task<string> ProcessDataSilently(string data)
{
// Skip the automatic Semantic Kernel response
_messageThread.SkipResponse = true;
// Perform your processing logic
var result = ProcessData(data);
// Optionally send a custom response if needed
if (result.RequiresNotification)
{
await _messageThread.SendChat($"Processing completed: {result.Summary}");
}
return result.Details;
}
[Capability("Handle user request with custom response format")]
[Parameter("request", "The user request")]
[Returns("Status of the operation")]
public async Task<string> HandleCustomResponse(string request)
{
// Skip automatic response to handle manually
_messageThread.SkipResponse = true;
try
{
var response = await ProcessRequest(request);
// Send formatted response with metadata
var metadata = new {
timestamp = DateTime.UtcNow,
requestType = "custom",
status = "success"
};
await _messageThread.SendChat(response.Message, metadata);
return "Success";
}
catch (Exception ex)
{
// Send error message manually
await _messageThread.SendChat($"An error occurred: {ex.Message}");
return "Error";
}
}
}
Client-Side Handling¶
When a response is skipped using SkipResponse = true
, clients still receive a response from the bot, but with important characteristics that allow for proper handling:
What Clients Receive¶
- Text Content: The client receives
null
as the text content instead of an actual message - Response Structure: The response still follows the normal bot response format, maintaining consistency
- Processing Signal: This serves as a signal that the agent's processing is complete
Client Implementation¶
Clients should check for null
text content and handle it appropriately:
// Example client-side handling
function handleBotResponse(responseText) {
if (responseText === null) {
// Mark processing as complete
setProcessingComplete(true);
return;
} else {
// Normal response - render the message
displayMessage(responseText);
}
}
Why This Design¶
This approach provides several benefits:
- Processing Completion Signal: Clients know when the agent has finished processing, even without a visible response
- UI State Management: Allows clients to properly hide typing indicators and loading states
- Consistent Response Format: Maintains the same response structure for easier client implementation
- Silent Operations Support: Enables smooth user experience for background operations and handoffs
Usage Patterns¶
1. Silent Handoffs¶
This is one of the most important use cases for skipping responses. When one agent hands off to another agent, the first agent should skip its response to prevent duplicate or confusing messages to the user.
[Capability("Transfer conversation to specialized support agent")]
[Parameter("userQuery", "The user's original query")]
[Parameter("context", "Additional context for the handoff")]
public async Task<string> HandoffToSupportAgent(string userQuery, string context = "")
{
// Skip response from this agent since the target agent will handle communication
_messageThread.SkipResponse = true;
// Prepare handoff context
var handoffMessage = string.IsNullOrEmpty(context)
? userQuery
: $"{userQuery}\n\nContext: {context}";
// Perform the handoff
_messageThread.SendHandoff(typeof(SupportBot), handoffMessage);
return "Handoff completed to support agent";
}
[Capability("Route technical queries to engineering team")]
[Parameter("technicalQuery", "The technical question from the user")]
[Parameter("urgency", "Priority level: low, medium, high")]
public async Task<string> RouteToEngineering(string technicalQuery, string urgency = "medium")
{
// Silent handoff - no response from routing agent
_messageThread.SkipResponse = true;
// Add routing metadata to the handoff
var routingContext = $"Priority: {urgency}\nQuery: {technicalQuery}";
// Hand off to engineering bot
_messageThread.SendHandoff(typeof(EngineeringBot), routingContext);
return $"Routed to engineering with {urgency} priority";
}
[Capability("Escalate complex issues to human agent")]
[Parameter("issue", "Description of the complex issue")]
[Parameter("previousAttempts", "What has been tried so far")]
public async Task<string> EscalateToHuman(string issue, string previousAttempts = "")
{
_messageThread.SkipResponse = true;
var escalationContext = $"Issue: {issue}";
if (!string.IsNullOrEmpty(previousAttempts))
{
escalationContext += $"\nPrevious attempts: {previousAttempts}";
}
// Handoff to human agent workflow
_messageThread.SendHandoff(typeof(HumanAgentBot), escalationContext);
return "Escalated to human agent";
}
2. Silent Background Processing¶
[Capability("Start background task")]
[Parameter("taskData", "Data for the background task")]
public async Task<string> StartBackgroundTask(string taskData)
{
_messageThread.SkipResponse = true;
// Start the task without immediate response
_ = Task.Run(async () => await ProcessInBackground(taskData));
return "Task started";
}
2. Conditional Response Handling¶
[Capability("Process with conditional response")]
[Parameter("input", "Input to process")]
[Parameter("silent", "Whether to process silently")]
public async Task<string> ConditionalProcess(string input, bool silent = false)
{
if (silent)
{
_messageThread.SkipResponse = true;
}
var result = await ProcessInput(input);
if (!silent)
{
// Let Semantic Kernel handle the response automatically
return $"Processed: {result}";
}
// Silent mode - no response will be sent
return result;
}
4. Custom Error Handling¶
[Capability("Validate and process data")]
[Parameter("data", "Data to validate and process")]
public async Task<string> ValidateAndProcess(string data)
{
var validation = ValidateData(data);
if (!validation.IsValid)
{
// Skip automatic response to send custom error format
_messageThread.SkipResponse = true;
var errorResponse = new {
error = true,
message = validation.ErrorMessage,
suggestions = validation.Suggestions
};
await _messageThread.SendData(errorResponse, "Validation failed");
return "Validation error";
}
// Valid data - let Semantic Kernel respond normally
return ProcessValidData(data);
}
Important Notes¶
-
Property Setting: The
SkipResponse
property must be set within the same capability method that you want to skip the response for. -
Response Responsibility: When you skip the automatic response, you become responsible for providing user feedback if needed. Consider the user experience carefully.
-
Return Values: Even when skipping responses, your capability method should still return meaningful values for logging and internal processing purposes.
-
Error Handling: Be especially careful with error scenarios when skipping responses - users should still be informed of failures through manual responses.
-
Threading: The
SkipResponse
property affects only the current execution context and thread.
Best Practices¶
- Use Sparingly: Only skip responses when you have a specific need and plan to handle user communication manually
- Silent Handoffs: Always use
SkipResponse = true
when performing handoffs to prevent duplicate responses - Provide Feedback: If the operation takes time, consider sending progress updates using
SendChat
- Handle Errors: Always ensure users receive appropriate error messages, even in skip response scenarios
- Document Behavior: Clearly document when and why capabilities skip automatic responses
- Test Handoff Flows: Verify that handoff scenarios work smoothly and users don't experience communication gaps
- Test Thoroughly: Verify that the user experience remains smooth when responses are skipped