Repository: AIDotNet/GraphRag.Net Branch: main Commit: ca82ac4dbf6b Files: 126 Total size: 681.4 KB Directory structure: gitextract_nfiuwx4k/ ├── .github/ │ └── workflows/ │ └── nuget-publish.yml ├── .gitignore ├── Directory.Build.props ├── GraphRag.Net.sln ├── LICENSE ├── README.en.md ├── README.md └── src/ ├── GraphRag.Net/ │ ├── Common/ │ │ ├── Options/ │ │ │ ├── GraphDBConnectionOption.cs │ │ │ ├── GraphOpenAIOption.cs │ │ │ ├── GraphSearchOption.cs │ │ │ ├── GraphSysOption.cs │ │ │ └── TextChunkerOption.cs │ │ └── ServiceDescriptionAttribute.cs │ ├── Domain/ │ │ ├── Interface/ │ │ │ ├── ICommunityDetectionService.cs │ │ │ ├── IGraphService.cs │ │ │ └── ISemanticService.cs │ │ ├── Model/ │ │ │ ├── Graph/ │ │ │ │ ├── Graph.cs │ │ │ │ ├── GraphModel.cs │ │ │ │ ├── GraphViewModel.cs │ │ │ │ ├── RelationShipModel.cs │ │ │ │ └── TextMemModel.cs │ │ │ └── PageList.cs │ │ └── Service/ │ │ ├── CommunityDetectionService.cs │ │ ├── GraphService.cs │ │ └── SemanticService.cs │ ├── Extensions/ │ │ └── ServiceCollectionExtensions.cs │ ├── GraphRag.Net.csproj │ ├── Repositories/ │ │ ├── Base/ │ │ │ ├── IRepository.cs │ │ │ ├── Repository.cs │ │ │ └── SqlSugarHelper.cs │ │ └── Graph/ │ │ ├── CommunitieNodes/ │ │ │ ├── CommunitieNodes.cs │ │ │ ├── CommunitieNodes_Repositories.cs │ │ │ └── ICommunitieNodes_Repositories.cs │ │ ├── Communities/ │ │ │ ├── Communities.cs │ │ │ ├── Communities_Repositories.cs │ │ │ └── ICommunities_Repositories.cs │ │ ├── Edges/ │ │ │ ├── Edges.cs │ │ │ ├── Edges_Repositories.cs │ │ │ └── IEdges_Repositories.cs │ │ ├── Global/ │ │ │ ├── Globals.cs │ │ │ ├── Globals_Repositories.cs │ │ │ └── IGlobals_Repositories.cs │ │ └── Nodes/ │ │ ├── INodes_Repositories.cs │ │ ├── Nodes.cs │ │ └── Nodes_Repositories.cs │ └── Utils/ │ ├── ConvertUtils.cs │ ├── OpenAIHttpClientHandler.cs │ └── RepoUtils/ │ ├── AppException.cs │ ├── ObjectExtensions.cs │ └── RepoFiles.cs └── GraphRag.Net.Web/ ├── .config/ │ └── dotnet-tools.json ├── App.razor ├── Components/ │ └── GlobalHeader/ │ ├── RightContent.razor │ └── RightContent.razor.cs ├── Controllers/ │ └── GraphController.cs ├── Extensions/ │ └── DateTimeExtension.cs ├── GraphRag.Net.Web.csproj ├── GraphRag.Net.Web.xml ├── Layouts/ │ ├── BasicLayout.razor │ ├── UserLayout.razor │ └── UserLayout.razor.css ├── Mock/ │ ├── MockChatCompletion.cs │ ├── MockTextCompletion.cs │ └── MockTextEmbeddingGeneratorService.cs ├── Models/ │ ├── ActivitiesType.cs │ ├── ActivityGroup.cs │ ├── ActivityProject.cs │ ├── ActivityUser.cs │ ├── AdvancedOperation.cs │ ├── AdvancedProfileData.cs │ ├── BasicGood.cs │ ├── BasicProfileDataType.cs │ ├── BasicProgress.cs │ ├── ChartData.cs │ ├── ChartDataItem.cs │ ├── ChatMessage.cs │ ├── CurrentUser.cs │ ├── FormItemLayout.cs │ ├── FormModel.cs │ ├── LayoutModel.cs │ ├── ListFormModel.cs │ ├── ListItemDataType.cs │ ├── LoginParamsType.cs │ ├── NoticeItem.cs │ ├── NoticeType.cs │ ├── OfflineChartDataItem.cs │ ├── OfflineDataItem.cs │ ├── RadarDataItem.cs │ └── SearchDataItem.cs ├── Pages/ │ ├── Graph/ │ │ ├── Chat.razor │ │ ├── Chat.razor.cs │ │ └── Graph.razor │ ├── Index.razor │ └── _Host.cshtml ├── Program.cs ├── Utils/ │ └── LongToDateTimeConverter.cs ├── _Imports.razor ├── appsettings.json ├── graphPlugins/ │ └── graph/ │ ├── community_search/ │ │ ├── config.json │ │ └── skprompt.txt │ ├── community_summaries/ │ │ ├── config.json │ │ └── skprompt.txt │ ├── create/ │ │ ├── config.json │ │ └── skprompt.txt │ ├── global_summaries/ │ │ ├── config.json │ │ └── skprompt.txt │ ├── mergedesc/ │ │ ├── config.json │ │ └── skprompt.txt │ ├── relationship/ │ │ ├── config.json │ │ └── skprompt.txt │ └── search/ │ ├── config.json │ └── skprompt.txt └── wwwroot/ ├── appsettings.json ├── css/ │ └── site.css ├── data/ │ ├── activities.json │ ├── advanced.json │ ├── basic.json │ ├── current_user.json │ ├── fake_chart_data.json │ ├── fake_list.json │ ├── menu.json │ ├── notice.json │ └── notices.json ├── index.html └── js/ ├── relation-graph.umd.js └── vue2.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/nuget-publish.yml ================================================ name: main on: push: branches: - main paths: - 'Directory.Build.props' jobs: deploy-nuget: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build run: dotnet build --configuration Release - name: Pack run: dotnet pack --no-build --configuration Release - name: Push NuGet package run: | dotnet nuget push **/*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ vars.XUZEYU }} ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ **/Properties/launchSettings.json # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_i.h *.ilk *.meta *.bin *.Debug *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # MFractors (Xamarin productivity tool) working folder .mfractor/ **/bin/ **/obj/ **/.vs/ /src/GraphRag.Net.Web/appsettings.Development.json **/tmp-memory-files/ **/tmp-memory-vectors/ /src/GraphRag.Net.Web/graph.db /src/GraphRag.Net.Web/graphmem.db /db /src/GraphRag.Net/GraphRag.Net.xml ================================================ FILE: Directory.Build.props ================================================ 0.2.0 1.17.1 ================================================ FILE: GraphRag.Net.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.10.35027.167 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphRag.Net.Web", "src\GraphRag.Net.Web\GraphRag.Net.Web.csproj", "{8C4B0AA1-7083-4BEA-9F12-2C20CDDB8426}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphRag.Net", "src\GraphRag.Net\GraphRag.Net.csproj", "{214FF6B8-E291-4CC8-94BC-16C1CD9DB3B8}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决方案项", "{A35F9835-DFCF-478A-9F24-E0B350161746}" ProjectSection(SolutionItems) = preProject Directory.Build.props = Directory.Build.props .github\workflows\nuget-publish.yml = .github\workflows\nuget-publish.yml README.md = README.md EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8C4B0AA1-7083-4BEA-9F12-2C20CDDB8426}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8C4B0AA1-7083-4BEA-9F12-2C20CDDB8426}.Debug|Any CPU.Build.0 = Debug|Any CPU {8C4B0AA1-7083-4BEA-9F12-2C20CDDB8426}.Release|Any CPU.ActiveCfg = Release|Any CPU {8C4B0AA1-7083-4BEA-9F12-2C20CDDB8426}.Release|Any CPU.Build.0 = Release|Any CPU {214FF6B8-E291-4CC8-94BC-16C1CD9DB3B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {214FF6B8-E291-4CC8-94BC-16C1CD9DB3B8}.Debug|Any CPU.Build.0 = Debug|Any CPU {214FF6B8-E291-4CC8-94BC-16C1CD9DB3B8}.Release|Any CPU.ActiveCfg = Release|Any CPU {214FF6B8-E291-4CC8-94BC-16C1CD9DB3B8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7892D425-42B4-4F17-B17A-38825D88FA7B} EndGlobalSection EndGlobal ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [2024] [许泽宇] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.en.md ================================================ [简体中文](./README.md) | English ## This is a simple implementation of dotnet referring to GraphRag Based on the implementation idea mentioned by Microsoft in the paper, GraphRAG mainly implements the following functions in the execution process: - Source Documents → Text Chunks: divide the source document into text blocks. - Text Chunks → Element Instances: Extract instances of graph nodes and edges from each text block. - Element Instances → Element Summaries: generate summaries for each diagram element. - Element Summaries → Graph Communities: use the community detection algorithm to divide the graph into communities. - Graph Communities → Community Summaries: generate summaries for each community. - Community Summaries → Community Answers → Global Answer: use community summaries to generate local answers, and then summarize these local answers to generate global answers. This project is a demo example, which is only used to learn GraphRAG ideas. ## Core Business Process ### 1. Overall Architecture Flow ```mermaid graph LR A["Document Import"] --> B["Graph Construction"] B --> C["Community Detection"] C --> D["Summary Generation"] D --> E["Query Retrieval"] subgraph "Data Flow" F["Raw Documents"] --> G["Text Chunks"] G --> H["Graph Data
Nodes+Edges"] H --> I["Community Structure"] I --> J["Multi-level Summaries"] J --> K["Intelligent Q&A"] end subgraph "Storage Layer" L["Vector Database
TextMemory"] M["Relational Database
Nodes/Edges/Communities/Globals"] end B -.-> L B -.-> M C -.-> M D -.-> M E -.-> L E -.-> M style A fill:#e1f5fe style E fill:#c8e6c9 style L fill:#fce4ec style M fill:#fff9c4 ``` ### 2. Document Import and Graph Construction Flow ```mermaid graph TD A["Document Input
Raw Documents/Text"] --> B["Text Chunking
TextChunker.SplitPlainTextLines
TextChunker.SplitPlainTextParagraphs"] B --> C["Overlapping Text Chunks
CreateOverlappingChunks
3 paragraphs/chunk, 1 paragraph overlap"] C --> D["LLM Extract Graph Data
SemanticService.CreateGraphAsync"] D --> E["Node Extraction
Entity Recognition+Type Classification"] D --> F["Relationship Extraction
Edges and Relationship Description"] E --> G["Node Deduplication
Vector Similarity Detection"] F --> H["Relationship Deduplication
Duplicate Edge Processing"] G --> I["Store to Database
Nodes Table"] H --> J["Store to Database
Edges Table"] I --> K["Vector Storage
TextMemory.SaveInformationAsync"] J --> L["Orphan Node Detection
ProcessOrphanNodesAsync"] L --> M["Graph Construction Complete"] style A fill:#e1f5fe style M fill:#c8e6c9 style D fill:#fff3e0 style K fill:#fce4ec ``` ### 3. Community Detection and Summary Generation Flow ```mermaid graph TD A["Graph Data
Nodes + Edges"] --> B["Build Graph Structure
Graph.AddEdge"] B --> C["Label Propagation Algorithm
FastLabelPropagationAlgorithm
10 iterations"] C --> D["Community Detection Results
Node→Community ID Mapping"] D --> E["Store Community Node Relations
CommunitieNodes Table"] E --> F["Group Nodes by Community
Extract Node Description"] F --> G["LLM Generate Community Summary
SemanticService.CommunitySummaries"] G --> H["Store Community Summary
Communities Table"] H --> I["Collect All Community Summaries"] I --> J["LLM Generate Global Summary
SemanticService.GlobalSummaries"] J --> K["Store Global Summary
Globals Table"] style A fill:#e1f5fe style C fill:#fff3e0 style G fill:#fff3e0 style J fill:#fff3e0 style K fill:#c8e6c9 ``` ### 4. Direct Graph Query Flow ```mermaid graph TD A["User Query
Question Input"] --> B["Vector Search
TextMemory.SearchAsync
Relevance Threshold 0.5"] B --> C{"Nodes Matched?"} C -->|Yes| D["Get Related Nodes
RetrieveTextMemModelList"] C -->|No| E["Retry with Lower Threshold
Threshold 0.3, Expand Search"] E --> D D --> F["Recursive Graph Expansion
GetGraphAllRecursion
Depth Limit+Node Count Limit"] F --> G["Token Count Estimation
EstimateTokenCount"] G --> H{"Exceed Token Limit?"} H -->|Yes| I["Trim Nodes by Weight
LimitGraphByTokenCount"] H -->|No| J["Build Query Graph
GraphModel"] I --> J J --> K["LLM Generate Answer
SemanticService.GetGraphAnswerAsync"] K --> L["Return Result"] style A fill:#e1f5fe style B fill:#fce4ec style F fill:#fff3e0 style K fill:#fff3e0 style L fill:#c8e6c9 ``` ### 5. Community Algorithm Query Flow ```mermaid graph TD A["User Query
Question Input"] --> B["Vector Search
Find Related Nodes"] B --> C{"Nodes Matched?"} C -->|Yes| D["Find Node Communities
GetGraphAllCommunitiesRecursion"] C -->|No| E["Use Global Summary
Globals Table"] D --> F["Get All Nodes in Community"] F --> G["Build Community Subgraph
Nodes+Edges"] G --> H["Get Related Community Summary
Communities Table"] H --> I["Get Global Summary
Globals Table"] I --> J["LLM Comprehensive Analysis
Graph+Community Summary+Global Summary"] E --> K["Answer Based on Global Summary Only"] J --> L["Return Answer"] K --> L style A fill:#e1f5fe style B fill:#fce4ec style D fill:#fff3e0 style J fill:#fff3e0 style K fill:#fff3e0 style L fill:#c8e6c9 ``` ### Core Algorithm Description 1. **Text Chunking Algorithm**: Uses overlapping window technique, each text chunk contains 3 paragraphs, adjacent chunks overlap by 1 paragraph to ensure continuity of relationship information. 2. **Community Detection Algorithm**: Adopts Fast Label Propagation Algorithm with 10 iterations to discover community structure in the graph. 3. **Vector Search Strategy**: First uses 0.5 relevance threshold for search, if results are insufficient, lowers to 0.3 for retry to ensure finding enough related nodes. 4. **Token Optimization Mechanism**: Real-time estimation of token usage, intelligently trims nodes by weight when exceeding limits to ensure LLM input effectiveness. 5. **Orphan Node Processing**: Automatically detects orphan nodes without relationship connections, attempts to establish relationships with other nodes through semantic search. ## You can directly reference the NuGet package in the project, or directly use the project to provide API services For convenience, the LLM interface is currently only compatible with the openai specification, and other large models can consider using one api class integration products Configure in appsettings.json ``` "GraphOpenAI": { "Key": "sk-xxx", "EndPoint": "https://api.antsk.cn/", "ChatModel": "gpt-4o-mini", "EmbeddingModel": "text-embedding-ada-002" }, "TextChunker": { "LinesToken": 100, "ParagraphsToken": 1000 }, "GraphDBConnection": { "DbType": "Sqlite", //PostgreSQL "DBConnection": "Data Source=graph.db", "VectorConnection": "graphmem.db", //If PostgreSQL is used, it can be consistent with DBConnection "VectorSize": 1536 //DbType=PostgreSQL needs to be set, sqlite can not be set }, "GraphSearch": { "SearchMinRelevance": 0.5, //Search for minimum relevance "SearchLimit": 3, //Limit the number of vector search nodes "NodeDepth": 3 ,//Retrieve node depth "MaxNodes": 100 //Retrieve the maximum number of nodes }, "GraphSys": { "RetryCounnt": 2 //Number of retries. Using the domestic model may cause json extraction failure. Increasing the number of retries can improve availability } ``` ## Startup project ``` dotnet run --project GraphRag.Net.Web.csproj ``` ## After starting the project, you can use the ``` http://localhost:5000/swagger ``` ## Open the swagger view interface ![Graoh](https://github.com/xuzeyu91/GraphRag.Net/blob/main/doc/api.png) ### You can also use the interface ``` http://localhost:5000/ ``` Open the UI interface of blazer. The page provides functions such as text import, file import, question and answer dialogue, and view knowledge map ![Graoh](https://github.com/xuzeyu91/GraphRag.Net/blob/main/doc/graph1.png) ## Nuget Package Usage ``` dotnet add package GraphRag.Net ``` ## In order to facilitate the adjustment and modification of prompt words, SK Plugin has separated the project. You need to put GraphRag Copy the graphPlugins directory in the Net. Web project to your project, and set [graphPlugins](https://github.com/AIDotNet/GraphRag.Net/tree/main/src/GraphRag.Net.Web/graphPlugins) ``` PreserveNewest ``` ### The default configuration uses the OpenAI standard interface. After configuring the OpenAI app settings, you can use the following code to inject After adding a package, you need to set the configuration file and dependency injection ``` //OpenAI configuration builder.Configuration.GetSection("GraphOpenAI").Get(); //Document Slicing Configuration builder.Configuration.GetSection("TextChunker").Get(); //Configure Database Links builder.Configuration.GetSection("GraphDBConnection").Get(); //System settings builder.Configuration.GetSection("GraphSys").Get(); //Inject AddGraphRagNet. Note that you need to inject the configuration file first, and then inject GraphRagNet builder.Services.AddGraphRagNet(); ``` ### If you want to access other models, you can refer to the following code, which abstracts the implementation of Kernel. You can customize the implementation ``` var kernelBuild = Kernel.CreateBuilder(); kernelBuild.Services.AddKeyedSingleton("mock-text", new MockTextCompletion()); kernelBuild.Services.AddKeyedSingleton("mock-chat", new MockChatCompletion()); kernelBuild.Services.AddSingleton((ITextEmbeddingGenerationService)new MockTextEmbeddingGeneratorService()); kernelBuild.Services.AddKeyedSingleton("mock-embedding", new MockTextEmbeddingGeneratorService()); builder.Services.AddGraphRagNet(kernelBuild.Build()); ``` #### It should be noted here that since the import may be carried out several times, the generated community and global information is not automatically called during import, so you need to call the generated community and global information according to the actual situation ``` await _graphService.GraphCommunitiesAsync(index); await _graphService.GraphGlobalAsync(index); ``` Inject IGraphService service when using. The following is the reference sample code ``` namespace GraphRag.Net.Api.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class GraphDemoController(IGraphService _graphService) : ControllerBase { /// /// 获取所有的索引数据 /// /// [HttpGet] public async Task GetAllIndex() { var graphModel = _graphService.GetAllIndex(); return Ok(graphModel); } /// /// 获取所有的图谱数据 /// /// /// [HttpGet] public async Task GetAllGraphs(string index) { if (string.IsNullOrEmpty(index)) { return Ok(new GraphViewModel()); } var graphModel = _graphService.GetAllGraphs(index); return Ok(graphModel); } /// /// 插入文本数据 /// /// /// [HttpPost] public async Task InsertGraphData(InputModel model) { await _graphService.InsertGraphDataAsync(model.Index, model.Input); return Ok(); } /// /// 搜索递归获取节点相关的所有边和节点进行图谱对话 /// /// /// [HttpPost] public async Task SearchGraph(InputModel model) { var result = await _graphService.SearchGraphAsync(model.Index, model.Input); return Ok(result); } /// /// 通过社区算法检索社区节点进行对话 /// /// /// [HttpPost] public async Task SearchGraphCommunity(InputModel model) { var result = await _graphService.SearchGraphCommunityAsync(model.Index, model.Input); return Ok(result); } /// /// 导入txt文档 /// /// /// /// [HttpPost] public async Task ImportTxt(string index,IFormFile file) { var forms = await Request.ReadFormAsync(); using (var stream = new StreamReader(file.OpenReadStream())) { var txt = await stream.ReadToEndAsync(); await _graphService.InsertTextChunkAsync(index,txt); return Ok(); } } /// /// 通过社区检测生成社区和摘要 /// /// /// [HttpGet] public async Task GraphCommunities(string index) { await _graphService.GraphCommunitiesAsync(index); return Ok(); } /// /// 通过社区摘要生成全局摘要 /// /// /// [HttpGet] public async Task GraphGlobal(string index) { await _graphService.GraphGlobalAsync(index); return Ok(); } /// /// 删除图谱数据 /// /// /// [HttpGet] public async Task DeleteGraph(string index) { await _graphService.DeleteGraph(index); return Ok(); } } public class InputModel { public string Index { get; set; } public string Input { get; set; } } } ``` ## Test DB. Some community friends pre trained some data in advance. The link is as follows. After downloading, it can be directly put into the project directory for replacement to test the experience ``` https://pan.quark.cn/s/bf2d21f29f85 ``` ## See AntSK for more Rag scenarios Project address:[AntSK](https://github.com/AIDotNet/AntSK) Experience environment: [Demo地址](https://demo.antsk.cn) User:test Pwd:test You are also welcome to join our WeChat communication group. You can add my WeChat: **xuzeyu91** and send it to the group ================================================ FILE: README.md ================================================ 简体中文 | [English](./README.en.md) ## 这是一个参考GraphRag的dotnet简易实现 基于微软在论文中提到的实现思路,执行过程GraphRAG主要实现了如下功能: - Source Documents → Text Chunks:将源文档分割成文本块。 - Text Chunks → Element Instances:从每个文本块中提取图节点和边的实例。 - Element Instances → Element Summaries:为每个图元素生成摘要。 - Element Summaries → Graph Communities:使用社区检测算法将图划分为社区。 - Graph Communities → Community Summaries:为每个社区生成摘要。 - Community Summaries → Community Answers → Global Answer:使用社区摘要生成局部答案,然后汇总这些局部答案以生成全局答案。 本项目为demo示例,仅用于学习GraphRAG思路。 ## 核心业务流程 ### 1. 整体架构流程 ```mermaid graph LR A["文档导入"] --> B["图谱构建"] B --> C["社区检测"] C --> D["摘要生成"] D --> E["查询检索"] subgraph "数据流转" F["原始文档"] --> G["文本块"] G --> H["图谱数据
节点+边"] H --> I["社区结构"] I --> J["多层摘要"] J --> K["智能问答"] end subgraph "存储层" L["向量数据库
TextMemory"] M["关系数据库
Nodes/Edges/Communities/Globals"] end B -.-> L B -.-> M C -.-> M D -.-> M E -.-> L E -.-> M style A fill:#e1f5fe style E fill:#c8e6c9 style L fill:#fce4ec style M fill:#fff9c4 ``` ### 2. 文档导入与图谱构建流程 ```mermaid graph TD A["文档输入
原始文档/文本"] --> B["文本切片
TextChunker.SplitPlainTextLines
TextChunker.SplitPlainTextParagraphs"] B --> C["重叠文本块
CreateOverlappingChunks
3段落/块,1段落重叠"] C --> D["LLM提取图数据
SemanticService.CreateGraphAsync"] D --> E["节点提取
实体识别+类型分类"] D --> F["关系提取
边和关系描述"] E --> G["节点去重合并
向量相似度检测"] F --> H["关系去重合并
重复边处理"] G --> I["存储到数据库
Nodes表"] H --> J["存储到数据库
Edges表"] I --> K["向量化存储
TextMemory.SaveInformationAsync"] J --> L["孤立节点检测
ProcessOrphanNodesAsync"] L --> M["图谱构建完成"] style A fill:#e1f5fe style M fill:#c8e6c9 style D fill:#fff3e0 style K fill:#fce4ec ``` ### 3. 社区检测与摘要生成流程 ```mermaid graph TD A["图谱数据
Nodes + Edges"] --> B["构建图结构
Graph.AddEdge"] B --> C["标签传播算法
FastLabelPropagationAlgorithm
10次迭代"] C --> D["社区检测结果
节点→社区ID映射"] D --> E["存储社区节点关系
CommunitieNodes表"] E --> F["按社区分组节点
提取节点描述信息"] F --> G["LLM生成社区摘要
SemanticService.CommunitySummaries"] G --> H["存储社区摘要
Communities表"] H --> I["收集所有社区摘要"] I --> J["LLM生成全局摘要
SemanticService.GlobalSummaries"] J --> K["存储全局摘要
Globals表"] style A fill:#e1f5fe style C fill:#fff3e0 style G fill:#fff3e0 style J fill:#fff3e0 style K fill:#c8e6c9 ``` ### 4. 直接图谱查询流程 ```mermaid graph TD A["用户查询
问题输入"] --> B["向量搜索
TextMemory.SearchAsync
相关度阈值0.5"] B --> C{"匹配到节点?"} C -->|是| D["获取相关节点
RetrieveTextMemModelList"] C -->|否| E["降低阈值重试
阈值0.3,扩大搜索"] E --> D D --> F["递归扩展图谱
GetGraphAllRecursion
深度限制+节点数限制"] F --> G["Token数量估算
EstimateTokenCount"] G --> H{"超过Token限制?"} H -->|是| I["按权重裁剪节点
LimitGraphByTokenCount"] H -->|否| J["构建查询图谱
GraphModel"] I --> J J --> K["LLM生成答案
SemanticService.GetGraphAnswerAsync"] K --> L["返回结果"] style A fill:#e1f5fe style B fill:#fce4ec style F fill:#fff3e0 style K fill:#fff3e0 style L fill:#c8e6c9 ``` ### 5. 社区算法查询流程 ```mermaid graph TD A["用户查询
问题输入"] --> B["向量搜索
找到相关节点"] B --> C{"匹配到节点?"} C -->|是| D["查找节点所属社区
GetGraphAllCommunitiesRecursion"] C -->|否| E["使用全局摘要
Globals表"] D --> F["获取社区内所有节点"] F --> G["构建社区子图
节点+边"] G --> H["获取相关社区摘要
Communities表"] H --> I["获取全局摘要
Globals表"] I --> J["LLM综合分析
图谱+社区摘要+全局摘要"] E --> K["仅基于全局摘要回答"] J --> L["返回答案"] K --> L style A fill:#e1f5fe style B fill:#fce4ec style D fill:#fff3e0 style J fill:#fff3e0 style K fill:#fff3e0 style L fill:#c8e6c9 ``` ### 核心算法说明 1. **文本切片算法**:使用重叠窗口技术,每个文本块包含3个段落,相邻块之间重叠1个段落,确保关系信息的连续性。 2. **社区检测算法**:采用快速标签传播算法(Fast Label Propagation Algorithm),通过10次迭代来发现图中的社区结构。 3. **向量搜索策略**:首先使用0.5的相关度阈值进行搜索,如果结果不足则降低至0.3重试,确保找到足够的相关节点。 4. **Token优化机制**:实时估算token使用量,当超过限制时按节点权重进行智能裁剪,保证LLM输入的有效性。 5. **孤立节点处理**:自动检测没有关系连接的孤立节点,通过语义搜索尝试为其建立与其他节点的关系。 ## 您可以直接在项目中引用NuGet包,或者直接使用本项目提供API服务 出于方便,LLM接口目前只兼容了openai的规范,其他大模型可以考虑使用one-api类的集成产品 在appsettings.json配置 ``` "GraphOpenAI": { "Key": "sk-xxx", "EndPoint": "https://api.antsk.cn/", "ChatModel": "gpt-4o-mini", "EmbeddingModel": "text-embedding-ada-002" }, "TextChunker": { "LinesToken": 100, "ParagraphsToken": 1000 }, "GraphDBConnection": { "DbType": "Sqlite", //PostgreSQL "DBConnection": "Data Source=graph.db", "VectorConnection": "graphmem.db", //如果用PostgreSQL,可以和DBConnection一致 "VectorSize": 1536 //DbType=PostgreSQL时需要设置,sqlite可以不设置 }, "GraphSearch": { "SearchMinRelevance": 0.5, //搜索最小相关性 "SearchLimit": 3, //向量搜索节点限制个数 "NodeDepth": 3 ,//检索节点深度 "MaxNodes": 100 //检索最大节点数 }, "GraphSys": { "RetryCounnt": 2 //重试次数,使用国产模型可能会出现json提取失败,增加重试次数可提高可用性 } ``` ## 启动项目 ``` dotnet run --project GraphRag.Net.Web.csproj ``` ## 启动项目后可以通过 ``` http://localhost:5000/swagger ``` ## 打开swagger查看接口 ![Graoh](https://github.com/xuzeyu91/GraphRag.Net/blob/main/doc/api.png) ### 也可以使用界面 ``` http://localhost:5000/ ``` 打开blazor的UI界面,页面提供了文本导入、文件导入,和问答对话,查看知识图谱等功能 ![Graoh](https://github.com/xuzeyu91/GraphRag.Net/blob/main/doc/graph1.png) ## Nuget包使用 ``` dotnet add package GraphRag.Net ``` ## 为了方便进行提示词调整与修改,SK Plugin我们剥离出了项目,您需要把GraphRag.Net.Web项目中的 graphPlugins目录拷贝到你的项目中,并设置: [graphPlugins](https://github.com/AIDotNet/GraphRag.Net/tree/main/src/GraphRag.Net.Web/graphPlugins) ``` PreserveNewest ``` ### 默认配置,使用OpenAI标准接口,在配置了OpenAI的appsettings后可以使用下面代码进行注入 添加包以后,需要进行配置文件的设置以及依赖注入 ``` //OpenAI配置 builder.Configuration.GetSection("GraphOpenAI").Get(); //文档切片配置 builder.Configuration.GetSection("TextChunker").Get(); //配置数据库链接 builder.Configuration.GetSection("GraphDBConnection").Get(); //系统设置 builder.Configuration.GetSection("GraphSys").Get(); //注入AddGraphRagNet,注意,需要先注入配置文件,然后再注入GraphRagNet builder.Services.AddGraphRagNet(); ``` ### 如果你想接入其他模型,可以参考以下代码,这里抽象了Kernel的实现,你可以自定义实现 ``` var kernelBuild = Kernel.CreateBuilder(); kernelBuild.Services.AddKeyedSingleton("mock-text", new MockTextCompletion()); kernelBuild.Services.AddKeyedSingleton("mock-chat", new MockChatCompletion()); kernelBuild.Services.AddSingleton((ITextEmbeddingGenerationService)new MockTextEmbeddingGeneratorService()); kernelBuild.Services.AddKeyedSingleton("mock-embedding", new MockTextEmbeddingGeneratorService()); builder.Services.AddGraphRagNet(kernelBuild.Build()); ``` #### 此处需要注意,由于导入可能分多次导入,没有在导入时自动调用生成社区和全局信息,需要自己根据实际情况调用生成社区和全局信息 ``` await _graphService.GraphCommunitiesAsync(index); await _graphService.GraphGlobalAsync(index); ``` 使用时注入 IGraphService 服务,以下为参考示例代码 ``` namespace GraphRag.Net.Api.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class GraphController(IGraphService _graphService) : ControllerBase { /// /// 获取所有的索引数据 /// /// [HttpGet] public async Task GetAllIndex() { var graphModel = _graphService.GetAllIndex(); return Ok(graphModel); } /// /// 获取所有的图谱数据 /// /// /// [HttpGet] public async Task GetAllGraphs(string index) { if (string.IsNullOrEmpty(index)) { return Ok(new GraphViewModel()); } var graphModel = _graphService.GetAllGraphs(index); return Ok(graphModel); } /// /// 插入文本数据 /// /// /// [HttpPost] public async Task InsertGraphData(InputModel model) { await _graphService.InsertGraphDataAsync(model.Index, model.Input); return Ok(); } /// /// 搜索递归获取节点相关的所有边和节点进行图谱对话 /// /// /// [HttpPost] public async Task SearchGraph(InputModel model) { var result = await _graphService.SearchGraphAsync(model.Index, model.Input); return Ok(result); } /// /// 通过社区算法检索社区节点进行对话 /// /// /// [HttpPost] public async Task SearchGraphCommunity(InputModel model) { var result = await _graphService.SearchGraphCommunityAsync(model.Index, model.Input); return Ok(result); } /// /// 导入txt文档 /// /// /// /// [HttpPost] public async Task ImportTxt(string index,IFormFile file) { var forms = await Request.ReadFormAsync(); using (var stream = new StreamReader(file.OpenReadStream())) { var txt = await stream.ReadToEndAsync(); await _graphService.InsertTextChunkAsync(index,txt); return Ok(); } } /// /// 通过社区检测生成社区和摘要 /// /// /// [HttpGet] public async Task GraphCommunities(string index) { await _graphService.GraphCommunitiesAsync(index); return Ok(); } /// /// 通过社区摘要生成全局摘要 /// /// /// [HttpGet] public async Task GraphGlobal(string index) { await _graphService.GraphGlobalAsync(index); return Ok(); } /// /// 删除图谱数据 /// /// /// [HttpGet] public async Task DeleteGraph(string index) { await _graphService.DeleteGraph(index); return Ok(); } } public class InputModel { public string Index { get; set; } public string Input { get; set; } } } ``` ## 测试DB,有社区朋友提前预训练了一些数据,链接如下,下载后直接放进项目目录替换即可测试体验 ``` https://pan.quark.cn/s/bf2d21f29f85 ``` ## 更多Rag场景可查看 AntSK 项目地址:[AntSK](https://github.com/AIDotNet/AntSK) 体验环境: [Demo地址](https://demo.antsk.cn) 账号:test 密码:test ================================================ FILE: src/GraphRag.Net/Common/Options/GraphDBConnectionOption.cs ================================================ namespace GraphRag.Net.Options { public class GraphDBConnectionOption { /// /// sqlite连接字符串 /// public static string DbType { get; set; } = "Sqlite"; /// /// 业务数据链接字符串 /// public static string DBConnection { get; set; } = $"Data Source=graph.db"; /// /// 向量数据连接字符串 /// public static string VectorConnection { get; set; } = "graphmem.db"; /// /// 向量数据维度,PG需要设置 /// public static int VectorSize { get; set; } = 1536; } } ================================================ FILE: src/GraphRag.Net/Common/Options/GraphOpenAIOption.cs ================================================ namespace GraphRag.Net.Options { public class GraphOpenAIOption { public static string EndPoint { get; set; } public static string Key { get; set; } public static string ChatModel { get; set; } public static string EmbeddingModel { get; set; } } } ================================================ FILE: src/GraphRag.Net/Common/Options/GraphSearchOption.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GraphRag.Net.Options { public class GraphSearchOption { /// /// 社区检索搜索最小向量相似度 /// public static double SearchMinRelevance { get; set; } = 0.5; /// /// 社区检索搜索向量节点数量 /// public static int SearchLimit { get; set; } = 3; /// /// 节点关系检索深度 /// public static int NodeDepth { get; set; } = 3; /// /// 节点检索最多节点数量 /// public static int MaxNodes { get; set; } = 300; /// /// 最大Token数量限制(32K) /// public static int MaxTokens { get; set; } = 32000; } } ================================================ FILE: src/GraphRag.Net/Common/Options/GraphSysOption.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GraphRag.Net.Common.Options { public class GraphSysOption { /// /// 重试次数 /// public static int RetryCounnt { get; set; } = 2; } } ================================================ FILE: src/GraphRag.Net/Common/Options/TextChunkerOption.cs ================================================ namespace GraphRag.Net.Options { public class TextChunkerOption { /// /// 行切片token /// public static int LinesToken { get; set; } = 100; /// /// 段落切片token /// public static int ParagraphsToken { get; set; } = 1000; } } ================================================ FILE: src/GraphRag.Net/Common/ServiceDescriptionAttribute.cs ================================================ using Microsoft.Extensions.DependencyInjection; namespace GraphRag.Net { public class ServiceDescriptionAttribute : Attribute { public ServiceDescriptionAttribute(Type serviceType, ServiceLifetime lifetime) { ServiceType = serviceType; Lifetime = lifetime; } public Type ServiceType { get; set; } public ServiceLifetime Lifetime { get; set; } } } ================================================ FILE: src/GraphRag.Net/Domain/Interface/ICommunityDetectionService.cs ================================================ using GraphRag.Net.Domain.Model.Graph; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GraphRag.Net.Domain.Interface { public interface ICommunityDetectionService { Dictionary FastLabelPropagationAlgorithm(Graph graph, int iterations = 10); } } ================================================ FILE: src/GraphRag.Net/Domain/Interface/IGraphService.cs ================================================ using GraphRag.Net.Domain.Model.Graph; using Microsoft.SemanticKernel; namespace GraphRag.Net.Domain.Interface { public interface IGraphService { /// /// 获取所有索引信息 /// /// List GetAllIndex(); /// /// 获取Graph数据 /// /// /// GraphViewModel GetAllGraphs(string index); /// /// 切片导入文本数据 /// /// /// /// Task InsertTextChunkAsync(string index, string input); /// /// 生成图谱数据 /// /// /// /// Task InsertGraphDataAsync(string index, string input); /// /// 搜索递归获取节点相关的所有边和节点 /// /// /// /// Task SearchGraphModel(string index, string input); /// /// 搜索递归获取节点相关的所有边和节点进行图谱对话 /// /// /// /// Task SearchGraphAsync(string index, string input); /// /// 通过社区算法匹配相关节点信息 /// /// /// /// Task SearchGraphCommunityModel(string index, string input); /// /// 搜索递归获取节点相关的所有边和节点进行图谱对话,流式返回 /// /// /// /// IAsyncEnumerable SearchGraphStreamAsync(string index, string input); /// /// 通过社区算法检索社区节点进行对话 /// /// /// /// Task SearchGraphCommunityAsync(string index, string input); /// /// 通过社区算法检索社区节点进行对话,流式返回 /// /// /// /// IAsyncEnumerable SearchGraphCommunityStreamAsync(string index, string input); /// /// 社区摘要 /// /// /// Task GraphCommunitiesAsync(string index); /// /// 全局摘要 /// /// /// Task GraphGlobalAsync(string index); /// /// 删除图谱数据 /// /// /// Task DeleteGraph(string index); } } ================================================ FILE: src/GraphRag.Net/Domain/Interface/ISemanticService.cs ================================================ using GraphRag.Net.Domain.Model.Graph; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Memory; namespace GraphRag.Net.Domain.Interface { public interface ISemanticService { Task CreateGraphAsync(string input); Task GetGraphAnswerAsync(string graph, string input); IAsyncEnumerable GetGraphAnswerStreamAsync(string graph, string input); Task GetGraphCommunityAnswerAsync(string graph, string community, string global, string input); IAsyncEnumerable GetGraphCommunityAnswerStreamAsync(string graph, string community, string global, string input); Task GetRelationship(string node1, string node2); Task MergeDesc(string desc1, string desc2); Task CommunitySummaries(string nodes); Task GlobalSummaries(string community); Task GetTextMemory(); } } ================================================ FILE: src/GraphRag.Net/Domain/Model/Graph/Graph.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GraphRag.Net.Domain.Model.Graph { public class Graph { public Dictionary> AdjacencyList { get; } public Graph() { AdjacencyList = new Dictionary>(); } public void AddEdge(string node1, string node2) { if (!AdjacencyList.ContainsKey(node1)) { AdjacencyList[node1] = new List(); } if (!AdjacencyList.ContainsKey(node2)) { AdjacencyList[node2] = new List(); } AdjacencyList[node1].Add(node2); AdjacencyList[node2].Add(node1); } } } ================================================ FILE: src/GraphRag.Net/Domain/Model/Graph/GraphModel.cs ================================================ using GraphRag.Net.Repositories; namespace GraphRag.Net.Domain.Model.Graph { public class GraphModel { public List Nodes { get; set; } public List Edges { get; set; } } } ================================================ FILE: src/GraphRag.Net/Domain/Model/Graph/GraphViewModel.cs ================================================ namespace GraphRag.Net.Domain.Model.Graph { public class GraphViewModel { public string rootId { get; set; } public List nodes { get; set; } = new List(); public List lines { get; set; } = new List(); } public class NodesViewModel { public string id { get; set; } public string text { get; set; } public string color { get; set; } = "#43a2f1"; public NodesDataModel data { get; set; }=new NodesDataModel(); } public class NodesDataModel { public string desc { get; set; } } public class LinesViewModel { public string from { get; set; } public string to { get; set; } public string text { get; set; } public string color { get; set; } = "#000"; } } ================================================ FILE: src/GraphRag.Net/Domain/Model/Graph/RelationShipModel.cs ================================================ using GraphRag.Net.Repositories; namespace GraphRag.Net.Domain.Model.Graph { public class RelationShipModel { public bool IsRelationship { get; set; } public Edges Edge { get; set; } } } ================================================ FILE: src/GraphRag.Net/Domain/Model/Graph/TextMemModel.cs ================================================ namespace GraphRag.Net.Domain.Model.Graph { public class TextMemModel { public string Id { get; set; } public string Text { get; set; } public double Relevance { get; set; } } } ================================================ FILE: src/GraphRag.Net/Domain/Model/PageList.cs ================================================ namespace GraphRag.Net.Model { public class PageList { //查询结果 public List List { get; set; } /// /// 当前页,从1开始 /// public int PageIndex { get; set; } /// /// 每页数量 /// public int PageSize { get; set; } /// /// 总数 /// public int TotalCount { get; set; } } } ================================================ FILE: src/GraphRag.Net/Domain/Service/CommunityDetectionService.cs ================================================ using GraphRag.Net.Domain.Interface; using GraphRag.Net.Domain.Model.Graph; using Microsoft.Extensions.DependencyInjection; namespace GraphRag.Net.Domain.Service { [ServiceDescription(typeof(ICommunityDetectionService), ServiceLifetime.Scoped)] public class CommunityDetectionService : ICommunityDetectionService { /// /// 标签传播算法 /// /// /// /// public Dictionary FastLabelPropagationAlgorithm(Graph graph, int iterations = 10) { // Initialize labels var labels = graph.AdjacencyList.Keys.ToDictionary(node => node, node => node); for (int iter = 0; iter < iterations; iter++) { // Shuffle nodes var nodes = graph.AdjacencyList.Keys.OrderBy(a => Guid.NewGuid()).ToList(); foreach (var node in nodes) { // Count neighbor labels var labelCounts = new Dictionary(); foreach (var neighbor in graph.AdjacencyList[node]) { if (!labelCounts.ContainsKey(labels[neighbor])) { labelCounts[labels[neighbor]] = 0; } labelCounts[labels[neighbor]]++; } // Find the label of highest frequency var maxCount = labelCounts.Values.Max(); var bestLabels = labelCounts.Where(x => x.Value == maxCount).Select(x => x.Key).ToList(); // Pick a random label among the smallest lexicographically in case of tie var newLabel = bestLabels.OrderBy(x => x).First(); if (labels[node] != newLabel) { labels[node] = newLabel; } } } return labels; } } } ================================================ FILE: src/GraphRag.Net/Domain/Service/GraphService.cs ================================================ using GraphRag.Net.Domain.Interface; using GraphRag.Net.Domain.Model.Graph; using GraphRag.Net.Options; using GraphRag.Net.Repositories; using GraphRag.Net.Utils; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Text; using Newtonsoft.Json; using SqlSugar; namespace GraphRag.Net.Domain.Service { [ServiceDescription(typeof(IGraphService), ServiceLifetime.Scoped)] public class GraphService( INodes_Repositories _nodes_Repositories, IEdges_Repositories _edges_Repositories, ISemanticService _semanticService, ICommunities_Repositories _communities_Repositories, ICommunitieNodes_Repositories _communitieNodes_Repositories, IGlobals_Repositories _globals_Repositories, ICommunityDetectionService _communityDetectionService ) : IGraphService { /// /// 获取所有索引信息 /// /// public List GetAllIndex() { var indexs = _nodes_Repositories.GetDB().Queryable().GroupBy(p => p.Index).Select(p => p.Index).ToList(); return indexs; } /// /// 获取Graph数据 /// /// public GraphViewModel GetAllGraphs(string index) { if (string.IsNullOrWhiteSpace(index)) { throw new ArgumentException("Index required value cannot be null."); } GraphViewModel graphViewModel = new GraphViewModel(); var nodes = _nodes_Repositories.GetList(p => p.Index == index); var edges = _edges_Repositories.GetList(p => p.Index == index); Dictionary TypeColor = new Dictionary(); Random random = new Random(); foreach (var n in nodes) { NodesViewModel nodesViewModel = new NodesViewModel() { id = n.Id, text = n.Name, data = new NodesDataModel() { desc = n.Desc.ConvertToString() } }; //处理相同的Type用相同的颜色 if (TypeColor.ContainsKey(n.Type)) { nodesViewModel.color = TypeColor[n.Type]; } else { nodesViewModel.color = $"#{random.Next(0x1000000):X6}"; TypeColor.Add(n.Type, nodesViewModel.color); } graphViewModel.nodes.Add(nodesViewModel); } foreach (var e in edges) { LinesViewModel linesViewModel = new LinesViewModel() { from = e.Source, to = e.Target, text = e.Relationship }; graphViewModel.lines.Add(linesViewModel); } return graphViewModel; } /// /// 切片导入文本数据 /// /// /// /// public async Task InsertTextChunkAsync(string index, string input) { if (string.IsNullOrWhiteSpace(index) || string.IsNullOrWhiteSpace(input)) { throw new ArgumentException("Values required for index and input cannot be null."); } var lines = TextChunker.SplitPlainTextLines(input, TextChunkerOption.LinesToken); var paragraphs = TextChunker.SplitPlainTextParagraphs(lines, TextChunkerOption.ParagraphsToken); // 优化文本分块:使用重叠窗口来保持关系信息 var optimizedChunks = CreateOverlappingChunks(paragraphs); foreach (var chunk in optimizedChunks) { await InsertGraphDataAsync(index, chunk); } } /// /// 创建重叠文本块以保持关系信息 /// /// /// private List CreateOverlappingChunks(List paragraphs) { var chunks = new List(); const int maxChunkSize = 3; // 每个块最多包含3个段落 const int overlapSize = 1; // 重叠1个段落 if (paragraphs.Count <= maxChunkSize) { // 如果段落数量不多,直接作为一个块 chunks.Add(string.Join("\n\n", paragraphs)); } else { // 创建重叠的文本块 for (int i = 0; i < paragraphs.Count; i += (maxChunkSize - overlapSize)) { var chunkParagraphs = paragraphs .Skip(i) .Take(maxChunkSize) .ToList(); if (chunkParagraphs.Count > 0) { var chunk = string.Join("\n\n", chunkParagraphs); // 避免重复的块 if (!chunks.Contains(chunk)) { chunks.Add(chunk); } } // 如果剩余段落不足一个完整块,退出循环 if (i + maxChunkSize >= paragraphs.Count) { break; } } } Console.WriteLine($"原始段落数: {paragraphs.Count}, 优化后块数: {chunks.Count}"); return chunks; } /// /// 生成图谱数据 /// /// /// /// public async Task InsertGraphDataAsync(string index, string input) { if (string.IsNullOrWhiteSpace(index) || string.IsNullOrWhiteSpace(input)) { throw new ArgumentException("Values required for index and input cannot be null."); } try { SemanticTextMemory textMemory = await _semanticService.GetTextMemory(); var graph = await _semanticService.CreateGraphAsync(input); Dictionary nodeDic = new Dictionary(); List newNodes = new List(); // 收集新插入的节点 foreach (var n in graph.Nodes) { string Id = Guid.NewGuid().ToString(); string text2 = $"Name:{n.Name};Type:{n.Type};Desc:{n.Desc}"; bool isContinue = false; //判断是否存在相同节点 var oldNode = _nodes_Repositories.GetFirst(p => p.Index == index && p.Name == n.Name); if (oldNode.IsNotNull() && !string.IsNullOrWhiteSpace(n.Desc)) { //相同节点关联edge关系 var newDesc = await _semanticService.MergeDesc(oldNode.Desc.ConvertToString(), n.Desc.ConvertToString()); if (string.IsNullOrEmpty(newDesc)) { //可能触发了LLM规则限制,简单粗暴来拼接吧 oldNode.Desc = oldNode.Desc.ConvertToString() + "; " + n.Desc.ConvertToString(); } else { oldNode.Desc = newDesc; } //更新描述 _nodes_Repositories.Update(oldNode); text2 = $"Name:{oldNode.Name};Type:{oldNode.Type};Desc:{oldNode.Desc}"; nodeDic.Add(n.Id, oldNode.Id); await textMemory.SaveInformationAsync(index, id: oldNode.Id, text: text2, cancellationToken: default); continue; } //优化相关节点发现:增加搜索数量和降低阈值 List potentialRelatedNodes = new List(); await foreach (MemoryQueryResult memory in textMemory.SearchAsync(index, text2, limit: 5, minRelevanceScore: 0.7)) { if (memory.Relevance == 1) { //相同节点进行合并 Console.WriteLine("节点合并"); nodeDic.Add(n.Id, memory.Metadata.Id); isContinue = true; break; } if (graph.Nodes.Select(p => p.Id).Contains(memory.Metadata.Id)) { //如果本次包含了向量近似的数据,则跳过 continue; } potentialRelatedNodes.Add(memory.Metadata.Id); } if (isContinue) { //节点合并,跳出循环 continue; } // 创建新节点 Nodes node = new Nodes() { Id = Id, Index = index, Name = n.Name, Type = n.Type, Desc = n.Desc.ConvertToString() }; if (!nodeDic.ContainsKey(n.Id)) { nodeDic.Add(n.Id, node.Id); } _nodes_Repositories.Insert(node); newNodes.Add(node); // 检查与潜在相关节点的关系 foreach (var relatedNodeId in potentialRelatedNodes) { var node1 = _nodes_Repositories.GetFirst(p => p.Id == relatedNodeId); if (node1 != null) { string text1 = $"Name:{node1.Name};Type:{node1.Type};Desc:{node1.Desc}"; var relationShip = await _semanticService.GetRelationship(text1, text2); if (relationShip.IsRelationship) { if (relationShip.Edge.Source == "node1") { relationShip.Edge.Source = node1.Id; relationShip.Edge.Target = Id; } else { relationShip.Edge.Source = Id; relationShip.Edge.Target = node1.Id; } if (!_edges_Repositories.IsAny(p => p.Target == relationShip.Edge.Target && p.Source == relationShip.Edge.Source)) { relationShip.Edge.Id = Guid.NewGuid().ToString(); relationShip.Edge.Index = index; _edges_Repositories.Insert(relationShip.Edge); } } } } //向量处理节点信息 await textMemory.SaveInformationAsync(index, id: node.Id, text: text2, cancellationToken: default); } foreach (var e in graph.Edges) { Edges edge = new Edges() { Id = Guid.NewGuid().ToString(), Index = index, Source = nodeDic[e.Source], Target = nodeDic[e.Target], Relationship = e.Relationship }; _edges_Repositories.Insert(edge); } // 检测和处理孤立节点 await ProcessOrphanNodesAsync(index, newNodes, textMemory); //查询Edges 的Source和Target 重复数据 var repeatEdges = _edges_Repositories.GetDB().Queryable() .GroupBy(p => new { p.Source, p.Target }) .Select(p => new { p.Source, p.Target, Count = SqlFunc.AggregateCount(p.Source) }) .ToList().Where(p => p.Count > 1).ToList(); //合并查询Edges 的Source和Target 重复数据 foreach (var edge in repeatEdges) { var edges = _edges_Repositories.GetList(p => p.Source == edge.Source && p.Target == edge.Target); var firstEdge = edges.First(); for (int i = 1; i < edges.Count(); i++) { if (firstEdge.Relationship == edges[i].Relationship) { //相同的边进行合并 _edges_Repositories.Delete(edges[i]); continue; } var newDesc = await _semanticService.MergeDesc(firstEdge.Relationship, edges[i].Relationship); firstEdge.Relationship = newDesc; _edges_Repositories.Update(firstEdge); _edges_Repositories.Delete(edges[i]); } } } catch (Exception ex) { Console.WriteLine($"插入数据失败:{ex.ToString()}"); } } /// /// 检索相关节点 /// /// /// /// /// public async Task SearchGraphModel(string index, string input) { if (string.IsNullOrWhiteSpace(index) || string.IsNullOrWhiteSpace(input)) { throw new ArgumentException("Values required for index and input cannot be null."); } var textMemModelList = await RetrieveTextMemModelList(index, input); if (textMemModelList.Any()) { var nodes = _nodes_Repositories.GetList(p => p.Index == index && textMemModelList.Select(c => c.Id).Contains(p.Id)); // 创建节点权重字典 Dictionary nodeWeights = textMemModelList.ToDictionary( t => t.Id, t => t.Relevance ); var graphModel = GetGraphAllRecursion(index, nodes, nodeWeights); // 计算预估的token数量,并在必要时限制节点数量 int estimatedTokens = EstimateTokenCount(graphModel); if (estimatedTokens > GraphSearchOption.MaxTokens) { Console.WriteLine($"预估Token数量 {estimatedTokens} 超过限制 {GraphSearchOption.MaxTokens},正在调整节点数量..."); graphModel = LimitGraphByTokenCount(graphModel, nodeWeights); } return graphModel; } else { return new GraphModel(); } } /// /// 估算图模型的token数量 /// /// 图模型 /// 估算的token数量 private int EstimateTokenCount(GraphModel model) { int tokenCount = 0; // 估算节点的token(每个单词约1.3个token,每个节点信息加上额外开销) foreach (var node in model.Nodes) { // 节点ID和名称估算 tokenCount += (node.Id?.Length ?? 0) / 3 + 2; tokenCount += (node.Name?.Length ?? 0) / 3 + 2; // 节点描述估算(一个汉字约等于1个token,英文单词约等于0.75个token) string desc = node.Desc ?? ""; int chineseCount = desc.Count(c => c >= 0x4E00 && c <= 0x9FFF); int otherCount = desc.Length - chineseCount; tokenCount += chineseCount + (int)(otherCount * 0.75); // 节点额外属性估算 tokenCount += 10; // 额外结构开销 } // 估算边的token tokenCount += model.Edges.Count * 10; // 每个边的结构信息约10个token // JSON结构开销 tokenCount += 200; return tokenCount; } /// /// 根据token数量限制图大小 /// /// 原始图模型 /// 节点权重 /// 裁剪后的图模型 private GraphModel LimitGraphByTokenCount(GraphModel model, Dictionary nodeWeights) { var result = new GraphModel(); // 将节点按权重排序 var sortedNodes = model.Nodes .OrderByDescending(n => nodeWeights.GetValueOrDefault(n.Id, 0)) .ToList(); // 从高权重节点开始添加,直到接近token限制 var selectedNodes = new List(); int currentTokens = 200; // 基础结构开销 foreach (var node in sortedNodes) { // 计算添加此节点后的token数 string desc = node.Desc ?? ""; int chineseCount = desc.Count(c => c >= 0x4E00 && c <= 0x9FFF); int otherCount = desc.Length - chineseCount; int nodeTokens = chineseCount + (int)(otherCount * 0.75) + (node.Id?.Length ?? 0) / 3 + (node.Name?.Length ?? 0) / 3 + 15; // 如果添加此节点会超过限制,跳过 if (currentTokens + nodeTokens > GraphSearchOption.MaxTokens * 0.9) { continue; } selectedNodes.Add(node); currentTokens += nodeTokens; } // 只保留连接选中节点的边 var selectedEdges = model.Edges.Where(e => selectedNodes.Any(n => n.Id == e.Source) && selectedNodes.Any(n => n.Id == e.Target) ).ToList(); result.Nodes = selectedNodes; result.Edges = selectedEdges; Console.WriteLine($"节点限制调整:从 {model.Nodes.Count} 个节点减少到 {result.Nodes.Count} 个节点"); Console.WriteLine($"预估调整后Token数:约 {currentTokens}"); return result; } /// /// 通过社区算法匹配相关节点信息 /// /// /// /// /// public async Task SearchGraphCommunityModel(string index, string input) { if (string.IsNullOrWhiteSpace(index) || string.IsNullOrWhiteSpace(input)) { throw new ArgumentException("Values required for index and input cannot be null."); } var textMemModelList = await RetrieveTextMemModelList(index, input); if (!textMemModelList.Any()) { // 尝试降低阈值重新检索 textMemModelList = await RetrieveTextMemModelList(index, input, 0.3, 5); } if (textMemModelList.Any()) { var nodes = _nodes_Repositories.GetList(p => p.Index == index && textMemModelList.Select(c => c.Id).Contains(p.Id)); var graphModel = GetGraphAllCommunitiesRecursion(index, nodes); // 计算预估的token数量,并在必要时限制节点数量 int estimatedTokens = EstimateTokenCount(graphModel); if (estimatedTokens > GraphSearchOption.MaxTokens) { Console.WriteLine($"社区检索:预估Token数量 {estimatedTokens} 超过限制 {GraphSearchOption.MaxTokens},正在调整节点数量..."); // 为社区节点创建权重字典(默认权重相同) Dictionary nodeWeights = graphModel.Nodes.ToDictionary( n => n.Id, n => 1.0 ); // 为初始检索到的节点赋予更高权重 foreach (var node in nodes) { if (nodeWeights.ContainsKey(node.Id)) { nodeWeights[node.Id] = 2.0; // 给初始节点更高权重 } } graphModel = LimitGraphByTokenCount(graphModel, nodeWeights); } return graphModel; } else { return new GraphModel(); } } /// /// 搜索递归获取节点相关的所有边和节点进行图谱对话 /// /// /// /// public async Task SearchGraphAsync(string index, string input) { var graphModel = await SearchGraphModel(index, input); string answer = await _semanticService.GetGraphAnswerAsync(JsonConvert.SerializeObject(graphModel), input); return answer; } /// /// 搜索递归获取节点相关的所有边和节点进行图谱对话,流式返回 /// /// /// /// public async IAsyncEnumerable SearchGraphStreamAsync(string index, string input) { var graphModel = await SearchGraphModel(index, input); if (graphModel.Nodes.Count() > 0) { var answerStream = _semanticService.GetGraphAnswerStreamAsync(JsonConvert.SerializeObject(graphModel), input); await foreach (var content in answerStream) { yield return content; } } } /// /// 通过社区算法检索社区节点进行对话 /// /// /// /// public async Task SearchGraphCommunityAsync(string index, string input) { string answer = ""; var graphModel = await SearchGraphCommunityModel(index, input); var global = _globals_Repositories.GetFirst(p => p.Index == index)?.Summaries; if (graphModel.Nodes.Count() > 0) { var community = string.Join(Environment.NewLine, _communities_Repositories.GetDB().Queryable().Where(p => p.Index == index).Select(p => p.Summaries).ToList()); //这里数据有点多,要通过语义进行一次过滤 answer = await _semanticService.GetGraphCommunityAnswerAsync(JsonConvert.SerializeObject(graphModel), community, global, input); } else { //如果没有匹配到节点信息,使用全局信息 answer = await _semanticService.GetGraphCommunityAnswerAsync("NoSearch", "NoSearch", global, input); } return answer; } /// /// 通过社区算法检索社区节点进行对话,流式返回 /// /// /// /// public async IAsyncEnumerable SearchGraphCommunityStreamAsync(string index, string input) { var textMemModelList = await RetrieveTextMemModelList(index, input); var global = _globals_Repositories.GetFirst(p => p.Index == index)?.Summaries; IAsyncEnumerable answer; //匹配到节点信息 var graphModel = await SearchGraphCommunityModel(index, input); if (graphModel.Nodes.Count() > 0) { var community = string.Join(Environment.NewLine, _communities_Repositories.GetDB().Queryable().Where(p => p.Index == index).Select(p => p.Summaries).ToList()); //这里数据有点多,要通过语义进行一次过滤 answer = _semanticService.GetGraphCommunityAnswerStreamAsync(JsonConvert.SerializeObject(graphModel), community, global, input); } else { //如果没有匹配到节点信息,使用全局信息 answer = _semanticService.GetGraphCommunityAnswerStreamAsync("NoSearch", "NoSearch", global, input); } await foreach (var content in answer) { yield return content; } } /// /// 社区摘要 /// /// /// public async Task GraphCommunitiesAsync(string index) { var nodes = _nodes_Repositories.GetList(p => p.Index == index); var edges = _edges_Repositories.GetList(p => p.Index == index); //删除社区数据 _communitieNodes_Repositories.Delete(p => p.Index == index); _communities_Repositories.Delete(p => p.Index == index); var graph = new Graph(); foreach (var edge in edges) { graph.AddEdge(edge.Source, edge.Target); } //重新计算社区 var result = _communityDetectionService.FastLabelPropagationAlgorithm(graph); Console.WriteLine("开始社区总结"); foreach (var kvp in result) { //插入社区节点数据 CommunitieNodes communitieNodes = new CommunitieNodes(); communitieNodes.Index = index; communitieNodes.CommunitieId = kvp.Value; communitieNodes.NodeId = kvp.Key; _communitieNodes_Repositories.Insert(communitieNodes); } //获取所有社区ID var communitieIds = _communitieNodes_Repositories.GetDB().Queryable().Where(p => p.Index == index).GroupBy(p => p.CommunitieId).Select(p => p.CommunitieId).ToList(); foreach (var communitieId in communitieIds) { var nodeList = _communitieNodes_Repositories.GetDB().Queryable() .LeftJoin((c, n) => c.NodeId == n.Id) .Where(c => c.CommunitieId == communitieId) .Select((c, n) => $"Name:{n.Name}; Type:{n.Type}; Desc:{n.Desc}") .ToList(); var nodeDescs = string.Join(Environment.NewLine, nodeList); var summaries = await _semanticService.CommunitySummaries(nodeDescs); Communities communities = new Communities() { CommunitieId = communitieId, Index = index, Summaries = summaries }; //插入社区总结数据 _communities_Repositories.Insert(communities); } } /// /// 全局摘要 /// /// /// public async Task GraphGlobalAsync(string index) { _globals_Repositories.Delete(p => p.Index == index); var communitieSummariesList = _communities_Repositories.GetDB().Queryable().Where(p => p.Index == index).Select(p => p.Summaries).ToList(); var communitieSummaries = string.Join(Environment.NewLine, communitieSummariesList); var globalSummaries = await _semanticService.GlobalSummaries(communitieSummaries); Globals globals = new Globals() { Index = index, Summaries = globalSummaries }; _globals_Repositories.Insert(globals); } /// /// 增强图谱关系:为现有节点发现和建立新的关系 /// /// /// public async Task EnhanceGraphRelationshipsAsync(string index) { if (string.IsNullOrWhiteSpace(index)) { throw new ArgumentException("Index required value cannot be null."); } Console.WriteLine("开始增强图谱关系..."); SemanticTextMemory textMemory = await _semanticService.GetTextMemory(); // 获取所有孤立或连接较少的节点 var allNodes = _nodes_Repositories.GetList(p => p.Index == index); var lowConnectedNodes = new List(); foreach (var node in allNodes) { var connectionCount = _edges_Repositories.GetDB().Queryable() .Where(e => (e.Source == node.Id || e.Target == node.Id) && e.Index == index) .Count(); // 如果连接数少于2个,认为是需要增强的节点 if (connectionCount < 2) { lowConnectedNodes.Add(node); } } Console.WriteLine($"发现 {lowConnectedNodes.Count} 个需要增强关系的节点"); // 为每个低连接节点尝试建立新关系 int enhancedCount = 0; foreach (var node in lowConnectedNodes) { try { int newConnections = await AttemptConnectOrphanNodeAsync(index, node, textMemory); if (newConnections > 0) { enhancedCount++; } } catch (Exception ex) { Console.WriteLine($"增强节点 {node.Name} 关系时出错:{ex.Message}"); } } Console.WriteLine($"关系增强完成,共为 {enhancedCount} 个节点建立了新关系"); } /// /// 批量关系验证:检查并优化现有关系的质量 /// /// /// public async Task ValidateAndOptimizeRelationshipsAsync(string index) { if (string.IsNullOrWhiteSpace(index)) { throw new ArgumentException("Index required value cannot be null."); } Console.WriteLine("开始验证和优化现有关系..."); var allEdges = _edges_Repositories.GetList(p => p.Index == index); var weakRelationships = new List(); // 识别可能需要优化的关系 foreach (var edge in allEdges) { // 检查关系描述是否过于简单或模糊 if (string.IsNullOrWhiteSpace(edge.Relationship) || edge.Relationship.Length < 3 || edge.Relationship.ToLower().Contains("related") || edge.Relationship.ToLower().Contains("associated")) { weakRelationships.Add(edge); } } Console.WriteLine($"发现 {weakRelationships.Count} 个需要优化的关系"); // 优化弱关系 int optimizedCount = 0; foreach (var edge in weakRelationships.Take(20)) // 限制处理数量 { try { var sourceNode = _nodes_Repositories.GetFirst(p => p.Id == edge.Source); var targetNode = _nodes_Repositories.GetFirst(p => p.Id == edge.Target); if (sourceNode != null && targetNode != null) { string sourceText = $"Name:{sourceNode.Name};Type:{sourceNode.Type};Desc:{sourceNode.Desc}"; string targetText = $"Name:{targetNode.Name};Type:{targetNode.Type};Desc:{targetNode.Desc}"; var newRelationship = await _semanticService.GetRelationship(sourceText, targetText); if (newRelationship.IsRelationship && !string.IsNullOrWhiteSpace(newRelationship.Edge.Relationship) && newRelationship.Edge.Relationship != edge.Relationship) { edge.Relationship = newRelationship.Edge.Relationship; _edges_Repositories.Update(edge); optimizedCount++; Console.WriteLine($"优化关系: {sourceNode.Name} -> {targetNode.Name}: {newRelationship.Edge.Relationship}"); } } } catch (Exception ex) { Console.WriteLine($"优化关系时出错:{ex.Message}"); } } Console.WriteLine($"关系优化完成,共优化了 {optimizedCount} 个关系"); } public async Task DeleteGraph(string index) { SemanticTextMemory textMemory = await _semanticService.GetTextMemory(); var nodes = await _nodes_Repositories.GetListAsync(p => p.Index == index); foreach (var node in nodes) { //删除向量数据 await textMemory.RemoveAsync(index, node.Id); } //删除索引数据 await _nodes_Repositories.DeleteAsync(p => p.Index == index); await _edges_Repositories.DeleteAsync(p => p.Index == index); await _communities_Repositories.DeleteAsync(p => p.Index == index); await _communitieNodes_Repositories.DeleteAsync(p => p.Index == index); await _globals_Repositories.DeleteAsync(p => p.Index == index); } #region 内部方法 /// /// 检测和处理孤立节点 /// /// /// /// /// private async Task ProcessOrphanNodesAsync(string index, List newNodes, SemanticTextMemory textMemory) { Console.WriteLine($"开始检测孤立节点,新增节点数:{newNodes.Count}"); foreach (var node in newNodes) { // 检查节点是否为孤立节点(没有任何边连接) bool hasConnections = _edges_Repositories.IsAny(p => (p.Source == node.Id || p.Target == node.Id) && p.Index == index); if (!hasConnections) { Console.WriteLine($"发现孤立节点:{node.Name}"); var connectionsFound = await AttemptConnectOrphanNodeAsync(index, node, textMemory); Console.WriteLine($"为节点 {node.Name} 建立了 {connectionsFound} 个新连接"); } } } /// /// 尝试为孤立节点建立连接 /// /// /// /// /// 返回建立的新连接数量 private async Task AttemptConnectOrphanNodeAsync(string index, Nodes orphanNode, SemanticTextMemory textMemory) { string nodeText = $"Name:{orphanNode.Name};Type:{orphanNode.Type};Desc:{orphanNode.Desc}"; // 使用更低的阈值和更多的搜索结果来寻找潜在关系 List candidateNodes = new List(); // 第一轮:基于节点描述搜索 await foreach (MemoryQueryResult memory in textMemory.SearchAsync(index, nodeText, limit: 10, minRelevanceScore: 0.5)) { if (memory.Metadata.Id != orphanNode.Id) { candidateNodes.Add(memory.Metadata.Id); } } // 第二轮:基于节点名称搜索(针对命名实体) if (candidateNodes.Count < 3) { await foreach (MemoryQueryResult memory in textMemory.SearchAsync(index, orphanNode.Name, limit: 5, minRelevanceScore: 0.6)) { if (memory.Metadata.Id != orphanNode.Id && !candidateNodes.Contains(memory.Metadata.Id)) { candidateNodes.Add(memory.Metadata.Id); } } } // 第三轮:基于节点类型搜索相同类型的实体 if (candidateNodes.Count < 2) { var sameTypeNodes = _nodes_Repositories.GetList(p => p.Index == index && p.Type == orphanNode.Type && p.Id != orphanNode.Id) .Take(5).Select(p => p.Id).ToList(); foreach (var nodeId in sameTypeNodes) { if (!candidateNodes.Contains(nodeId)) { candidateNodes.Add(nodeId); } } } // 尝试建立关系 int connectionsFound = 0; foreach (var candidateId in candidateNodes.Take(5)) // 限制检查数量以控制成本 { var candidateNode = _nodes_Repositories.GetFirst(p => p.Id == candidateId); if (candidateNode != null) { string candidateText = $"Name:{candidateNode.Name};Type:{candidateNode.Type};Desc:{candidateNode.Desc}"; try { var relationShip = await _semanticService.GetRelationship(candidateText, nodeText); if (relationShip.IsRelationship) { // 确定关系方向 string sourceId, targetId; if (relationShip.Edge.Source == "node1") { sourceId = candidateNode.Id; targetId = orphanNode.Id; } else { sourceId = orphanNode.Id; targetId = candidateNode.Id; } // 检查关系是否已存在 if (!_edges_Repositories.IsAny(p => p.Source == sourceId && p.Target == targetId && p.Index == index)) { var edge = new Edges() { Id = Guid.NewGuid().ToString(), Index = index, Source = sourceId, Target = targetId, Relationship = relationShip.Edge.Relationship }; _edges_Repositories.Insert(edge); connectionsFound++; Console.WriteLine($"为孤立节点 {orphanNode.Name} 建立关系:{relationShip.Edge.Relationship} -> {candidateNode.Name}"); // 如果已经找到足够的连接,停止搜索 if (connectionsFound >= 2) { break; } } } } catch (Exception ex) { Console.WriteLine($"为孤立节点建立关系时出错:{ex.Message}"); } } } if (connectionsFound == 0) { Console.WriteLine($"警告:未能为孤立节点 {orphanNode.Name} 建立任何关系"); } return connectionsFound; } /// /// 基于搜索条件检索TextMemModel的列表。 /// /// 索引 /// 输入文本 /// 最小相关性阈值 /// 搜索结果限制 /// private async Task> RetrieveTextMemModelList(string index, string input, double? minRelevance = null, int? limit = null) { SemanticTextMemory textMemory = await _semanticService.GetTextMemory(); List textMemModelList = new List(); // 使用提供的阈值或默认配置 double relevanceThreshold = minRelevance ?? GraphSearchOption.SearchMinRelevance; int resultLimit = limit ?? GraphSearchOption.SearchLimit; int matchCount = 0; await foreach (MemoryQueryResult memory in textMemory.SearchAsync(index, input, limit: resultLimit, minRelevanceScore: relevanceThreshold)) { matchCount++; var textMemModel = new TextMemModel() { Id = memory.Metadata.Id, Text = memory.Metadata.Text, Relevance = memory.Relevance }; textMemModelList.Add(textMemModel); } // 如果结果不足,尝试降低阈值 if (matchCount < 2 && relevanceThreshold > 0.3) { double lowerThreshold = Math.Max(0.3, relevanceThreshold - 0.2); Console.WriteLine($"结果不足,降低阈值至:{lowerThreshold}重试"); await foreach (MemoryQueryResult memory in textMemory.SearchAsync(index, input, limit: resultLimit + 2, minRelevanceScore: lowerThreshold)) { if (!textMemModelList.Any(t => t.Id == memory.Metadata.Id)) { matchCount++; var textMemModel = new TextMemModel() { Id = memory.Metadata.Id, Text = memory.Metadata.Text, Relevance = memory.Relevance }; textMemModelList.Add(textMemModel); } } } // 按相关性排序 textMemModelList = textMemModelList.OrderByDescending(t => t.Relevance).ToList(); Console.WriteLine($"向量匹配数:{matchCount}, 最高相关度:{(textMemModelList.Any() ? textMemModelList.First().Relevance.ToString("F2") : "N/A")}"); return textMemModelList; } /// /// 递归获取节点相关的所有边和节点 /// /// 索引 /// 初始节点列表 /// 节点权重字典 /// private GraphModel GetGraphAllRecursion(string index, List initialNodes, Dictionary nodeWeights) { var allNodes = new List(initialNodes); var allEdges = new List(); var nodesToExplore = new List(initialNodes); int depth = 0; while (nodesToExplore.Count > 0) { if (depth >= GraphSearchOption.NodeDepth || allNodes.Count >= GraphSearchOption.MaxNodes) { break; } // 按权重排序待探索节点 nodesToExplore = nodesToExplore .OrderByDescending(n => nodeWeights.ContainsKey(n.Id) ? nodeWeights[n.Id] : 0) .ToList(); var currentNodes = nodesToExplore.Take(Math.Min(5, nodesToExplore.Count)).ToList(); nodesToExplore.RemoveRange(0, currentNodes.Count); var newEdges = GetEdges(index, currentNodes); if (!newEdges.Any()) { continue; } // 添加新边,避免重复 foreach (var edge in newEdges) { if (!allEdges.Any(e => e.Source == edge.Source && e.Target == edge.Target)) { allEdges.Add(edge); // 为新发现的节点设置权重 double parentWeight = nodeWeights.GetValueOrDefault(edge.Source, 0); double weightDecay = 0.8; // 权重衰减因子 if (!nodeWeights.ContainsKey(edge.Target)) { nodeWeights[edge.Target] = parentWeight * weightDecay; } } } // 获取新节点 var newNodes = GetNodes(index, newEdges); var nodesToAdd = newNodes.Where(n => !allNodes.Any(existingNode => existingNode.Id == n.Id)).ToList(); allNodes.AddRange(nodesToAdd); nodesToExplore.AddRange(nodesToAdd); depth++; } // 如果节点数超过限制,保留权重最高的节点 if (allNodes.Count > GraphSearchOption.MaxNodes) { allNodes = allNodes .OrderByDescending(n => nodeWeights.GetValueOrDefault(n.Id, 0)) .Take(GraphSearchOption.MaxNodes) .ToList(); // 确保边的节点都在保留的节点中 allEdges = allEdges .Where(e => allNodes.Any(n => n.Id == e.Source) && allNodes.Any(n => n.Id == e.Target)) .ToList(); } return new GraphModel { Nodes = allNodes, Edges = allEdges }; } /// /// 通过社区算法检索社区节点 /// /// /// /// private GraphModel GetGraphAllCommunitiesRecursion(string index, List initialNodes) { var allNodes = new List(); var allEdges = new List(); var nodeIds = initialNodes.Select(x => x.Id).ToList(); var communitiesNodes = _communities_Repositories.GetDB().Queryable() .LeftJoin((c, cn) => c.CommunitieId == cn.CommunitieId) .LeftJoin((c, cn, n) => cn.NodeId == n.Id) .Where((c, cn, n) => nodeIds.Contains(n.Id)).Select((c, cn, n) => new Nodes() { Index = n.Index, Id = n.Id, Name = n.Name, Type = n.Type, Desc = n.Desc }).ToList(); allNodes.AddRange(communitiesNodes); var newEdges = GetEdges(index, allNodes); foreach (var edge in newEdges) { if (!allEdges.Any(e => e.Source == edge.Source && e.Target == edge.Target)) { allEdges.Add(edge); } } // 如果节点数超过最大限制,进行截断 if (allNodes.Count > GraphSearchOption.MaxNodes) { allNodes = allNodes.Take(GraphSearchOption.MaxNodes).ToList(); } // 需要相应地处理 allEdges,确保边的节点在 allNodes 中 allEdges = allEdges.Where(e => allNodes.Any(p => p.Id == e.Source) && allNodes.Any(p => p.Id == e.Target)).ToList(); return new GraphModel { Nodes = allNodes, Edges = allEdges }; } /// /// 获取边信息 /// /// 索引 /// 节点列表 /// private List GetEdges(string index, List nodes) { var nodeIds = nodes.Select(x => x.Id).ToList(); var edges = new List(); edges = _edges_Repositories.GetList(x => x.Index == index && nodeIds.Contains(x.Source) && nodeIds.Contains(x.Target)); return edges; } /// /// 获取节点信息 /// /// 索引 /// 边列表 /// private List GetNodes(string index, List edges) { var targets = edges.Select(p => p.Target).ToList(); var sources = edges.Select(p => p.Source).ToList(); List nodeIds = new List(); nodeIds.AddRange(targets); nodeIds.AddRange(sources); var nodes = _nodes_Repositories.GetList(p => p.Index == index || nodeIds.Contains(p.Id)); return nodes; } /// /// 获取相关社区ID列表 /// private List GetRelevantCommunities(string index, List nodeIds) { var communities = _communitieNodes_Repositories.GetDB().Queryable() .Where(cn => nodeIds.Contains(cn.NodeId)) .Select(cn => cn.CommunitieId) .Distinct() .ToList(); return communities; } #endregion } } ================================================ FILE: src/GraphRag.Net/Domain/Service/SemanticService.cs ================================================ using Azure.AI.OpenAI; using GraphRag.Net.Options; using GraphRag.Net.Domain.Interface; using GraphRag.Net.Utils; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.OpenAI; using Microsoft.SemanticKernel.Connectors.Postgres; using Microsoft.SemanticKernel.Connectors.Sqlite; using Microsoft.SemanticKernel.Memory; using Npgsql; using Newtonsoft.Json; using GraphRag.Net.Domain.Model.Graph; using Polly; using Polly.Retry; using GraphRag.Net.Common.Options; namespace GraphRag.Net.Domain.Service { [ServiceDescription(typeof(ISemanticService), ServiceLifetime.Scoped)] public class SemanticService(Kernel _kernel) : ISemanticService { public async Task CreateGraphAsync(string input) { var retryPolicy = Policy.Handle().RetryAsync(GraphSysOption.RetryCounnt, (ex, count) => { Console.WriteLine($"CreateGraphAsync失败,重试{count}次,异常信息{ex.Message}"); }); var result = await retryPolicy.ExecuteAsync(async () => { OpenAIPromptExecutionSettings settings = new() { Temperature = 0, ResponseFormat = ChatCompletionsResponseFormat.JsonObject }; KernelFunction createFun = _kernel.Plugins.GetFunction("graph", "create"); var args = new KernelArguments(settings) { ["input"] = input, }; var skresult = await _kernel.InvokeAsync(createFun, args); string json = skresult.GetValue()?.Trim() ?? ""; var graph = JsonConvert.DeserializeObject(json); return graph; }); return result; } public async Task GetGraphAnswerAsync(string graph, string input) { KernelFunction createFun = _kernel.Plugins.GetFunction("graph", "search"); var args = new KernelArguments() { ["graph"] = graph, ["input"] = input, }; var skresult = await _kernel.InvokeAsync(createFun, args); string result = skresult.GetValue()?.Trim() ?? ""; return result; } public async IAsyncEnumerable GetGraphAnswerStreamAsync(string graph, string input) { KernelFunction createFun = _kernel.Plugins.GetFunction("graph", "search"); var args = new KernelArguments() { ["graph"] = graph, ["input"] = input, }; var skresult = _kernel.InvokeStreamingAsync(createFun, args); await foreach (var content in skresult) { yield return content; } } public async Task GetGraphCommunityAnswerAsync(string graph, string community, string global, string input) { KernelFunction createFun = _kernel.Plugins.GetFunction("graph", "community_search"); var args = new KernelArguments() { ["graph"] = graph, ["community"] = community, ["global"] = global, ["input"] = input, }; var skresult = await _kernel.InvokeAsync(createFun, args); string result = skresult.GetValue()?.Trim() ?? ""; return result; } public async IAsyncEnumerable GetGraphCommunityAnswerStreamAsync(string graph, string community, string global, string input) { KernelFunction createFun = _kernel.Plugins.GetFunction("graph", "community_search"); var args = new KernelArguments() { ["graph"] = graph, ["community"] = community, ["global"] = global, ["input"] = input, }; var skresult = _kernel.InvokeStreamingAsync(createFun, args); await foreach (var content in skresult) { yield return content; } } public async Task GetRelationship(string node1, string node2) { var retryPolicy = Policy.Handle().RetryAsync(GraphSysOption.RetryCounnt, (ex, count) => { Console.WriteLine($"GetRelationship失败,重试{count}次,异常信息{ex.Message}"); }); var result = await retryPolicy.ExecuteAsync(async () => { OpenAIPromptExecutionSettings settings = new() { Temperature = 0, ResponseFormat = ChatCompletionsResponseFormat.JsonObject }; KernelFunction createFun = _kernel.Plugins.GetFunction("graph", "relationship"); var args = new KernelArguments(settings) { ["node1"] = node1, ["node2"] = node2, }; var skresult = await _kernel.InvokeAsync(createFun, args); string json = skresult.GetValue()?.Trim() ?? ""; var relation = JsonConvert.DeserializeObject(json); return relation; }); return result; } public async Task MergeDesc(string desc1, string desc2) { KernelFunction createFun = _kernel.Plugins.GetFunction("graph", "mergedesc"); var args = new KernelArguments() { ["desc1"] = desc1, ["desc2"] = desc2, }; var skresult = await _kernel.InvokeAsync(createFun, args); string result = skresult.GetValue()?.Trim() ?? ""; return result; } public async Task CommunitySummaries(string nodes) { KernelFunction createFun = _kernel.Plugins.GetFunction("graph", "community_summaries"); var args = new KernelArguments() { ["nodes"] = nodes }; var skresult = await _kernel.InvokeAsync(createFun, args); string result = skresult.GetValue()?.Trim() ?? ""; return result; } public async Task GlobalSummaries(string community) { KernelFunction createFun = _kernel.Plugins.GetFunction("graph", "global_summaries"); var args = new KernelArguments() { ["community"] = community }; var skresult = await _kernel.InvokeAsync(createFun, args); string result = skresult.GetValue()?.Trim() ?? ""; return result; } /// /// 获取SemanticTextMemory /// /// /// public async Task GetTextMemory() { IMemoryStore memoryStore = null; switch (GraphDBConnectionOption.DbType) { case "Sqlite": memoryStore = await SqliteMemoryStore.ConnectAsync(GraphDBConnectionOption.VectorConnection); break; case "PostgreSQL": NpgsqlDataSourceBuilder dataSourceBuilder = new(GraphDBConnectionOption.VectorConnection); dataSourceBuilder.UseVector(); NpgsqlDataSource dataSource = dataSourceBuilder.Build(); memoryStore = new PostgresMemoryStore(dataSource, vectorSize: 1536, schema: "public"); break; } if (memoryStore == null) { throw new InvalidOperationException("GraphDBConnection error failed to initialize memory store."); } var handler = new OpenAIHttpClientHandler(); var httpClient = new HttpClient(handler); httpClient.Timeout = TimeSpan.FromMinutes(10); var embeddingGenerator = new OpenAITextEmbeddingGenerationService(GraphOpenAIOption.EmbeddingModel, GraphOpenAIOption.Key, httpClient: new HttpClient(handler)); SemanticTextMemory textMemory = new(memoryStore, embeddingGenerator); return textMemory; } } } ================================================ FILE: src/GraphRag.Net/Extensions/ServiceCollectionExtensions.cs ================================================ using System.Reflection; using GraphRag.Net; using GraphRag.Net.Options; using GraphRag.Net.Repositories; using GraphRag.Net.Utils; using Microsoft.SemanticKernel; using SqlSugar; namespace Microsoft.Extensions.DependencyInjection { /// /// 容器扩展 /// public static class ServiceCollectionExtensions { /// /// 从程序集中加载类型并添加到容器中 /// /// 容器 /// public static IServiceCollection AddGraphRagNet(this IServiceCollection services, Kernel _kernel = null) { Type attributeType = typeof(ServiceDescriptionAttribute); //var refAssembyNames = Assembly.GetExecutingAssembly().GetReferencedAssemblies(); Assembly assembly = Assembly.Load("GraphRag.Net"); var types = assembly.GetTypes(); foreach (var classType in types) { if (!classType.IsAbstract && classType.IsClass && classType.IsDefined(attributeType, false)) { ServiceDescriptionAttribute serviceAttribute = (classType.GetCustomAttribute(attributeType) as ServiceDescriptionAttribute); switch (serviceAttribute.Lifetime) { case ServiceLifetime.Scoped: services.AddScoped(serviceAttribute.ServiceType, classType); break; case ServiceLifetime.Singleton: services.AddSingleton(serviceAttribute.ServiceType, classType); break; case ServiceLifetime.Transient: services.AddTransient(serviceAttribute.ServiceType, classType); break; } } } CodeFirst(); InitSK(services, _kernel); return services; } /// /// 初始化SK /// /// /// 可以提供自定义Kernel static void InitSK(IServiceCollection services,Kernel _kernel = null) { var handler = new OpenAIHttpClientHandler(); services.AddTransient((serviceProvider) => { if (_kernel == null) { _kernel = Kernel.CreateBuilder() .AddOpenAIChatCompletion( modelId: GraphOpenAIOption.ChatModel, apiKey: GraphOpenAIOption.Key, httpClient: new HttpClient(handler) ) .Build(); } //导入插件 if (!_kernel.Plugins.Any(p => p.Name == "graph")) { var pluginPatth = Path.Combine(RepoFiles.SamplePluginsPath(), "graph"); Console.WriteLine($"pluginPatth:{pluginPatth}"); _kernel.ImportPluginFromPromptDirectory(pluginPatth); } return _kernel; }); } /// /// 初始化DB /// static void CodeFirst() { // 获取仓储服务 var _repository = new Nodes_Repositories(); // 创建数据库(如果不存在) _repository.GetDB().DbMaintenance.CreateDatabase(); // 在所有程序集中查找具有[SugarTable]特性的类 var assembly = Assembly.GetExecutingAssembly(); // 获取该程序集中所有具有SugarTable特性的类型 var entityTypes = assembly.GetTypes() .Where(type => TypeIsEntity(type)); // 为每个找到的类型初始化数据库表 foreach (var type in entityTypes) { _repository.GetDB().CodeFirst.InitTables(type); } } static bool TypeIsEntity(Type type) { // 检查类型是否具有SugarTable特性 return type.GetCustomAttributes(typeof(SugarTable), inherit: false).Length > 0; } } } ================================================ FILE: src/GraphRag.Net/GraphRag.Net.csproj ================================================  net6.0;net7.0;net8.0 latest $(Version) enable enable 许泽宇 GraphRag.Net xuzeyu GraphRag.Net GraphRag for .NET –这是一个参考GraphRag的DotNet简易实现。 基于微软在论文中提到的实现思路,执行过程GraphRAG主要实现了如下功能: Source Documents → Text Chunks:将源文档分割成文本块。 Text Chunks → Element Instances:从每个文本块中提取图节点和边的实例。 Element Instances → Element Summaries:为每个图元素生成摘要。 Element Summaries → Graph Communities:使用社区检测算法将图划分为社区。 Graph Communities → Community Summaries:为每个社区生成摘要。 Community Summaries → Community Answers → Global Answer:使用社区摘要生成局部答案,然后汇总这些局部答案以生成全局答案。 商务需求联系微信xuzeyu91 https://github.com/xuzeyu91/GraphRag.Net AI, Rag, GraphRag Apache-2.0 CA1050,CA1707,CA2007,VSTHRD111,CS1591,RCS1110,CA5394,SKEXP0001,SKEXP0002,SKEXP0003,SKEXP0004,SKEXP0010,SKEXP0011,,SKEXP0012,SKEXP0020,SKEXP0021,SKEXP0022,SKEXP0023,SKEXP0024,SKEXP0025,SKEXP0026,SKEXP0027,SKEXP0028,SKEXP0029,SKEXP0030,SKEXP0031,SKEXP0032,SKEXP0040,SKEXP0041,SKEXP0042,SKEXP0050,SKEXP0051,SKEXP0052,SKEXP0053,SKEXP0054,SKEXP0055,SKEXP0060,SKEXP0061,SKEXP0101,SKEXP0102 True GraphRag.Net.xml ================================================ FILE: src/GraphRag.Net/Repositories/Base/IRepository.cs ================================================  using GraphRag.Net.Model; using SqlSugar; using System.Linq.Expressions; namespace GraphRag.Net.Base { public interface IRepository { SqlSugarScope GetDB(); List GetList(); Task> GetListAsync(); List GetList(Expression> whereExpression); Task> GetListAsync(Expression> whereExpression); int Count(Expression> whereExpression); Task CountAsync(Expression> whereExpression); PageList GetPageList(Expression> whereExpression, PageModel page); Task> GetPageListAsync(Expression> whereExpression, PageModel page); PageList GetPageList(Expression> whereExpression, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc); Task> GetPageListAsync(Expression> whereExpression, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc); PageList GetPageList(List conditionalList, PageModel page); Task> GetPageListAsync(List conditionalList, PageModel page); PageList GetPageList(List conditionalList, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc); Task> GetPageListAsync(List conditionalList, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc); T GetById(dynamic id); Task GetByIdAsync(dynamic id); T GetSingle(Expression> whereExpression); Task GetSingleAsync(Expression> whereExpression); T GetFirst(Expression> whereExpression); Task GetFirstAsync(Expression> whereExpression); bool Insert(T obj); Task InsertAsync(T obj); bool InsertRange(List objs); Task InsertRangeAsync(List objs); int InsertReturnIdentity(T obj); Task InsertReturnIdentityAsync(T obj); long InsertReturnBigIdentity(T obj); Task InsertReturnBigIdentityAsync(T obj); bool DeleteByIds(dynamic[] ids); Task DeleteByIdsAsync(dynamic[] ids); bool Delete(dynamic id); Task DeleteAsync(dynamic id); bool Delete(T obj); Task DeleteAsync(T obj); bool Delete(Expression> whereExpression); Task DeleteAsync(Expression> whereExpression); bool Update(T obj); Task UpdateAsync(T obj); bool UpdateRange(List objs); bool InsertOrUpdate(T obj); Task InsertOrUpdateAsync(T obj); Task UpdateRangeAsync(List objs); bool IsAny(Expression> whereExpression); Task IsAnyAsync(Expression> whereExpression); } } ================================================ FILE: src/GraphRag.Net/Repositories/Base/Repository.cs ================================================ using GraphRag.Net.Model; using SqlSugar; using System.Linq.Expressions; namespace GraphRag.Net.Base { public class Repository : SimpleClient where T : class, new() { public Repository(ISqlSugarClient context = null) : base(context)//注意这里要有默认值等于null { if (context == null) { } } //注意:如果使用Client不能写成静态的,Scope并发更高 public static SqlSugarScope SqlScope = SqlSugarHelper.SqlScope(); public SimpleClient CurrentDb { get { return new SimpleClient(SqlScope); } }//用来处理T表的常用操作 #region 通用方法 public virtual SqlSugarScope GetDB() { return SqlScope; } /// /// 获取所有list /// /// public virtual List GetList() { return CurrentDb.GetList(); } /// /// 获取所有list-异步 /// /// public virtual async Task> GetListAsync() { return await CurrentDb.GetListAsync(); } /// /// 根据lambda查询 /// /// /// public virtual List GetList(Expression> whereExpression) { return CurrentDb.GetList(whereExpression); } /// /// 根据lambda查询-异步 /// /// /// public virtual async Task> GetListAsync(Expression> whereExpression) { return await CurrentDb.GetListAsync(whereExpression); } /// /// 根据lambda表达式获取数量 /// /// /// public virtual int Count(Expression> whereExpression) { return CurrentDb.Count(whereExpression); } /// /// 根据lambda表达式获取数量-异步 /// /// /// public virtual async Task CountAsync(Expression> whereExpression) { return await CurrentDb.CountAsync(whereExpression); } /// /// 获取分页 /// /// /// /// public virtual PageList GetPageList(Expression> whereExpression, PageModel page) { PageList list = new PageList(); list.List = CurrentDb.GetPageList(whereExpression, page); list.PageIndex = page.PageIndex; list.PageSize = page.PageSize; list.TotalCount = page.TotalCount; return list; } /// /// 获取分页-异步 /// /// /// /// public virtual async Task> GetPageListAsync(Expression> whereExpression, PageModel page) { PageList list = new PageList(); list.List = await CurrentDb.GetPageListAsync(whereExpression, page); list.PageIndex = page.PageIndex; list.PageSize = page.PageSize; list.TotalCount = page.TotalCount; return list; } public virtual PageList GetPageList(Expression> whereExpression, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc) { PageList list = new PageList(); list.List = CurrentDb.GetPageList(whereExpression, page, orderByExpression, orderByType); list.PageIndex = page.PageIndex; list.PageSize = page.PageSize; list.TotalCount = page.TotalCount; return list; } public virtual async Task> GetPageListAsync(Expression> whereExpression, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc) { PageList list = new PageList(); list.List = await CurrentDb.GetPageListAsync(whereExpression, page, orderByExpression, orderByType); list.PageIndex = page.PageIndex; list.PageSize = page.PageSize; list.TotalCount = page.TotalCount; return list; } public virtual PageList GetPageList(List conditionalList, PageModel page) { PageList list = new PageList(); list.List = CurrentDb.GetPageList(conditionalList, page); list.PageIndex = page.PageIndex; list.PageSize = page.PageSize; list.TotalCount = page.TotalCount; return list; } public virtual async Task> GetPageListAsync(List conditionalList, PageModel page) { PageList list = new PageList(); list.List = await CurrentDb.GetPageListAsync(conditionalList, page); list.PageIndex = page.PageIndex; list.PageSize = page.PageSize; list.TotalCount = page.TotalCount; return list; } public virtual PageList GetPageList(List conditionalList, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc) { PageList list = new PageList(); list.List = CurrentDb.GetPageList(conditionalList, page, orderByExpression, orderByType); list.PageIndex = page.PageIndex; list.PageSize = page.PageSize; list.TotalCount = page.TotalCount; return list; } public virtual async Task> GetPageListAsync(List conditionalList, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc) { PageList list = new PageList(); list.List = await CurrentDb.GetPageListAsync(conditionalList, page, orderByExpression, orderByType); list.PageIndex = page.PageIndex; list.PageSize = page.PageSize; list.TotalCount = page.TotalCount; return list; } /// /// 根据id获取实体 /// /// /// public virtual T GetById(dynamic id) { return CurrentDb.GetById(id); } /// /// 根据id获取实体-异步 /// /// /// public virtual async Task GetByIdAsync(dynamic id) { return await CurrentDb.GetByIdAsync(id); } /// /// 根据lambda获取单个对象 (注意,需要确保唯一,如果获取到2个会报错,这种场景需要使用GetFirst) /// /// /// public virtual T GetSingle(Expression> whereExpression) { return CurrentDb.GetSingle(whereExpression); //Db.Queryable().First(whereExpression); } /// /// 根据lambda获取单个对象-异步 (注意,需要确保唯一,如果获取到2个会报错,这种场景需要使用GetFirst) /// /// /// public virtual async Task GetSingleAsync(Expression> whereExpression) { return await CurrentDb.GetSingleAsync(whereExpression); //await Db.Queryable().FirstAsync(whereExpression); } /// /// 根据lambda获取单个对象 /// /// /// public virtual T GetFirst(Expression> whereExpression) { return GetDB().Queryable().First(whereExpression); } /// /// 根据lambda获取单个对象 --异步 /// /// /// public virtual async Task GetFirstAsync(Expression> whereExpression) { return await GetDB().Queryable().FirstAsync(whereExpression); } /// /// 实体插入 /// /// /// public virtual bool Insert(T obj) { return CurrentDb.Insert(obj); } /// /// 实体插入-异步 /// /// /// public virtual async Task InsertAsync(T obj) { return await CurrentDb.InsertAsync(obj); } /// /// 批量插入 /// /// /// public virtual bool InsertRange(List objs) { return CurrentDb.InsertRange(objs); } /// /// 批量插入-异步 /// /// /// public virtual async Task InsertRangeAsync(List objs) { return await CurrentDb.InsertRangeAsync(objs); } /// /// 插入返回自增列 /// /// /// public virtual int InsertReturnIdentity(T obj) { return CurrentDb.InsertReturnIdentity(obj); } /// /// 插入返回自增列-异步 /// /// /// public virtual async Task InsertReturnIdentityAsync(T obj) { return await CurrentDb.InsertReturnIdentityAsync(obj); } /// /// 插入返回longid /// /// /// public virtual long InsertReturnBigIdentity(T obj) { return CurrentDb.InsertReturnBigIdentity(obj); } /// /// 插入返回longid-异步 /// /// /// public virtual async Task InsertReturnBigIdentityAsync(T obj) { return await CurrentDb.InsertReturnBigIdentityAsync(obj); } /// /// 批量删除 /// /// /// public virtual bool DeleteByIds(dynamic[] ids) { return CurrentDb.DeleteByIds(ids); } /// /// 批量删除-异步 /// /// /// public virtual async Task DeleteByIdsAsync(dynamic[] ids) { return await CurrentDb.DeleteByIdsAsync(ids); } /// /// 根据主键删除 /// /// /// public virtual bool Delete(dynamic id) { return CurrentDb.DeleteById(id); } /// /// 根据主键删除-异步 /// /// /// public virtual async Task DeleteAsync(dynamic id) { return await CurrentDb.DeleteByIdAsync(id); } /// /// 根据实体删除 /// /// /// public virtual bool Delete(T obj) { return CurrentDb.Delete(obj); } /// /// 根据实体删除-异步 /// /// /// public virtual async Task DeleteAsync(T obj) { return await CurrentDb.DeleteAsync(obj); } /// /// 根据表达式删除 /// /// /// public virtual bool Delete(Expression> whereExpression) { return CurrentDb.Delete(whereExpression); } /// /// 根据表达式删除-异步 /// /// /// public virtual async Task DeleteAsync(Expression> whereExpression) { return await CurrentDb.DeleteAsync(whereExpression); } /// /// 更新 /// /// /// public virtual bool Update(T obj) { return CurrentDb.Update(obj); } /// /// 更新-异步 /// /// /// public virtual async Task UpdateAsync(T obj) { return await CurrentDb.UpdateAsync(obj); } /// /// 批量更新 /// /// /// public virtual bool UpdateRange(List objs) { return CurrentDb.UpdateRange(objs); } /// /// 新增或修改 /// /// /// public virtual bool InsertOrUpdate(T obj) { return CurrentDb.InsertOrUpdate(obj); } /// /// 新增或修改-异步 /// /// /// public virtual async Task InsertOrUpdateAsync(T obj) { return await CurrentDb.InsertOrUpdateAsync(obj); } /// /// 批量更新-异步 /// /// /// public virtual async Task UpdateRangeAsync(List objs) { return await CurrentDb.UpdateRangeAsync(objs); } /// /// 是否包含元素 /// /// /// public virtual bool IsAny(Expression> whereExpression) { return CurrentDb.IsAny(whereExpression); } /// /// 是否包含元素-异步 /// /// /// public virtual async Task IsAnyAsync(Expression> whereExpression) { return await CurrentDb.IsAnyAsync(whereExpression); } #endregion 通用方法 } } ================================================ FILE: src/GraphRag.Net/Repositories/Base/SqlSugarHelper.cs ================================================ using GraphRag.Net.Options; using SqlSugar; using System.Reflection; namespace GraphRag.Net.Base { public class SqlSugarHelper() { /// /// sqlserver连接 /// public static SqlSugarScope SqlScope() { string DBType = GraphDBConnectionOption.DbType; string ConnectionString = GraphDBConnectionOption.DBConnection; var config = new ConnectionConfig() { ConnectionString = ConnectionString, InitKeyType = InitKeyType.Attribute,//从特性读取主键和自增列信息 IsAutoCloseConnection = true, ConfigureExternalServices = new ConfigureExternalServices { //注意: 这儿AOP设置不能少 EntityService = (c, p) => { /***高版C#写法***/ //支持string?和string if (p.IsPrimarykey == false && new NullabilityInfoContext() .Create(c).WriteState is NullabilityState.Nullable) { p.IsNullable = true; } } } }; DbType dbType = (DbType)Enum.Parse(typeof(DbType), DBType); config.DbType = dbType; var scope = new SqlSugarScope(config, Db => { }); return scope; } } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/CommunitieNodes/CommunitieNodes.cs ================================================ using SqlSugar; namespace GraphRag.Net.Repositories { /// /// 社区与节点关系 /// [SugarTable("CommunitieNodes")] [SugarIndex("i_communitienodes_index", nameof(CommunitieNodes.Index), OrderByType.Asc)] [SugarIndex("i_communitienodes_communitieid", nameof(CommunitieNodes.CommunitieId), OrderByType.Asc)] [SugarIndex("i_communitienodes_nodeid", nameof(CommunitieNodes.NodeId), OrderByType.Asc)] public class CommunitieNodes { /// /// 索引信息 /// public string Index { get; set; } /// /// 社区关键节点ID /// public string CommunitieId { get; set; } /// /// 节点ID /// public string NodeId { get; set; } } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/CommunitieNodes/CommunitieNodes_Repositories.cs ================================================ using GraphRag.Net.Base; using Microsoft.Extensions.DependencyInjection; namespace GraphRag.Net.Repositories { [ServiceDescription(typeof(ICommunitieNodes_Repositories), ServiceLifetime.Scoped)] public class CommunitieNodes_Repositories : Repository, ICommunitieNodes_Repositories { } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/CommunitieNodes/ICommunitieNodes_Repositories.cs ================================================ using GraphRag.Net.Base; namespace GraphRag.Net.Repositories { public interface ICommunitieNodes_Repositories : IRepository { } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Communities/Communities.cs ================================================ using SqlSugar; namespace GraphRag.Net.Repositories { /// /// 社区 /// [SugarTable("Communities")] [SugarIndex("i_communities_index", nameof(Communities.Index), OrderByType.Asc)] public class Communities { /// /// 社区ID /// [SugarColumn(IsPrimaryKey = true)] public string CommunitieId { get; set; } /// /// 索引信息 /// public string Index { get; set; } /// /// 社区摘要 /// [SugarColumn(ColumnDataType = "varchar(4000)")] public string Summaries { get; set; } } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Communities/Communities_Repositories.cs ================================================ using GraphRag.Net.Base; using Microsoft.Extensions.DependencyInjection; namespace GraphRag.Net.Repositories { [ServiceDescription(typeof(ICommunities_Repositories), ServiceLifetime.Scoped)] public class Communities_Repositories : Repository, ICommunities_Repositories { } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Communities/ICommunities_Repositories.cs ================================================ using GraphRag.Net.Base; namespace GraphRag.Net.Repositories { public interface ICommunities_Repositories : IRepository { } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Edges/Edges.cs ================================================ using SqlSugar; namespace GraphRag.Net.Repositories { /// /// 边 /// [SugarTable("Edges")] [SugarIndex("i_edges_index", nameof(Edges.Index), OrderByType.Asc)] [SugarIndex("i_edges_source", nameof(Edges.Source), OrderByType.Asc)] [SugarIndex("i_edges_target", nameof(Edges.Target), OrderByType.Asc)] public class Edges { /// /// 主键 /// [SugarColumn(IsPrimaryKey = true)] public string Id { get; set; } /// /// 索引 /// public string Index { get; set; } /// /// 源ID /// public string Source { get; set; } /// /// 目标ID /// public string Target { get; set; } /// /// 关系信息 /// [SugarColumn(ColumnDataType = "varchar(2000)")] public string Relationship { get; set; } } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Edges/Edges_Repositories.cs ================================================ using GraphRag.Net.Base; using Microsoft.Extensions.DependencyInjection; namespace GraphRag.Net.Repositories { [ServiceDescription(typeof(IEdges_Repositories), ServiceLifetime.Scoped)] public class Edges_Repositories : Repository, IEdges_Repositories { } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Edges/IEdges_Repositories.cs ================================================ using GraphRag.Net.Base; namespace GraphRag.Net.Repositories { public interface IEdges_Repositories : IRepository { } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Global/Globals.cs ================================================ using SqlSugar; namespace GraphRag.Net.Repositories { /// /// 社区 /// [SugarTable("Globals")] [SugarIndex("i_global_index", nameof(Globals.Index), OrderByType.Asc)] public class Globals { /// /// 索引信息 /// public string Index { get; set; } /// /// 全局摘要 /// [SugarColumn(ColumnDataType = "varchar(4000)")] public string Summaries { get; set; } } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Global/Globals_Repositories.cs ================================================ using GraphRag.Net.Base; using Microsoft.Extensions.DependencyInjection; namespace GraphRag.Net.Repositories { [ServiceDescription(typeof(IGlobals_Repositories), ServiceLifetime.Scoped)] public class Globals_Repositories : Repository, IGlobals_Repositories { } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Global/IGlobals_Repositories.cs ================================================ using GraphRag.Net.Base; namespace GraphRag.Net.Repositories { public interface IGlobals_Repositories : IRepository { } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Nodes/INodes_Repositories.cs ================================================ using GraphRag.Net.Base; namespace GraphRag.Net.Repositories { public interface INodes_Repositories : IRepository { } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Nodes/Nodes.cs ================================================ using SqlSugar; namespace GraphRag.Net.Repositories { /// /// 节点 /// [SugarTable("Nodes")] [SugarIndex("i_nodes_index", nameof(Nodes.Index), OrderByType.Asc)] public class Nodes { /// /// 主键 /// [SugarColumn(IsPrimaryKey = true)] public string Id { get; set; } /// /// 索引 /// public string Index { get; set; } /// /// 名称 /// public string Name { get; set; } /// /// 类型 /// public string Type { get; set; } /// /// 节点描述 /// [SugarColumn(ColumnDataType = "varchar(2000)")] public string? Desc { get; set; } } } ================================================ FILE: src/GraphRag.Net/Repositories/Graph/Nodes/Nodes_Repositories.cs ================================================ using GraphRag.Net.Base; using Microsoft.Extensions.DependencyInjection; namespace GraphRag.Net.Repositories { [ServiceDescription(typeof(INodes_Repositories), ServiceLifetime.Scoped)] public class Nodes_Repositories : Repository, INodes_Repositories { } } ================================================ FILE: src/GraphRag.Net/Utils/ConvertUtils.cs ================================================ using Newtonsoft.Json; using System.ComponentModel; using System.Reflection; using System.Text.RegularExpressions; using System.Web; namespace GraphRag.Net.Utils { internal static class ConvertUtils { /// /// 判断是否为空,为空返回true /// /// /// public static bool IsNull(this object data) { //如果为null if (data == null) { return true; } //如果为"" if (data.GetType() == typeof(String)) { if (string.IsNullOrEmpty(data.ToString().Trim())) { return true; } } return false; } /// /// 判断是否为空,为空返回true /// /// /// public static bool IsNotNull(this object data) { //如果为null if (data == null) { return false; } //如果为"" if (data.GetType() == typeof(String)) { if (string.IsNullOrEmpty(data.ToString().Trim())) { return false; } } return true; } /// /// 判断是否为空,为空返回true /// /// /// public static bool IsNull(string data) { //如果为null if (data == null) { return true; } //如果为"" if (data.GetType() == typeof(String)) { if (string.IsNullOrEmpty(data.ToString().Trim())) { return true; } } return false; } /// /// 将obj类型转换为string /// /// /// public static string ConvertToString(this object s) { if (s == null) { return ""; } else { return Convert.ToString(s); } } /// /// object 转int32 /// /// /// public static Int32 ConvertToInt32(this object s) { int i = 0; if (s == null) { return 0; } else { int.TryParse(s.ToString(), out i); } return i; } /// /// object 转int32 /// /// /// public static Int64 ConvertToInt64(this object s) { long i = 0; if (s == null) { return 0; } else { long.TryParse(s.ToString(), out i); } return i; } /// /// 将字符串转double /// /// /// public static double ConvertToDouble(this object s) { double i = 0; if (s == null) { return 0; } else { double.TryParse(s.ToString(), out i); } return i; } /// /// 转换为datetime类型 /// /// /// public static DateTime ConvertToDateTime(this string s) { DateTime dt = new DateTime(); if (s == null || s == "") { return DateTime.Now; } DateTime.TryParse(s, out dt); return dt; } /// /// 转换为datetime类型的格式字符串 /// /// 要转换的对象 /// 格式化字符串 /// public static string ConvertToDateTime(this string s, string y) { DateTime dt = new DateTime(); DateTime.TryParse(s, out dt); return dt.ToString(y); } /// /// 将字符串转换成decimal /// /// /// public static decimal ConvertToDecimal(this object s) { decimal d = 0; if (s == null || s == "") { return 0; } Decimal.TryParse(s.ToString(), out d); return d; } /// /// decimal保留2位小数 /// public static decimal DecimalFraction(this decimal num) { return Convert.ToDecimal(num.ToString("f2")); } /// /// 替换html种的特殊字符 /// /// /// public static string ReplaceHtml(this string s) { return s.Replace("<", "<").Replace(">", ">").Replace("&", "&").Replace(""", "\""); } /// /// 流转byte /// /// /// public static byte[] StreamToByte(this Stream stream) { byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); // 设置当前流的位置为流的开始 stream.Seek(0, SeekOrigin.Begin); return bytes; } public static string JsonToMarkDown(this string s) { return $"{Environment.NewLine}```json{Environment.NewLine}{s}{Environment.NewLine}```{Environment.NewLine}"; } /// /// json参数转化querystring参数 /// /// /// public static string ToQueryString(this Dictionary parameters) { var nameValueCollection = HttpUtility.ParseQueryString(string.Empty); foreach (var param in parameters) { nameValueCollection[param.Key] = param.Value; } return nameValueCollection.ToString(); } /// /// 忽略大小写匹配 /// /// /// /// public static bool ComparisonIgnoreCase(this string s, string value) { return s.Equals(value, StringComparison.OrdinalIgnoreCase); } public static string GetDescription(this Enum value) { FieldInfo field = value.GetType().GetField(value.ToString()); if (field != null) { DescriptionAttribute attribute = (DescriptionAttribute)field.GetCustomAttribute(typeof(DescriptionAttribute)); if (attribute != null) { return attribute.Description; } } return value.ToString(); } /// /// 是否为流式请求 /// /// /// public static bool IsStream(this string value) { // 正则表达式忽略空格的情况 string pattern = @"\s*""stream""\s*:\s*true\s*"; // 使用正则表达式匹配 bool contains = Regex.IsMatch(value, pattern); return contains; } /// /// \uxxxx转中文,保留换行符号 /// /// /// public static string Unescape(this string value) { if (value.IsNull()) { return ""; } try { Formatting formatting = Formatting.None; object jsonObj = JsonConvert.DeserializeObject(value); string unescapeValue = JsonConvert.SerializeObject(jsonObj, formatting); return unescapeValue; } catch (Exception ex) { Console.WriteLine(ex.ToString()); return ""; } } } } ================================================ FILE: src/GraphRag.Net/Utils/OpenAIHttpClientHandler.cs ================================================ using GraphRag.Net.Options; using System.Text; using System.Text.RegularExpressions; namespace GraphRag.Net.Utils { internal class OpenAIHttpClientHandler : HttpClientHandler { protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { UriBuilder uriBuilder; Regex regex = new Regex(@"(https?)://([^/:]+)(:\d+)?/(.*)"); Match match = regex.Match(GraphOpenAIOption.EndPoint); var mediaType = request.Content.Headers.ContentType.MediaType; string requestBody = (await request.Content.ReadAsStringAsync()).Unescape(); var uncaseBody = new StringContent(requestBody, Encoding.UTF8, mediaType); request.Content = uncaseBody; if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" && request.Content != null) { //便于调试查看请求prompt Console.WriteLine(requestBody); } if (match.Success) { string xieyi = match.Groups[1].Value; string host = match.Groups[2].Value; string port = match.Groups[3].Value; // 可选的端口号 string route = match.Groups[4].Value; // 如果port不为空,它将包含冒号,所以你可能需要去除它 port = string.IsNullOrEmpty(port) ? port : port.Substring(1); // 拼接host和端口号 var hostnew = string.IsNullOrEmpty(port) ? host : $"{host}:{port}"; switch (request.RequestUri.LocalPath) { case "/v1/chat/completions": //替换代理 uriBuilder = new UriBuilder(request.RequestUri) { // 这里是你要修改的 URL Scheme = $"{xieyi}://{hostnew}/", Host = host, Path = route + "v1/chat/completions", }; if (port.ConvertToInt32() != 0) { uriBuilder.Port = port.ConvertToInt32(); } request.RequestUri = uriBuilder.Uri; break; case "/v1/embeddings": uriBuilder = new UriBuilder(request.RequestUri) { // 这里是你要修改的 URL Scheme = $"{xieyi}://{host}/", Host = host, Path = route + "v1/embeddings", }; if (port.ConvertToInt32() != 0) { uriBuilder.Port = port.ConvertToInt32(); } request.RequestUri = uriBuilder.Uri; break; } } // 接着,调用基类的 SendAsync 方法将你的修改后的请求发出去 HttpResponseMessage response = await base.SendAsync(request, cancellationToken); return response; } } } ================================================ FILE: src/GraphRag.Net/Utils/RepoUtils/AppException.cs ================================================ // Copyright (c) Microsoft. All rights reserved. namespace GraphRag.Net.Utils; internal class AppException : Exception { public AppException() : base() { } public AppException(string message) : base(message) { } public AppException(string message, Exception innerException) : base(message, innerException) { } } ================================================ FILE: src/GraphRag.Net/Utils/RepoUtils/ObjectExtensions.cs ================================================ using System.Text.Json; namespace GraphRag.Net.Utils { internal static class ObjectExtensions { private static readonly JsonSerializerOptions s_jsonOptionsCache = new() { WriteIndented = true }; public static string AsJson(this object obj) { return JsonSerializer.Serialize(obj, s_jsonOptionsCache); } } } ================================================ FILE: src/GraphRag.Net/Utils/RepoUtils/RepoFiles.cs ================================================ // Copyright (c) Microsoft. All rights reserved. using System.Reflection; namespace GraphRag.Net.Utils; internal static class RepoFiles { /// /// Scan the local folders from the repo, looking for "samples/plugins" folder. /// /// The full path to samples/plugins public static string SamplePluginsPath() { string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; string folderName = "graphPlugins"; string FindPluginsDirectory(string startDir, string targetFolder) { string currDir = Path.GetFullPath(startDir); const int maxAttempts = 10; for (int i = 0; i < maxAttempts; i++) { string potentialPath = Path.Combine(currDir, targetFolder); if (Directory.Exists(potentialPath)) { return potentialPath; } currDir = Path.GetFullPath(Path.Combine(currDir, "..")); } return null; // Not found after max attempts. } // Check in the BaseDirectory and its parent directories string path = FindPluginsDirectory(baseDirectory, folderName) ?? FindPluginsDirectory(baseDirectory + Path.DirectorySeparatorChar + folderName, folderName); if (string.IsNullOrEmpty(path)) { throw new AppException("Plugins directory not found. The app needs the plugins from the repo to work."); } return path; } } ================================================ FILE: src/GraphRag.Net.Web/.config/dotnet-tools.json ================================================ { "version": 1, "isRoot": true, "tools": { "dotnet-ef": { "version": "8.0.8", "commands": [ "dotnet-ef" ], "rollForward": false } } } ================================================ FILE: src/GraphRag.Net.Web/App.razor ================================================

