Skip to content

Commit 9fc27ed

Browse files
committed
Improve docs
1 parent 90605de commit 9fc27ed

2 files changed

Lines changed: 164 additions & 76 deletions

File tree

docs/FAQ.md

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,35 @@
55
There are two main modalities, **As a Service** and **Serverless**, plus
66
customizations you can apply.
77

8-
Running Semantic Memory as a Service allows you to **interact with the memory
9-
via HTTP, in any language**. The repo contains a Memory Web client written in
10-
C# and some examples showing how to do the same from command line with `curl`.
11-
We will provide soon Web clients written in other languages. Semantic Memory
12-
Service is designed to run as an internal service, behind your backend,
13-
similarly to a DB, so you should not expose the service to public traffic
14-
without having your authentication in front, similar to how design a typical
15-
backend integrated with a SQL server, Service Bus, etc.
16-
One important benefit of the service, it's designed to scale horizontally and
17-
with **durable queues for long-running operations**.
18-
See the [service documentation](../dotnet/Service/README.md) for more details.
19-
[Here](../examples/002-dotnet-WebClient/README.md) you can find an example
20-
showing the web client.
21-
22-
Alternatively, you can **embed Semantic Memory directly into your C#
23-
applications**, using the **Serverless Memory client**. This is limited to
24-
.NET applications and doesn't allow mixing .NET pipelines with other languages,
25-
e.g. pipeline handlers written in Python or TypeScript. The serverless approach
26-
can be very useful for console applications, tests and demos. **The API is the
27-
same** API offered by the service, so it's possible to switch from Service to
28-
Serverless changing only the memory client instance.
29-
[Here](../examples/001-dotnet-Serverless/README.md) you can find an example
30-
showing the serverless client.
8+
1. Running Semantic Memory as a Service allows you to **interact with the memory
9+
via HTTP, in any language**. The repo contains a Memory Web client for .NET
10+
and some examples showing how to do the same from command line with `curl`.
11+
We will provide soon Web clients written in other languages.
12+
13+
Semantic Memory Service is designed to run as an internal service, behind
14+
your backend, similarly to a DB, so you should not expose the service to
15+
public traffic without authenticating your users first, similar to a typical
16+
backend integrated with a SQL server, Service Bus, etc.
17+
18+
One important benefit of the service, the solution can scale horizontally
19+
and can support long running operation reliably using durable queues.
20+
21+
For more details, see the [service documentation](../dotnet/Service/README.md).
22+
23+
[Here](../examples/002-dotnet-WebClient/README.md) you can find an example
24+
showing the web client interacting with the service.
25+
26+
2. Alternatively, you can **embed Semantic Memory directly into your .NET
27+
applications**, using the **Serverless Memory client**. This is limited to
28+
.NET applications and doesn't allow mixing .NET pipelines with pipeline
29+
handlers written in Python or TypeScript. The serverless approach can be
30+
very useful to create console applications, run tests and demos.
31+
32+
**The serverless memory API is the same** offered by the service, so it's
33+
possible to switch from Service to Serverless changing only few lines code.
34+
35+
[Here](../examples/001-dotnet-Serverless/README.md) you can find an example
36+
showing the serverless client.
3137

