Skip to content

Commit d3d77e8

Browse files
Add a query cache POC into Text2SQL (#19)
1 parent a2944bd commit d3d77e8

20 files changed

+1511
-202
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ It is intended that the plugins and skills provided in this repository, are adap
66

77
## Components
88

9-
- `./text_2_sql` contains an two Multi-Shot implementation for Text2SQL generation and querying which can be used to answer questions backed by a database as a knowledge base. A prompt based and vector based approach are shown, both of which exhibit great performance in answering sql queries. With these plugins, your RAG application can now access and pull data from any SQL table exposed to it to answer questions.
10-
- `./adi_function_app` contains code for linking Azure Document Intelligence with AI Search to process complex documents with charts and images, and uses multi-modal models (gpt4o) to interpret and understand these. With this custom skill, the RAG application can draw insights from complex charts and images during the vector search.
9+
- `./text_2_sql` contains an three Multi-Shot implementations for Text2SQL generation and querying which can be used to answer questions backed by a database as a knowledge base. A **prompt based** and **vector based** approach are shown, both of which exhibit great performance in answering sql queries. Additionally, a further iteration on the vector based approach is shown which uses a **query cache** to further speed up generation. With these plugins, your RAG application can now access and pull data from any SQL table exposed to it to answer questions.
10+
- `./adi_function_app` contains code for linking **Azure Document Intelligence** with AI Search to process complex documents with charts and images, and uses **multi-modal models (gpt4o)** to interpret and understand these. With this custom skill, the RAG application can **draw insights from complex charts** and images during the vector search.
1111
- `./deploy_ai_search` provides an easy Python based utility for deploying an index, indexer and corresponding skillset for AI Search and for Text2SQL.
1212

1313
The above components have been successfully used on production RAG projects to increase the quality of responses.

adi_function_app/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ If `chunk_by_page` header is `False`:
206206

207207
**Page wise analysis in ADI is recommended to avoid splitting tables / figures across multiple chunks, when the chunking is performed.**
208208

209+
## Other Provided Custom Skills
210+
211+
Due to a AI Search product limitation that AI Search cannot connect to AI Services behind Private Endpoints, we provide a Custom Key Phrase Extraction Skill that will work within a Private Endpoint environment.
212+
213+
Additionally, a custom cleaning skill is provided to clean the chunks before vectorisation takes place.
214+
209215
## Production Considerations
210216

211217
Below are some of the considerations that should be made before using this custom skill in production:

deploy_ai_search/README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,30 @@ The associated scripts in this portion of the repository contains pre-built scri
88
2. Adjust `rag_documents.py` with any changes to the index / indexer. The `get_skills()` method implements the skills pipeline. Make any adjustments here in the skills needed to enrich the data source.
99
3. Run `deploy.py` with the following args:
1010

11-
- `indexer_type rag`. This selects the `RagDocumentsAISearch` sub class.
11+
- `index_type rag`. This selects the `RagDocumentsAISearch` sub class.
1212
- `enable_page_chunking True`. This determines whether page wise chunking is applied in ADI, or whether the inbuilt skill is used for TextSplit. **Page wise analysis in ADI is recommended to avoid splitting tables / figures across multiple chunks, when the chunking is performed.**
1313
- `rebuild`. Whether to delete and rebuild the index.
1414
- `suffix`. Optional parameter that will apply a suffix onto the deployed index and indexer. This is useful if you want deploy a test version, before overwriting the main version.
1515

1616
## Steps for Text2SQL Index Deployment
1717

18+
### Entity Schema Index
19+
1820
1. Update `.env` file with the associated values. Not all values are required dependent on whether you are using System / User Assigned Identities or a Key based authentication.
1921
2. Adjust `text_2_sql.py` with any changes to the index / indexer. The `get_skills()` method implements the skills pipeline. Make any adjustments here in the skills needed to enrich the data source.
2022
3. Run `deploy.py` with the following args:
2123

22-
- `indexer_type text_2_sql`. This selects the `Text2SQLAISearch` sub class.
24+
- `index_type text_2_sql`. This selects the `Text2SQLAISearch` sub class.
25+
- `rebuild`. Whether to delete and rebuild the index.
26+
- `suffix`. Optional parameter that will apply a suffix onto the deployed index and indexer. This is useful if you want deploy a test version, before overwriting the main version.
27+
28+
### Query Cache Index
29+
30+
1. Update `.env` file with the associated values. Not all values are required dependent on whether you are using System / User Assigned Identities or a Key based authentication.
31+
2. Adjust `text_2_sql_query_cache.py` with any changes to the index. **There is no provided indexer or skillset for this cache, it is expected that application code will write directly to it.**
32+
3. Run `deploy.py` with the following args:
33+
34+
- `index_type text_2_sql_query_cache`. This selects the `Text2SQLQueryCacheAISearch` sub class.
2335
- `rebuild`. Whether to delete and rebuild the index.
2436
- `suffix`. Optional parameter that will apply a suffix onto the deployed index and indexer. This is useful if you want deploy a test version, before overwriting the main version.
2537

deploy_ai_search/deploy.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,28 @@
33
import argparse
44
from rag_documents import RagDocumentsAISearch
55
from text_2_sql import Text2SqlAISearch
6+
from text_2_sql_query_cache import Text2SqlQueryCacheAISearch
67

78

89
def deploy_config(arguments: argparse.Namespace):
910
"""Deploy the indexer configuration based on the arguments passed.
1011
1112
Args:
1213
arguments (argparse.Namespace): The arguments passed to the script"""
13-
if arguments.indexer_type == "rag":
14+
if arguments.index_type == "rag":
1415
index_config = RagDocumentsAISearch(
1516
suffix=arguments.suffix,
1617
rebuild=arguments.rebuild,
1718
enable_page_by_chunking=arguments.enable_page_chunking,
1819
)
19-
elif arguments.indexer_type == "text_2_sql":
20+
elif arguments.index_type == "text_2_sql":
2021
index_config = Text2SqlAISearch(
2122
suffix=arguments.suffix, rebuild=arguments.rebuild
2223
)
24+
elif arguments.index_type == "text_2_sql_query_cache":
25+
index_config = Text2SqlQueryCacheAISearch(
26+
suffix=arguments.suffix, rebuild=arguments.rebuild
27+
)
2328
else:
2429
raise ValueError("Invalid Indexer Type")
2530

@@ -32,7 +37,7 @@ def deploy_config(arguments: argparse.Namespace):
3237
if __name__ == "__main__":
3338
parser = argparse.ArgumentParser(description="Process some arguments.")
3439
parser.add_argument(
35-
"--indexer_type",
40+
"--index_type",
3641
type=str,
3742
required=True,
3843
help="Type of Indexer want to deploy.",

deploy_ai_search/environment.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class IndexerType(Enum):
1313

1414
RAG_DOCUMENTS = "rag-documents"
1515
TEXT_2_SQL = "text-2-sql"
16+
TEXT_2_SQL_QUERY_CACHE = "text-2-sql-query-cache"
1617

1718

1819
class IdentityType(Enum):

deploy_ai_search/text_2_sql.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ def __init__(self, suffix: str | None = None, rebuild: bool | None = False):
4040

4141
self.parsing_mode = BlobIndexerParsingMode.JSON_ARRAY
4242

43-
self.entities = []
44-
4543
def get_index_fields(self) -> list[SearchableField]:
4644
"""This function returns the index fields for sql index.
4745
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
from azure.search.documents.indexes.models import (
5+
SearchFieldDataType,
6+
SearchField,
7+
SearchableField,
8+
SemanticField,
9+
SemanticPrioritizedFields,
10+
SemanticConfiguration,
11+
SemanticSearch,
12+
SimpleField,
13+
ComplexField,
14+
)
15+
from ai_search import AISearch
16+
from environment import (
17+
IndexerType,
18+
)
19+
20+
21+
class Text2SqlQueryCacheAISearch(AISearch):
22+
"""This class is used to deploy the sql index."""
23+
24+
def __init__(self, suffix: str | None = None, rebuild: bool | None = False):
25+
"""Initialize the Text2SqlAISearch class. This class implements the deployment of the sql index.
26+
27+
Args:
28+
suffix (str, optional): The suffix for the indexer. Defaults to None. If an suffix is provided, it is assumed to be a test indexer.
29+
rebuild (bool, optional): Whether to rebuild the index. Defaults to False.
30+
"""
31+
self.indexer_type = IndexerType.TEXT_2_SQL_QUERY_CACHE
32+
super().__init__(suffix, rebuild)
33+
34+
def get_index_fields(self) -> list[SearchableField]:
35+
"""This function returns the index fields for sql index.
36+
37+
Returns:
38+
list[SearchableField]: The index fields for sql index"""
39+
40+
fields = [
41+
SimpleField(
42+
name="Id", type=SearchFieldDataType.String, key=True, retrievable=False
43+
),
44+
SearchableField(
45+
name="Question",
46+
type=SearchFieldDataType.String,
47+
analyzer_name="keyword",
48+
),
49+
SearchField(
50+
name="QuestionEmbedding",
51+
type=SearchFieldDataType.Collection(SearchFieldDataType.Single),
52+
vector_search_dimensions=self.environment.open_ai_embedding_dimensions,
53+
vector_search_profile_name=self.vector_search_profile_name,
54+
),
55+
SearchableField(
56+
name="Query", type=SearchFieldDataType.String, filterable=True
57+
),
58+
SearchField(
59+
name="QueryEmbedding",
60+
type=SearchFieldDataType.Collection(SearchFieldDataType.Single),
61+
vector_search_dimensions=self.environment.open_ai_embedding_dimensions,
62+
vector_search_profile_name=self.vector_search_profile_name,
63+
),
64+
ComplexField(
65+
name="Schemas",
66+
collection=True,
67+
fields=[
68+
SearchableField(
69+
name="Entity",
70+
type=SearchFieldDataType.String,
71+
filterable=True,
72+
),
73+
ComplexField(
74+
name="Columns",
75+
collection=True,
76+
fields=[
77+
SearchableField(
78+
name="Name", type=SearchFieldDataType.String
79+
),
80+
SearchableField(
81+
name="Definition", type=SearchFieldDataType.String
82+
),
83+
SearchableField(
84+
name="Type", type=SearchFieldDataType.String
85+
),
86+
SimpleField(
87+
name="AllowedValues",
88+
type=SearchFieldDataType.String,
89+
collection=True,
90+
),
91+
SimpleField(
92+
name="SampleValues",
93+
type=SearchFieldDataType.String,
94+
collection=True,
95+
),
96+
],
97+
),
98+
],
99+
),
100+
]
101+
102+
return fields
103+
104+
def get_semantic_search(self) -> SemanticSearch:
105+
"""This function returns the semantic search configuration for sql index
106+
107+
Returns:
108+
SemanticSearch: The semantic search configuration"""
109+
110+
semantic_config = SemanticConfiguration(
111+
name=self.semantic_config_name,
112+
prioritized_fields=SemanticPrioritizedFields(
113+
title_field=SemanticField(field_name="Question"),
114+
keywords_fields=[
115+
SemanticField(field_name="Query"),
116+
],
117+
),
118+
)
119+
120+
semantic_search = SemanticSearch(configurations=[semantic_config])
121+
122+
return semantic_search

images/Plugin Based RAG Flow.png

-6.13 KB
Loading

text_2_sql/.env

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
OpenAI__CompletionDeployment=<openAICompletionDeploymentId>
2-
OpenAI__EmbeddingModel=<openAIEmbeddingModelName>
3-
OpenAI__Endpoint=<openAIEndpoint>
4-
OpenAI__ApiKey=<openAIKey if using non managed identity>
5-
OpenAI__ApiVersion=<openAIApiVersion>
6-
Text2Sql__DatabaseEngine=<databaseEngine>
7-
Text2Sql__DatabaseName=<databaseName>
8-
Text2Sql__DatabaseConnectionString=<databaseConnectionString>
9-
AIService__AzureSearchOptions__Endpoint=<searchServiceEndpoint>
10-
AIService__AzureSearchOptions__Key=<searchServiceKey if not using identity>
11-
AIService__AzureSearchOptions__RagDocuments__Index=<ragDocumentsIndexName>
12-
AIService__AzureSearchOptions__Text2Sql__Index=<text2SQLIndexName>
13-
AIService__AzureSearchOptions__RagDocuments__SemanticConfig=<ragDocumentsSemanticConfig>
14-
AIService__AzureSearchOptions__Text2Sql__SemanticConfig=<text2SQLSemanticConfig>
15-
IdentityType=<identityType> # system_assigned or user_assigned or key
16-
ClientId=<clientId if using user assigned identity>
1+
OpenAI__CompletionDeployment=<openAICompletionDeploymentId>
2+
OpenAI__EmbeddingModel=<openAIEmbeddingModelName>
3+
OpenAI__Endpoint=<openAIEndpoint>
4+
OpenAI__ApiKey=<openAIKey if using non managed identity>
5+
OpenAI__ApiVersion=<openAIApiVersion>
6+
Text2Sql__DatabaseEngine=<databaseEngine>
7+
Text2Sql__UseQueryCache=<whether to use the query cache first or not>
8+
Text2Sql__DatabaseName=<databaseName>
9+
Text2Sql__DatabaseConnectionString=<databaseConnectionString>
10+
AIService__AzureSearchOptions__Endpoint=<searchServiceEndpoint>
11+
AIService__AzureSearchOptions__Key=<searchServiceKey if not using identity>
12+
AIService__AzureSearchOptions__RagDocuments__Index=<ragDocumentsIndexName>
13+
AIService__AzureSearchOptions__Text2Sql__Index=<text2SQLIndexName>
14+
AIService__AzureSearchOptions__Text2SqlQueryCache__Index=<text2SQLIndexName>
15+
AIService__AzureSearchOptions__RagDocuments__SemanticConfig=<ragDocumentsSemanticConfig>
16+
AIService__AzureSearchOptions__Text2Sql__SemanticConfig=<text2SQLSemanticConfig>
17+
AIService__AzureSearchOptions__Text2SqlQueryCache__SemanticConfig=<text2SQLSemanticConfig>
18+
IdentityType=<identityType> # system_assigned or user_assigned or key
19+
ClientId=<clientId if using user assigned identity>

0 commit comments

Comments
 (0)