Skip to content

Commit 3490a9f

Browse files
committed
Resolve merge from main
2 parents c52866b + 2a78664 commit 3490a9f

File tree

1,973 files changed

+109637
-80207
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,973 files changed

+109637
-80207
lines changed

.github/_typos.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ extend-exclude = [
2626
"SK-dotnet.sln.DotSettings",
2727
"**/azure_ai_search_hotel_samples/README.md",
2828
"**/Demos/ProcessFrameworkWithAspire/ProcessFramework.Aspire/ProcessFramework.Aspire.ProcessOrchestrator/Program.cs",
29-
"**/Demos/ProcessFrameworkWithAspire/**/*.http"
29+
"**/Demos/ProcessFrameworkWithAspire/**/*.http",
30+
"**/samples/Concepts/Resources/travel-destination-overview.txt"
3031
]
3132

3233
[default.extend-words]

.github/workflows/dotnet-build-and-test.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,19 @@ jobs:
8383
for solution in $SOLUTIONS; do
8484
dotnet build $solution -c ${{ matrix.configuration }} --warnaserror
8585
done
86+
- name: Package install check
87+
shell: bash
88+
if: matrix.os == 'ubuntu-latest'
89+
run: |
90+
export SOLUTIONS=$(find ./dotnet/ -type f -name "*.sln" | tr '\n' ' ')
91+
for solution in $SOLUTIONS; do
92+
dotnet pack $solution -c ${{ matrix.configuration }} --no-build --no-restore --output ./artifacts
93+
done
94+
dotnet new console --name packcheck --output consoleapp
95+
dotnet add consoleapp/packcheck.csproj package Microsoft.SemanticKernel --source ./artifacts
96+
dotnet build consoleapp/packcheck.csproj
97+
rm -rf ./artifacts
98+
rm -rf ./consoleapp
8699
87100
- name: Run Unit Tests
88101
shell: bash
@@ -156,7 +169,7 @@ jobs:
156169

157170
# Generate test reports and check coverage
158171
- name: Generate test reports
159-
uses: danielpalme/ReportGenerator-GitHub-Action@5.4.4
172+
uses: danielpalme/ReportGenerator-GitHub-Action@5.4.7
160173
with:
161174
reports: "./TestResults/Coverage/**/coverage.cobertura.xml"
162175
targetdir: "./TestResults/Reports"

.github/workflows/dotnet-format.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ jobs:
2525
fail-fast: false
2626
matrix:
2727
include:
28-
- { dotnet: "8.0", configuration: Release, os: ubuntu-latest }
2928
- { dotnet: "9.0", configuration: Release, os: ubuntu-latest }
3029

3130
runs-on: ${{ matrix.os }}

.github/workflows/python-integration-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ env:
3232
AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
3333
AZURE_OPENAI_AUDIO_TO_TEXT_ENDPOINT: ${{ secrets.AZURE_OPENAI_AUDIO_TO_TEXT_ENDPOINT }}
3434
AZURE_OPENAI_TEXT_TO_AUDIO_ENDPOINT: ${{ secrets.AZURE_OPENAI_TEXT_TO_AUDIO_ENDPOINT }}
35-
AZURE_AI_AGENT_PROJECT_CONNECTION_STRING: ${{ secrets.AZURE_AI_AGENT_PROJECT_CONNECTION_STRING }}
35+
AZURE_AI_AGENT_ENDPOINT: ${{ secrets.AZURE_AI_AGENT_ENDPOINT }}
3636
AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME: ${{ secrets.AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME }}
3737
AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME }}
3838
BING_API_KEY: ${{ secrets.BING_API_KEY }}

.github/workflows/python-test-coverage-report.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
with:
4444
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
4545
issue-number: ${{ env.PR_NUMBER }}
46-
pytest-coverage-path: python/python-coverage.txt
46+
pytest-xml-coverage-path: python/python-coverage.xml
4747
title: "Python Test Coverage Report"
4848
badge-title: "Python Test Coverage"
4949
junitxml-title: "Python Unit Test Overview"