3238
![image](https://github.com/microsoft/semantic-memory/assets/371009/83d6487f-75f2-42d9-9ab5-ea6aed65231b)
3339

@@ -36,25 +42,35 @@ showing the serverless client.
3642
In order to protect users data, you should follow these design principles:
3743

3844
* Use Semantic Memory as **a private backend component**, similar to a SQL
39-
Server, without granting direct access.
40-
* Authenticate your users in your backend using a secure solution like Azure
45+
Server, without granting direct access. When using Semantic Memory as a
46+
service, consider assigning the service a reserved IP, accessible only to
47+
your IP, and using HTTPS only.
48+
* Authenticate users in your backend using a secure solution like Azure
4149
Active Directory, extract the user ID from the signed credentials like JWT
42-
tokens or client certs.
43-
* **Use Semantic Memory Tags as Security Filters**. See
44-
[Security Filters](SECURITY_FILTERS.md) for more details.
50+
tokens or client certs, and tag every interaction with Semantic Memory with
51+
this User ID
52+
* **Use Semantic Memory Tags as Security Filters**. Make sure every API call
53+
to Semantic Memory uses a User tag, both when reading and writing to memory.
54+
See [Security Filters](SECURITY_FILTERS.md) for more details.
4555

4656
![Network diagram](network.png)
4757

58+
![image](https://github.com/microsoft/semantic-memory/assets/371009/83d6487f-75f2-42d9-9ab5-ea6aed65231b)
59+
4860
### Is it possible to download web pages and turn the content into memory? Can I ask questions about the content of a web page?
4961

50-
Yes, the memory API includes a `ImportWebPageAsync` method that can be used
62+
Yes, the memory API includes an `ImportWebPageAsync` method that can be used
5163
to take a web page content, and process the text content like files. Once
5264
the content is imported, asking questions is very simple:
5365

5466
```csharp
55-
var docId = await memory.ImportWebPageAsync("https://raw.githubusercontent.com/microsoft/semantic-memory/main/README.md");
67+
// Import memories from a web page
68+
var docId = await memory.ImportWebPageAsync(
69+
"https://raw.githubusercontent.com/microsoft/semantic-memory/main/README.md");
5670

57-
var answer = await memory.AskAsync("Where can I store my semantic memory records?", MemoryFilters.ByDocument(docId));
71+
// Answer questions using the page content to ground the answer
72+
var answer = await memory.AskAsync("Where can I store my semantic memory records?",
73+
MemoryFilters.ByDocument(docId));
5874
```
5975

6076
![image](https://github.com/microsoft/semantic-memory/assets/371009/83d6487f-75f2-42d9-9ab5-ea6aed65231b)
@@ -63,9 +79,12 @@ var answer = await memory.AskAsync("Where can I store my semantic memory records
6379

6480
When uploading a file (or multiple files), you can specify a document ID,
6581
or you can let the service generate a document ID for you. You will see these
66-
Document IDs also when getting answers. When sending a question, it's
67-
possible to **include a filter**, so it's possible to filter by tags and
68-
**by document ID**. Here's an example:
82+
Document IDs also when getting answers.
83+
84+
When sending a question, it's possible to **include a filter**, so it's possible
85+
to filter by tags and **by document ID**.
86+
87+
Here's an example:
6988

7089
```csharp
7190
string docId = await memory.ImportDocumentAsync("manual.pdf");
@@ -80,12 +99,19 @@ can save and use for questions.
8099
In the second example ("book.docx"), the document ID is fixed, chosen by the
81100
client.
82101

83-
And this is the code showing how to ask a questions using only a specific document:
102+
And this is the code showing how to ask a questions using only a specific
103+
document:
84104

85105
```csharp
86-
var answer1 = await memory.AskAsync("What's the produc name?", MemoryFilters.ByDocument(docId));
106+
var answer1 = await memory.AskAsync("What's the product name?",
107+
MemoryFilters.ByDocument(docId));
87108

88-
var answer2 = await memory.AskAsync("What's the total population?", MemoryFilters.ByDocument("europe001"));
109+
var answer2 = await memory.AskAsync("What's the total population?",
110+
MemoryFilters.ByDocument("europe001"));
89111
```
90112

91113
![image](https://github.com/microsoft/semantic-memory/assets/371009/18ea98ee-1210-498d-8513-56abc795ce4d)
114+
115+
If you have any question, please do not hesitate to
116+
[open a new issue](https://github.com/microsoft/semantic-memory/issues/new)
117+
in the Semantic Memory repository. Thanks!

docs/SECURITY_FILTERS.md

Lines changed: 101 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Security Filters
22

3+
This document provides some guidance about how to organize your documents in
4+
order to secure your data, e.g. making sure users can access only data
5+
meant to be accessible to them.
6+
37
Semantic Memory allows to organize memories with two main approaches, which
48
can also be used together for maximum flexibility.
59

@@ -27,16 +31,22 @@ to each document a User ID tag that your application can filter by.
2731

2832
**Vector DBs like Azure Cognitive Search, Qdrant, Pinecone, etc. don't offer
2933
document-level permissions** and search results can't vary by user.
30-
Documents stored in Vector DBs though can be decorated with metadata and can
31-
be filtered when searching, applying some filters.
34+
Vector storages are optimized to store large quantity of documents indexed
35+
using embedding vectors, and to quickly find similar documents.
36+
Vector records stored in Vector DBs though can be decorated with metadata, and
37+
can be filtered when searching, applying some logical filters.
38+
39+
Semantic Memory leverages this capability, and uses specific native filters
40+
on all the supported Vector DBs (Azure Cognitive Search, Qdrant, etc), removing
41+
the need to learn ad-hoc filtering syntax, allowing to **tag every memory
42+
during the ingestion**, and allowing to **filter by tag when searching**,
43+
during the retrieval process.
3244

33-
Semantic Memory makes this transparent regardless of the vector DB selected,
34-
allowing to **tag every memory during the ingestion**, and allowing to **filter
35-
by tag when searching**, during the retrieval process.
45+
Tags are free and customizable. Multiple tags can be used and each tag can
46+
have multiple values. Tags can be used to filter by user, by type, etc. and
47+
in particular can be leveraged for your security scenarios.
3648

37-
Tags are completely free and customizable. Multiple tags can be used and each
38-
tag can have multiple values, so you can create your custom security filters.
39-
Here's some example scenarios:
49+
Here's some examples:
4050

4151
* Use a "userID" tag to restrict records to one or multiple users.
4252
* Use a "userEmail" tag to restrict records using the user email address.
@@ -52,10 +62,10 @@ cascade deletions, etc.
5262
> so you should consider these two important points:
5363
>
5464
> 1. Memories stored without tags are visible only when searching without
55-
> filters, e.g. they are visible to all users.
56-
>2. Searching without filters searches the entire index. If you are using tags
57-
> as security filters, **you should always filter by tags when retrieving**
58-
> information.
65+
> filters, e.g. they are visible to all users.
66+
> 2. Searching without filters searches the entire index. If you are using tags
67+
> as security filters, **you should always filter by tags when retrieving**
68+
> information.
5969
6070
## Code examples
6171

@@ -65,8 +75,10 @@ Simple file upload, without tags or explicit index name. The associated
6575
memory records can't be filterable and are stored in the default index.
6676

6777
```csharp
78+
// Upload a file into memory. This file has no tags.
6879
var docId = await memory.ImportDocumentAsync("project.docx");
69-
80+
81+
// Ask a question, without tags. This will search the entire index.
7082
var answer = await memory.AskAsync("what's the project timeline?");
7183
```
7284

@@ -75,6 +87,7 @@ memory records can't be filtered by tags, but are isolated in a dedicated
7587
index.
7688

7789
```csharp
90+
// Upload a file in a specific index.
7891
var docId = await memory.ImportDocumentAsync("project.docx", index: "index001");
7992

8093
// NO ANSWER: the data is not in the default index
@@ -84,6 +97,13 @@ var answer = await memory.AskAsync("what's the project timeline?");
8497
var answer = await memory.AskAsync("what's the project timeline?", index: "index001");
8598
```
8699

100+
### Security Filters
101+
102+
These examples use the `user` tag to secure data retrieval, making sure the
103+
current user can see only data tagged by their user ID.
104+
105+
#### Example 1
106+
87107
File upload with a `user` tag. The associated memory records can be filtered
88108
using the `user` tag.
89109

@@ -92,67 +112,109 @@ a filter**.
92112

93113
```csharp
94114
var docId = await memory.ImportDocumentAsync(new Document()
95-
.AddFile("project.docx")
96-
.AddTag("user", "USER-333"));
115+
.AddFile("project.docx")
116+
.AddTag("user", "USER-333"));
97117

98118
// OK
99119
var answer = await memory.AskAsync("what's the project timeline?");
100120

101121
// OK
102-
var answer = await memory.AskAsync("what's the project timeline?", MemoryFilters.ByTag("user", "USER-333"));
122+
var answer = await memory.AskAsync("what's the project timeline?",
123+
MemoryFilters.ByTag("user", "USER-333"));
103124

104-
// NO ANSWER: memories are tagged with 'USER-333', so filter 'USER-444' will not match the information extracted from project.docs
105-
var answer = await memory.AskAsync("what's the project timeline?", MemoryFilters.ByTag("user", "USER-444"));
125+
// NO ANSWER: memories are tagged with 'USER-333', so filter 'USER-444'
126+
// will not match the information extracted from project.docs
127+
var answer = await memory.AskAsync("what's the project timeline?",
128+
MemoryFilters.ByTag("user", "USER-444"));
106129
```
107130

131+
#### Example 2
132+
108133
Very similar to previous example, using a specific index.
109134

110135
```csharp
136+
// Upload a document in specific user and tag with user ID.
111137
var docId = await memory.ImportDocumentAsync(new Document()
112-
.AddFile("project.docx")
113-
.AddTag("user", "USER-333"),
114-
index: "index002");
138+
.AddFile("project.docx")
139+
.AddTag("user", "USER-333"),
140+
index: "index002");
115141

116142
// NO ANSWER: the data is not in the default index
117143
var answer = await memory.AskAsync("what's the project timeline?");
118144

119-
// NO ANSWER: the data is not in the default index
120-
var answer = await memory.AskAsync("what's the project timeline?", MemoryFilters.ByTag("user", "USER-333"));
145+
// NO ANSWER: even if the filter is correct, the data is not in the default index
146+
var answer = await memory.AskAsync("what's the project timeline?",
147+
MemoryFilters.ByTag("user", "USER-333"));
121148

122149
// OK
123-
var answer = await memory.AskAsync("what's the project timeline?", MemoryFilters.ByTag("user", "USER-333"), index: "index002");
150+
var answer = await memory.AskAsync("what's the project timeline?",
151+
MemoryFilters.ByTag("user", "USER-333"),
152+
index: "index002");
153+
154+
// IMPORTANT: this command is missing the user tag and the service will return the data.
155+
// This is equivalent to an admin having full access.
156+
var answer = await memory.AskAsync("what's the project timeline?",
157+
index: "index002");
124158
```
125159

160+
#### Example 3
161+
126162
Example showing how to apply multiple tags, even for the same tag name.
127-
E.g. in this case the document information is tagged with two user IDs,
163+
164+
In this case the document information is tagged with two user IDs,
128165
so both users can ask for questions.
129166

130167
```csharp
168+
// Upload file, allow two users to access
131169
var docId = await memory.ImportDocumentAsync(new Document()
132-
.AddFile("project.docx")
133-
.AddTag("user", "USER-333")
134-
.AddTag("user", "USER-444"));
170+
.AddFile("project.docx")
171+
.AddTag("user", "USER-333")
172+
.AddTag("user", "USER-444"));
135173

136-
// OK
137-
var answer = await memory.AskAsync("what's the project timeline?", MemoryFilters.ByTag("user", "USER-333"));
174+
// OK: USER-333 tag matches
175+
var answer = await memory.AskAsync("what's the project timeline?",
176+
MemoryFilters.ByTag("user", "USER-333"));
138177

139-
// OK
140-
var answer = await memory.AskAsync("what's the project timeline?", MemoryFilters.ByTag("user", "USER-444"));
178+
// OK: USER-444 tag matches
179+
var answer = await memory.AskAsync("what's the project timeline?",
180+
MemoryFilters.ByTag("user", "USER-444"));
141181
```
142182

183+
#### Example 4
184+
143185
Finally , tags can be used also for categorizing data:
144186

145187
```csharp
188+
// Upload file, allow two users to access, and add a content type tag for extra filtering
146189
var docId = await memory.ImportDocumentAsync(new Document()
147-
.AddFile("project.docx")
148-
.AddTag("user", "USER-333")
149-
.AddTag("user", "USER-444")
150-
.AddTag("type", "planning"));
190+
.AddFile("project.docx")
191+
.AddTag("user", "USER-333")
192+
.AddTag("user", "USER-444")
193+
.AddTag("type", "planning"));
151194