Sorry, there's nothing at this address.

================================================ FILE: src/GraphRag.Net.Web/Components/GlobalHeader/RightContent.razor ================================================ @namespace GraphRag.Net.Web.Components @inherits AntDomComponentBase ================================================ FILE: src/GraphRag.Net.Web/Components/GlobalHeader/RightContent.razor.cs ================================================ using AntDesign; using AntDesign.ProLayout; using GraphRag.Net.Web.Models; using Microsoft.AspNetCore.Components; namespace GraphRag.Net.Web.Components { public partial class RightContent { private CurrentUser _currentUser = new CurrentUser(); private NoticeIconData[] _notifications = { }; private NoticeIconData[] _messages = { }; private NoticeIconData[] _events = { }; private int _count = 0; private List> DefaultOptions { get; set; } = new List> { new AutoCompleteDataItem { Label = "umi ui", Value = "umi ui" }, new AutoCompleteDataItem { Label = "Pro Table", Value = "Pro Table" }, new AutoCompleteDataItem { Label = "Pro Layout", Value = "Pro Layout" } }; public AvatarMenuItem[] AvatarMenuItems { get; set; } = new AvatarMenuItem[] { new() { Key = "center", IconType = "user", Option = "个人中心"}, new() { Key = "setting", IconType = "setting", Option = "个人设置"}, new() { IsDivider = true }, new() { Key = "logout", IconType = "logout", Option = "退出登录"} }; [Inject] protected NavigationManager NavigationManager { get; set; } [Inject] protected MessageService MessageService { get; set; } protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); SetClassMap(); } protected void SetClassMap() { ClassMapper .Clear() .Add("right"); } public void HandleSelectUser(MenuItem item) { switch (item.Key) { case "center": NavigationManager.NavigateTo("/account/center"); break; case "setting": NavigationManager.NavigateTo("/account/settings"); break; case "logout": NavigationManager.NavigateTo("/user/login"); break; } } public void HandleSelectLang(MenuItem item) { } public async Task HandleClear(string key) { switch (key) { case "notification": _notifications = new NoticeIconData[] { }; break; case "message": _messages = new NoticeIconData[] { }; break; case "event": _events = new NoticeIconData[] { }; break; } await MessageService.Success($"清空了{key}"); } public async Task HandleViewMore(string key) { await MessageService.Info("Click on view more"); } } } ================================================ FILE: src/GraphRag.Net.Web/Controllers/GraphController.cs ================================================ using GraphRag.Net.Domain.Interface; using GraphRag.Net.Domain.Model.Graph; using Microsoft.AspNetCore.Mvc; namespace GraphRag.Net.Api.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class GraphController(IGraphService _graphService) : ControllerBase { /// /// 获取所有的索引数据 /// /// [HttpGet] public async Task GetAllIndex() { var graphModel = _graphService.GetAllIndex(); return Ok(graphModel); } /// /// 获取所有的图谱数据 /// /// /// [HttpGet] public async Task GetAllGraphs(string index) { if (string.IsNullOrEmpty(index)) { return Ok(new GraphViewModel()); } var graphModel = _graphService.GetAllGraphs(index); return Ok(graphModel); } /// /// 插入文本数据 /// /// /// [HttpPost] public async Task InsertGraphData(InputModel model) { await _graphService.InsertGraphDataAsync(model.Index, model.Input); return Ok(); } /// /// 搜索递归获取节点相关的所有边和节点进行图谱对话 /// /// /// [HttpPost] public async Task SearchGraph(InputModel model) { var result = await _graphService.SearchGraphAsync(model.Index, model.Input); return Ok(result); } /// /// 通过社区算法检索社区节点进行对话 /// /// /// [HttpPost] public async Task SearchGraphCommunity(InputModel model) { var result = await _graphService.SearchGraphCommunityAsync(model.Index, model.Input); return Ok(result); } /// /// 导入txt文档 /// /// /// /// [HttpPost] public async Task ImportTxt(string index,IFormFile file) { var forms = await Request.ReadFormAsync(); using (var stream = new StreamReader(file.OpenReadStream())) { var txt = await stream.ReadToEndAsync(); await _graphService.InsertTextChunkAsync(index,txt); return Ok(); } } /// /// 通过社区检测生成社区和摘要 /// /// /// [HttpGet] public async Task GraphCommunities(string index) { await _graphService.GraphCommunitiesAsync(index); return Ok(); } /// /// 通过社区摘要生成全局摘要 /// /// /// [HttpGet] public async Task GraphGlobal(string index) { await _graphService.GraphGlobalAsync(index); return Ok(); } /// /// 删除图谱数据 /// /// /// [HttpGet] public async Task DeleteGraph(string index) { await _graphService.DeleteGraph(index); return Ok(); } } public class InputModel { public string Index { get; set; } public string Input { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Extensions/DateTimeExtension.cs ================================================ namespace GraphRag.Net.Web { public static class DateTimeExtension { private const int Second = 1; private const int Minute = 60 * Second; private const int Hour = 60 * Minute; private const int Day = 24 * Hour; private const int Month = 30 * Day; // todo: Need to be localized public static string ToFriendlyDisplay(this DateTime dateTime) { var ts = DateTime.Now - dateTime; var delta = ts.TotalSeconds; if (delta < 0) { return "not yet"; } if (delta < 1 * Minute) { return ts.Seconds == 1 ? "1 second ago" : ts.Seconds + " seconds ago"; } if (delta < 2 * Minute) { return "1 minute ago"; } if (delta < 45 * Minute) { return ts.Minutes + "minute"; } if (delta < 90 * Minute) { return "1 hour ago"; } if (delta < 24 * Hour) { return ts.Hours + " hours ago"; } if (delta < 48 * Hour) { return "yesterday"; } if (delta < 30 * Day) { return ts.Days + " days ago"; } if (delta < 12 * Month) { var months = Convert.ToInt32(Math.Floor((double)ts.Days / 30)); return months <= 1 ? "A month ago" : months + " months ago"; } else { var years = Convert.ToInt32(Math.Floor((double)ts.Days / 365)); return years <= 1 ? "a year ago" : years + " years ago"; } } } } ================================================ FILE: src/GraphRag.Net.Web/GraphRag.Net.Web.csproj ================================================ net6.0;net7.0;net8.0 latest enable enable 许泽宇 GraphRag.Net xuzeyu 商务需求联系微信xuzeyu91 True GraphRag.Net.Web.xml CA1050,CA1707,CA2007,VSTHRD111,CS1591,RCS1110,CA5394,SKEXP0001,SKEXP0002,SKEXP0003,SKEXP0004,SKEXP0010,SKEXP0011,,SKEXP0012,SKEXP0020,SKEXP0021,SKEXP0022,SKEXP0023,SKEXP0024,SKEXP0025,SKEXP0026,SKEXP0027,SKEXP0028,SKEXP0029,SKEXP0030,SKEXP0031,SKEXP0032,SKEXP0040,SKEXP0041,SKEXP0042,SKEXP0050,SKEXP0051,SKEXP0052,SKEXP0053,SKEXP0054,SKEXP0055,SKEXP0060,SKEXP0061,SKEXP0101,SKEXP0102,KMEXP01 PreserveNewest graphPlugins\ PreserveNewest true graphPlugins\ PreserveNewest true graphPlugins\ PreserveNewest true graphPlugins\ PreserveNewest true graphPlugins\ PreserveNewest true graphPlugins\ PreserveNewest true graphPlugins\ PreserveNewest true ================================================ FILE: src/GraphRag.Net.Web/GraphRag.Net.Web.xml ================================================ GraphRag.Net.Web 获取所有的索引数据 获取所有的图谱数据 插入文本数据 搜索递归获取节点相关的所有边和节点进行图谱对话 通过社区算法检索社区节点进行对话 导入txt文档 通过社区检测生成社区和摘要 通过社区摘要生成全局摘要 删除图谱数据 ================================================ FILE: src/GraphRag.Net.Web/Layouts/BasicLayout.razor ================================================ @namespace GraphRag.Net.Web @inherits LayoutComponentBase @Body @code { private MenuDataItem[] _menuData = { }; [Inject] public HttpClient HttpClient { get; set; } protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); _menuData = await HttpClient.GetFromJsonAsync("data/menu.json"); } public LinkItem[] Links { get; set; } = { new LinkItem { Key = "GraphRag.Net", Title = "GraphRag.Net", Href = "https://github.com/xuzeyu91/GraphRag.Net", BlankTarget = true, } }; } ================================================ FILE: src/GraphRag.Net.Web/Layouts/UserLayout.razor ================================================ @namespace GraphRag.Net.Web @using OneOf @inherits LayoutComponentBase
GraphRag.Net.Web
@Body
@code { public LinkItem[] Links { get; set; } = { new LinkItem { Key = "GraphRag.Net", Title = "GraphRag.Net", Href = "https://github.com/xuzeyu91/GraphRag.Net", BlankTarget = true, } }; } ================================================ FILE: src/GraphRag.Net.Web/Layouts/UserLayout.razor.css ================================================ /* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */ /* stylelint-disable no-duplicate-selectors */ /* stylelint-disable */ /* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */ .container__b__0 { display: flex; flex-direction: column; height: 100vh; overflow: auto; background: #f0f2f5; } .container__b__0 .lang { width: 100%; height: 40px; line-height: 44px; text-align: right; } .container__b__0 .lang :global(.ant-dropdown-trigger) { margin-right: 24px; } .container__b__0 .content { flex: 1; padding: 32px 0; } .container__b__0 .top { text-align: center; } .container__b__0 .header { height: 44px; line-height: 44px; } .container__b__0 .header a { text-decoration: none; } .container__b__0 .logo { height: 44px; margin-right: 16px; vertical-align: top; } .container__b__0 .title { position: relative; top: 2px; color: rgba(0, 0, 0, 0.85); font-weight: 600; font-size: 33px; font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif; } .container__b__0 .desc { margin-top: 12px; margin-bottom: 40px; color: rgba(0, 0, 0, 0.45); font-size: 14px; } @media (min-width: 768px) { .container__b__0 { background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg'); background-repeat: no-repeat; background-position: center 110px; background-size: 100%; } .container__b__0 .content { padding: 32px 0 24px; } } ================================================ FILE: src/GraphRag.Net.Web/Mock/MockChatCompletion.cs ================================================ using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using System.Runtime.CompilerServices; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Unicode; namespace GraphRag.Net.Web.Mock { public class MockChatCompletion : IChatCompletionService { private readonly Dictionary _attributes = new(); private string _chatId; private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { NumberHandling = JsonNumberHandling.AllowReadingFromString, Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }; public IReadOnlyDictionary Attributes => _attributes; public MockChatCompletion() { } public async Task> GetChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { StringBuilder sb = new(); string result = $"这是一条Mock数据,便于聊天测试,你的消息是:{chatHistory.LastOrDefault().ToString()}"; return [new(AuthorRole.Assistant, result.ToString())]; } public async IAsyncEnumerable GetStreamingChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { StringBuilder sb = new(); string result = $"这是一条Mock数据,便于聊天测试,你的消息是:{chatHistory.LastOrDefault().ToString()}"; foreach (var c in result) { yield return new StreamingChatMessageContent(AuthorRole.Assistant, c.ToString()); } } } } ================================================ FILE: src/GraphRag.Net.Web/Mock/MockTextCompletion.cs ================================================ using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Services; using Microsoft.SemanticKernel.TextGeneration; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Unicode; namespace GraphRag.Net.Web.Mock { public class MockTextCompletion : ITextGenerationService, IAIService { private readonly Dictionary _attributes = new(); private string _chatId; private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { NumberHandling = JsonNumberHandling.AllowReadingFromString, Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }; public IReadOnlyDictionary Attributes => _attributes; public MockTextCompletion() { } public async Task> GetTextContentsAsync(string prompt, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default) { StringBuilder sb = new(); string result = $"这是一条Mock数据,便于聊天测试,你的消息是:{prompt}"; return [new(result.ToString())]; } public async IAsyncEnumerable GetStreamingTextContentsAsync(string prompt, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default) { StringBuilder sb = new(); string result = $"这是一条Mock数据,便于聊天测试,你的消息是:{prompt}"; foreach (var c in result) { var streamingTextContent = new StreamingTextContent(c.ToString(), modelId: "mock"); yield return streamingTextContent; } } } } ================================================ FILE: src/GraphRag.Net.Web/Mock/MockTextEmbeddingGeneratorService.cs ================================================ using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Embeddings; using Microsoft.SemanticKernel.Services; namespace GraphRag.Net.Web.Mock { public sealed class MockTextEmbeddingGeneratorService : ITextEmbeddingGenerationService { private Dictionary AttributesInternal { get; } = []; public IReadOnlyDictionary Attributes => this.AttributesInternal; public MockTextEmbeddingGeneratorService() { } public async Task>> GenerateEmbeddingsAsync( IList data, Kernel? kernel = null, CancellationToken cancellationToken = default) { IList> results = new List>(); float[] array1 = { 1.0f, 2.0f, 3.0f }; float[] array2 = { 4.0f, 5.0f, 6.0f }; float[] array3 = { 7.0f, 8.0f, 9.0f }; // 将数组包装为ReadOnlyMemory并添加到列表中 results.Add(new ReadOnlyMemory(array1)); results.Add(new ReadOnlyMemory(array2)); results.Add(new ReadOnlyMemory(array3)); return results; } public void Dispose() { } } } ================================================ FILE: src/GraphRag.Net.Web/Models/ActivitiesType.cs ================================================ namespace GraphRag.Net.Web.Models { public class ActivitiesType { public string Id { get; set; } public DateTime UpdatedAt { get; set; } public ActivityUser User { get; set; } public ActivityGroup Group { get; set; } public ActivityProject Project { get; set; } public string Template { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/ActivityGroup.cs ================================================ namespace GraphRag.Net.Web.Models { public class ActivityGroup { public string Name { get; set; } public string Link { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/ActivityProject.cs ================================================ namespace GraphRag.Net.Web.Models { public class ActivityProject { public string Name { get; set; } public string Link { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/ActivityUser.cs ================================================ namespace GraphRag.Net.Web.Models { public class ActivityUser { public string Name { get; set; } public string Avatar { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/AdvancedOperation.cs ================================================ namespace GraphRag.Net.Web.Models { public class AdvancedOperation { public string Key { get; set; } public string Type { get; set; } public string Name { get; set; } public string Status { get; set; } public string UpdatedAt { get; set; } public string Memo { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/AdvancedProfileData.cs ================================================ namespace GraphRag.Net.Web.Models { public class AdvancedProfileData { public AdvancedOperation[] AdvancedOperation1 { get; set; } public AdvancedOperation[] AdvancedOperation2 { get; set; } public AdvancedOperation[] AdvancedOperation3 { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/BasicGood.cs ================================================ namespace GraphRag.Net.Web.Models { public class BasicGood { public string Id { get; set; } public string Name { get; set; } public string Barcode { get; set; } public string Price { get; set; } public string Num { get; set; } public string Amount { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/BasicProfileDataType.cs ================================================ namespace GraphRag.Net.Web.Models { public class BasicProfileDataType { public BasicGood[] BasicGoods { get; set; } public BasicProgress[] BasicProgress { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/BasicProgress.cs ================================================ namespace GraphRag.Net.Web.Models { public class BasicProgress { public string Key { get; set; } public string Time { get; set; } public string Rate { get; set; } public string Status { get; set; } public string Operator { get; set; } public string Cost { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/ChartData.cs ================================================ namespace GraphRag.Net.Web.Models { public class ChartData { public ChartDataItem[] VisitData { get; set; } public ChartDataItem[] VisitData2 { get; set; } public ChartDataItem[] SalesData { get; set; } public SearchDataItem[] SearchData { get; set; } public OfflineDataItem[] OfflineData { get; set; } public OfflineChartDataItem[] OfflineChartData { get; set; } public ChartDataItem[] SalesTypeData { get; set; } public ChartDataItem[] SalesTypeDataOnline { get; set; } public ChartDataItem[] SalesTypeDataOffline { get; set; } public RadarDataItem[] RadarData { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/ChartDataItem.cs ================================================ namespace GraphRag.Net.Web.Models { public class ChartDataItem { public string X { get; set; } public int Y { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/ChatMessage.cs ================================================ namespace GraphRag.Net.Web.Models { public class ChatMessage { } } ================================================ FILE: src/GraphRag.Net.Web/Models/CurrentUser.cs ================================================ namespace GraphRag.Net.Web.Models { public class TagType { public string Key { get; set; } public string Label { get; set; } } public class GeographicType { public TagType Province { get; set; } public TagType City { get; set; } } public class CurrentUser { public string Name { get; set; } public string Avatar { get; set; } public string Userid { get; set; } public NoticeType[] Notice { get; set; } = { }; public string Email { get; set; } public string Signature { get; set; } public string Title { get; set; } public string Group { get; set; } public TagType[] Tags { get; set; } = { }; public int NotifyCount { get; set; } public int UnreadCount { get; set; } public string Country { get; set; } public GeographicType Geographic { get; set; } public string Address { get; set; } public string Phone { get; set; } } public class UserLiteItem { public string Avater { get; set; } public string Title { get; set; } public string Description { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/FormItemLayout.cs ================================================ using AntDesign; namespace GraphRag.Net.Web.Models { public class FormItemLayout { public ColLayoutParam LabelCol { get; set; } public ColLayoutParam WrapperCol { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/FormModel.cs ================================================ namespace GraphRag.Net.Web.Models { public class StepFormModel { public string ReceiverAccountType { get; set; } = "ant-design@alipay.com"; public string ReceiverAccount { get; set; } = "test@example.com"; public string ReceiverName { get; set; } = "Alex"; public string PayAccount { get; set; } public string Password { get; set; } = "500"; public string Amount { get; set; } = "12345678"; } public class AdvancedFormModel { public string Name { get; set; } public string Url { get; set; } public string Owner { get; set; } public string Approver { get; set; } public DateTime?[] DateRange { get; set; } public string Type { get; set; } public string Name2 { get; set; } public string Url2 { get; set; } public string Owner2 { get; set; } public string Approver2 { get; set; } public DateTime? DateRange2 { get; set; } public string Type2 { get; set; } } public class BasicFormModel { public string Title { get; set; } public string Client { get; set; } public string Invites { get; set; } public int Disclosure { get; set; } public int Weight { get; set; } public string Standard { get; set; } public string Goal { get; set; } public DateTime?[] DateRange { get; set; } } public class Owner { public string Id { get; set; } public string Name { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/LayoutModel.cs ================================================ using AntDesign; namespace GraphRag.Net.Web.Models { public class LayoutModel { public static FormItemLayout _formItemLayout = new FormItemLayout { LabelCol = new ColLayoutParam { Xs = new EmbeddedProperty { Span = 24 }, Sm = new EmbeddedProperty { Span = 7 }, }, WrapperCol = new ColLayoutParam { Xs = new EmbeddedProperty { Span = 24 }, Sm = new EmbeddedProperty { Span = 12 }, Md = new EmbeddedProperty { Span = 10 }, } }; public static FormItemLayout _submitFormLayout = new FormItemLayout { WrapperCol = new ColLayoutParam { Xs = new EmbeddedProperty { Span = 24, Offset = 0 }, Sm = new EmbeddedProperty { Span = 10, Offset = 7 }, } }; public static ListGridType _listGridType = new ListGridType { Gutter = 16, Xs = 1, Sm = 2, Md = 3, Lg = 3, Xl = 4, Xxl = 4 }; } } ================================================ FILE: src/GraphRag.Net.Web/Models/ListFormModel.cs ================================================ namespace GraphRag.Net.Web.Models { public class ListFormModel { public string Owner { get; set; } = "wzj"; public string ActiveUser { get; set; } public string Satisfaction { get; set; } } public class BasicListFormModel { public string Status { get; set; } = "all"; public string SearchKeyword { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/ListItemDataType.cs ================================================ using GraphRag.Net.Web.Utils; using System.Text.Json.Serialization; namespace GraphRag.Net.Web.Models { public class Member { public string Avatar { get; set; } public string Name { get; set; } public string Id { get; set; } } public class ListItemDataType { public string Id { get; set; } public string Owner { get; set; } public string Title { get; set; } public string Avatar { get; set; } public string Cover { get; set; } public string Status { get; set; } public int Percent { get; set; } public string Logo { get; set; } public string Href { get; set; } public string Body { get; set; } public string SubDescription { get; set; } public string Description { get; set; } public int ActiveUser { get; set; } public int NewUser { get; set; } public int Star { get; set; } public int Like { get; set; } public int Message { get; set; } public string Content { get; set; } public Member[] Members { get; set; } [JsonConverter(typeof(LongToDateTimeConverter))] public DateTime UpdatedAt { get; set; } [JsonConverter(typeof(LongToDateTimeConverter))] public DateTime CreatedAt { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/LoginParamsType.cs ================================================ using System.ComponentModel.DataAnnotations; namespace GraphRag.Net.Web.Models { public class LoginParamsType { [Required] public string UserName { get; set; } [Required] public string Password { get; set; } public string Mobile { get; set; } public string Captcha { get; set; } public string LoginType { get; set; } public bool AutoLogin { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/NoticeItem.cs ================================================ using AntDesign.ProLayout; namespace GraphRag.Net.Web.Models { public class NoticeItem : NoticeIconData { public string Id { get; set; } public string Type { get; set; } public string Status { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/NoticeType.cs ================================================ namespace GraphRag.Net.Web.Models { public class NoticeType { public string Id { get; set; } public string Title { get; set; } public string Logo { get; set; } public string Description { get; set; } public string UpdatedAt { get; set; } public string Member { get; set; } public string Href { get; set; } public string MemberLink { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/OfflineChartDataItem.cs ================================================ namespace GraphRag.Net.Web.Models { public class OfflineChartDataItem { public long X { get; set; } public int Y1 { get; set; } public int Y2 { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/OfflineDataItem.cs ================================================ namespace GraphRag.Net.Web.Models { public class OfflineDataItem { public string Name { get; set; } public float Cvr { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/RadarDataItem.cs ================================================ namespace GraphRag.Net.Web.Models { public class RadarDataItem { public string Name { get; set; } public string Label { get; set; } public int Value { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Models/SearchDataItem.cs ================================================ namespace GraphRag.Net.Web.Models { public class SearchDataItem { public int Index { get; set; } public string Keywod { get; set; } public int Count { get; set; } public int Range { get; set; } public int Status { get; set; } } } ================================================ FILE: src/GraphRag.Net.Web/Pages/Graph/Chat.razor ================================================ @namespace GraphRag.Net.Web.Pages.Graph @page "/Chat" @using GraphRag.Net.Domain.Interface
🔍 搜索图谱内容