.github/workflows/python-test-coverage.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ jobs:
3636
- name: Install the project
3737
run: uv sync --all-extras --dev
3838
- name: Test with pytest
39-
run: uv run --frozen pytest -q --junitxml=pytest.xml --cov=semantic_kernel --cov-report=term-missing:skip-covered ./tests/unit | tee python-coverage.txt
39+
run: uv run --frozen pytest -q --junitxml=pytest.xml --cov=semantic_kernel --cov-report=term-missing:skip-covered --cov-report=xml:python-coverage.xml ./tests/unit
4040
- name: Upload coverage report
4141
uses: actions/upload-artifact@v4
4242
with:
4343
path: |
44-
python/python-coverage.txt
44+
python/python-coverage.xml
4545
python/pytest.xml
4646
python/pr_number
4747
overwrite: true

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ Build a system of specialized agents that can collaborate:
239239

240240
```python
241241
import asyncio
242-
from semantic_kernel.agents import ChatCompletionAgent
242+
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread
243243
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, OpenAIChatCompletion
244244

245245
billing_agent = ChatCompletionAgent(
@@ -262,7 +262,7 @@ triage_agent = ChatCompletionAgent(
262262
plugins=[billing_agent, refund_agent],
263263
)
264264

265-
thread: None
265+
thread: ChatHistoryAgentThread = None
266266

267267
async def main() -> None:
268268
print("Welcome to the chat bot!\n Type 'exit' to exit.\n Try to get some billing or refund help.")
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
---
2+
# These are optional elements. Feel free to remove any of them.
3+
status: accepted
4+
contact: westey-m
5+
date: 2025-04-17
6+
deciders: westey-m, markwallace-microsoft, alliscode, TaoChenOSU, moonbox3, crickman
7+
consulted: westey-m, markwallace-microsoft, alliscode, TaoChenOSU, moonbox3, crickman
8+
informed: westey-m, markwallace-microsoft, alliscode, TaoChenOSU, moonbox3, crickman
9+
---
10+
11+
# Agents with Memory
12+
13+
## What do we mean by Memory?
14+
15+
By memory we mean the capability to remember information and skills that are learned during
16+
a conversation and re-use those later in the same conversation or later in a subsequent conversation.
17+
18+
## Context and Problem Statement
19+
20+
Today we support multiple agent types with different characteristics:
21+
22+
1. In process vs remote.
23+
2. Remote agents that store and maintain conversation state in the service vs those that require the caller to provide conversation state on each invocation.
24+
25+
We need to support advanced memory capabilities across this range of agent types.
26+
27+
### Memory Scope
28+
29+
Another aspect of memory that is important to consider is the scope of different memory types.
30+
Most agent implementations have instructions and skills but the agent is not tied to a single conversation.
31+
On each invocation of the agent, the agent is told which conversation to participate in, during that invocation.
32+
33+
Memories about a user or about a conversation with a user is therefore extracted from one of these conversation and recalled
34+
during the same or another conversation with the same user.
35+
These memories will typically contain information that the user would not like to share with other users of the system.
36+
37+
Other types of memories also exist which are not tied to a specific user or conversation.
38+
E.g. an Agent may learn how to do something and be able to do that in many conversations with different users.
39+
With these type of memories there is of cousrse risk in leaking personal information between different users which is important to guard against.
40+
41+
### Packaging memory capabilities
42+
43+
All of the above memory types can be supported for any agent by attaching software components to conversation threads.
44+
This is achieved via a simple mechanism of:
45+
46+
1. Inspecting and using messages as they are passed to and from the agent.
47+
2. Passing additional context to the agent per invocation.
48+
49+
With our current `AgentThread` implementation, when an agent is invoked, all input and output messages are already passed to the `AgentThread`
50+
and can be made available to any components attached to the `AgentThread`.
51+
Where agents are remote/external and manage conversation state in the service, passing the messages to the `AgentThread` may not have any
52+
affect on the thread in the service. This is OK, since the service will have already updated the thread during the remote invocation.
53+
It does however, still allow us to subscribe to messages in any attached components.
54+
55+
For the second requirement of getting additional context per invocation, the agent may ask the thread passed to it, to in turn ask
56+
each of the components attached to it, to provide context to pass to the Agent.
57+
This enables the component to provide memories that it contains to the Agent as needed.
58+
59+
Different memory capabilities can be built using separate components. Each component would have the following characteristics:
60+
61+
1. May store some context that can be provided to the agent per invocation.
62+
2. May inspect messages from the conversation to learn from the conversation and build its context.
63+
3. May register plugins to allow the agent to directly store, retrieve, update or clear memories.
64+
65+
### Suspend / Resume
66+
67+
Building a service to host an agent comes with challenges.
68+
It's hard to build a stateful service, but service consumers expect an experience that looks stateful from the outside.
69+
E.g. on each invocation, the user expects that the service can continue a conversation they are having.
70+
71+
This means that where the the service is exposing a local agent with local conversation state management (e.g. via `ChatHistory`)
72+
that conversation state needs to be loaded and persisted for each invocation of the service.
73+
74+
It also means that any memory components that may have some in-memory state will need to be loaded and persisted too.
75+
76+
For cases like this, the `OnSuspend` and `OnResume` methods allow notification of the components that they need to save or reload their state.
77+
It is up to each of these components to decide how and where to save state to or load state from.
78+
79+
## Proposed interface for Memory Components
80+
81+
The types of events that Memory Components require are not unique to memory, and can be used to package up other capabilities too.
82+
The suggestion is therefore to create a more generally named type that can be used for other scenarios as well and can even
83+
be used for non-agent scenarios too.
84+
85+
This type should live in the `Microsoft.SemanticKernel.Abstractions` nuget, since these components can be used by systems other than just agents.
86+
87+
```csharp
88+
namespace Microsoft.SemanticKernel;
89+
90+
public abstract class AIContextBehavior
91+
{
92+
public virtual IReadOnlyCollection<AIFunction> AIFunctions => Array.Empty<AIFunction>();
93+
94+
public virtual Task OnThreadCreatedAsync(string? threadId, CancellationToken cancellationToken = default);
95+
public virtual Task OnThreadDeleteAsync(string? threadId, CancellationToken cancellationToken = default);
96+
97+
// OnThreadCheckpointAsync not included in initial release, maybe in future.
98+
public virtual Task OnThreadCheckpointAsync(string? threadId, CancellationToken cancellationToken = default);
99+
100+
public virtual Task OnNewMessageAsync(string? threadId, ChatMessage newMessage, CancellationToken cancellationToken = default);
101+
public abstract Task<string> OnModelInvokeAsync(ICollection<ChatMessage> newMessages, CancellationToken cancellationToken = default);
102+
103+
public virtual Task OnSuspendAsync(string? threadId, CancellationToken cancellationToken = default);
104+
public virtual Task OnResumeAsync(string? threadId, CancellationToken cancellationToken = default);
105+
}
106+
```
107+
108+
## Managing multiple components
109+
110+
To manage multiple components I propose that we have a `AIContextBehavior`.
111+
This class allows registering components and delegating new message notifications, ai invocation calls, etc. to the contained components.
112+
113+
## Integrating with agents
114+
115+
I propose to add a `AIContextBehaviorManager` to the `AgentThread` class, allowing us to attach components to any `AgentThread`.
116+
117+
When an `Agent` is invoked, we will call `OnModelInvokeAsync` on each component via the `AIContextBehaviorManager` to get
118+
a combined set of context to pass to the agent for this invocation. This will be internal to the `Agent` class and transparent to the user.
119+
120+
```csharp
121+
var additionalInstructions = await currentAgentThread.OnModelInvokeAsync(messages, cancellationToken).ConfigureAwait(false);
122+
```
123+
124+
## Usage examples
125+
126+
### Multiple threads using the same memory component
127+
128+
```csharp
129+
// Create a vector store for storing memories.
130+
var vectorStore = new InMemoryVectorStore();
131+
// Create a memory store that is tired to a "Memories" collection in the vector store and stores memories under the "user/12345" namespace.
132+
using var textMemoryStore = new VectorDataTextMemoryStore<string>(vectorStore, textEmbeddingService, "Memories", "user/12345", 1536);
133+
134+
// Create a memory component to will pull user facts from the conversation, store them in the vector store
135+
// and pass them to the agent as additional instructions.
136+
var userFacts = new UserFactsMemoryComponent(this.Fixture.Agent.Kernel, textMemoryStore);
137+
138+
// Create a thread and attach a Memory Component.
139+
var agentThread1 = new ChatHistoryAgentThread();
140+
agentThread1.ThreadExtensionsManager.Add(userFacts);
141+
var asyncResults1 = agent.InvokeAsync("Hello, my name is Caoimhe.", agentThread1);
142+
143+
// Create a second thread and attach a Memory Component.
144+
var agentThread2 = new ChatHistoryAgentThread();
145+
agentThread2.ThreadExtensionsManager.Add(userFacts);
146+
var asyncResults2 = agent.InvokeAsync("What is my name?.", agentThread2);
147+
// Expected response contains Caoimhe.
148+
```
149+
150+
### Using a RAG component
151+
152+
```csharp
153+
// Create Vector Store and Rag Store/Component
154+
var vectorStore = new InMemoryVectorStore();
155+
using var ragStore = new TextRagStore<string>(vectorStore, textEmbeddingService, "Memories", 1536, "group/g2");
156+
var ragComponent = new TextRagComponent(ragStore, new TextRagComponentOptions());
157+
158+
// Upsert docs into vector store.
159+
await ragStore.UpsertDocumentsAsync(
160+
[
161+
new TextRagDocument("The financial results of Contoso Corp for 2023 is as follows:\nIncome EUR 174 000 000\nExpenses EUR 152 000 000")
162+
{
163+
SourceName = "Contoso 2023 Financial Report",
164+
SourceReference = "https://www.consoso.com/reports/2023.pdf",
165+
Namespaces = ["group/g2"]
166+
}
167+
]);
168+
169+
// Create a new agent thread and register the Rag component
170+
var agentThread = new ChatHistoryAgentThread();
171+
agentThread.ThreadExtensionsManager.RegisterThreadExtension(ragComponent);
172+
173+
// Inovke the agent.
174+
var asyncResults1 = agent.InvokeAsync("What was the income of Contoso for 2023", agentThread);
175+
// Expected response contains the 174M income from the document.
176+
```
177+
178+
## Decisions to make
179+
180+
### Extension base class name
181+
182+
1. ConversationStateExtension
183+
184+
1.1. Long
185+
186+
2. MemoryComponent
187+
188+
2.1. Too specific
189+
190+
3. AIContextBehavior
191+
192+
Decided 3. AIContextBehavior.
193+
194+
### Location for abstractions
195+
196+
1. Microsoft.SemanticKernel.<baseclass>
197+
2. Microsoft.SemanticKernel.Memory.<baseclass>
198+
3. Microsoft.SemanticKernel.Memory.<baseclass> (in separate nuget)
199+
200+
Decided: 1. Microsoft.SemanticKernel.<baseclass>.
201+
202+
### Location for memory components
203+
204+
1. A nuget for each component
205+
2. Microsoft.SemanticKernel.Core nuget
206+
3. Microsoft.SemanticKernel.Memory nuget
207+
4. Microsoft.SemanticKernel.ConversationStateExtensions nuget
208+
209+
Decided: 2. Microsoft.SemanticKernel.Core nuget

0 commit comments

Comments
 (0)