Compare commits

...

29 Commits

Author SHA1 Message Date
zyxucp
8d78270007 fix 修复导入文件无法导入的bug 2024-03-20 13:47:57 +08:00
zyxucp
ae6d61ee6d Merge branch 'main' of https://github.com/AIDotNet/AntSK 2024-03-20 09:52:13 +08:00
zyxucp
74a7c94619 fix 修改配置文件目录层级 2024-03-20 09:52:02 +08:00
zeyu xu
bdd34ac786 update docker file 2024-03-19 22:37:28 +08:00
zeyu xu
0e754b4732 fix 注释 2024-03-19 22:09:50 +08:00
zeyu xu
48a9fcfabf fix 修改样式,提取KMOption 2024-03-19 21:53:49 +08:00
zyxucp
7ae2b9ac3b Merge pull request #33 from ElderJames/feat/chat-file
support embeding files in chat
2024-03-19 21:32:21 +08:00
zeyu xu
a6bfe3c69b Merge branch 'main' of github.com:AIDotNet/AntSK 2024-03-19 20:40:24 +08:00
zeyu xu
6b146f4750 忽略本地目录 2024-03-19 20:40:19 +08:00
zeyu xu
1387e716d1 忽略tmp-memory-vectors/ 2024-03-19 20:39:31 +08:00
James Yeung
fadc350047 support embeding files in chat 2024-03-19 16:39:27 +08:00
zyxucp
8af4994a7d Merge pull request #32 from ElderJames/feat/DashScope
Add DashScope support for Kernal Memory
2024-03-19 15:54:05 +08:00
zyxucp
b505b61bfe fix 增加ask提示词 2024-03-19 15:36:01 +08:00
James Yeung
5d086c9383 Add DashScope support for Kernal Memory
Support DashScope
2024-03-19 13:35:45 +08:00
zeyu xu
dd0e367dc8 fix 增加默认端口5000 2024-03-19 00:06:36 +08:00
zyxucp
7110eea912 fix 修改注释错误 2024-03-18 17:26:40 +08:00
zyxucp
154e67ef98 fix 判断无插件不走function call 2024-03-18 17:17:03 +08:00
zyxucp
4a04423373 add swagger token 2024-03-18 16:04:55 +08:00
zyxucp
470ea50ebb update readme.en 2024-03-17 23:53:47 +08:00
zyxucp
2a30f3f221 fix 修改post提示词 2024-03-17 20:07:53 +08:00
zyxucp
315f58fdba fix 修改api插件 2024-03-17 20:04:48 +08:00
zyxucp
f027682175 add post的Function call 2024-03-17 17:23:50 +08:00
zyxucp
2618dffcd6 fix 修复知识库ID错误导致搜索不到的问题 2024-03-17 15:56:59 +08:00
zyxucp
ee42d5870b fix 增加清空导入插件功能 2024-03-17 12:05:32 +08:00
zyxucp
27500b1e08 fix 修改错误命名空间和类 2024-03-17 11:45:24 +08:00
zyxucp
4d71a98724 fix 修改Function Call注释获取不到的问题 2024-03-17 11:30:02 +08:00
zyxucp
b8ba0ab391 Merge pull request #30 from wmchuang/feature/km_disk
Feature/km disk
Thank you very much for your contribution
2024-03-16 21:24:08 +08:00
王闯
b589612913 feat: 解决disk模式下重启 向量文件读取不到的问题 2024-03-16 17:45:45 +08:00
王闯
ec4b440469 feat: 调整知识库搜索 没结果时直接返回 2024-03-16 17:16:29 +08:00
47 changed files with 899 additions and 443 deletions

3
.gitignore vendored
View File

