├── .gitattributes ├── .gitignore ├── DistanceFunctions.cs ├── Properties └── AssemblyInfo.cs ├── README.md ├── SQLRAG.application ├── AzureOpenaiFunctions.cs ├── OpenaiFunction.cs ├── Properties │ └── AssemblyInfo.cs ├── RagFunction.cs ├── SQLRAG.application.sqlproj ├── SqlRAGCertificate.sql ├── SqlRagStoredProcedure.cs ├── dbo.EncryptedKeys.sql ├── dbo.KnowledgeBase.sql ├── dbo.QueryIntentCache.sql ├── dbo.WebPilotLogs.sql └── postdeployscript.sql ├── SQLRAG.publish.xml ├── SQLRAG.sln ├── SQLRAG.sqlproj ├── SqlArray.cs ├── TextFunction.cs └── assets ├── ChatCompletion.png ├── ChatCompletion_demo.sql ├── QueryCache.png └── QueryIntentCache_demo.sql /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd 364 | /SQLQuery1.sql 365 | -------------------------------------------------------------------------------- /DistanceFunctions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.SqlClient; 4 | using System.Data.SqlTypes; 5 | using Microsoft.SqlServer.Server; 6 | 7 | public partial class DistanceFunctions 8 | { 9 | 10 | 11 | 12 | /// 13 | /// Calculates the cosine similarity between two vectors. 14 | /// 15 | /// The first vector. 16 | /// The second vector. 17 | /// The cosine similarity between the two vectors. 18 | [SqlFunction] 19 | public static SqlDouble CosineSimilarity(SqlArray vector1, SqlArray vector2) 20 | { 21 | if (vector1.IsNull || vector2.IsNull) 22 | { 23 | return SqlDouble.Null; 24 | } 25 | 26 | double[] vec1 = vector1.Data; 27 | double[] vec2 = vector2.Data; 28 | 29 | if (vec1.Length != vec2.Length) 30 | return SqlDouble.Null; 31 | 32 | double dotProduct = 0.0; 33 | double normVec1 = 0.0; 34 | double normVec2 = 0.0; 35 | 36 | for (int i = 0; i < vec1.Length; i++) 37 | { 38 | dotProduct += vec1[i] * vec2[i]; 39 | normVec1 += vec1[i] * vec1[i]; 40 | normVec2 += vec2[i] * vec2[i]; 41 | } 42 | 43 | if (normVec1 == 0.0 || normVec2 == 0.0) 44 | return 0; 45 | 46 | return new SqlDouble(dotProduct / (Math.Sqrt(normVec1) * Math.Sqrt(normVec2))); 47 | } 48 | 49 | 50 | [SqlFunction] 51 | public static SqlDouble EuclideanDistance(SqlArray vector1, SqlArray vector2) 52 | { 53 | if (vector1.IsNull || vector2.IsNull) 54 | { 55 | return SqlDouble.Null; 56 | } 57 | 58 | double[] vec1 = vector1.Data; 59 | double[] vec2 = vector2.Data; 60 | 61 | if (vec1.Length != vec2.Length) 62 | return SqlDouble.Null; 63 | 64 | double sum = 0.0; 65 | for (int i = 0; i < vec1.Length; i++) 66 | { 67 | sum += Math.Pow(vec1[i] - vec2[i], 2); 68 | } 69 | 70 | return Math.Sqrt(sum); 71 | } 72 | 73 | 74 | [SqlFunction] 75 | public static SqlDouble ManhattanDistance(SqlArray vector1, SqlArray vector2) 76 | { 77 | if (vector1.IsNull || vector2.IsNull) 78 | { 79 | return SqlDouble.Null; 80 | } 81 | 82 | double[] vec1 = vector1.Data; 83 | double[] vec2 = vector2.Data; 84 | 85 | if (vec1.Length != vec2.Length) 86 | return SqlDouble.Null; 87 | 88 | double sum = 0.0; 89 | for (int i = 0; i < vec1.Length; i++) 90 | { 91 | sum += Math.Abs(vec1[i] - vec2[i]); 92 | } 93 | 94 | return sum; 95 | } 96 | 97 | 98 | 99 | [SqlFunction] 100 | public static SqlDouble HammingDistance(SqlString InputString1, SqlString InputString2) 101 | { 102 | if (InputString1.IsNull || InputString2.IsNull) 103 | { 104 | return SqlDouble.Null; 105 | } 106 | 107 | char[] vec1 = InputString1.Value.ToCharArray(); 108 | char[] vec2 = InputString2.Value.ToCharArray(); 109 | 110 | if (vec1.Length != vec2.Length) 111 | return SqlDouble.Null; 112 | 113 | int distance = 0; 114 | for (int i = 0; i < vec1.Length; i++) 115 | { 116 | if (vec1[i] != vec2[i]) 117 | distance++; 118 | } 119 | 120 | return distance; 121 | } 122 | 123 | [SqlFunction] 124 | public static SqlDouble ChebyshevDistance(SqlArray vector1, SqlArray vector2) 125 | { 126 | if (vector1.IsNull || vector2.IsNull) 127 | { 128 | return SqlDouble.Null; 129 | } 130 | 131 | double[] vec1 = vector1.Data; 132 | double[] vec2 = vector2.Data; 133 | 134 | if (vec1.Length != vec2.Length) 135 | return SqlDouble.Null; 136 | 137 | double maxDifference = 0.0; 138 | for (int i = 0; i < vec1.Length; i++) 139 | { 140 | double difference = Math.Abs(vec1[i] - vec2[i]); 141 | if (difference > maxDifference) 142 | maxDifference = difference; 143 | } 144 | 145 | return maxDifference; 146 | } 147 | 148 | 149 | 150 | 151 | [SqlFunction] 152 | public static SqlDouble MinkowskiDistance(SqlArray vector1, SqlArray vector2, SqlDouble p) 153 | { 154 | if (vector1.IsNull || vector2.IsNull) 155 | { 156 | return SqlDouble.Null; 157 | } 158 | 159 | if (p.Value < 1) 160 | return SqlDouble.Null; // p 必須大於等於1 161 | 162 | double[] vec1 = vector1.Data; 163 | double[] vec2 = vector2.Data; 164 | 165 | 166 | if (vec1.Length != vec2.Length) 167 | return SqlDouble.Null; 168 | 169 | double sum = 0.0; 170 | for (int i = 0; i < vec1.Length; i++) 171 | { 172 | sum += Math.Pow(Math.Abs(vec1[i] - vec2[i]), p.Value); 173 | } 174 | 175 | return Math.Pow(sum, 1.0 / p.Value); 176 | } 177 | 178 | 179 | [SqlFunction] 180 | public static SqlInt32 LevenshteinDistance([SqlFacet(MaxSize = -1)] SqlString InputString1, [SqlFacet(MaxSize = -1)] SqlString InputString2) 181 | { 182 | if (InputString1.IsNull || InputString2.IsNull) 183 | { 184 | return SqlInt32.Null; 185 | } 186 | 187 | int n = InputString1.Value.Length; 188 | int m = InputString2.Value.Length; 189 | int[,] d = new int[n + 1, m + 1]; 190 | 191 | // Step 1 192 | if (n == 0) 193 | { 194 | return new SqlInt32(m); 195 | } 196 | 197 | if (m == 0) 198 | { 199 | return new SqlInt32(n); 200 | } 201 | 202 | // Step 2 203 | for (int i = 0; i <= n; d[i, 0] = i++) 204 | { 205 | } 206 | 207 | for (int j = 0; j <= m; d[0, j] = j++) 208 | { 209 | } 210 | 211 | // Step 3 212 | for (int i = 1; i <= n; i++) 213 | { 214 | //Step 4 215 | for (int j = 1; j <= m; j++) 216 | { 217 | // Step 5 218 | int cost = (InputString2.Value[j - 1] == InputString1.Value[i - 1]) ? 0 : 1; 219 | 220 | // Step 6 221 | d[i, j] = Math.Min( 222 | Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), 223 | d[i - 1, j - 1] + cost); 224 | } 225 | } 226 | // Step 7 227 | return new SqlInt32(d[n, m]); 228 | } 229 | 230 | 231 | 232 | 233 | } 234 | 235 | 236 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | // 組件的一般資訊是由下列的屬性集控制。 4 | // 變更這些屬性的值即可修改 5 | // SQLCLR 組件的相關資訊。 6 | [assembly: AssemblyTitle("SQLRAG")] 7 | [assembly: AssemblyDescription("")] 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("DataDecision.ai")] 10 | [assembly: AssemblyProduct("SQLRAG")] 11 | [assembly: AssemblyCopyright("Copyright © 2023")] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | 15 | // 組件的版本資訊是由下列四項值構成: 16 | // 17 | // 主要版本 18 | // 次要版本 19 | // 組建編號 20 | // 修訂版本 21 | // 22 | [assembly: AssemblyVersion("0.1.0.0")] 23 | [assembly: AssemblyFileVersion("0.1.0.0")] 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQLRAG 2 | ## 讓SQL Server也能執行向量相似性查詢 3 | 4 | 目前已經實現功能: 5 | - 各種相似性比較CLR函數: CosineSimilarity, EuclideanDistance... [參考 (https://chat.openai.com/share/40723def-afdd-4f83-adb3-fbcae34b617a)] 6 | - 透過CLR函數調用Openai API(GetEmbedding, ChatCompletion...) 7 | 8 | 9 | ## 安裝 10 | 最小安裝要求:SQL Server 2022, Visual Studio 2022(需安裝SSDT) 11 | 12 | 1. 首先要啟用SQL Server CLR: 13 | ```sql 14 | use SQLRAG 15 | sp_configure 'show advanced options', 1; 16 | RECONFIGURE; 17 | sp_configure 'clr enabled', 1; 18 | RECONFIGURE; 19 | ``` 20 | 21 | 2. 有以下幾種方式可以安裝: 22 | - 使用release中提供的SQLRAG_CREATE.sql直接執行即可完成安裝(適合首次安裝者) 23 | - 使用release中提供的dacpac檔,參考[網頁 (https://learn.microsoft.com/zh-tw/sql/relational-databases/data-tier-applications/upgrade-a-data-tier-application?view=sql-server-ver16)]內容安裝。 24 | - 使用release中提供的原始碼,使用Visual Studio 2022開啟後修改各專案的資料庫連線後直接部署方案 25 | 26 | 3. 如果要使用OpenaiFunction,之前的做法是在OpenaiFunction.cs中輸入你實際的OPENAI_API_KEY,這樣的做法是不能確保API KEY的安全的。在這一版中,我在SqlRAG資料庫中增加了dbo.EncryptedKeys資料表,其中KeyValue部分透過憑證加密。看起來複雜但我已經將自動調用的部分處理好,開發者只需要透過以下SQL 語法將API KEY加密後寫入資料表中即可。 27 | ```sql 28 | use SQLRAG 29 | Declare @cleartext varchar(512)='sk-輸入你的OPENAI_API_KEYAPI KEY' 30 | Declare @encrytext varbinary(4000)=EncryptByCert(Cert_ID('SqlRAGCertificate'), @cleartext) 31 | 32 | INSERT INTO [SQLRAG].[dbo].[EncryptedKeys] 33 | VALUES ( N'OPENAI_API_KEY', N'調用OPENAI所用之API KEY',@encrytext ); 34 | ``` 35 | 之後則需要透過顯式的聲明基於哪個憑證以及對應的密碼來進行解密: 36 | ```sql 37 | 38 | DecryptByCert(Cert_ID('SqlRAGCertificate'), EncryptByCert(Cert_ID('SqlRAGCertificate'), @cleartext) ,'P@ssw0rd') 39 | 40 | ``` 41 | 42 | 如果你是Azure Openai Service用戶則需要將api key以及endpoint加密後存入 43 | ```sql 44 | 45 | use SQLRAG 46 | Declare @cleartext varchar(512)='***' 47 | Declare @encrytext varbinary(4000)=EncryptByCert(Cert_ID('SqlRAGCertificate'), @cleartext) 48 | 49 | Declare @cleartext2 varchar(512)='https://***.openai.azure.com' 50 | Declare @encrytext2 varbinary(4000)=EncryptByCert(Cert_ID('SqlRAGCertificate'), @cleartext2) 51 | 52 | Declare @cleartext3 varchar(512)='***' 53 | Declare @encrytext3 varbinary(4000)=EncryptByCert(Cert_ID('SqlRAGCertificate'), @cleartext3) 54 | 55 | INSERT INTO [SQLRAG].[dbo].[EncryptedKeys] 56 | VALUES ( N'AZURE_OPENAI_API_KEY', N'調用Azure Openai Service 所用之API KEY',@encrytext ); 57 | INSERT INTO [SQLRAG].[dbo].[EncryptedKeys] 58 | VALUES ( N'AZURE_OPENAI_ENDPOINT', N'調用Azure Openai Service 所用之endpoint',@encrytext2 ); 59 | INSERT INTO [SQLRAG].[dbo].[EncryptedKeys] 60 | VALUES ( N'OPENAI_API_VERSION', N'調用Azure Openai Service 所用之API Version',@encrytext3 ); 61 | 62 | ``` 63 | 64 | 65 | 66 | 4. assets中的QueryIntentCache_demo.sql (語意快取範例)以及ChatCompletion_demo.sql(ChatGPT回答範例) 67 | 68 | ![ChatCompletion](assets/QueryCache.png) 69 | ![ChatCompletion](assets/ChatCompletion.png) 70 | 71 | 72 | -------------------------------------------------------------------------------- /SQLRAG.application/AzureOpenaiFunctions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.SqlClient; 4 | using System.Data.SqlTypes; 5 | using System.IO; 6 | using System.Net; 7 | using System.Reflection; 8 | using System.Security.Policy; 9 | using System.Text.RegularExpressions; 10 | using Microsoft.SqlServer.Server; 11 | 12 | public partial class AzureOpenaiFunction 13 | { 14 | 15 | private static string GetKey() 16 | { 17 | System.Data.SqlClient.SqlConnection.ColumnEncryptionKeyCacheTtl = TimeSpan.Zero; 18 | using (SqlConnection conn = new SqlConnection("context connection=true")) 19 | { 20 | conn.Open(); 21 | SqlCommand cmd = new SqlCommand( 22 | @"Declare @encrytext varbinary(4000)=(SELECT [KeyValue] FROM [SQLRAG].[dbo].[EncryptedKeys] WHERE [KeyName]='AZURE_OPENAI_API_KEY') 23 | Declare @decrytext varchar(512)=DecryptByCert(Cert_ID('SqlRAGCertificate'),@encrytext,N'SqlRAGP@ssw0rd') 24 | select @decrytext ", conn); 25 | 26 | using (SqlDataReader reader = cmd.ExecuteReader()) 27 | { 28 | while (reader.Read()) 29 | { 30 | return (string)reader[0]; 31 | } 32 | } 33 | return string.Empty; 34 | } 35 | } 36 | 37 | private static string GetEndpoint() 38 | { 39 | System.Data.SqlClient.SqlConnection.ColumnEncryptionKeyCacheTtl = TimeSpan.Zero; 40 | using (SqlConnection conn = new SqlConnection("context connection=true")) 41 | { 42 | conn.Open(); 43 | SqlCommand cmd = new SqlCommand( 44 | @"Declare @encrytext varbinary(4000)=(SELECT [KeyValue] FROM [SQLRAG].[dbo].[EncryptedKeys] WHERE [KeyName]='AZURE_OPENAI_ENDPOINT') 45 | Declare @decrytext varchar(512)=DecryptByCert(Cert_ID('SqlRAGCertificate'),@encrytext,N'SqlRAGP@ssw0rd') 46 | select @decrytext ", conn); 47 | 48 | using (SqlDataReader reader = cmd.ExecuteReader()) 49 | { 50 | while (reader.Read()) 51 | { 52 | return (string)reader[0]; 53 | } 54 | } 55 | return string.Empty; 56 | } 57 | } 58 | 59 | private static string GetVersion() 60 | { 61 | System.Data.SqlClient.SqlConnection.ColumnEncryptionKeyCacheTtl = TimeSpan.Zero; 62 | using (SqlConnection conn = new SqlConnection("context connection=true")) 63 | { 64 | conn.Open(); 65 | SqlCommand cmd = new SqlCommand( 66 | @"Declare @encrytext varbinary(4000)=(SELECT [KeyValue] FROM [SQLRAG].[dbo].[EncryptedKeys] WHERE [KeyName]='OPENAI_API_VERSION') 67 | Declare @decrytext varchar(512)=DecryptByCert(Cert_ID('SqlRAGCertificate'),@encrytext,N'SqlRAGP@ssw0rd') 68 | select @decrytext ", conn); 69 | 70 | using (SqlDataReader reader = cmd.ExecuteReader()) 71 | { 72 | while (reader.Read()) 73 | { 74 | return (string)reader[0]; 75 | } 76 | } 77 | return string.Empty; 78 | } 79 | } 80 | 81 | 82 | [SqlFunction(DataAccess = DataAccessKind.Read)] 83 | public static SqlArray GetAzureEmbedding([SqlFacet(MaxSize = -1)] SqlString inputText,SqlString deploymentName) 84 | { 85 | 86 | string apiKey = GetKey(); 87 | string apiUrl = ""; 88 | string endpoint = GetEndpoint(); 89 | string apiVersion = GetVersion(); 90 | if (!deploymentName.IsNull) 91 | { 92 | apiUrl = string.Format("{0}/openai/deployments/{1}/embeddings?api-version={2}", endpoint, deploymentName.Value, apiVersion); 93 | } 94 | else 95 | { 96 | throw new ArgumentException(); 97 | } 98 | 99 | string requestBody = $"{{\"input\": \"{inputText}\",\"encoding_format\":\"float\"}}"; 100 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 101 | try 102 | { 103 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl); 104 | request.UserAgent = 105 | "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; WOW64; " + 106 | "Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; " + 107 | ".NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618; " + 108 | "InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0)"; 109 | request.Method = "POST"; 110 | request.Headers["api-key"] = apiKey; 111 | request.Headers["Accept-Language"] = "zh-TW"; 112 | request.ContentType = "application/json"; 113 | 114 | 115 | using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream())) 116 | { 117 | streamWriter.Write(requestBody); 118 | } 119 | 120 | HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 121 | string result; 122 | using (StreamReader streamReader = new StreamReader(response.GetResponseStream())) 123 | { 124 | result = streamReader.ReadToEnd(); 125 | 126 | } 127 | return new SqlArray(System.Array.ConvertAll(ParseEmbedding(result).Split(','), Double.Parse)); // 返回結果 128 | } 129 | catch (Exception ex) 130 | { 131 | // 錯誤處理 132 | throw ex; 133 | } 134 | } 135 | 136 | 137 | 138 | 139 | 140 | [return: SqlFacet(MaxSize = -1)] 141 | [SqlFunction(DataAccess = DataAccessKind.Read)] 142 | public static SqlString AzureChatCompletion([SqlFacet(MaxSize = -1)] SqlString inputPrompt, [SqlFacet(MaxSize = -1)] SqlString systemProimpt = default(SqlString), SqlString deploymentName = default(SqlString)) 143 | { 144 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 145 | string apiKey = GetKey(); 146 | string apiUrl = ""; 147 | string endpoint = GetEndpoint(); 148 | string apiVersion = GetVersion(); 149 | if (!deploymentName.IsNull) 150 | { 151 | apiUrl = string.Format("{0}/openai/deployments/{1}/chat/completions?api-version={2}", endpoint, deploymentName.Value, apiVersion); 152 | } 153 | else 154 | { 155 | throw new ArgumentException(); 156 | } 157 | 158 | 159 | string requestBody1 = $@"{{""messages"": [{{""role"": ""system"", ""content"": ""{systemProimpt.ToString()}""}},{{""role"": ""user"", ""content"": ""{inputPrompt.ToString()}""}}]}}"; 160 | string requestBody = systemProimpt.IsNull || String.IsNullOrWhiteSpace(systemProimpt.Value) ? $@"{{ ""messages"": [{{""role"": ""user"", ""content"": ""{inputPrompt.ToString()}""}}]}}" : requestBody1; 161 | 162 | try 163 | { 164 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl); 165 | request.UserAgent = 166 | "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; WOW64; " + 167 | "Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; " + 168 | ".NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618; " + 169 | "InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0)"; 170 | request.Method = "POST"; 171 | request.Headers["api-key"] = apiKey; 172 | request.Headers["Accept-Language"] = "zh-TW"; 173 | request.ContentType = "application/json"; 174 | 175 | using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream())) 176 | { 177 | streamWriter.Write(requestBody); 178 | } 179 | 180 | HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 181 | using (StreamReader streamReader = new StreamReader(response.GetResponseStream())) 182 | { 183 | string result = streamReader.ReadToEnd(); 184 | 185 | return ParseChatting(result); // 返回結果 186 | } 187 | } 188 | catch (Exception ex) 189 | { 190 | // 錯誤處理 191 | return new SqlString($"Error: {ex.Message}"); 192 | } 193 | } 194 | 195 | 196 | 197 | private static string ParseEmbedding(string jsonResponse) 198 | { 199 | // 簡單的 JSON 解析來提取 embedding 200 | string startPattern = "\"embedding\": ["; 201 | string endPattern = "]"; 202 | int startIndex = jsonResponse.IndexOf(startPattern) + startPattern.Length; 203 | int endIndex = jsonResponse.IndexOf(endPattern, startIndex); 204 | string embeddingString = jsonResponse.Substring(startIndex, endIndex - startIndex); 205 | 206 | // 清理和格式化數據 207 | embeddingString = embeddingString.Replace("\n", "").Replace("\r", "").Replace(" ", ""); 208 | return embeddingString; 209 | } 210 | 211 | private const RegexOptions ExpressionOptions = RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase; 212 | private static SqlString ParseChatting(string jsonResponse) 213 | { 214 | 215 | Match match = Regex.Match(jsonResponse, "\"content\":\"(.*?)\"", ExpressionOptions); 216 | // 判斷是否匹配成功 217 | if (match.Success) 218 | { 219 | // 獲取第一個子組的值 220 | string content = match.Groups[1].Value; 221 | return new SqlString(content); 222 | } 223 | else 224 | { 225 | return SqlString.Null; 226 | } 227 | 228 | } 229 | 230 | 231 | } 232 | -------------------------------------------------------------------------------- /SQLRAG.application/OpenaiFunction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.SqlClient; 4 | using System.Net; 5 | using System.Data.SqlTypes; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Microsoft.SqlServer.Server; 9 | using System.IO; 10 | using System.Reflection; 11 | using System.Data.Common; 12 | using System.Collections.ObjectModel; 13 | using System.CodeDom.Compiler; 14 | using System.Text.RegularExpressions; 15 | 16 | 17 | 18 | public partial class OpenaiFunction 19 | { 20 | 21 | 22 | 23 | private static string GetKey() 24 | { 25 | //System.Data.SqlClient.SqlConnection.ColumnEncryptionKeyCacheTtl = TimeSpan.Zero; 26 | using (SqlConnection conn = new SqlConnection("context connection=true")) 27 | { 28 | conn.Open(); 29 | SqlCommand cmd = new SqlCommand( 30 | @"Declare @encrytext varbinary(4000)=(SELECT [KeyValue] FROM [SQLRAG].[dbo].[EncryptedKeys] WHERE [KeyName]='OPENAI_API_KEY') 31 | Declare @decrytext varchar(512)=DecryptByCert(Cert_ID('SqlRAGCertificate'),@encrytext,N'SqlRAGP@ssw0rd') 32 | select @decrytext ", conn); 33 | 34 | using (SqlDataReader reader = cmd.ExecuteReader()) 35 | { 36 | while (reader.Read()) 37 | { 38 | return (string)reader[0]; 39 | } 40 | } 41 | return string.Empty; 42 | } 43 | } 44 | 45 | 46 | [SqlFunction(DataAccess = DataAccessKind.Read)] 47 | public static SqlArray GetEmbedding([SqlFacet(MaxSize = -1)] SqlString inputText) 48 | { 49 | 50 | string apiKey = GetKey(); 51 | string apiUrl = "https://api.openai.com/v1/embeddings"; 52 | string requestBody = $"{{\"model\":\"text-embedding-ada-002\",\"input\": \"{inputText}\",\"encoding_format\":\"float\"}}"; 53 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 54 | 55 | try 56 | { 57 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl); 58 | request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; WOW64; " + 59 | "Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; " + 60 | ".NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618; " + 61 | "InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0)"; 62 | request.Method = "POST"; 63 | request.Headers["Authorization"] = $"Bearer {apiKey}"; 64 | request.Headers["Accept-Language"] = "zh-TW"; 65 | request.ContentType = "application/json"; 66 | 67 | using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream())) 68 | { 69 | streamWriter.Write(requestBody); 70 | } 71 | 72 | using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) 73 | { 74 | string result; 75 | using (StreamReader streamReader = new StreamReader(response.GetResponseStream())) 76 | { 77 | result = streamReader.ReadToEnd(); 78 | } 79 | 80 | return new SqlArray(System.Array.ConvertAll(ParseEmbedding(result).Split(','), Double.Parse)); // 返回結果 81 | } 82 | } 83 | catch (Exception) 84 | { 85 | return null; 86 | } 87 | } 88 | 89 | 90 | 91 | 92 | 93 | [return: SqlFacet(MaxSize = -1)] 94 | [SqlFunction(DataAccess = DataAccessKind.Read)] 95 | public static SqlString ChatCompletion([SqlFacet(MaxSize = -1)] SqlString inputPrompt, [SqlFacet(MaxSize = -1)] SqlString systemProimpt = default(SqlString), SqlString model = default(SqlString)) 96 | { 97 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 98 | string apiKey = GetKey(); 99 | string apiUrl = "https://api.openai.com/v1/chat/completions"; 100 | string modelToUse = model.IsNull || String.IsNullOrWhiteSpace(model.Value) ? "gpt-3.5-turbo" : model.Value; 101 | 102 | string requestBody1 =$"{{\"model\": \"{modelToUse}\", \"messages\": [{{\"role\": \"system\", \"content\": \"{systemProimpt.Value}\"}},{{\"role\": \"user\", \"content\": \"{inputPrompt.Value}\"}}]}}"; 103 | string requestBody = systemProimpt.IsNull || String.IsNullOrWhiteSpace(systemProimpt.Value) ? $"{{\"model\": \"{modelToUse}\", \"messages\": [{{\"role\": \"user\", \"content\": \"{inputPrompt.ToString()}\"}}]}}" : requestBody1; 104 | 105 | try 106 | { 107 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl); 108 | request.UserAgent = 109 | "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; WOW64; " + 110 | "Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; " + 111 | ".NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618; " + 112 | "InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0)"; 113 | request.Method = "POST"; 114 | request.Headers["Authorization"] = $"Bearer {apiKey}"; 115 | request.Headers["Accept-Language"] = "zh-TW"; 116 | request.ContentType = "application/json"; 117 | 118 | using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream())) 119 | { 120 | streamWriter.Write(requestBody); 121 | } 122 | 123 | HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 124 | using (StreamReader streamReader = new StreamReader(response.GetResponseStream())) 125 | { 126 | string result = streamReader.ReadToEnd(); 127 | return ParseChatting(result); // 返回結果 128 | } 129 | } 130 | catch (Exception ex) 131 | { 132 | // 錯誤處理 133 | return new SqlString($"Error: {ex.Message}"); 134 | } 135 | } 136 | 137 | 138 | 139 | private static string ParseEmbedding(string jsonResponse) 140 | { 141 | // 簡單的 JSON 解析來提取 embedding 142 | string startPattern = "\"embedding\": ["; 143 | string endPattern = "]"; 144 | int startIndex = jsonResponse.IndexOf(startPattern) + startPattern.Length; 145 | int endIndex = jsonResponse.IndexOf(endPattern, startIndex); 146 | string embeddingString = jsonResponse.Substring(startIndex, endIndex - startIndex); 147 | 148 | // 清理和格式化數據 149 | embeddingString = embeddingString.Replace("\n", "").Replace("\r", "").Replace(" ", ""); 150 | return embeddingString; 151 | } 152 | 153 | private const RegexOptions ExpressionOptions = RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase; 154 | private static SqlString ParseChatting(string jsonResponse) 155 | { 156 | 157 | Match match = Regex.Match(jsonResponse, "\"content\": \"(.*?)\"", ExpressionOptions); 158 | // 判斷是否匹配成功 159 | if (match.Success) 160 | { 161 | // 獲取第一個子組的值 162 | string content = match.Groups[1].Value; 163 | return new SqlString(content); 164 | } 165 | else 166 | { 167 | return SqlString.Null; 168 | } 169 | 170 | } 171 | 172 | 173 | 174 | //[return: SqlFacet(MaxSize = -1)] 175 | //[SqlFunction(DataAccess = DataAccessKind.Read)] 176 | //public static SqlString Translate2zhtw([SqlFacet(MaxSize = -1)] SqlString inputPrompt, SqlString model = default(SqlString)) 177 | //{ 178 | 179 | // ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 180 | // string apiKey = GetKey(); 181 | // string apiUrl = "https://api.openai.com/v1/chat/completions"; 182 | // string modelToUse = model.IsNull || String.IsNullOrWhiteSpace(model.Value) ? "gpt-3.5-turbo" : model.Value; 183 | 184 | // string requestBody = $"{{\"model\": \"{modelToUse}\", \"messages\": [{{\"role\": \"user\", \"content\": \"請直接將以下文字內容翻譯為繁體中文:\r\n{inputPrompt.Value}\"}}]}}"; 185 | 186 | // try 187 | // { 188 | // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl); 189 | // request.Method = "POST"; 190 | // request.Headers["Authorization"] = $"Bearer {apiKey}"; 191 | // request.Headers["Accept-Language"] = "zh-TW"; 192 | // request.ContentType = "application/json"; 193 | 194 | // using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream())) 195 | // { 196 | // streamWriter.Write(requestBody); 197 | // } 198 | 199 | // HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 200 | // using (StreamReader streamReader = new StreamReader(response.GetResponseStream())) 201 | // { 202 | // string result = streamReader.ReadToEnd(); 203 | // return ParseChatting(result); // 返回結果 204 | // } 205 | // } 206 | // catch (Exception ex) 207 | // { 208 | // // 錯誤處理 209 | // return new SqlString($"Error: {ex.Message}"); 210 | // } 211 | 212 | 213 | //} 214 | 215 | 216 | 217 | } 218 | -------------------------------------------------------------------------------- /SQLRAG.application/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | // 組件的一般資訊是由下列的屬性集控制。 4 | // 變更這些屬性的值即可修改 5 | // SQLCLR 組件的相關資訊。 6 | [assembly: AssemblyTitle("SQLRAG.application")] 7 | [assembly: AssemblyDescription("")] 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("DataDecision.ai")] 10 | [assembly: AssemblyProduct("SQLRAG.application")] 11 | [assembly: AssemblyCopyright("Copyright © 2023")] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | 15 | // 組件的版本資訊是由下列四項值構成: 16 | // 17 | // 主要版本 18 | // 次要版本 19 | // 組建編號 20 | // 修訂版本 21 | // 22 | [assembly: AssemblyVersion("0.1.0.0")] 23 | [assembly: AssemblyFileVersion("0.1.0.0")] 24 | -------------------------------------------------------------------------------- /SQLRAG.application/RagFunction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.SqlClient; 4 | using System.Data.SqlTypes; 5 | using Microsoft.SqlServer.Server; 6 | 7 | public partial class RagFunctions 8 | { 9 | 10 | [return: SqlFacet(MaxSize = -1)] 11 | [SqlFunction(DataAccess = DataAccessKind.Read)] 12 | public static SqlString GetCachedSQL([SqlFacet(MaxSize = -1)] SqlString question) 13 | { 14 | using (SqlConnection connection = new SqlConnection("context connection=true")) 15 | { 16 | SqlArray embedded = OpenaiFunction.GetEmbedding(question); 17 | connection.Open(); 18 | SqlCommand command = new SqlCommand("Declare @embedded SqlArray \r\nset @embedded=SqlArray::Parse(@embedded_string) \r\nselect top 1 [GeneratedTSQL]\r\nFROM\r\n(SELECT [QueryIntent] \r\n,[GeneratedTSQL]\r\n,[CreateDate]\r\n,SqlRAG.dbo.CosineSimilarity(@embedded,VectorizedQueryIntent) as cosine_similarity\r\n,1-SqlRAG.dbo.EuclideanDistance(@embedded,VectorizedQueryIntent) as euclidean_similarity\r\nFROM [SqlRAG].[dbo].[QueryIntentCache]\r\nwhere ExecStatus is null) A\r\nWHERE (cosine_similarity>=0.96) or (cosine_similarity>=0.90 and euclidean_similarity>0.70) or (cosine_similarity=-1 and euclidean_similarity>-1)\r\norder by cosine_similarity desc", connection); 19 | SqlParameter paraEmbedded = new SqlParameter("embedded_string", SqlDbType.NVarChar,-1); 20 | paraEmbedded.Value = embedded.ToString(); 21 | command.Parameters.Add(paraEmbedded); 22 | return new SqlString((string)command.ExecuteScalar()); 23 | 24 | 25 | 26 | 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SQLRAG.application/SQLRAG.application.sqlproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | SQLRAG.application 7 | 2.0 8 | 4.1 9 | {60c728d1-4969-4138-8344-fb002ded1f59} 10 | Microsoft.Data.Tools.Schema.Sql.Sql160DatabaseSchemaProvider 11 | Database 12 | 13 | SQLRAG 14 | SQLRAG.application 15 | 1028,CI 16 | BySchemaAndSchemaType 17 | True 18 | v4.7.2 19 | CS 20 | Properties 21 | False 22 | True 23 | True 24 | UNSAFE 25 | dbo 26 | Chinese_Taiwan_Stroke_CI_AS 27 | True 28 | True 29 | False 30 | 0.1.0.0 31 | 32 | 33 | bin\Release\ 34 | $(MSBuildProjectName).sql 35 | False 36 | pdbonly 37 | true 38 | false 39 | true 40 | prompt 41 | 4 42 | 43 | 44 | bin\Debug\ 45 | $(MSBuildProjectName).sql 46 | false 47 | true 48 | full 49 | false 50 | true 51 | true 52 | prompt 53 | 4 54 | x64 55 | 56 | 57 | 11.0 58 | True 59 | 11.0 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | DoNotCopy 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | SQLRAG 95 | {7e86c45f-4d32-4ab0-af50-79b6270edaee} 96 | True 97 | True 98 | 99 | 100 | -------------------------------------------------------------------------------- /SQLRAG.application/SqlRAGCertificate.sql: -------------------------------------------------------------------------------- 1 | CREATE CERTIFICATE [SqlRAGCertificate] 2 | ENCRYPTION BY PASSWORD = 'SqlRAGP@ssw0rd' 3 | WITH SUBJECT = 'certificate_subject' 4 | -------------------------------------------------------------------------------- /SQLRAG.application/SqlRagStoredProcedure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Data.SqlTypes; 6 | using Microsoft.SqlServer.Server; 7 | 8 | public partial class StoredProcedures 9 | { 10 | 11 | [Microsoft.SqlServer.Server.SqlProcedure] 12 | public static void CheckQueryIntentCache(SqlString question) 13 | { 14 | using (SqlConnection connection = new SqlConnection("context connection=true")) 15 | { 16 | SqlArray embedded = OpenaiFunction.GetEmbedding(question); 17 | connection.Open(); 18 | SqlCommand command = new SqlCommand("SELECT top 1 [GeneratedTSQL]\r\nFROM (\r\nSELECT [QueryIntent]\r\n ,[GeneratedTSQL]\r\n\t ,[CreateDate]\r\n\t ,SqlRAG.dbo.CosineSimilarity(@embedded,VectorizedQueryIntent) as cosine_similarity\r\n\t ,1-SqlRAG.dbo.EuclideanDistance(@embedded,VectorizedQueryIntent) as euclidean_similarity\r\n\t ,1-SqlRAG.dbo.MinkowskiDistance(@embedded,VectorizedQueryIntent,2.5) as minkowski_similarity\r\n FROM [SqlRAG].[dbo].[QueryIntentCache] \r\n where ExecStatus is null) A\r\n WHERE (cosine_similarity>=0.96) or (cosine_similarity>=0.90 and euclidean_similarity>0.70)\r\n order by cosine_similarity desc", connection); 19 | SqlParameter paraEmbedded = new SqlParameter("embedded", embedded); 20 | command.Parameters.Add(paraEmbedded); 21 | SqlContext.Pipe.ExecuteAndSend(command); 22 | SqlDataReader r = command.ExecuteReader(); 23 | 24 | } 25 | } 26 | 27 | 28 | [Microsoft.SqlServer.Server.SqlProcedure] 29 | public static void InsertQueryIntentCache(SqlString query_intent, [SqlFacet(MaxSize = -1)] SqlString embedded, [SqlFacet(MaxSize = -1)] SqlString tsql, [SqlFacet(MaxSize = -1)] SqlString data, SqlString exec_status) 30 | { 31 | using (SqlConnection connection = new SqlConnection("context connection=true")) 32 | { 33 | 34 | connection.Open(); 35 | SqlCommand command = new SqlCommand("INSERT INTO SQLRAG.[dbo].[QueryIntentCache]\r\n ([QueryIntent]\r\n ,[VectorizedQueryIntent]\r\n ,[GeneratedTSQL]\r\n ,[GeneratedData]\r\n ,[ExecStatus])\r\n VALUES\r\n (@query_intent\r\n ,SqlArray::Parse(@embedded)\r\n ,@tsql\r\n ,@data\r\n ,@exec_status)", connection); 36 | SqlParameter paraQuestion = new SqlParameter("query_intent", query_intent.Value); 37 | SqlParameter paraEmbedded = new SqlParameter("embedded", embedded.Value); 38 | SqlParameter paraTSql = new SqlParameter("tsql", tsql.Value); 39 | SqlParameter paraData = new SqlParameter("data", data.Value); 40 | SqlParameter paraExecStatus = new SqlParameter("exec_status", exec_status.Value); 41 | 42 | 43 | command.Parameters.Add(paraQuestion); 44 | command.Parameters.Add(paraEmbedded); 45 | command.Parameters.Add(paraTSql); 46 | command.Parameters.Add(paraData); 47 | command.Parameters.Add(paraExecStatus); 48 | command.ExecuteNonQuery(); 49 | 50 | } 51 | } 52 | 53 | [Microsoft.SqlServer.Server.SqlProcedure] 54 | public static void InsertKnowledgeBase(SqlGuid id, SqlGuid parent_id, SqlInt32 ordinal, SqlBoolean is_rewrite, SqlInt16 source_type, SqlString url, [SqlFacet(MaxSize = -1)] SqlString text_content, [SqlFacet(MaxSize = -1)] SqlString raw) 55 | { 56 | 57 | using (SqlConnection connection = new SqlConnection("context connection=true")) 58 | { 59 | try 60 | { 61 | SqlArray embedded=SqlArray.Null; 62 | try 63 | { 64 | embedded = OpenaiFunction.GetEmbedding(text_content); 65 | } 66 | catch 67 | { 68 | 69 | } 70 | connection.Open(); 71 | SqlCommand command = new SqlCommand("INSERT INTO SQLRAG.[dbo].[KnowledgeBase]\r\n ([PartId]\r\n ,[ParentId]\r\n ,[Ordinal]\r\n ,[IsRewrite]\r\n ,[SourceType]\r\n ,[Url]\r\n ,[TextContent]\r\n ,[Embeddings]\r\n ,[Raw])\r\n VALUES\r\n (@id ,@parent_id, @ordinal , @is_rewrite, @source_type,@url, @text_content,@embedded, @raw)", connection); 72 | SqlParameter para_id = new SqlParameter("id", id); 73 | SqlParameter para_parentid = new SqlParameter("parent_id", parent_id); 74 | SqlParameter para_ordinal = new SqlParameter("ordinal", ordinal); 75 | SqlParameter para_isrewrite = new SqlParameter("is_rewrite", is_rewrite); 76 | SqlParameter para_source_type = new SqlParameter("source_type", source_type); 77 | SqlParameter para_url = new SqlParameter("url", url); 78 | SqlParameter para_text_content = new SqlParameter("text_content", text_content); 79 | SqlParameter paraEmbedded = new SqlParameter("embedded", embedded); 80 | SqlParameter para_raw = new SqlParameter("raw", raw); 81 | 82 | para_url.SqlDbType = SqlDbType.NVarChar; 83 | para_text_content.SqlDbType = SqlDbType.NVarChar; 84 | para_raw.SqlDbType = SqlDbType.NVarChar; 85 | paraEmbedded.SqlDbType = SqlDbType.Udt; 86 | paraEmbedded.UdtTypeName = "SqlArray"; 87 | 88 | command.Parameters.Add(para_id); 89 | command.Parameters.Add(para_parentid); 90 | command.Parameters.Add(para_ordinal); 91 | command.Parameters.Add(para_isrewrite); 92 | command.Parameters.Add(para_source_type); 93 | command.Parameters.Add(para_url); 94 | command.Parameters.Add(para_text_content); 95 | command.Parameters.Add(paraEmbedded); 96 | command.Parameters.Add(para_raw); 97 | command.ExecuteNonQuery(); 98 | 99 | } 100 | catch (System.Net.WebException we) 101 | { 102 | throw new Exception(we.Message + "\r\n" + we.InnerException.Message + "\r\n" + we.Response + "\r\n" + we.StackTrace); 103 | } 104 | 105 | 106 | } 107 | } 108 | 109 | 110 | 111 | } 112 | -------------------------------------------------------------------------------- /SQLRAG.application/dbo.EncryptedKeys.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[EncryptedKeys]( 2 | [KeyName] [nvarchar](50) NOT NULL, 3 | [KeyDesc] [nvarchar](512) NULL, 4 | [KeyValue] VARBINARY(4000) NOT NULL, 5 | CONSTRAINT [PK_EncryptedKeys] PRIMARY KEY CLUSTERED 6 | ( 7 | [KeyName] ASC 8 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] 9 | ) ON [PRIMARY] -------------------------------------------------------------------------------- /SQLRAG.application/dbo.KnowledgeBase.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[KnowledgeBase]( 2 | [PartId] [uniqueidentifier] NOT NULL, 3 | [ParentId] [uniqueidentifier] NULL, 4 | [Ordinal] int NULL, 5 | [IsRewrite] [bit] NULL DEFAULT 0, 6 | [SourceType] [smallint] NULL, 7 | [Url] [nvarchar](512) NULL, 8 | [TextContent] [nvarchar](max) NOT NULL, 9 | [Embeddings] [dbo].[SqlArray] NULL, 10 | [Raw] [nvarchar](max) NULL, 11 | [CreateDate] [datetime] NOT NULL, 12 | CONSTRAINT [PK_TextContent] PRIMARY KEY CLUSTERED 13 | ( 14 | [PartId] ASC 15 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] 16 | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 17 | GO 18 | 19 | ALTER TABLE [dbo].[KnowledgeBase] ADD CONSTRAINT [DF_KnowledgeBase_CreateDate] DEFAULT (getdate()) FOR [CreateDate] 20 | GO 21 | 22 | -------------------------------------------------------------------------------- /SQLRAG.application/dbo.QueryIntentCache.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[QueryIntentCache]( 2 | [id] [uniqueidentifier] NOT NULL, 3 | [QueryIntent] [nvarchar](max) NULL, 4 | [VectorizedQueryIntent] SqlArray NULL, 5 | [GeneratedTSQL] [nvarchar](max) NULL, 6 | [GeneratedData] [nvarchar](max) NULL, 7 | [CreateDate] [datetime] NULL, 8 | [ExecStatus] [nvarchar](256) NULL, 9 | CONSTRAINT [PK_QueryIntentCache] PRIMARY KEY CLUSTERED 10 | ( 11 | [id] ASC 12 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] 13 | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 14 | GO 15 | 16 | ALTER TABLE [dbo].[QueryIntentCache] ADD CONSTRAINT [DF_QueryIntentCache_id] DEFAULT (newid()) FOR [id] 17 | GO 18 | 19 | ALTER TABLE [dbo].[QueryIntentCache] ADD CONSTRAINT [DF_QueryIntentCache_CreateDate] DEFAULT (getdate()) FOR [CreateDate] 20 | GO 21 | 22 | -------------------------------------------------------------------------------- /SQLRAG.application/dbo.WebPilotLogs.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[WebPilotLogs] 2 | ( 3 | [Id] UNIQUEIDENTIFIER NOT NULL PRIMARY KEY DEFAULT (newid()), 4 | [ParentId] UNIQUEIDENTIFIER NULL 5 | ) 6 | -------------------------------------------------------------------------------- /SQLRAG.application/postdeployscript.sql: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /SQLRAG.publish.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | False 5 | SQLRAG 6 | SQLRAG.sql 7 | 1 8 | Data Source=.;Persist Security Info=True;User ID=sa;Pooling=False;Multiple Active Result Sets=False;Connect Timeout=60;Encrypt=True;Trust Server Certificate=True;Command Timeout=0 9 | False 10 | False 11 | False 12 | True 13 | False 14 | False 15 | True 16 | True 17 | False 18 | False 19 | True 20 | True 21 | True 22 | False 23 | False 24 | False 25 | False 26 | True 27 | True 28 | True 29 | True 30 | True 31 | True 32 | True 33 | True 34 | 35 | -------------------------------------------------------------------------------- /SQLRAG.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34309.116 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "SQLRAG", "SQLRAG.sqlproj", "{7E86C45F-4D32-4AB0-AF50-79B6270EDAEE}" 7 | EndProject 8 | Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "SQLRAG.application", "SQLRAG.application\SQLRAG.application.sqlproj", "{60C728D1-4969-4138-8344-FB002DED1F59}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {7E86C45F-4D32-4AB0-AF50-79B6270EDAEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {7E86C45F-4D32-4AB0-AF50-79B6270EDAEE}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {7E86C45F-4D32-4AB0-AF50-79B6270EDAEE}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 19 | {7E86C45F-4D32-4AB0-AF50-79B6270EDAEE}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {7E86C45F-4D32-4AB0-AF50-79B6270EDAEE}.Release|Any CPU.Build.0 = Release|Any CPU 21 | {7E86C45F-4D32-4AB0-AF50-79B6270EDAEE}.Release|Any CPU.Deploy.0 = Release|Any CPU 22 | {60C728D1-4969-4138-8344-FB002DED1F59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {60C728D1-4969-4138-8344-FB002DED1F59}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {60C728D1-4969-4138-8344-FB002DED1F59}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 25 | {60C728D1-4969-4138-8344-FB002DED1F59}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {60C728D1-4969-4138-8344-FB002DED1F59}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {60C728D1-4969-4138-8344-FB002DED1F59}.Release|Any CPU.Deploy.0 = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | GlobalSection(ExtensibilityGlobals) = postSolution 33 | SolutionGuid = {7F450B2E-31E1-42AA-8C3C-5E32BA01E5E3} 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /SQLRAG.sqlproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | SQLRAG 7 | 2.0 8 | 4.1 9 | {7e86c45f-4d32-4ab0-af50-79b6270edaee} 10 | Microsoft.Data.Tools.Schema.Sql.Sql160DatabaseSchemaProvider 11 | Database 12 | 13 | SQLRAG 14 | SQLRAG 15 | 1028,CI 16 | BySchemaAndSchemaType 17 | True 18 | v4.7.2 19 | CS 20 | Properties 21 | False 22 | True 23 | True 24 | True 25 | True 26 | Chinese_Taiwan_Stroke_CI_AS 27 | True 28 | dbo 29 | SAFE 30 | True 31 | 0.1.0.0 32 | 33 | 34 | bin\Release\ 35 | $(MSBuildProjectName).sql 36 | False 37 | pdbonly 38 | true 39 | false 40 | true 41 | prompt 42 | 4 43 | 44 | 45 | bin\Debug\ 46 | $(MSBuildProjectName).sql 47 | false 48 | true 49 | full 50 | true 51 | true 52 | true 53 | prompt 54 | 4 55 | x64 56 | True 57 | 58 | 59 | 11.0 60 | True 61 | 11.0 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Always 90 | 91 | 92 | 93 | 94 | 95 | PreserveNewest 96 | 97 | 98 | PreserveNewest 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /SqlArray.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Data.SqlTypes; 6 | using System.Security.Cryptography; 7 | using Microsoft.SqlServer.Server; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Security.Policy; 11 | using System.Data.Linq; 12 | 13 | 14 | 15 | [Serializable] 16 | [Microsoft.SqlServer.Server.SqlUserDefinedType(Format.UserDefined, MaxByteSize = -1, IsByteOrdered = true, IsFixedLength = false, Name = "SqlArray")] 17 | public struct SqlArray : INullable, IBinarySerialize 18 | { 19 | // Private 成員 20 | private bool _null; 21 | private double[] _data; 22 | 23 | public static SqlArray Null 24 | { 25 | get 26 | { 27 | SqlArray h = new SqlArray(); 28 | h._null = true; 29 | return h; 30 | } 31 | } 32 | 33 | 34 | public double[] Data 35 | { 36 | get 37 | { 38 | // 將程式碼放在此處 39 | return _data; 40 | } 41 | private set { _data = value; } 42 | 43 | } 44 | 45 | internal SqlArray(bool isNull) { _null = isNull; _data = Array.Empty(); } 46 | 47 | //public SqlArray() 48 | //{ 49 | // _null = true; 50 | // _data = []; 51 | //} 52 | 53 | public SqlArray(double[] dataArray) 54 | { 55 | _null = false; 56 | _data = dataArray; 57 | } 58 | 59 | //public static SqlArray operator +(SqlArray a) => a; 60 | 61 | public static SqlArray operator -(SqlArray a) 62 | { 63 | // 檢查維度是否匹配,實際實現中應該添加更多錯誤處理 64 | var result = new double[a._data.Length]; 65 | for (int i = 0; i < a._data.Length; i++) 66 | { 67 | result[i] = -a._data[i]; 68 | } 69 | return new SqlArray(result); 70 | } 71 | 72 | public static SqlBoolean Equals(SqlArray a, SqlArray b) 73 | { 74 | if (a.Length != b.Length) 75 | { 76 | return new SqlBoolean(false); 77 | } 78 | else 79 | { 80 | for (int i = 0; i < a.Length; i++) 81 | { 82 | if (a._data[i] != b._data[i]) 83 | { 84 | return new SqlBoolean(false); 85 | } 86 | } 87 | return new SqlBoolean(true); 88 | } 89 | } 90 | 91 | 92 | public static SqlBoolean operator ==(SqlArray a, SqlArray b) 93 | { 94 | if (a.Length != b.Length) 95 | { 96 | return new SqlBoolean(false); 97 | } 98 | else 99 | { 100 | for (int i = 0; i < a.Length; i++) 101 | { 102 | if (a._data[i] != b._data[i]) 103 | { 104 | return new SqlBoolean(false); 105 | } 106 | } 107 | return new SqlBoolean(true); 108 | } 109 | } 110 | 111 | public static SqlBoolean operator !=(SqlArray a, SqlArray b) 112 | { 113 | if (a.Length != b.Length) 114 | { 115 | return new SqlBoolean(true); 116 | } 117 | else 118 | { 119 | for (int i = 0; i < a.Length; i++) 120 | { 121 | if (a._data[i] != b._data[i]) 122 | { 123 | return new SqlBoolean(true); 124 | } 125 | } 126 | return new SqlBoolean(false); 127 | } 128 | } 129 | 130 | public override int GetHashCode() 131 | => _data.GetHashCode(); 132 | 133 | public override bool Equals(object other) 134 | => other is SqlArray otherArray && _data == otherArray._data; 135 | 136 | //public bool Equals(SqlArray other) 137 | // => _data == other._data; 138 | 139 | 140 | [SqlMethod(IsDeterministic = true, IsPrecise = true,Name ="Add")] 141 | public static SqlArray Add(SqlArray a, SqlArray b) 142 | { 143 | 144 | var result = new double[a._data.Length]; 145 | for (int i = 0; i < a._data.Length; i++) 146 | { 147 | result[i] = a._data[i] + b._data[i]; 148 | } 149 | return new SqlArray(result); 150 | } 151 | 152 | public static SqlArray operator +(SqlArray a, SqlArray b) 153 | { 154 | // 檢查維度是否匹配,實際實現中應該添加更多錯誤處理 155 | var result = new double[a._data.Length]; 156 | for (int i = 0; i < a._data.Length; i++) 157 | { 158 | result[i] = a._data[i] + b._data[i]; 159 | } 160 | return new SqlArray(result); 161 | } 162 | 163 | [SqlMethod(IsDeterministic = true, IsPrecise = true)] 164 | public static SqlArray Subtract(SqlArray a, SqlArray b) 165 | { 166 | var result = new double[a._data.Length]; 167 | for (int i = 0; i < a._data.Length; i++) 168 | { 169 | result[i] = a._data[i] - b._data[i]; 170 | } 171 | return new SqlArray(result); 172 | } 173 | public static SqlArray operator -(SqlArray a, SqlArray b) 174 | { 175 | 176 | var result = new double[a._data.Length]; 177 | for (int i = 0; i < a._data.Length; i++) 178 | { 179 | result[i] = a._data[i] - b._data[i]; 180 | } 181 | return new SqlArray(result); 182 | } 183 | 184 | [SqlMethod(IsDeterministic = true, IsPrecise = true)] 185 | public static SqlArray Divide(SqlArray a, SqlArray b) 186 | { 187 | if (System.Array.Exists(b._data, x => x == 0)) 188 | { 189 | throw new DivideByZeroException(); 190 | } 191 | var result = new double[a._data.Length]; 192 | for (int i = 0; i < a._data.Length; i++) 193 | { 194 | result[i] = a._data[i] / b._data[i]; 195 | } 196 | return new SqlArray(result); 197 | } 198 | public static SqlArray operator /(SqlArray a, SqlArray b) 199 | { 200 | if (System.Array.Exists(b._data, x => x == 0)) 201 | { 202 | throw new DivideByZeroException(); 203 | } 204 | var result = new double[a._data.Length]; 205 | for (int i = 0; i < a._data.Length; i++) 206 | { 207 | result[i] = a._data[i] / b._data[i]; 208 | } 209 | return new SqlArray(result); 210 | } 211 | 212 | [SqlMethod(IsDeterministic = true, IsPrecise = true)] 213 | public static SqlArray Multiply(SqlArray a, SqlArray b) 214 | { 215 | // 檢查維度是否匹配,實際實現中應該添加更多錯誤處理 216 | var result = new double[a._data.Length]; 217 | for (int i = 0; i < a._data.Length; i++) 218 | { 219 | result[i] = a._data[i] * b._data[i]; 220 | } 221 | return new SqlArray(result); 222 | } 223 | [SqlMethod(IsDeterministic = true, IsPrecise = true)] 224 | public static SqlArray Dot(SqlArray a, SqlArray b) 225 | { 226 | // 檢查維度是否匹配,實際實現中應該添加更多錯誤處理 227 | var result = new double[a._data.Length]; 228 | for (int i = 0; i < a._data.Length; i++) 229 | { 230 | result[i] = a._data[i] * b._data[i]; 231 | } 232 | return new SqlArray(result); 233 | } 234 | public static SqlArray operator *(SqlArray a, SqlArray b) 235 | { 236 | // 檢查維度是否匹配,實際實現中應該添加更多錯誤處理 237 | var result = new double[a._data.Length]; 238 | for (int i = 0; i < a._data.Length; i++) 239 | { 240 | result[i] = a._data[i] * b._data[i]; 241 | } 242 | return new SqlArray(result); 243 | } 244 | 245 | 246 | public static explicit operator SqlArray(SqlString s) 247 | { 248 | if (s.IsNull) 249 | return Null; 250 | 251 | // 將程式碼放在此處 252 | return new SqlArray(System.Array.ConvertAll(s.Value.Split(','), Double.Parse)); 253 | } 254 | public static implicit operator SqlArray(string s) 255 | { 256 | if (string.IsNullOrEmpty(s)) 257 | return Null; 258 | 259 | // 將程式碼放在此處 260 | return new SqlArray(System.Array.ConvertAll(s.Split(','), Double.Parse)); 261 | } 262 | public static explicit operator string(SqlArray x) 263 | { 264 | return string.Join(",", x._data); 265 | } 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | [return: SqlFacet(MaxSize = -1)] 282 | [SqlMethod(IsDeterministic = true, IsPrecise = false)] 283 | public override string ToString() 284 | { 285 | 286 | string _str = string.Join(",", _data); 287 | return _str; 288 | 289 | } 290 | 291 | public bool IsNull 292 | { 293 | get 294 | { 295 | return _null; 296 | } 297 | } 298 | 299 | public int Rank 300 | { 301 | get 302 | { 303 | 304 | return _data.Rank; 305 | } 306 | } 307 | 308 | public int Length 309 | { 310 | get 311 | { 312 | return _data.Length; 313 | } 314 | } 315 | 316 | public long LongLength 317 | { 318 | get 319 | { 320 | return _data.LongLength; 321 | } 322 | } 323 | 324 | 325 | 326 | 327 | 328 | [SqlMethod(IsDeterministic = true, IsPrecise = true)] 329 | public static SqlArray Parse([SqlFacet(MaxSize = -1)] SqlString s) 330 | { 331 | if (s.IsNull) 332 | return Null; 333 | 334 | // 將程式碼放在此處 335 | return new SqlArray(System.Array.ConvertAll(s.Value.Split(','), Double.Parse)); 336 | } 337 | 338 | //[SqlMethod(IsDeterministic = true, IsPrecise = true)] 339 | //public static SqlArray add(SqlArray a, SqlArray b) 340 | //{ 341 | // if (a._data.Length != b._data.Length) 342 | // { 343 | // throw new ArgumentException("維度不匹配"); 344 | // } 345 | // var result = new double[a._data.Length]; 346 | // for (int i = 0; i < a._data.Length; i++) 347 | // { 348 | // result[i] = a._data[i] + b._data[i]; 349 | // } 350 | // return new SqlArray(result); 351 | //} 352 | 353 | //[SqlMethod(IsDeterministic = true, IsPrecise = true)] 354 | //public static SqlArray subtract(SqlArray a, SqlArray b) 355 | //{ 356 | // if (a._data.Length != b._data.Length) 357 | // { 358 | // throw new ArgumentException("維度不匹配"); 359 | // } 360 | // var result = new double[a._data.Length]; 361 | // for (int i = 0; i < a._data.Length; i++) 362 | // { 363 | // result[i] = a._data[i] - b._data[i]; 364 | // } 365 | // return new SqlArray(result); 366 | //} 367 | 368 | 369 | 370 | public SqlArray Concate(List arrays) 371 | { 372 | List data_list = new List(); 373 | foreach (SqlArray a in arrays) 374 | { 375 | data_list.AddRange(a.Data); 376 | } 377 | return new SqlArray(data_list.ToArray()); 378 | } 379 | [SqlMethodAttribute(IsDeterministic = true, IsPrecise = true)] 380 | public static SqlInt32 ArgMax(SqlArray arr) 381 | { 382 | int idx = 0; 383 | double max_value = arr._data[0]; 384 | for(int i=0;i< arr._data.Length;i++) 385 | { 386 | double d = arr._data[i]; 387 | if (d > max_value) 388 | { 389 | idx = i; 390 | max_value = d; 391 | } 392 | } 393 | return new SqlInt32(idx); 394 | } 395 | 396 | [SqlMethodAttribute(IsDeterministic = true, IsPrecise = true)] 397 | public static SqlInt32 ArgMin(SqlArray arr) 398 | { 399 | int idx = 0; 400 | double min_value = arr._data[0]; 401 | for (int i = 0; i < arr._data.Length; i++) 402 | { 403 | double d = arr._data[i]; 404 | if (d segAsSentence(string txt) 22 | { 23 | // 使用正則表達式處理字符串 24 | txt = Regex.Replace(txt, Prefixes, "$1"); 25 | txt = Regex.Replace(txt, Websites, "$1"); 26 | if (txt.Contains("Ph.D")) txt = txt.Replace("Ph.D.", "PhD"); 27 | txt = Regex.Replace(txt, "\\s" + Alphabets + "[.] ", " $1 "); 28 | txt = Regex.Replace(txt, Acronyms + " " + Starters, "$1 $2"); 29 | txt = Regex.Replace(txt, Alphabets + "[.]" + Alphabets + "[.]" + Alphabets + "[.]", "$1$2$3"); 30 | txt = Regex.Replace(txt, Alphabets + "[.]" + Alphabets + "[.]", "$1$2"); 31 | txt = Regex.Replace(txt, Numbers + "[.]" + Numbers, "$1$2"); 32 | txt = Regex.Replace(txt, " " + Suffixes + "[.] " + Starters, " $1 $2"); 33 | txt = Regex.Replace(txt, " " + Suffixes + "[.]", " $1"); 34 | txt = Regex.Replace(txt, " " + Alphabets + "[.]", " $1"); 35 | 36 | // 處理引號和句末標點的組合 37 | txt = txt.Replace(".”", "”.") 38 | .Replace(".\"", "\".") 39 | .Replace("!\"", "\"!") 40 | .Replace("?\"", "\"?"); 41 | 42 | // 替換省略號和接下來的字符 43 | txt = Regex.Replace(txt, "(\\.\\.\\.)([^”’])", ""); 44 | 45 | // 將句末的點、問號、驚嘆號替換為 46 | txt = txt.Replace(".", ".") 47 | .Replace("?", "?") 48 | .Replace("!", "!") 49 | .Replace("", "."); 50 | 51 | // 對中文的標點進行處理 52 | txt = Regex.Replace(txt, "([。!?\\?])([^”’])", "$1$2"); 53 | txt = Regex.Replace(txt, "(\\.{6})([^”’])", "$1$2"); 54 | txt = Regex.Replace(txt, "([。!?\\?][”’])([^。!?\\?])", "$1$2"); 55 | 56 | 57 | // 移除尾部空白並按分割 58 | txt = txt.Trim(); 59 | List sentences = new List(txt.Split(new string[] { "" }, StringSplitOptions.RemoveEmptyEntries)); 60 | return sentences; 61 | } 62 | 63 | [return: SqlFacet(MaxSize = -1)] 64 | [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true)] 65 | public static SqlString SplitText([SqlFacet(MaxSize = -1)] SqlString inputText) 66 | { 67 | string returnstring = string.Join("\r\n", segAsSentence(inputText.Value)); 68 | 69 | return new SqlString (returnstring); 70 | } 71 | 72 | [return: SqlFacet(MaxSize = -1)] 73 | [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true)] 74 | public static SqlString ChineseFull2Half([SqlFacet(MaxSize = -1)] SqlString inputText) 75 | { 76 | string returnstring = string.Join("\r\n", segAsSentence(inputText.Value)); 77 | 78 | return new SqlString(returnstring); 79 | } 80 | 81 | [return: SqlFacet(MaxSize = -1)] 82 | [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true)] 83 | public static SqlString ChineseHalf2Full([SqlFacet(MaxSize = -1)] SqlString inputText) 84 | { 85 | string returnstring = string.Join("\r\n", segAsSentence(inputText.Value)); 86 | 87 | return new SqlString(returnstring); 88 | } 89 | 90 | 91 | [Microsoft.SqlServer.Server.SqlFunction] 92 | [return: SqlFacet(MaxSize = -1)] 93 | public static SqlString ToTraditionalChinese([SqlFacet(MaxSize = -1)] SqlString InputString) 94 | { 95 | // 將程式碼放在此處 96 | return new SqlString(Microsoft.VisualBasic.Strings.StrConv(InputString.Value, Microsoft.VisualBasic.VbStrConv.TraditionalChinese, 2052)); 97 | } 98 | 99 | [Microsoft.SqlServer.Server.SqlFunction] 100 | [return: SqlFacet(MaxSize = -1)] 101 | public static SqlString ToSimplifiedChinese([SqlFacet(MaxSize = -1)] SqlString InputString) 102 | { 103 | 104 | return new SqlString(Microsoft.VisualBasic.Strings.StrConv(InputString.Value, Microsoft.VisualBasic.VbStrConv.SimplifiedChinese, 2052)); 105 | } 106 | 107 | 108 | [Microsoft.SqlServer.Server.SqlFunction] 109 | [return: SqlFacet(MaxSize = -1)] 110 | public static SqlString RegexReplace([SqlFacet(MaxSize = -1)] string InputString, string PattermString, string ReplaceWord) 111 | { 112 | 113 | MatchCollection matchcollection = Regex.Matches(InputString, PattermString, ExpressionOptions); 114 | if (matchcollection != null) 115 | { 116 | 117 | foreach (Match item in matchcollection) 118 | { 119 | InputString = InputString.Replace(item.Value, ReplaceWord); 120 | } 121 | return new SqlString(InputString); 122 | 123 | } 124 | else 125 | { 126 | return SqlString.Null; 127 | } 128 | } 129 | 130 | private const RegexOptions ExpressionOptions = RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase; 131 | 132 | [Microsoft.SqlServer.Server.SqlFunction] 133 | public static SqlString RegexMatch([SqlFacet(MaxSize = -1)] string InputString, string PattermString, int Index) 134 | { 135 | if (string.IsNullOrEmpty(InputString)) 136 | { 137 | return SqlString.Null; 138 | } 139 | else 140 | { 141 | 142 | MatchCollection matchcollection = Regex.Matches(InputString, PattermString, ExpressionOptions); 143 | if (matchcollection != null) 144 | { 145 | if (Index == 0) 146 | { 147 | string returnstring = ""; 148 | foreach (Match item in matchcollection) 149 | { 150 | returnstring += item.Value; 151 | } 152 | return new SqlString(returnstring); 153 | } 154 | else if (Index <= matchcollection.Count && Index >= 1) 155 | { 156 | 157 | return new SqlString(matchcollection[Index - 1].Value); 158 | } 159 | else 160 | { 161 | return SqlString.Null; 162 | } 163 | } 164 | else 165 | { 166 | return SqlString.Null; 167 | } 168 | } 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /assets/ChatCompletion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllanYiin/SQLRAG/a72d1028b7f5f60e96a7f4f14052af752d8200c6/assets/ChatCompletion.png -------------------------------------------------------------------------------- /assets/ChatCompletion_demo.sql: -------------------------------------------------------------------------------- 1 | --dbo.ChatCompletion(@inputPrompt, @systemProimpt, @model) 2 | 3 | select dbo.ChatCompletion('什麼是搜索增強生成RAG?','','') 4 | 5 | select dbo.ChatCompletion('關於代理貴公司產品一事,不知道你們考慮的如何,是否可以進入簽約階段?' 6 | ,'你是一個20年以上的商業日文翻譯專家,接下來任何輸入給你的內容你都能將它翻譯成得體的日文' 7 | ,'gpt-4-1106-preview') 8 | -------------------------------------------------------------------------------- /assets/QueryCache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllanYiin/SQLRAG/a72d1028b7f5f60e96a7f4f14052af752d8200c6/assets/QueryCache.png -------------------------------------------------------------------------------- /assets/QueryIntentCache_demo.sql: -------------------------------------------------------------------------------- 1 | Declare @question varchar(max) 2 | set @question = SqlRAG.dbo.GetEmbedding('2013年度各產品大分類的毛利率') 3 | 4 | SELECT [QueryIntent] 5 | ,[GeneratedTSQL] 6 | ,SqlRAG.dbo.CosineSimilarity(@question,VectorizedQueryIntent) as cosine_similarity 7 | ,1-SqlRAG.dbo.EuclideanDistance(@question,VectorizedQueryIntent) as euclidean_similarity 8 | ,1-SqlRAG.dbo.MinkowskiDistance(@question,VectorizedQueryIntent,2.5) as minkowski_similarity 9 | FROM [SqlRAG].[dbo].[QueryIntentCache] 10 | order by 5 desc 11 | --------------------------------------------------------------------------------