mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
### Motivation and Context <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> Address https://github.com/microsoft/semantic-kernel/issues/10284 Integration of AWS Bedrock agent to SK Python ### Description 1. Implementation of Bedrock agent in SK (essentially an agent connector to the Bedrock agent service). 2. A dedicated channel that works with Bedrock agents. 3. Samples on how to use Bedrock agents. Unit and integration tests will be included in another PR to prevent this PR from becoming too big. <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [x] The code builds clean without any errors or warnings - [x] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [x] All unit tests pass, and I have added new tests where possible - [x] I didn't break anyone 😄
130 lines
4.8 KiB
Python
130 lines
4.8 KiB
Python
# Copyright (c) Microsoft. All rights reserved.
|
|
|
|
import logging
|
|
import uuid
|
|
from collections.abc import Iterable
|
|
from typing import ClassVar
|
|
|
|
from pydantic import Field
|
|
|
|
from semantic_kernel.agents.channels.agent_channel import AgentChannel
|
|
from semantic_kernel.functions.kernel_arguments import KernelArguments
|
|
from semantic_kernel.kernel import Kernel
|
|
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
|
from semantic_kernel.prompt_template.kernel_prompt_template import KernelPromptTemplate
|
|
from semantic_kernel.prompt_template.prompt_template_base import PromptTemplateBase
|
|
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
|
|
from semantic_kernel.utils.experimental_decorator import experimental_class
|
|
from semantic_kernel.utils.naming import generate_random_ascii_name
|
|
from semantic_kernel.utils.validation import AGENT_NAME_REGEX
|
|
|
|
logger: logging.Logger = logging.getLogger(__name__)
|
|
|
|
|
|
@experimental_class
|
|
class Agent(KernelBaseModel):
|
|
"""Base abstraction for all Semantic Kernel agents.
|
|
|
|
An agent instance may participate in one or more conversations.
|
|
A conversation may include one or more agents.
|
|
In addition to identity and descriptive meta-data, an Agent
|
|
must define its communication protocol, or AgentChannel.
|
|
|
|
Attributes:
|
|
name: The name of the agent (optional).
|
|
description: The description of the agent (optional).
|
|
id: The unique identifier of the agent (optional). If no id is provided,
|
|
a new UUID will be generated.
|
|
instructions: The instructions for the agent (optional)
|
|
"""
|
|
|
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
description: str | None = None
|
|
name: str = Field(default_factory=lambda: f"agent_{generate_random_ascii_name()}", pattern=AGENT_NAME_REGEX)
|
|
instructions: str | None = None
|
|
kernel: Kernel = Field(default_factory=Kernel)
|
|
channel_type: ClassVar[type[AgentChannel] | None] = None
|
|
arguments: KernelArguments | None = None
|
|
prompt_template: PromptTemplateBase | None = None
|
|
|
|
def get_channel_keys(self) -> Iterable[str]:
|
|
"""Get the channel keys.
|
|
|
|
Returns:
|
|
A list of channel keys.
|
|
"""
|
|
if not self.channel_type:
|
|
raise NotImplementedError("Unable to get channel keys. Channel type not configured.")
|
|
yield self.channel_type.__name__
|
|
|
|
async def create_channel(self) -> AgentChannel:
|
|
"""Create a channel.
|
|
|
|
Returns:
|
|
An instance of AgentChannel.
|
|
"""
|
|
if not self.channel_type:
|
|
raise NotImplementedError("Unable to create channel. Channel type not configured.")
|
|
return self.channel_type()
|
|
|
|
async def format_instructions(self, kernel: Kernel, arguments: KernelArguments | None = None) -> str | None:
|
|
"""Format the instructions.
|
|
|
|
Args:
|
|
kernel: The kernel instance.
|
|
arguments: The kernel arguments.
|
|
|
|
Returns:
|
|
The formatted instructions.
|
|
"""
|
|
if self.prompt_template is None:
|
|
if self.instructions is None:
|
|
return None
|
|
self.prompt_template = KernelPromptTemplate(
|
|
prompt_template_config=PromptTemplateConfig(template=self.instructions)
|
|
)
|
|
return await self.prompt_template.render(kernel, arguments)
|
|
|
|
def merge_arguments(self, override_args: KernelArguments | None) -> KernelArguments:
|
|
"""Merge the arguments with the override arguments.
|
|
|
|
Args:
|
|
override_args: The arguments to override.
|
|
|
|
Returns:
|
|
The merged arguments. If both are None, return None.
|
|
"""
|
|
if not self.arguments:
|
|
if not override_args:
|
|
return KernelArguments()
|
|
return override_args
|
|
|
|
if not override_args:
|
|
return self.arguments
|
|
|
|
# Both are not None, so merge with precedence for override_args.
|
|
merged_execution_settings = self.arguments.execution_settings or {}
|
|
if override_args.execution_settings:
|
|
merged_execution_settings.update(override_args.execution_settings)
|
|
|
|
merged_params = dict(self.arguments)
|
|
merged_params.update(override_args)
|
|
|
|
return KernelArguments(settings=merged_execution_settings, **merged_params)
|
|
|
|
def __eq__(self, other):
|
|
"""Check if two agents are equal."""
|
|
if isinstance(other, Agent):
|
|
return (
|
|
self.id == other.id
|
|
and self.name == other.name
|
|
and self.description == other.description
|
|
and self.instructions == other.instructions
|
|
and self.channel_type == other.channel_type
|
|
)
|
|
return False
|
|
|
|
def __hash__(self):
|
|
"""Get the hash of the agent."""
|
|
return hash((self.id, self.name, self.description, self.instructions, self.channel_type))
|