@@ -337,8 +337,9 @@ ASALocalRun/
/AntSK/appsettings.Development.json
/AntSK.db
**/tmp-memory-files/*
**/tmp-memory-vectors/*
/src/AntSK/AntSK.db
/src/AntSK/appsettings.Development.json
/src/AntSK.db
/src/AntSK/llama_models
/src/AntSK/AntSK.xml
/src/AntSK/AntSK.xml

View File

@@ -1,41 +1,42 @@
[简体中文](./README.md) | English
# AntSK
## Based on AI knowledge base/agent created by Net8+AntBlazor+SemanticKernel
## An AI knowledge base/intelligent agent built with .Net 8+AntBlazor+SemanticKernel
## Core functions
## Core Features
- **Semantic Kernel**: It uses advanced natural language processing technology to accurately understand, process and respond to complex semantic queries, and provides users with accurate information retrieval and recommendation services.
- **Semantic Kernel**: Utilizes advanced natural language processing technologies to accurately understand, process, and respond to complex semantic queries, providing users with precise information retrieval and recommendation services.
- **Kernel Memory**: It has the ability to continuously learn and store knowledge points. AntSK has a long-term memory function to accumulate experience and provide a more personalized interactive experience.
- **Kernel Memory**: Capable of continuous learning and knowledge storage, AntSK has a long-term memory function, accumulating experience to offer more personalized interaction experiences.
- **Knowledge base**: Knowledge base documents can be created by importing knowledge base documents (Word, PDF, Excel, Txt, Markdown, Json, PPT) and other forms.
- **Knowledge base**: Import knowledge into the database through documents (Word, PDF, Excel, Txt, Markdown, Json, PPT) and manage knowledge base documents.
- **API plug-in system**: an open API plug-in system that allows third-party developers or service providers to easily integrate their services into AntSK and continuously enhance application functions.
- **GPTs Generation**The platform supports the creation of personalized GPT models, try building your own GPT model.
- **Online search**: AntSK can obtain the latest information in real time to ensure that the information received by users is always the most timely and relevant.
- **API Interface Release**: Internal functions are provided as APIs for developers to integrate AntSK into other applications, enhancing application intelligence.
- **GPTs generation**: This platform supports the creation of personalized GPT models and attempts to build your own GPT models.
- **API Plugin System**: An open API plugin system allows third-party developers or service providers to easily integrate their services into AntSK, continuously enhancing application functions.
- **.Net Plugin System**: An open dll plugin system allows third-party developers or service providers to integrate their business functions into AntSK by generating dlls with the standard format codes, continuously enhancing application functions.
- **API interface publishing**: internal functions are provided externally in the form of API, so that developers can easily translate Xzy AntSK KnowledgeBase is integrated into other applications to enhance application intelligence.
- **Model management**: Adapt and manage different models from different vendors.
- **Internet Search**: AntSK can retrieve the latest information in real-time, ensuring that the information users receive is always timely and relevant.
- **Model management**: Adapts and manages different models from various manufacturers. It also supports offline running of models in 'gguf' format supported by llama.cpp.
- **National Information Creation**: AntSK supports domestic models and databases, and can operate under information creation conditions.
@@ -43,11 +44,11 @@
AntSK is applicable to a variety of business scenarios, such as:
AntSK is suitable for various business scenarios, such as:
- Enterprise level knowledge management system
- Corporate knowledge management systems
- Automatic customer service and chat robot
- Automated customer service and chatbots
- Enterprise Search Engine
@@ -57,7 +58,7 @@ AntSK is applicable to a variety of business scenarios, such as:
- Education and online learning platform
- Other interesting AI Apps
- Other interesting AI applications
@@ -115,25 +116,71 @@ Let's see the effect
## How do I get started?
Login is the default login account and password
Here I am using Postgres as a data and vector store, because the Semantic Kernel and Kernel Memory both support it, though you can switch to others.
Here I use Postgres as data storage and vector storage, because both the Semantic Kernel and Kernel Memory support it. Of course, you can switch to other ones.
The model by default supports local models in 'gguf' format from openai, azure openai, and llama. If you need to use other models, you can integrate them using the one-api.
The model supports openai by default. If you need to use azure openai and need to adjust the dependency injection of SK, you can also use one api for integration.
The login configuration in the configuration file is the default account and password.
The following configuration files need to be configured
The following configuration files are needed:
## Using Docker Compose
Provided pg version appsettings. json and simplified version (Sqlite+disk) Docker Compose. simple. yml
Download Docker Compose.yml from the project root directory, and then place the configuration file appsettings.json and it in a unified directory,
The image of PG has been prepared here. You can modify the default account password in Docker Compose.yml, and your appsettings. json database connection needs to be consistent.
Then you can enter the directory and execute it
A appsettings.json for the pg version and a simplified version (Sqlite+disk) docker-compose.simple.yml are provided.
Download docker-compose.yml from the project root directory, then place the configuration file appsettings.json in the same directory,
The pg image has already been prepared. You can modify the default account and password in the docker-compose.yml, and then your appsettings.json database connection needs to be consistent.
Then you can enter the directory and execute
```
docker compose up - d
```
To start AntSK
to start AntSK.
Some meanings of configuration files
How to mount local models and model download directories in docker
```
# Non-host version, does not use local proxy
version: '3.8'
services:
antsk:
container_name: antsk
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.1
ports:
- 5000:5000
networks:
- antsk
depends_on:
- antskpg
restart: always
environment:
- ASPNETCORE_URLS=http://*:5000
volumes:
- ./appsettings.json:/app/appsettings.json # local configuration file must be placed in the same directory
- D://model:/app/model
networks:
antsk:
```
Using this as an example, the meaning is to mount the local folder D://model from Windows into the container /app/model. If so, your appsettings.json model directory should be configured as
```
model/xxx.gguf
```
Some meanings of the configuration file
// (The rest of the information is omitted as it's unnecessary for the translation example context.)
Solving the missing style issue:
Execute under AntSK/src/AntSK:
```
dotnet clean
dotnet build
dotnet publish "AntSK.csproj"
```
Then go to AntSK/src/AntSK/bin/Release/net8.0/publish
```
dotnet AntSK.dll
```
```
@@ -142,12 +189,6 @@ Some meanings of configuration files
"DbType": "Sqlite",
"ConnectionStrings": "Data Source=AntSK.db;"
},
"OpenAIOption": {
"EndPoint": "http://localhost:5000/llama/",
"Key": "NotNull",
"Model": "gpt4-turbo",
"EmbeddingModel": "text-embedding-ada-002"
},
"KernelMemory": {
"VectorDb": "Disk",
"ConnectionString": "Host=;Port=;Database=antsk;Username=;Password=",
@@ -156,7 +197,8 @@ Some meanings of configuration files
"LLamaSharp": {
"RunType": "GPU",
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf"
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"FileDirectory": "D:\\Code\\AI\\AntBlazor\\model\\"
},
"Login": {
"User": "admin",
@@ -176,10 +218,6 @@ Some meanings of configuration files
DBConnection DbType
//Connection string, corresponding strings need to be used according to different DB types
DBConnection ConnectionStrings
//You can use an online API that conforms to the OpenAI format (domestic models use one API adapter), or you can use AntSK's built-in llama API, with the IP and port being the AntSK startup address
OpenAIOption EndPoint
//Model key, if using a local model, it can default to Notnull. Chinese cannot be used here
OpenAIOption Key
//The type of vector storage supports Postgres Disk Memory, where Postgres requires the configuration of ConnectionString
KernelMemory VectorDb
//The running mode used by the local model is GUP CPU. If using an online API, you can freely use one

View File

@@ -3,7 +3,7 @@ version: '3.8'
services:
antsk:
container_name: antsk
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.1
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.2
ports:
- 5000:5000
networks:

View File

@@ -18,7 +18,7 @@ services:
- ./pg/data:/var/lib/postgresql/data
antsk:
container_name: antsk
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.1
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.2
ports:
- 5000:5000
networks:

View File

@@ -112,6 +112,20 @@
<param name="app"></param>
<param name="_kernel"></param>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.ImportApiFunction(AntSK.Domain.Repositories.Apps,System.Collections.Generic.List{Microsoft.SemanticKernel.KernelFunction})">
<summary>
导入API插件
</summary>
<param name="app"></param>
<param name="functions"></param>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.ImportNativeFunction(AntSK.Domain.Repositories.Apps,System.Collections.Generic.List{Microsoft.SemanticKernel.KernelFunction})">
<summary>
导入原生插件
</summary>
<param name="app"></param>
<param name="functions"></param>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.RegisterPluginsWithKernel(Microsoft.SemanticKernel.Kernel)">
<summary>
注册默认插件
@@ -217,6 +231,11 @@
会话模型ID
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.EmbeddingModelID">
<summary>
Embedding 模型Id
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.Temperature">
<summary>
温度
@@ -724,6 +743,13 @@
<param name="stream"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Utils.ConvertUtils.ToQueryString(System.Collections.Generic.Dictionary{System.String,System.String})">
<summary>
json参数转化querystring参数
</summary>
<param name="parameters"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Utils.RepoFiles.SamplePluginsPath">
<summary>
Scan the local folders from the repo, looking for "samples/plugins" folder.

View File

@@ -13,6 +13,6 @@ namespace AntSK.Domain.Domain.Interface
{
IAsyncEnumerable<StreamingKernelContent> SendChatByAppAsync(Apps app, string questions, string history);
IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, string history, List<RelevantSource> relevantSources = null);
IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, string history, string filePath, List<RelevantSource> relevantSources = null);
}
}
}

View File

@@ -1,11 +1,24 @@
using AntSK.Domain.Domain.Model.Dto;
using AntDesign;
using AntSK.Domain.Domain.Model.Dto;
using AntSK.Domain.Repositories;
using Microsoft.KernelMemory;
namespace AntSK.Domain.Domain.Interface
{
public interface IKMService
{
MemoryServerless GetMemory(Apps app);
MemoryServerless GetMemoryByKMS(string kmsID, SearchClientConfig searchClientConfig = null);
Task<List<KMFile>> GetDocumentByFileID(string kmsid, string fileid);
Task<List<KMFile>> GetDocumentByFileID(string kmsId, string fileId);
Task<List<RelevantSource>> GetRelevantSourceList(string kmsIdListStr, string msg);
List<UploadFileItem> FileList { get; }
bool BeforeUpload(UploadFileItem file);
void OnSingleCompleted(UploadInfo fileinfo);
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Model.Constant
{
public class KmsConstantcs
{
public const string KmsIdTag = "kmsid";
public const string KmsIndex = "kms";
public const string KmsSearchNull="知识库未搜索到相关内容";
}
}

View File

@@ -2,14 +2,13 @@
{
public class KMFile
{
public string DocumentId { get; set; }
public string Text { get; set; }
public string Url { get; set; }
public string? Url { get; set; }
public string LastUpdate { get; set; }
public string Schema { get; set; }
public string File { get; set; }
}
}
}

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AntSK.Domain.Domain.Model.Dto
{
public class RelevantSource
@@ -12,5 +7,10 @@ namespace AntSK.Domain.Domain.Model.Dto
public string Text { get; set; }
public float Relevance { get; set; }
public override string ToString()
{
return $"[file:{SourceName};Relevance:{(Relevance * 100):F2}%]:{Text}";
}
}
}

View File

@@ -1,15 +1,29 @@
namespace AntSK.Domain.Domain.Model.Enum
using System.ComponentModel.DataAnnotations;
namespace AntSK.Domain.Domain.Model.Enum
{
/// <summary>
/// AI类型
/// </summary>
public enum AIType
{
[Display(Name = "Open AI")]
OpenAI = 1,
[Display(Name = "Azure Open AI")]
AzureOpenAI = 2,
[Display(Name = "LLama本地模型")]
LLamaSharp = 3,
[Display(Name = "星火大模型")]
SparkDesk = 4,
Mock = 5,
[Display(Name = "灵积大模型")]
DashScope = 5,
[Display(Name = "模拟输出")]
Mock = 100,
}
/// <summary>

View File

@@ -11,5 +11,13 @@ namespace AntSK.Domain.Domain.Model.Fun
public string Name { get; set; }
public string Description { get; set; }
public FunType FunType { get; set; }
}
public enum FunType
{
System=1,
Import=2
}
}

View File

@@ -10,7 +10,11 @@
/// 发送是true 接收是false
/// </summary>
public bool IsSend { get; set; } = false;
public DateTime CreateTime { get; set; }
public string? FilePath { get; set; }
public string? FileName { get; set; }
}
}
}

View File

@@ -3,16 +3,15 @@ using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Repositories;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AntSK.Domain.Utils;
using Microsoft.KernelMemory;
using Markdig;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Model.Dto;
using AntSK.Domain.Domain.Model.Constant;
using DocumentFormat.OpenXml.Drawing;
using System.Reflection.Metadata;
using Microsoft.KernelMemory;
using System.Collections.Generic;
using Markdig;
namespace AntSK.Domain.Domain.Service
{
@@ -40,7 +39,7 @@ namespace AntSK.Domain.Domain.Service
var _kernel = _kernelService.GetKernelByApp(app);
var temperature = app.Temperature / 100;//存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
if (!string.IsNullOrEmpty(app.ApiFunctionList)|| !string.IsNullOrEmpty(app.NativeFunctionList))//这里还需要加上本地插件的
if (!string.IsNullOrEmpty(app.ApiFunctionList) || !string.IsNullOrEmpty(app.NativeFunctionList))//这里还需要加上本地插件的
{
_kernelService.ImportFunctionsByApp(app, _kernel);
settings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
@@ -53,50 +52,51 @@ namespace AntSK.Domain.Domain.Service
}
}
public async IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, string history, List<RelevantSource> relevantSources = null)
public async IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, string history, string filePath, List<RelevantSource> relevantSources = null)
{
var relevantSourceList = await _kMService.GetRelevantSourceList(app.KmsIdList, questions);
var _kernel = _kernelService.GetKernelByApp(app);
//知识库问答
var filters = new List<MemoryFilter>();
var kmsidList = app.KmsIdList.Split(",");
//只取第一个知识库的配置
var _memory = _kMService.GetMemoryByKMS(kmsidList.FirstOrDefault());
foreach (var kmsid in kmsidList)
if (!string.IsNullOrWhiteSpace(filePath))
{
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
}
var xlresult = await _memory.SearchAsync(questions, index: "kms", filters: filters);
string dataMsg = "";
if (xlresult != null)
{
foreach (var item in xlresult.Results)
{
foreach (var part in item.Partitions)
{
dataMsg += $"[file:{item.SourceName};Relevance:{(part.Relevance * 100).ToString("F2")}%]:{part.Text}{Environment.NewLine}";
var memory = _kMService.GetMemory(app);
var fileId = Guid.NewGuid().ToString();
var result = await memory.ImportDocumentAsync(new Microsoft.KernelMemory.Document(fileId).AddFile(filePath)
.AddTag(KmsConstantcs.KmsIdTag, app.Id)
, index: KmsConstantcs.KmsIndex);
if (relevantSources.IsNotNull())
{
string sourceName = item.SourceName;
var fileDetail = _kmsDetails_Repositories.GetFirst(p => p.FileGuidName == item.SourceName);
if (fileDetail.IsNotNull())
{
sourceName = fileDetail.FileName;
}
relevantSources.Add(new RelevantSource() { SourceName = sourceName, Text = Markdown.ToHtml(part.Text), Relevance = part.Relevance });
}
}
var filters = new MemoryFilter().ByTag(KmsConstantcs.KmsIdTag, app.Id);
var searchResult = await memory.SearchAsync(questions, index: KmsConstantcs.KmsIndex, filters: [filters]);
relevantSourceList.AddRange(searchResult.Results.SelectMany(item => item.Partitions.Select(part => new RelevantSource()
{
SourceName = item.SourceName,
Text = Markdown.ToHtml(part.Text),
Relevance = part.Relevance
})));
}
var dataMsg = new StringBuilder();
if (relevantSourceList.Any())
{
relevantSources?.AddRange(relevantSourceList);
foreach (var item in relevantSources)
{
dataMsg.AppendLine(item.ToString());
}
KernelFunction jsonFun = _kernel.Plugins.GetFunction("KMSPlugin", "Ask");
var chatResult = _kernel.InvokeStreamingAsync(function: jsonFun,
arguments: new KernelArguments() { ["doc"] = dataMsg, ["history"] = history, ["questions"] = questions });
MessageInfo info = null;
await foreach (var content in chatResult)
{
yield return content;
}
}
else
{
yield return new StreamingTextContent(KmsConstantcs.KmsSearchNull);
}
}
}
}

View File

@@ -5,6 +5,9 @@ using System.Xml;
using AntSK.Domain.Common;
using AntSK.Domain.Utils;
using System.Text.RegularExpressions;
using Microsoft.SemanticKernel;
using HtmlAgilityPack;
using System.Collections.Generic;
namespace AntSK.Domain.Domain.Service
{
@@ -47,7 +50,7 @@ namespace AntSK.Domain.Domain.Service
markedMethods.AddRange(type.GetMethods().Where(m =>
{
DescriptionAttribute da = (DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
return da != null && da.Description == "AntSK";
return da != null && da.Description.Contains( "AntSK");
}));
}
}
@@ -62,7 +65,7 @@ namespace AntSK.Domain.Domain.Service
markedMethods.AddRange(type.GetMethods().Where(m =>
{
DescriptionAttribute da = (DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
return da != null && da.Description == "AntSK";
return da != null && da.Description.Contains("AntSK");
}));
}
}
@@ -76,19 +79,10 @@ namespace AntSK.Domain.Domain.Service
key = Regex.Replace(key, pattern, "_");
_methodCache.TryAdd(key, method);
var xmlCommentHelper = new XmlCommentHelper();
xmlCommentHelper.LoadAll();
var description = xmlCommentHelper.GetMethodComment(method);
var dict = xmlCommentHelper.GetParameterComments(method);
var parameters = method.GetParameters().Select(x => (x.Name, x.ParameterType, dict[x.Name])).ToArray();
var returnType = xmlCommentHelper.GetMethodReturnComment(method);
if (string.IsNullOrEmpty(description))
{
description = "导入插件";
}
var description= method.GetCustomAttribute<DescriptionAttribute>().Description.ConvertToString().Replace("AntSK:","");
var returnType = method.ReturnParameter.GetCustomAttribute<DescriptionAttribute>().Description.ConvertToString();
var parameters = method.GetParameters().Select(x => (x.Name, x.ParameterType,x.GetCustomAttribute<DescriptionAttribute>()?.Description)).ToArray();
// 假设 _methodInfos 是一个已经定义好的字典,用来保存方法的相关信息
_methodInfos.TryAdd(key, (description, (method.ReflectedType, returnType), parameters));
}
}
@@ -123,6 +117,6 @@ namespace AntSK.Domain.Domain.Service
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
}
}
}

View File

@@ -1,6 +1,7 @@
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Model.Constant;
using AntSK.Domain.Repositories;
using Microsoft.KernelMemory;
@@ -29,8 +30,8 @@ namespace AntSK.Domain.Domain.Service
{
var importResult = _memory.ImportDocumentAsync(new Document(fileid)
.AddFile(req.FilePath)
.AddTag("kmsid", req.KmsId)
, index: "kms").Result;
.AddTag(KmsConstantcs.KmsIdTag, req.KmsId)
, index: KmsConstantcs.KmsIndex).Result;
//查询文档数量
var docTextList = _kMService.GetDocumentByFileID(km.Id, fileid).Result;
string fileGuidName = Path.GetFileName(req.FilePath);
@@ -43,8 +44,8 @@ namespace AntSK.Domain.Domain.Service
case ImportType.Url:
{
//导入url
var importResult = _memory.ImportWebPageAsync(req.Url, fileid, new TagCollection() { { "kmsid", req.KmsId } }
, index: "kms").Result;
var importResult = _memory.ImportWebPageAsync(req.Url, fileid, new TagCollection() { { KmsConstantcs.KmsIdTag, req.KmsId } }
, index: KmsConstantcs.KmsIndex).Result;
//查询文档数量
var docTextList = _kMService.GetDocumentByFileID(km.Id, fileid).Result;
req.KmsDetail.Url = req.Url;
@@ -54,8 +55,8 @@ namespace AntSK.Domain.Domain.Service
case ImportType.Text:
//导入文本
{
var importResult = _memory.ImportTextAsync(req.Text, fileid, new TagCollection() { { "kmsid", req.KmsId } }
, index: "kms").Result;
var importResult = _memory.ImportTextAsync(req.Text, fileid, new TagCollection() { { KmsConstantcs.KmsIdTag, req.KmsId } }
, index: KmsConstantcs.KmsIndex).Result;
//查询文档数量
var docTextList = _kMService.GetDocumentByFileID(km.Id, fileid).Result;
req.KmsDetail.Url = req.Url;

View File

@@ -1,29 +1,75 @@
using AntSK.Domain.Common.DependencyInjection;
using AntDesign;
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model.Constant;
using AntSK.Domain.Domain.Model.Dto;
using AntSK.Domain.Domain.Other;
using AntSK.Domain.Options;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using DocumentFormat.OpenXml.Drawing.Diagrams;
using LLama;
using LLamaSharp.KernelMemory;
using Markdig;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Configuration;
using Microsoft.KernelMemory;
using Microsoft.KernelMemory.Configuration;
using Microsoft.KernelMemory.ContentStorage.DevTools;
using Microsoft.KernelMemory.FileSystem.DevTools;
using Microsoft.KernelMemory.MemoryStorage;
using Microsoft.KernelMemory.MemoryStorage.DevTools;
using Microsoft.KernelMemory.Postgres;
namespace AntSK.Domain.Domain.Service
{
[ServiceDescription(typeof(IKMService), ServiceLifetime.Scoped)]
public class KMService(
IConfiguration _config,
IKmss_Repositories _kmss_Repositories,
IAIModels_Repositories _aIModels_Repositories
) : IKMService
IKmss_Repositories _kmss_Repositories,
IAIModels_Repositories _aIModels_Repositories,
IMessageService? _message
) : IKMService
{
private MemoryServerless _memory;
private List<UploadFileItem> _fileList = [];
public List<UploadFileItem> FileList => _fileList;
public MemoryServerless GetMemory(Apps app)
{
var chatModel = _aIModels_Repositories.GetFirst(p => p.Id == app.ChatModelID);
var embedModel = _aIModels_Repositories.GetFirst(p => p.Id == app.EmbeddingModelID);
var chatHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(chatModel.EndPoint);
var embeddingHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(embedModel.EndPoint);
var searchClientConfig = new SearchClientConfig
{
MaxAskPromptSize = 2048,
MaxMatchesCount = 3,
AnswerTokens = 1000,
EmptyAnswer = KmsConstantcs.KmsSearchNull
};
var memoryBuild = new KernelMemoryBuilder()
.WithSearchClientConfig(searchClientConfig)
//.WithCustomTextPartitioningOptions(new TextPartitioningOptions
//{
// MaxTokensPerLine = app.MaxTokensPerLine,
// MaxTokensPerParagraph = kms.MaxTokensPerParagraph,
// OverlappingTokens = kms.OverlappingTokens
//})
;
//加载会话模型
WithTextGenerationByAIType(memoryBuild, chatModel, chatHttpClient);
//加载向量模型
WithTextEmbeddingGenerationByAIType(memoryBuild, embedModel, embeddingHttpClient);
//加载向量库
WithMemoryDbByVectorDB(memoryBuild);
_memory = memoryBuild.Build<MemoryServerless>();
return _memory;
}
public MemoryServerless GetMemoryByKMS(string kmsID, SearchClientConfig searchClientConfig = null)
{
//if (_memory.IsNull())
@@ -45,24 +91,24 @@ namespace AntSK.Domain.Domain.Service
MaxAskPromptSize = 2048,
MaxMatchesCount = 3,
AnswerTokens = 1000,
EmptyAnswer = "知识库未搜索到相关内容"
EmptyAnswer = KmsConstantcs.KmsSearchNull
};
}
var memoryBuild = new KernelMemoryBuilder()
.WithSearchClientConfig(searchClientConfig)
.WithCustomTextPartitioningOptions(new TextPartitioningOptions
{
MaxTokensPerLine = kms.MaxTokensPerLine,
MaxTokensPerParagraph = kms.MaxTokensPerParagraph,
OverlappingTokens = kms.OverlappingTokens
});
//加载huihu 模型
.WithSearchClientConfig(searchClientConfig)
.WithCustomTextPartitioningOptions(new TextPartitioningOptions
{
MaxTokensPerLine = kms.MaxTokensPerLine,
MaxTokensPerParagraph = kms.MaxTokensPerParagraph,
OverlappingTokens = kms.OverlappingTokens
});
//加载会话模型
WithTextGenerationByAIType(memoryBuild, chatModel, chatHttpClient);
//加载向量模型
WithTextEmbeddingGenerationByAIType(memoryBuild, embedModel, embeddingHttpClient);
//加载向量库
WithMemoryDbByVectorDB(memoryBuild, _config);
WithMemoryDbByVectorDB(memoryBuild);
_memory = memoryBuild.Build<MemoryServerless>();
return _memory;
@@ -70,10 +116,10 @@ namespace AntSK.Domain.Domain.Service
//else {
// return _memory;
//}
}
private void WithTextEmbeddingGenerationByAIType(IKernelMemoryBuilder memory, AIModels embedModel, HttpClient embeddingHttpClient)
private void WithTextEmbeddingGenerationByAIType(IKernelMemoryBuilder memory, AIModels embedModel,
HttpClient embeddingHttpClient)
{
switch (embedModel.AIType)
{
@@ -84,6 +130,7 @@ namespace AntSK.Domain.Domain.Service
EmbeddingModel = embedModel.ModelName
}, null, false, embeddingHttpClient);
break;
case Model.Enum.AIType.AzureOpenAI:
memory.WithAzureOpenAITextEmbeddingGeneration(new AzureOpenAIConfig()
{
@@ -94,15 +141,20 @@ namespace AntSK.Domain.Domain.Service
APIType = AzureOpenAIConfig.APITypes.EmbeddingGeneration,
});
break;
case Model.Enum.AIType.LLamaSharp:
var (weights, parameters) = LLamaConfig.GetLLamaConfig(embedModel.ModelName);
var embedder = new LLamaEmbedder(weights, parameters);
memory.WithLLamaSharpTextEmbeddingGeneration(new LLamaSharpTextEmbeddingGenerator(embedder));
break;
case Model.Enum.AIType.DashScope:
memory.WithDashScopeDefaults(embedModel.ModelKey);
break;
}
}
private void WithTextGenerationByAIType(IKernelMemoryBuilder memory, AIModels chatModel, HttpClient chatHttpClient)
private void WithTextGenerationByAIType(IKernelMemoryBuilder memory, AIModels chatModel,
HttpClient chatHttpClient)
{
switch (chatModel.AIType)
{
@@ -113,6 +165,7 @@ namespace AntSK.Domain.Domain.Service
TextModel = chatModel.ModelName
}, null, chatHttpClient);
break;
case Model.Enum.AIType.AzureOpenAI:
memory.WithAzureOpenAITextGeneration(new AzureOpenAIConfig()
{
@@ -123,20 +176,27 @@ namespace AntSK.Domain.Domain.Service
APIType = AzureOpenAIConfig.APITypes.TextCompletion,
});
break;
case Model.Enum.AIType.LLamaSharp:
var (weights, parameters) = LLamaConfig.GetLLamaConfig(chatModel.ModelName);
var context = weights.CreateContext(parameters);
var executor = new StatelessExecutor(weights, parameters);
memory.WithLLamaSharpTextGeneration(new LlamaSharpTextGenerator(weights, context, executor));
break;
case Model.Enum.AIType.DashScope:
memory.WithDashScopeTextGeneration(new Cnblogs.KernelMemory.AI.DashScope.DashScopeConfig
{
ApiKey = chatModel.ModelKey,
});
break;
}
}
private void WithMemoryDbByVectorDB(IKernelMemoryBuilder memory, IConfiguration _config)
private void WithMemoryDbByVectorDB(IKernelMemoryBuilder memory)
{
string VectorDb = _config["KernelMemory:VectorDb"].ConvertToString();
string ConnectionString = _config["KernelMemory:ConnectionString"].ConvertToString();
string TableNamePrefix = _config["KernelMemory:TableNamePrefix"].ConvertToString();
string VectorDb = KernelMemoryOption.VectorDb.ConvertToString();
string ConnectionString = KernelMemoryOption.ConnectionString.ConvertToString();
string TableNamePrefix = KernelMemoryOption.TableNamePrefix.ConvertToString();
switch (VectorDb)
{
case "Postgres":
@@ -146,14 +206,16 @@ namespace AntSK.Domain.Domain.Service
TableNamePrefix = TableNamePrefix
});
break;
case "Disk":
memory.WithSimpleFileStorage(new SimpleFileStorageConfig()
memory.WithSimpleVectorDb(new SimpleVectorDbConfig()
{
StorageType = FileSystemTypes.Disk
StorageType = FileSystemTypes.Disk,
});
break;
case "Memory":
memory.WithSimpleFileStorage(new SimpleFileStorageConfig()
memory.WithSimpleVectorDb(new SimpleVectorDbConfig()
{
StorageType = FileSystemTypes.Volatile
});
@@ -161,36 +223,103 @@ namespace AntSK.Domain.Domain.Service
}
}
public async Task<List<KMFile>> GetDocumentByFileID(string kmsid, string fileid)
public async Task<List<KMFile>> GetDocumentByFileID(string kmsId, string fileId)
{
var _memory = GetMemoryByKMS(kmsid);
var memories = await _memory.ListIndexesAsync();
var memoryDbs = _memory.Orchestrator.GetMemoryDbs();
List<KMFile> docTextList = new List<KMFile>();
var memory = GetMemoryByKMS(kmsId);
var memories = await memory.ListIndexesAsync();
var memoryDbs = memory.Orchestrator.GetMemoryDbs();
var docTextList = new List<KMFile>();
foreach (var memoryIndex in memories)
{
foreach (var memoryDb in memoryDbs)
{
var items = await memoryDb.GetListAsync(memoryIndex.Name, new List<MemoryFilter>() { new MemoryFilter().ByDocument(fileid) }, 100, true).ToListAsync();
foreach (var item in items)
var items = await memoryDb.GetListAsync(memoryIndex.Name, new List<MemoryFilter>() { new MemoryFilter().ByDocument(fileId) }, 100, true).ToListAsync();
docTextList.AddRange(items.Select(item => new KMFile()
{
KMFile file = new KMFile()
{
Text = item.Payload.FirstOrDefault(p => p.Key == "text").Value.ConvertToString(),
Url = item.Payload.FirstOrDefault(p => p.Key == "url").Value.ConvertToString(),
LastUpdate = item.Payload.FirstOrDefault(p => p.Key == "last_update").Value.ConvertToString(),
Schema = item.Payload.FirstOrDefault(p => p.Key == "schema").Value.ConvertToString(),
File = item.Payload.FirstOrDefault(p => p.Key == "file").Value.ConvertToString(),
};
docTextList.Add(file);
}
DocumentId = item.GetDocumentId(),
Text = item.GetPartitionText(),
Url = item.GetWebPageUrl(),
LastUpdate = item.GetLastUpdate().LocalDateTime.ToString("yyyy-MM-dd HH:mm:ss"),
File = item.GetFileName()
}));
}
}
return docTextList;
}
public async Task<List<RelevantSource>> GetRelevantSourceList(string kmsIdListStr, string msg)
{
var result = new List<RelevantSource>();
if (string.IsNullOrWhiteSpace(kmsIdListStr))
return result;
var kmsIdList = kmsIdListStr.Split(",");
if (!kmsIdList.Any()) return result;
var memory = GetMemoryByKMS(kmsIdList.FirstOrDefault()!);
var filters = kmsIdList.Select(kmsId => new MemoryFilter().ByTag(KmsConstantcs.KmsIdTag, kmsId)).ToList();
var searchResult = await memory.SearchAsync(msg, index: KmsConstantcs.KmsIndex, filters: filters);
if (!searchResult.NoResult)
{
foreach (var item in searchResult.Results)
{
result.AddRange(item.Partitions.Select(part => new RelevantSource()
{
SourceName = item.SourceName,
Text = Markdown.ToHtml(part.Text),
Relevance = part.Relevance
}));
}
}
return result;
}
public bool BeforeUpload(UploadFileItem file)
{
List<string> types = new List<string>() {
"text/plain",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/pdf",
"application/json",
"text/x-markdown",
"text/markdown"
};
string[] exceptExts = [".md", ".pdf"];
var validTypes = types.Contains(file.Type) || exceptExts.Contains(file.Ext);
if (!validTypes && file.Ext != ".md")
{
_message.Error("文件格式错误,请重新选择!");
}
var IsLt500K = file.Size < 1024 * 1024 * 100;
if (!IsLt500K)
{
_message.Error("文件需不大于100MB!");
}
return validTypes && IsLt500K;
}
public void OnSingleCompleted(UploadInfo fileinfo)
{
if (fileinfo.File.State == UploadState.Success)
{
//文件列表
_fileList.Add(new UploadFileItem()
{
FileName = fileinfo.File.FileName,
Url = fileinfo.File.Url = fileinfo.File.Response
});
}
}
}
}
}

View File

@@ -15,6 +15,8 @@ using System;
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
using AntSK.LLM.Mock;
using AntSK.Domain.Domain.Model.Enum;
using System.Reflection;
using DocumentFormat.OpenXml.Drawing;
namespace AntSK.Domain.Domain.Service
{
@@ -95,6 +97,11 @@ namespace AntSK.Domain.Domain.Service
var options = new SparkDeskOptions { AppId = chatModel.EndPoint, ApiSecret = chatModel.ModelKey, ApiKey = chatModel.ModelName, ModelVersion = Sdcb.SparkDesk.ModelVersion.V3_5 };
builder.Services.AddKeyedSingleton<ITextGenerationService>("spark-desk", new SparkDeskTextCompletion(options, app.Id));
break;
case Model.Enum.AIType.DashScope:
builder.Services.AddDashScopeChatCompletion(chatModel.ModelKey, chatModel.ModelName);
break;
case Model.Enum.AIType.Mock:
builder.Services.AddKeyedSingleton<ITextGenerationService>("mock", new MockTextCompletion());
break;
@@ -113,9 +120,23 @@ namespace AntSK.Domain.Domain.Service
{
return;
}
List<KernelFunction> apiFunctions = new List<KernelFunction>();
List<KernelFunction> functions = new List<KernelFunction>();
//API插件
ImportApiFunction(app, functions);
//本地函数插件
ImportNativeFunction(app, functions);
_kernel.ImportPluginFromFunctions("AntSkFunctions", functions);
}
/// <summary>
/// 导入API插件
/// </summary>
/// <param name="app"></param>
/// <param name="functions"></param>
private void ImportApiFunction(Apps app, List<KernelFunction> functions)
{
if (!string.IsNullOrWhiteSpace(app.ApiFunctionList))
{
//开启自动插件调用
@@ -124,14 +145,24 @@ namespace AntSK.Domain.Domain.Service
foreach (var api in apiList)
{
var returnType = new KernelReturnParameterMetadata() { Description = api.OutputPrompt };
switch (api.Method)
{
case HttpMethodType.Get:
apiFunctions.Add(_kernel.CreateFunctionFromMethod((string msg) =>
var getParametes = new List<KernelParameterMetadata>() {
new KernelParameterMetadata("jsonbody"){
Name="json参数字符串",
ParameterType=typeof(string),
Description=$"需要根据背景文档:{Environment.NewLine}{api.InputPrompt} {Environment.NewLine}提取出对应的json格式字符串参考如下格式:{Environment.NewLine}{api.Query}"
}
};
functions.Add(_kernel.CreateFunctionFromMethod((string jsonbody) =>
{
try
{
Console.WriteLine(msg);
//将json 转换为query参数
var queryString = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonbody);
RestClient client = new RestClient();
RestRequest request = new RestRequest(api.Url, Method.Get);
foreach (var header in api.Header.ConvertToString().Split("\n"))
@@ -143,13 +174,9 @@ namespace AntSK.Domain.Domain.Service
}
}
//这里应该还要处理一次参数提取,等后面再迭代
foreach (var query in api.Query.ConvertToString().Split("\n"))
foreach (var q in queryString)
{
var queryArray = query.Split("=");
if (queryArray.Length == 2)
{
request.AddQueryParameter(queryArray[0], queryArray[1]);
}
request.AddQueryParameter(q.Key, q.Value);
}
var result = client.Execute(request);
return result.Content;
@@ -158,15 +185,22 @@ namespace AntSK.Domain.Domain.Service
{
return "调用失败:" + ex.Message;
}
}, api.Name, $"{api.Describe}"));
}, api.Name, api.Describe, getParametes, returnType));
break;
case HttpMethodType.Post:
apiFunctions.Add(_kernel.CreateFunctionFromMethod((string msg) =>
//处理json body
var postParametes = new List<KernelParameterMetadata>() {
new KernelParameterMetadata("jsonbody"){
Name="json参数字符串",
ParameterType=typeof(string),
Description=$"需要根据背景文档:{Environment.NewLine}{api.InputPrompt} {Environment.NewLine}提取出对应的json格式字符串参考如下格式:{Environment.NewLine}{api.JsonBody}"
}
};
functions.Add(_kernel.CreateFunctionFromMethod((string jsonBody) =>
{
try
{
Console.WriteLine(msg);
Console.WriteLine(jsonBody);
RestClient client = new RestClient();
RestRequest request = new RestRequest(api.Url, Method.Post);
foreach (var header in api.Header.ConvertToString().Split("\n"))
@@ -178,7 +212,7 @@ namespace AntSK.Domain.Domain.Service
}
}
//这里应该还要处理一次参数提取,等后面再迭代
request.AddJsonBody(api.JsonBody.ConvertToString());
request.AddJsonBody(jsonBody.ConvertToString());
var result = client.Execute(request);
return result.Content;
}
@@ -186,19 +220,27 @@ namespace AntSK.Domain.Domain.Service
{
return "调用失败:" + ex.Message;
}
}, api.Name, $"{api.Describe}"));
}, api.Name, api.Describe, postParametes, returnType));
break;
}
}
}
//本地函数插件
}
/// <summary>
/// 导入原生插件
/// </summary>
/// <param name="app"></param>
/// <param name="functions"></param>
private void ImportNativeFunction(Apps app, List<KernelFunction> functions)
{
if (!string.IsNullOrWhiteSpace(app.NativeFunctionList))//需要添加判断应用是否开启了本地函数插件
{
var nativeIdList = app.NativeFunctionList.Split(",");
_functionService.SearchMarkedMethods();
using var scope= _serviceProvider.CreateScope();
using var scope = _serviceProvider.CreateScope();
foreach (var func in _functionService.Functions)
{
@@ -208,11 +250,10 @@ namespace AntSK.Domain.Domain.Service
var parameters = methodInfo.Parameters.Select(x => new KernelParameterMetadata(x.ParameterName) { ParameterType = x.ParameterType, Description = x.Description });
var returnType = new KernelReturnParameterMetadata() { ParameterType = methodInfo.ReturnType.ParameterType, Description = methodInfo.ReturnType.Description };
var target = ActivatorUtilities.CreateInstance(scope.ServiceProvider, func.Value.DeclaringType);
apiFunctions.Add(_kernel.CreateFunctionFromMethod(func.Value, target, func.Key, methodInfo.Description, parameters, returnType));
functions.Add(_kernel.CreateFunctionFromMethod(func.Value, target, func.Key, methodInfo.Description, parameters, returnType));
}
}
}
_kernel.ImportPluginFromFunctions("AntSkFunctions", apiFunctions);
}
/// <summary>
@@ -223,7 +264,7 @@ namespace AntSK.Domain.Domain.Service
{
kernel.ImportPluginFromObject(new ConversationSummaryPlugin(), "ConversationSummaryPlugin");
//kernel.ImportPluginFromObject(new TimePlugin(), "TimePlugin");
kernel.ImportPluginFromPromptDirectory(Path.Combine(RepoFiles.SamplePluginsPath(), "KMSPlugin"));
kernel.ImportPluginFromPromptDirectory(System.IO.Path.Combine(RepoFiles.SamplePluginsPath(), "KMSPlugin"));
}
/// <summary>

View File

@@ -20,6 +20,7 @@ namespace AntSK.Domain.Repositories
/// </summary>
[Required]
public string Describe { get; set; }
/// <summary>
/// 图标
/// </summary>
@@ -38,6 +39,11 @@ namespace AntSK.Domain.Repositories
[Required]
public string? ChatModelID { get; set; }
/// <summary>
/// Embedding 模型Id
/// </summary>
public string? EmbeddingModelID { get; set; }
/// <summary>
/// 温度
/// </summary>
@@ -61,14 +67,14 @@ namespace AntSK.Domain.Repositories
[SugarColumn(ColumnDataType = "varchar(1000)")]
public string? NativeFunctionList { get; set; }
/// <summary>
/// 知识库ID列表
/// </summary>
public string? KmsIdList { get; set; }
/// <summary>
/// API调用秘钥
/// </summary>
public string? SecretKey { get; set; }
}
}
}

View File

@@ -1,4 +1,6 @@
namespace AntSK.Domain.Utils
using System.Web;
namespace AntSK.Domain.Utils
{
public static class ConvertUtils
{
@@ -231,5 +233,22 @@
{
return $"{Environment.NewLine}```json{Environment.NewLine}{s}{Environment.NewLine}```{Environment.NewLine}";
}
/// <summary>
/// json参数转化querystring参数
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
public static string ToQueryString(this Dictionary<string, string> parameters)
{
var nameValueCollection = HttpUtility.ParseQueryString(string.Empty);
foreach (var param in parameters)
{
nameValueCollection[param.Key] = param.Value;
}
return nameValueCollection.ToString();
}
}
}

View File

@@ -5,5 +5,4 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -7,15 +7,13 @@ namespace AntSK.Test
/// </summary>
public class TestFunctionImport
{
/// <summary>
/// 获取名称
/// </summary>
/// <returns>返回名称</returns>
[Description("AntSK")]
public string GetName()
//导入的Function Call插件请安此格式书写函数描述需要以AntSK:开头,返回值描述需要以return:Description,参数描述需要使用Description
[Description("AntSK:获取产品详情")]
[return: Description("返回信息详情")]
public string GetInfo([Description("产品名称")] string Name)
{
return $"""
我的名字是AntSK,
我的名字是{Name},
我的作者是许泽宇
我是一个AI 知识库/智能体项目
""";

View File

@@ -23,6 +23,9 @@
</ItemGroup>
<ItemGroup>
<None Update="plugins\KMSPlugin\Ask1\skprompt.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="plugins\KMSPlugin\Ask\skprompt.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@@ -9,26 +9,29 @@ using Microsoft.AspNetCore.Mvc;
namespace AntSK.Controllers
{
/// <summary>
///
/// KMSController
/// </summary>
/// <param name="_taskBroker"></param>
[Route("api/[controller]/[action]")]
[ApiController]
public class KMSController : ControllerBase
{
private readonly IKmsDetails_Repositories _kmsDetails_Repositories;
private readonly IKMService _iKMService;
private readonly IKmsDetails_Repositories _kmsDetailsRepositories;
private readonly BackgroundTaskBroker<ImportKMSTaskReq> _taskBroker;
public KMSController(
IKmsDetails_Repositories kmsDetails_Repositories,
IKMService iKMService,
IKmsDetails_Repositories kmsDetailsRepositories,
BackgroundTaskBroker<ImportKMSTaskReq> taskBroker
)
)
{
_kmsDetails_Repositories = kmsDetails_Repositories;
_iKMService = iKMService;
_kmsDetailsRepositories = kmsDetailsRepositories;
_taskBroker = taskBroker;
}
/// <summary>
/// 导入任务
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> ImportKMSTask(ImportKMSTaskDTO model)
{
@@ -43,11 +46,11 @@ namespace AntSK.Controllers
Type = model.ImportType.ToString().ToLower()
};
_kmsDetails_Repositories.Insert(detail);
await _kmsDetailsRepositories.InsertAsync(detail);
req.KmsDetail = detail;
_taskBroker.QueueWorkItem(req);
Console.WriteLine("api/kms/ImportKMSTask 结束");
return Ok();
}
}
}
}

View File

@@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Mvc;
namespace AntSK.Controllers
{
/// <summary>
/// 对外接口
/// </summary>
@@ -18,10 +17,10 @@ namespace AntSK.Controllers
/// <returns></returns>
[HttpPost]
[Route("api/v1/chat/completions")]
public async Task chat(OpenAIModel model)
public async Task Chat(OpenAIModel model)
{
string sk = HttpContext.Request.Headers["Authorization"].ConvertToString();
await _openApiService.Chat(model, sk, HttpContext);
}
}
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace AntSK.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class TestFunctionCallController : ControllerBase
{
[HttpPost]
public IActionResult GetInfo(InfoDto dto)
{
Console.Write(JsonConvert.SerializeObject(dto));
return Ok($"你的姓名是:{dto.name},年龄是:{dto.age},性别是男,爱好编程");
}
}
public class InfoDto
{
public string name { get; set; }
public string age { get; set; }
}
}

View File

@@ -38,6 +38,14 @@
</Select>
<Button Type="@ButtonType.Link" OnClick="NavigateModelList">去创建</Button>
</FormItem>
<FormItem Label="Embedding模型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Select DataSource="@_embedignList"
@bind-Value="@context.EmbeddingModelID"
ValueProperty="c=>c.Id"
LabelProperty="c=>'【'+c.AIType.ToString()+'】'+c.ModelDescription">
</Select>
<Button Type="@ButtonType.Link" OnClick="NavigateModelList">去创建</Button>
</FormItem>
@if (@context.Type == AppType.chat.ToString())
{

View File

@@ -44,15 +44,17 @@ namespace AntSK.Pages.AppPage
public Dictionary<string, string> _funList = new Dictionary<string, string>();
private List<AIModels> _chatList { get; set; }
private List<AIModels> _chatList;
private List<AIModels> _embedignList;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_kmsList = _kmss_Repositories.GetList();
_apiList = _apis_Repositories.GetList();
var models=_aimodels_Repositories.GetList();
_chatList = models.Where(p => p.AIModelType == AIModelType.Chat).ToList();
_embedignList = models.Where(p => p.AIModelType == AIModelType.Embedding).ToList();
_chatList = _aimodels_Repositories.GetList(p => p.AIModelType == AIModelType.Chat);
_functionService.SearchMarkedMethods();
foreach (var func in _functionService.Functions)
{

View File

@@ -58,7 +58,7 @@ namespace AntSK.Pages.AppPage
private void GetDesc()
{
_desc = @$"为了方便其他应用对接接口符合openai规范省略了温度TopP等参数。{Environment.NewLine}BaseUrl:{Environment.NewLine}{_openApiUrl} {Environment.NewLine}headers:{Environment.NewLine}Authorization: ""{_appModel.SecretKey}"" {Environment.NewLine}Body: {Environment.NewLine}{JsonConvert.SerializeObject(new OpenAIModel() { messages = new List<OpenAIMessage>() { new OpenAIMessage() { role = "user", content = "" } } }, Formatting.Indented)}";
_desc = @$"为了方便其他应用对接接口符合openai规范省略了温度TopP等参数。{Environment.NewLine}BaseUrl:{Environment.NewLine}{_openApiUrl} {Environment.NewLine}headers:{Environment.NewLine}Authorization:Bearer ""{_appModel.SecretKey}"" {Environment.NewLine}Body: {Environment.NewLine}{JsonConvert.SerializeObject(new OpenAIModel() { messages = new List<OpenAIMessage>() { new OpenAIMessage() { role = "user", content = "" } } }, Formatting.Indented)}";
}
private void GetScript()

View File

@@ -23,7 +23,7 @@
<a href="@( NavigationManager.BaseUri + "openchat/" + AppId)" target="_blank">分享使用</a>
</TitleTemplate>
<Body>
<div id="scrollDiv" style="height: calc(75vh - 170px); overflow-y: auto; overflow-x: hidden;">
<div id="scrollDiv" style="height: calc(75vh - 190px); overflow-y: auto; overflow-x: hidden;">
<GridRow Gutter="(8, 8)" Style="margin:0">
<Virtualize Items="@(MessageList.OrderBy(o => o.CreateTime).ToList())" Context="item">
@if (item.IsSend)
@@ -32,7 +32,17 @@
<GridCol Span="23">
<div class="chat-bubble sent">
<Popover Title="@item.CreateTime.ToString()">
@(item.Context)
<Unbound>
<Flex Vertical RefBack="context">
@if (item.FileName != null)
{
<p class="message-file">
<Upload DefaultFileList="[new(){ FileName= item.FileName }]" />
</p>
}
<p>@(item.Context)</p>
</Flex>
</Unbound>
</Popover>
</div>
<Icon Style="float:right;margin-top:10px;" Type="copy" Theme="outline" OnClick="async () =>await OnCopyAsync(item)" />
@@ -58,13 +68,27 @@
</Virtualize>
</GridRow>
</div>
<div style="height: 10px;"></div>
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding">
<Suffix>
<Button Icon="clear" Type="@(ButtonType.Link)" OnClick="@(async () => await OnClearAsync())" Disabled="@Sendding"></Button>
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())" Disabled="@Sendding"></Button>
</Suffix>
</AntDesign.Input>
<Flex Vertical>
@if (fileList.Count > 0)
{
<Upload DefaultFileList="fileList" OnRemove="HandleFileRemove" />
}
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding"></AntDesign.Input>
</Flex>
<Flex Justify="end">
<Upload Action="@("api/File/UploadFile")"
Name="file"
Accept="*/*"
ShowUploadList="false"
BeforeUpload="_kMService.BeforeUpload"
OnSingleCompleted="OnSingleCompleted">
<Button Icon="@IconType.Outline.Upload" Type="@(ButtonType.Link)" />
</Upload>
<Button Icon="clear" Type="@(ButtonType.Link)" OnClick="@(async () => await OnClearAsync())" Disabled="@Sendding"></Button>
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())" Disabled="@Sendding"></Button>
</Flex>
</Body>
</Card>
</Spin>
@@ -79,61 +103,61 @@
</Extra>
<Body>
<AntList Bordered DataSource="@_relevantSources" Style="padding:10px;">
<ChildContent Context="item">
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
<Body>
@((MarkupString)(@item.Text))
</Body>
</ChildContent>
</AntList>
</Body>
</Card>
</GridCol>
</GridRow>
<ChildContent Context="item">
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
<Body>
@((MarkupString)(@item.Text))
</Body>
</ChildContent>
</AntList>
</Body>
</Card>
</GridCol>
</GridRow>
<style>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
justify-content: center;
align-items: flex-start;
height: 100vh;
}
body {
font-family: Arial, sans-serif;
margin: 0;
justify-content: center;
align-items: flex-start;
height: 100vh;
}
.chat-container {
width: 350px;
border: 1px solid #ccc;
border-radius: 5px;
overflow: hidden;
display: flex;
flex-direction: column;
background-color: #fff;
padding-bottom: 15px;
}
.chat-container {
width: 350px;
border: 1px solid #ccc;
border-radius: 5px;
overflow: hidden;
display: flex;
flex-direction: column;
background-color: #fff;
padding-bottom: 15px;
}
.chat-bubble {
padding: 10px;
margin: 10px;
margin-bottom: 0;
border-radius: 5px;
max-width: 70%;
position: relative;
}
.chat-bubble {
padding: 10px;
margin: 10px;
margin-bottom: 0;
border-radius: 5px;
max-width: 70%;
position: relative;
}
.received {
background-color: #f0f0f0;
align-self: flex-start;
float: left;
}
.received {
background-color: #f0f0f0;
align-self: flex-start;
float: left;
}
.sent {
background-color: #daf8cb;
align-self: flex-end;
float: right;
position: relative;
}
</style>
@code {
.sent {
background-color: #daf8cb;
align-self: flex-end;
float: right;
position: relative;
}
</style>
@code {
}

View File

@@ -4,12 +4,7 @@ using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Microsoft.KernelMemory;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using RestSharp;
using System.Text;
using AntSK.Domain.Utils;
using Markdig;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Model.Dto;
@@ -18,31 +13,20 @@ namespace AntSK.Pages.ChatPage
{
public partial class Chat
{
[Parameter]
public string AppId { get; set; }
[Inject]
protected MessageService? Message { get; set; }
[Inject]
protected IApps_Repositories _apps_Repositories { get; set; }
[Inject]
protected IApis_Repositories _apis_Repositories { get; set; }
[Inject]
protected IKmss_Repositories _kmss_Repositories { get; set; }
[Inject]
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
[Inject] IJSRuntime _JSRuntime { get; set; }
[Parameter] public string AppId { get; set; }
[Inject] protected MessageService? Message { get; set; }
[Inject] protected IApps_Repositories _apps_Repositories { get; set; }
[Inject] protected IApis_Repositories _apis_Repositories { get; set; }
[Inject] protected IKmss_Repositories _kmss_Repositories { get; set; }
[Inject] protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
[Inject] private IJSRuntime _JSRuntime { get; set; }
[Inject]
protected IKernelService _kernelService { get; set; }
[Inject]
protected IKMService _kMService { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
[Inject]
IChatService _chatService { get; set; }
[Inject] protected IKernelService _kernelService { get; set; }
[Inject] protected IKMService _kMService { get; set; }
[Inject] private IConfirmService _confirmService { get; set; }
[Inject] private IChatService _chatService { get; set; }
[Inject]
private ILogger<Chat> Logger { get; set; }
[Inject] private ILogger<Chat> Logger { get; set; }
protected bool _loading = false;
protected List<MessageInfo> MessageList = [];
@@ -50,14 +34,20 @@ namespace AntSK.Pages.ChatPage
protected string _json = "";
protected bool Sendding = false;
List<RelevantSource> _relevantSources = new List<RelevantSource>();
private List<RelevantSource> _relevantSources = new List<RelevantSource>();
protected List<Apps> _list = new List<Apps>();
private List<UploadFileItem> fileList = [];
private Upload _uploadRef;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_list = _apps_Repositories.GetList();
}
protected async Task OnSendAsync()
{
try
@@ -74,33 +64,40 @@ namespace AntSK.Pages.ChatPage
return;
}
var filePath = fileList.FirstOrDefault()?.Url;
var fileName = fileList.FirstOrDefault()?.FileName;
MessageList.Add(new MessageInfo()
{
ID = Guid.NewGuid().ToString(),
Context = _messageInput,
CreateTime = DateTime.Now,
IsSend = true
IsSend = true,
FilePath = filePath,
FileName = fileName
});
var prompt = _messageInput;
_messageInput = "";
fileList.Clear();
Sendding = true;
await SendAsync(_messageInput);
_messageInput = "";
await SendAsync(prompt, filePath);
Sendding = false;
}
catch (System.Exception ex)
{
Sendding = false;
Logger.LogError( ex,"对话异常");
_ = Message.Error("异常:"+ex.Message, 2);
Logger.LogError(ex, "对话异常");
_ = Message.Error("异常:" + ex.Message, 2);
}
}
protected async Task OnCopyAsync(MessageInfo item)
{
await Task.Run(() =>
{
_messageInput = item.Context;
});
await Task.Run(() => { _messageInput = item.Context; });
}
protected async Task OnClearAsync()
@@ -121,7 +118,8 @@ namespace AntSK.Pages.ChatPage
_ = Message.Info("没有会话记录");
}
}
protected async Task<bool> SendAsync(string questions)
protected async Task<bool> SendAsync(string questions, string? filePath)
{
string msg = "";
//处理多轮会话
@@ -133,13 +131,14 @@ namespace AntSK.Pages.ChatPage
switch (app.Type)
{
case "chat":
case "chat" when filePath == null:
//普通会话
await SendChat(questions, msg, app);
break;
case "kms":
default:
//知识库问答
await SendKms(questions, msg, app);
await SendKms(questions, msg, filePath, app);
break;
}
@@ -151,14 +150,13 @@ namespace AntSK.Pages.ChatPage
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="filePath"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task SendKms(string questions, string msg, Apps app)
private async Task SendKms(string questions, string msg, string filePath, Apps app)
{
MessageInfo info = null;
var chatResult = _chatService.SendKmsByAppAsync(app, questions, msg, _relevantSources);
var chatResult = _chatService.SendKmsByAppAsync(app, questions, msg, filePath, _relevantSources);
await foreach (var content in chatResult)
{
if (info == null)
@@ -176,15 +174,14 @@ namespace AntSK.Pages.ChatPage
info.HtmlAnswers += content.ConvertToString();
await Task.Delay(50);
}
await InvokeAsync(StateHasChanged);
}
//全部处理完后再处理一次Markdown
await MarkDown(info);
}
/// <summary>
/// 发送普通对话
/// </summary>
@@ -194,7 +191,7 @@ namespace AntSK.Pages.ChatPage
/// <returns></returns>
private async Task SendChat(string questions, string history, Apps app)
{
MessageInfo info =null;
MessageInfo info = null;
var chatResult = _chatService.SendChatByAppAsync(app, questions, history);
await foreach (var content in chatResult)
{
@@ -213,25 +210,27 @@ namespace AntSK.Pages.ChatPage
info.HtmlAnswers += content.ConvertToString();
await Task.Delay(50);
}
await InvokeAsync(StateHasChanged);
}
//全部处理完后再处理一次Markdown
await MarkDown(info);
}
private async Task MarkDown(MessageInfo info)
{
if (info.IsNotNull())
{
// info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
info!.HtmlAnswers = Markdown.ToHtml(info.HtmlAnswers);
}
await InvokeAsync(StateHasChanged);
await _JSRuntime.InvokeVoidAsync("Prism.highlightAll");
await _JSRuntime.ScrollToBottomAsync("scrollDiv");
}
/// <summary>
/// 历史会话的会话总结
/// </summary>
@@ -254,6 +253,7 @@ namespace AntSK.Pages.ChatPage
history.Append($"assistant:{item.Context}{Environment.NewLine}");
}
}
if (MessageList.Count > 10)
{
//历史会话大于10条进行总结
@@ -262,7 +262,8 @@ namespace AntSK.Pages.ChatPage
}
else
{
var msg = $"history{Environment.NewLine}{history.ToString()}{Environment.NewLine}{Environment.NewLine}";
var msg =
$"history{Environment.NewLine}{history.ToString()}{Environment.NewLine}{Environment.NewLine}";
return msg;
}
}
@@ -272,7 +273,23 @@ namespace AntSK.Pages.ChatPage
}
}
}
private void OnSingleCompleted(UploadInfo fileInfo)
{
fileList.Add(new()
{
FileName = fileInfo.File.FileName,
Url = fileInfo.File.Url = fileInfo.File.Response,
Ext = fileInfo.File.Ext,
State = UploadState.Success,
});
_kMService.OnSingleCompleted(fileInfo);
}
}
private async Task<bool> HandleFileRemove(UploadFileItem file)
{
fileList.RemoveAll(x => x.FileName == file.FileName);
await Task.Yield();
return true;
}
}
}