152195
// No information found, the type tag doesn't match
153-
var answer = await memory.AskAsync("what's the project timeline?", MemoryFilters.ByTag("user", "USER-333").ByTag("type", "email"));
196+
var answer = await memory.AskAsync("what's the project timeline?",
197+
MemoryFilters.ByTag("user", "USER-333")
198+
.ByTag("type", "email"));
154199

155200
// OK
156-
var answer = await memory.AskAsync("what's the project timeline?", MemoryFilters.ByTag("user", "USER-333").ByTag("type", "planning"));
201+
var answer = await memory.AskAsync("what's the project timeline?",
202+
MemoryFilters.ByTag("user", "USER-333")
203+
.ByTag("type", "planning"));
204+
205+
```
206+
207+
# Security best practices
208+
209+
Summarizing, we recommend these best practices to secure Semantic Memory usage:
157210

158-
```
211+
* Use Semantic Memory as **a private backend component**, similar to a SQL
212+
Server, without granting direct access. When using Semantic Memory as a
213+
service, consider assigning the service a reserved IP, accessible only to
214+
your IP, and using HTTPS only.
215+
* Authenticate users in your backend using a secure solution like Azure
216+
Active Directory, extract the user ID from the signed credentials like JWT
217+
tokens or client certs, and tag every interaction with Semantic Memory with
218+
this User ID
219+
* **Use Semantic Memory Tags as Security Filters**. Make sure every API call
220+
to Semantic Memory uses a User tag, both when reading and writing to memory.

0 commit comments

Comments
 (0)