View File

@@ -152,7 +152,7 @@ namespace AntSK.Pages.ChatPage
private async Task SendKms(string questions, string msg, Apps app)
{
MessageInfo info = null;
var chatResult=_chatService.SendKmsByAppAsync(app, questions, msg);
var chatResult=_chatService.SendKmsByAppAsync(app, questions, "" ,msg);
await foreach (var content in chatResult)
{
if (info == null)

View File

@@ -139,7 +139,7 @@
<Text>
2、支持国产信创数据库
</Text>
<br
<br>
<Text>
3、支持信创环境运行
</Text>

View File

@@ -141,7 +141,7 @@
Drag
Multiple
Accept="*/*"
BeforeUpload="BeforeUpload"
BeforeUpload="iKMService.BeforeUpload"
OnSingleCompleted="OnSingleCompleted">
<p class="ant-upload-drag-icon">
<Icon Type="inbox" />

View File

@@ -21,16 +21,16 @@ namespace AntSK.Pages.KmsPage
private readonly KmsDetails _model = new KmsDetails();
bool _urlVisible = false;
bool _urlConfirmLoading = false;
private bool _urlVisible = false;
private bool _urlConfirmLoading = false;
bool _fileVisible = false;
bool _fileConfirmLoading = false;
private bool _fileVisible = false;
private bool _fileConfirmLoading = false;
bool _textVisible = false;
bool _textConfirmLoading = false;
private bool _textVisible = false;
private bool _textConfirmLoading = false;
List<FileInfoModel> fileList = new List<FileInfoModel>();
private List<FileInfoModel> fileList = new List<FileInfoModel>();
private Form<UrlModel> _urlForm;
private UrlModel urlModel = new UrlModel();
@@ -50,6 +50,7 @@ namespace AntSK.Pages.KmsPage
[Inject]
protected IConfirmService _confirmService { get; set; }
[Inject]
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
@@ -57,16 +58,19 @@ namespace AntSK.Pages.KmsPage
protected IKmss_Repositories _kmss_Repositories { get; set; }
private MemoryServerless _memory { get; set; }
[Inject]
protected IKMService iKMService { get; set; }
[Inject]
protected MessageService? _message { get; set; }
[Inject]
protected BackgroundTaskBroker<ImportKMSTaskReq> _taskBroker { get; set; }
[Inject]
protected IHttpService _httpService { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
@@ -81,6 +85,7 @@ namespace AntSK.Pages.KmsPage
{
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
}
/// <summary>
/// 根据文档ID获取文档
/// </summary>
@@ -88,11 +93,13 @@ namespace AntSK.Pages.KmsPage
/// <returns></returns>
#region Url
public class UrlModel
{
[Required]
public string Url { get; set; }
}
private async Task UrlHandleOk(MouseEventArgs e)
{
try
@@ -106,22 +113,25 @@ namespace AntSK.Pages.KmsPage
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
_urlVisible = false;
urlModel.Url = "";
_ = _message.Info("加入队列,进入后台处理中!", 2);
_ = _message.Info("加入队列,进入后台处理中!", 2);
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
private void UrlHandleCancel(MouseEventArgs e)
{
_urlVisible = false;
}
private void UrlShowModal()
{
_urlVisible = true;
}
#endregion
#endregion Url
#region Text
@@ -130,6 +140,7 @@ namespace AntSK.Pages.KmsPage
[Required]
public string Text { get; set; }
}
private async Task TextHandleOk(MouseEventArgs e)
{
try
@@ -144,26 +155,27 @@ namespace AntSK.Pages.KmsPage
_textVisible = false;
textModel.Text = "";
_ = _message.Info("加入队列,进入后台处理中!", 2);
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
private void TextHandleCancel(MouseEventArgs e)
{
_textVisible = false;
}
private void TextShowModal()
{
_textVisible = true;
}
#endregion
#endregion Text
#region File
private async Task FileHandleOk(MouseEventArgs e)
{
try
@@ -177,7 +189,7 @@ namespace AntSK.Pages.KmsPage
FilePath = item.FilePath,
FileName = item.FileName
});
}
}
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
//上传文档
_fileVisible = false;
@@ -189,43 +201,17 @@ namespace AntSK.Pages.KmsPage
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
private void FileHandleCancel(MouseEventArgs e)
{
_fileVisible = false;
}
private void FileShowModal()
{
_fileVisible = true;
}
bool BeforeUpload(UploadFileItem file)
{
List<string> types = new List<string>() {
"text/plain",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/pdf",
"application/json",
"text/x-markdown",
"text/markdown"
};
var IsType = types.Contains(file.Type);
if (!IsType && file.Ext != ".md")
{
_message.Error("文件格式错误,请重新选择!");
}
var IsLt500K = file.Size < 1024 * 1024 * 100;
if (!IsLt500K)
{
_message.Error("文件需不大于100MB!");
}
return IsType && IsLt500K;
}
private void OnSingleCompleted(UploadInfo fileinfo)
{
if (fileinfo.File.State == UploadState.Success)
@@ -271,6 +257,6 @@ namespace AntSK.Pages.KmsPage
}
}
#endregion
#endregion File
}
}
}

View File

@@ -14,7 +14,7 @@
Style="margin-top: 8px;"
OnFinish="HandleSubmit">
<FormItem Label="注意" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<h1>API插件需要使用FunctionCall能力建议使用GPT4国产模型可能不太行</h1>
<h3>API插件需要使用Function Call能力建议使用GPT4国产模型可能不太行</h3>
</FormItem>
<FormItem Label="Api名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入Api名称" @bind-Value="@context.Name" />
@@ -35,7 +35,7 @@
@if (context.Method == HttpMethodType.Get)
{
<FormItem Label="Query" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<TextArea Placeholder="一个一行,以等号分割。例如:name=a" @bind-Value="@context.Query" MinRows="5" />
<TextArea Placeholder="把query构造成json格式便于大模型识别" @bind-Value="@context.Query" MinRows="5" />
</FormItem>
}
else if (context.Method == HttpMethodType.Post)

View File

@@ -1,4 +1,4 @@
@namespace AntSK.Pages.ApiPage
@namespace AntSK.Pages.FunPage
@using AntSK.Domain.Repositories
@using AntSK.Domain.Domain.Model.Enum
@using AntSK.Domain.Domain.Model.Fun
@@ -29,6 +29,9 @@
<Button Type="dashed" class="newButton" @onclick="AddFun">
<Icon Type="plus" Theme="outline" /> 创建函数
</Button>
<Button Type="dashed" class="newButton" @onclick="ClearFun">
<Icon Type="clear" Theme="outline" /> 清空导入函数
</Button>
}
else
{

View File

@@ -1,14 +1,16 @@
using AntDesign;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model.Fun;
using AntSK.Domain.Domain.Service;
using AntSK.Domain.Repositories;
using AntSK.Models;
using DocumentFormat.OpenXml.Office2010.Excel;
using HtmlAgilityPack;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.SemanticKernel;
namespace AntSK.Pages.ApiPage
namespace AntSK.Pages.FunPage
{
public partial class FunList
{
@@ -26,6 +28,7 @@ namespace AntSK.Pages.ApiPage
[Inject]
protected MessageService? _message { get; set; }
bool _fileVisible = false;
bool _fileConfirmLoading = false;
List<FileInfoModel> fileList = new List<FileInfoModel>();
@@ -71,6 +74,17 @@ namespace AntSK.Pages.ApiPage
private async Task AddFun() {
_fileVisible = true;
}
private async Task ClearFun()
{
var content = "清空自定义函数将会删除全部导入函数并且需要程序重启后下次生效如不是DLL冲突等原因不建议清空是否要清空";
var title = "清空自定义函数";
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
await _funs_Repositories.DeleteAsync(p=>true);
await InitData("");
}
}
private async Task FileHandleOk(MouseEventArgs e)

View File

@@ -72,6 +72,15 @@
<InputPassword @bind-Value="@context.ModelKey" Placeholder="APISecret" Size="@InputSize.Large" />
</FormItem>
}
@if (context.AIType == AIType.DashScope)
{
<FormItem Label="API KEY" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入API KEY" @bind-Value="@context.ModelKey" />
</FormItem>
<FormItem Label="模型名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型名称" @bind-Value="@context.ModelName" />
</FormItem>
}
@if (context.AIType == AIType.LLamaSharp)
{
<FormItem Label="模型路径" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
@@ -79,7 +88,6 @@
<AutoComplete Options="_modelFiles" Placeholder="请输入模型路径" @bind-Value="@context.ModelName" />
<Button OnClick="()=>_downloadModalVisible=true">从Haggingface下载</Button>
</InputGroup>
</FormItem>
}
@if (context.AIType == AIType.Mock)

View File

@@ -13,6 +13,7 @@ using LLama.Native;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
using System.Text.Encodings.Web;
@@ -70,6 +71,26 @@ builder.Services.AddSwaggerGen(c =>
return actionVersion.Any(v => v == docName);
return version.Any(v => v == docName);
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Description = "Directly enter bearer {token} in the box below (note that there is a space between bearer and token)",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference()
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
}
}, Array.Empty<string>()
}
});
});
//Mapper
builder.Services.AddMapper();
@@ -80,6 +101,7 @@ builder.Services.AddBackgroundTaskBroker().AddHandler<ImportKMSTaskReq, BackGrou
builder.Configuration.GetSection("DBConnection").Get<DBConnectionOption>();
builder.Configuration.GetSection("Login").Get<LoginOption>();
builder.Configuration.GetSection("LLamaSharp").Get<LLamaSharpOption>();
builder.Configuration.GetSection("KernelMemory").Get<KernelMemoryOption>();
if (LLamaSharpOption.RunType.ToUpper() == "CPU")
{
NativeLibraryConfig

View File

@@ -21,12 +21,10 @@ namespace AntSK.Services.OpenApi
[ServiceDescription(typeof(IOpenApiService), ServiceLifetime.Scoped)]
public class OpenApiService(
IApps_Repositories _apps_Repositories,
IKmss_Repositories _kmss_Repositories,
IKmsDetails_Repositories _kmsDetails_Repositories,
IKernelService _kernelService,
IKMService _kMService,
IChatService _chatService
) : IOpenApiService
) : IOpenApiService
{
public async Task Chat(OpenAIModel model, string sk, HttpContext HttpContext)
{
@@ -46,7 +44,8 @@ namespace AntSK.Services.OpenApi
{
OpenAIStreamResult result1 = new OpenAIStreamResult();
result1.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
result1.choices = new List<StreamChoicesModel>() { new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
result1.choices = new List<StreamChoicesModel>()
{ new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
await SendChatStream(HttpContext, result1, app, msg);
HttpContext.Response.ContentType = "application/json";
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result1));
@@ -57,12 +56,14 @@ namespace AntSK.Services.OpenApi
{
OpenAIResult result2 = new OpenAIResult();
result2.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
result2.choices = new List<ChoicesModel>() { new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
result2.choices = new List<ChoicesModel>()
{ new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
result2.choices[0].message.content = await SendChat(msg, app);
HttpContext.Response.ContentType = "application/json";
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result2));
await HttpContext.Response.CompleteAsync();
}
break;
case "kms":
@@ -71,7 +72,8 @@ namespace AntSK.Services.OpenApi
{
OpenAIStreamResult result3 = new OpenAIStreamResult();
result3.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
result3.choices = new List<StreamChoicesModel>() { new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
result3.choices = new List<StreamChoicesModel>()
{ new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
await SendKmsStream(HttpContext, result3, app, msg);
HttpContext.Response.ContentType = "application/json";
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result3));
@@ -81,12 +83,14 @@ namespace AntSK.Services.OpenApi
{
OpenAIResult result4 = new OpenAIResult();
result4.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
result4.choices = new List<ChoicesModel>() { new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
result4.choices = new List<ChoicesModel>()
{ new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
result4.choices[0].message.content = await SendKms(msg, app);
HttpContext.Response.ContentType = "application/json";
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result4));
await HttpContext.Response.CompleteAsync();
}
break;
}
}
@@ -96,7 +100,6 @@ namespace AntSK.Services.OpenApi
{
HttpContext.Response.Headers.Add("Content-Type", "text/event-stream");
var chatResult = _chatService.SendChatByAppAsync(app, msg, "");
int i = 0;
await foreach (var content in chatResult)
{
result.choices[0].delta.content = content.ConvertToString();
@@ -116,7 +119,6 @@ namespace AntSK.Services.OpenApi
/// <summary>
/// 发送普通对话
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="app"></param>
/// <returns></returns>
@@ -128,30 +130,31 @@ namespace AntSK.Services.OpenApi
//如果模板为空,给默认提示词
app.Prompt = app.Prompt.ConvertToString() + "{{$input}}";
}
var _kernel = _kernelService.GetKernelByApp(app);
var temperature = app.Temperature / 100;//存的是0~100需要缩小
var temperature = app.Temperature / 100; //存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
_kernelService.ImportFunctionsByApp(app, _kernel);
settings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
var promptTemplateFactory = new KernelPromptTemplateFactory();
var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(app.Prompt));
if (!string.IsNullOrEmpty(app.ApiFunctionList) || !string.IsNullOrEmpty(app.NativeFunctionList))//这里还需要加上本地插件的
{
_kernelService.ImportFunctionsByApp(app, _kernel);
settings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
}
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
var chatResult = await _kernel.InvokeAsync(function: func, arguments: new KernelArguments() { ["input"] = msg });
var chatResult =await _kernel.InvokeAsync(function: func, arguments: new KernelArguments() { ["input"] = msg });
if (chatResult.IsNotNull())
{
string answers = chatResult.GetValue<string>();
result = answers;
}
return result;
}
private async Task SendKmsStream(HttpContext HttpContext, OpenAIStreamResult result, Apps app, string msg)
{
HttpContext.Response.Headers.Add("Content-Type", "text/event-stream");
var chatResult = _chatService.SendKmsByAppAsync(app, msg, "");
var chatResult = _chatService.SendKmsByAppAsync(app, msg,"", "");
int i = 0;
await foreach (var content in chatResult)
{
@@ -172,35 +175,23 @@ namespace AntSK.Services.OpenApi
/// <summary>
/// 发送知识库问答
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task<string> SendKms(string msg, Apps app)
{
var _kernel = _kernelService.GetKernelByApp(app);
var _memory = _kMService.GetMemoryByKMS(app.KmsIdList.Split(",").FirstOrDefault());
string result = "";
//知识库问答
var filters = new List<MemoryFilter>();
var _kernel = _kernelService.GetKernelByApp(app);
var kmsidList = app.KmsIdList.Split(",");
foreach (var kmsid in kmsidList)
var relevantSource = await _kMService.GetRelevantSourceList(app.KmsIdList, msg);
var dataMsg = new StringBuilder();
if (relevantSource.Any())
{
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
}
var xlresult = await _memory.SearchAsync(msg, index: "kms", filters: filters);
string dataMsg = "";
if (xlresult != null)
{
foreach (var item in xlresult.Results)
foreach (var item in relevantSource)
{
foreach (var part in item.Partitions)
{
dataMsg += $"[file:{item.SourceName};Relevance:{(part.Relevance * 100).ToString("F2")}%]:{part.Text}{Environment.NewLine}";
}
dataMsg.AppendLine(item.ToString());
}
KernelFunction jsonFun = _kernel.Plugins.GetFunction("KMSPlugin", "Ask");
var chatResult = await _kernel.InvokeAsync(function: jsonFun,
arguments: new KernelArguments() { ["doc"] = dataMsg, ["history"] = "", ["questions"] = msg });
@@ -210,14 +201,15 @@ namespace AntSK.Services.OpenApi
result = answers;
}
}
return result;
}
/// <summary>
/// 历史会话的会话总结
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="app"></param>
/// <param name="model"></param>
/// <returns></returns>
private async Task<string> HistorySummarize(Apps app, OpenAIModel model)
{
@@ -238,7 +230,8 @@ namespace AntSK.Services.OpenApi
}
else
{
var msg = $"history{history.ToString()}{Environment.NewLine} user{questions}"; ;
var msg = $"history{history.ToString()}{Environment.NewLine} user{questions}";
;
return msg;
}
}

View File

@@ -7,6 +7,7 @@
"Microsoft.Hosting.Lifetime": "Information"
}
},
"urls":"http://*:5000",
"ProSettings": {
"NavTheme": "light",
"Layout": "side",
@@ -34,9 +35,9 @@
},
"LLamaSharp": {
"RunType": "GPU",
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"FileDirectory": "D:\\Code\\AI\\AntBlazor\\model\\"
"Chat": "D:\\git\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"Embedding": "D:\\git\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"FileDirectory": "D:\\git\\AntBlazor\\model\\"
},
"Login": {
"User": "admin",

View File

@@ -6,13 +6,9 @@ namespace AntSK.plugins.Functions
{
public class FunctionTest(IAIModels_Repositories Repository)
{
/// <summary>
/// 获取订单信息
/// </summary>
/// <param name="id">订单号</param>
/// <returns>订单信息</returns>
[Description("AntSK")]
public string GetOrder(int id)
[Description("AntSK:获取订单信息")]
[return: Description("订单信息")]
public string GetOrder([Description("订单号")] string id)
{
return $"""
订单ID: {id}
@@ -23,11 +19,10 @@ namespace AntSK.plugins.Functions
""";
}
/// <summary>
/// 获取模型
/// </summary>
/// <returns>模型列表</returns>
[Description("AntSK")]
[Description("AntSK:获取模型")]
[return: Description("模型列表")]
public string GetModels()
{
var models = Repository.GetList();

View File

@@ -1,11 +1,11 @@
Facts:
====
{{$doc}}
--------------------------
History:{{$history}}
--------------------------
Question: {{$questions}}
--------------------------
Given only the facts above, provide a comprehensive/detailed answer.
You don't know where the knowledge comes from, just answer.
Answer in the same language as the question,
If you don't have sufficient information, reply with '知识库未搜索到相关内容'.
History:{{$history}}
Question: {{$questions}}
Answer:

View File

@@ -0,0 +1,30 @@
{
"schema": 1,
"type": "completion",
"description": "知识库问答",
"completion": {
"temperature": 0.0,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"input": {
"parameters": [
{
"name": "doc",
"description": "背景文档。",
"defaultValue": ""
},
{
"name": "history",
"description": "历史聊天记录。",
"defaultValue": ""
},
{
"name": "questions",
"description": "用户问题。",
"defaultValue": ""
}
]
}
}

View File

@@ -0,0 +1,17 @@
使用<data></data>标记的内容作为你的知识:
<data>
{{$doc}}
</data>
--------------------------
回答要求:
- 如果你不清楚答案,你需要澄清
- 避免提及你是从<data></data>获取的知识
- 保持答案与<data></data>众描述一致
- 使用Markdown语法优化回答格式。
- 如果Markdown有图片则正常显示
--------------------------
历史聊天记录:{{$history}}
--------------------------
用户问题: {{$questions}}

View File

@@ -7,7 +7,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SemanticKernel" Version="1.4.0" />
<PackageReference Include="Cnblogs.KernelMemory.AI.DashScope" Version="0.1.0" />
<PackageReference Include="Cnblogs.SemanticKernel.Connectors.DashScope" Version="0.2.0" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.6.2" />
<PackageReference Include="Sdcb.SparkDesk" Version="3.0.0" />
</ItemGroup>