diff --git a/Directory.Packages.props b/Directory.Packages.props
index 60258d5f0..f7205a9ae 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -14,6 +14,7 @@
+
@@ -105,6 +106,7 @@
+
@@ -277,6 +279,10 @@
+
+
+
+
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN.Abp.AI.Agent.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN.Abp.AI.Agent.csproj
new file mode 100644
index 000000000..4fac1853b
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN.Abp.AI.Agent.csproj
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0
+ LINGYUN.Abp.AI.Agent
+ LINGYUN.Abp.AI.Agent
+ false
+ false
+ false
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AbpAIAgentModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AbpAIAgentModule.cs
new file mode 100644
index 000000000..6ae562ede
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AbpAIAgentModule.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.AI.Agent;
+
+[DependsOn(typeof(AbpAICoreModule))]
+public class AbpAIAgentModule : AbpModule
+{
+
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs
new file mode 100644
index 000000000..62dd55dca
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs
@@ -0,0 +1,80 @@
+using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.Localization;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+using Volo.Abp.AI;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.AI.Agent;
+public class ChatClientAgentFactory : IChatClientAgentFactory, ISingletonDependency
+{
+ private readonly static ConcurrentDictionary _chatClientAgentCache = new();
+
+ private readonly IChatClientFactory _chatClientFactory;
+ private readonly IStringLocalizerFactory _stringLocalizerFactory;
+ private readonly IWorkspaceDefinitionManager _workspaceDefinitionManager;
+ public ChatClientAgentFactory(
+ IChatClientFactory chatClientFactory,
+ IStringLocalizerFactory stringLocalizerFactory,
+ IWorkspaceDefinitionManager workspaceDefinitionManager)
+ {
+ _chatClientFactory = chatClientFactory;
+ _stringLocalizerFactory = stringLocalizerFactory;
+ _workspaceDefinitionManager = workspaceDefinitionManager;
+ }
+
+ public async virtual Task CreateAsync()
+ {
+ var workspace = WorkspaceNameAttribute.GetWorkspaceName();
+ if (_chatClientAgentCache.TryGetValue(workspace, out var chatClientAgent))
+ {
+ return chatClientAgent;
+ }
+
+ var chatClient = await _chatClientFactory.CreateAsync();
+
+ var workspaceDefine = await _workspaceDefinitionManager.GetOrNullAsync(workspace);
+
+ string? description = null;
+ if (workspaceDefine?.Description != null)
+ {
+ description = workspaceDefine.Description.Localize(_stringLocalizerFactory);
+ }
+
+ chatClientAgent = chatClient.CreateAIAgent(
+ instructions: workspaceDefine?.SystemPrompt,
+ name: workspaceDefine?.Name,
+ description: description);
+
+ _chatClientAgentCache.TryAdd(workspace, chatClientAgent);
+
+ return chatClientAgent;
+ }
+
+ public async virtual Task CreateAsync(string workspace)
+ {
+ if (_chatClientAgentCache.TryGetValue(workspace, out var chatClientAgent))
+ {
+ return chatClientAgent;
+ }
+ var workspaceDefine = await _workspaceDefinitionManager.GetAsync(workspace);
+ var chatClient = await _chatClientFactory.CreateAsync(workspace);
+
+ string? description = null;
+ if (workspaceDefine.Description != null)
+ {
+ description = workspaceDefine.Description.Localize(_stringLocalizerFactory);
+ }
+
+ chatClientAgent = chatClient.CreateAIAgent(
+ instructions: workspaceDefine.SystemPrompt,
+ name: workspaceDefine.Name,
+ description: description);
+
+ _chatClientAgentCache.TryAdd(workspace, chatClientAgent);
+
+ return chatClientAgent;
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs
new file mode 100644
index 000000000..0d15511fd
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs
@@ -0,0 +1,13 @@
+using JetBrains.Annotations;
+using Microsoft.Agents.AI;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.AI.Agent;
+public interface IChatClientAgentFactory
+{
+ [NotNull]
+ Task CreateAsync();
+
+ [NotNull]
+ Task CreateAsync(string workspace);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/README.md b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/README.md
new file mode 100644
index 000000000..5d2730e6c
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/README.md
@@ -0,0 +1,98 @@
+# LINGYUN.Abp.AI.Agent
+
+[Abp AI Module](https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence) 扩展.
+
+## 功能特性
+
+
+## 模块引用
+
+```csharp
+[DependsOn(typeof(AbpAIAgentModule))]
+public class YouProjectModule : AbpModule
+{
+ // other
+}
+```
+
+## 依赖模块
+
+* [AbpAIModule](https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence)
+* [AbpLocalizationModule](https://abp.io/docs/latest/framework/fundamentals/localization)
+
+## 基本用法
+
+> 兼容 `IKernelAccessor` 与 `IChatClientAccessor` 的语法.
+
+```csharp
+using LINGYUN.Abp.AI;
+using LINGYUN.Abp.AI.Agent;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.SemanticKernel;
+using Volo.Abp;
+
+var application = await AbpApplicationFactory.CreateAsync(options =>
+{
+ options.UseAutofac();
+});
+
+await application.InitializeAsync();
+
+var chatClientAgentFactory = application.ServiceProvider.GetRequiredService();
+
+var agent = await chatClientAgentFactory.CreateAsync();
+
+var agentResponse = agent.RunStreamingAsync("解释一下线性代数");
+
+await foreach (var item in agentResponse)
+{
+ Console.Write(item);
+}
+Console.WriteLine();
+
+await application.ShutdownAsync();
+
+Console.WriteLine();
+Console.WriteLine("AI Console completed!");
+
+Console.ReadKey();
+```
+
+> 支持动态工作区语法.
+
+```csharp
+using LINGYUN.Abp.AI;
+using LINGYUN.Abp.AI.Agent;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.SemanticKernel;
+using Volo.Abp;
+
+var application = await AbpApplicationFactory.CreateAsync(options =>
+{
+ options.UseAutofac();
+});
+
+await application.InitializeAsync();
+
+var chatClientAgentFactory = application.ServiceProvider.GetRequiredService();
+
+var agent = await chatClientAgentFactory.CreateAsync("YouWorkspace");
+
+var agentResponse = agent.RunStreamingAsync("解释一下线性代数");
+
+await foreach (var item in agentResponse)
+{
+ Console.Write(item);
+}
+Console.WriteLine();
+
+await application.ShutdownAsync();
+
+Console.WriteLine();
+Console.WriteLine("AI Console completed!");
+
+Console.ReadKey();
+
+```
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj
new file mode 100644
index 000000000..1c67a9757
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0
+ LINGYUN.Abp.AI.Core
+ LINGYUN.Abp.AI.Core
+ false
+ false
+ false
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs
new file mode 100644
index 000000000..8718805c2
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs
@@ -0,0 +1,52 @@
+using LINGYUN.Abp.AI.Localization;
+using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using Volo.Abp.AI;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.AI;
+
+[DependsOn(
+ typeof(AbpAIModule),
+ typeof(AbpLocalizationModule))]
+public class AbpAICoreModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ AutoAddDefinitionProviders(context.Services);
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.Resources.Add();
+ });
+
+ Configure(options =>
+ {
+ options.ChatClientProviders.Add();
+ });
+ }
+
+ private static void AutoAddDefinitionProviders(IServiceCollection services)
+ {
+ var definitionProviders = new List();
+
+ services.OnRegistered(context =>
+ {
+ if (typeof(IWorkspaceDefinitionProvider).IsAssignableFrom(context.ImplementationType))
+ {
+ definitionProviders.Add(context.ImplementationType);
+ }
+ });
+
+ services.Configure(options =>
+ {
+ options.DefinitionProviders.AddIfNotContains(definitionProviders);
+ });
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs
new file mode 100644
index 000000000..8297e1b58
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs
@@ -0,0 +1,22 @@
+using LINGYUN.Abp.AI.Workspaces;
+using System.Collections.Generic;
+using Volo.Abp.Collections;
+
+namespace LINGYUN.Abp.AI;
+public class AbpAICoreOptions
+{
+ public ITypeList DefinitionProviders { get; }
+ public ITypeList ChatClientProviders { get; }
+ public ITypeList KernelProviders { get; }
+
+ public HashSet DeletedWorkspaces { get; }
+
+ public AbpAICoreOptions()
+ {
+ DefinitionProviders = new TypeList();
+ ChatClientProviders = new TypeList();
+ KernelProviders = new TypeList();
+
+ DeletedWorkspaces = new HashSet();
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs
new file mode 100644
index 000000000..bbdfa9c90
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs
@@ -0,0 +1,82 @@
+using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.Extensions.AI;
+using System;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.AI;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.AI;
+public class ChatClientFactory : IChatClientFactory, ISingletonDependency
+{
+ private readonly static ConcurrentDictionary _chatClientCache = new();
+ protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; }
+ protected IChatClientProviderManager ChatClientProviderManager { get; }
+ protected IServiceProvider ServiceProvider { get; }
+
+ public ChatClientFactory(
+ IWorkspaceDefinitionManager workspaceDefinitionManager,
+ IChatClientProviderManager chatClientProviderManager,
+ IServiceProvider serviceProvider)
+ {
+ WorkspaceDefinitionManager = workspaceDefinitionManager;
+ ChatClientProviderManager = chatClientProviderManager;
+ ServiceProvider = serviceProvider;
+ }
+
+ public async virtual Task CreateAsync()
+ {
+ var workspace = WorkspaceNameAttribute.GetWorkspaceName();
+ if (_chatClientCache.TryGetValue(workspace, out var chatClient))
+ {
+ return chatClient;
+ }
+
+ var chatClientAccessorType = typeof(IChatClientAccessor<>).MakeGenericType(typeof(TWorkspace));
+ var chatClientAccessor = ServiceProvider.GetService(chatClientAccessorType);
+ if (chatClientAccessor != null &&
+ chatClientAccessor is IChatClientAccessor accessor &&
+ accessor.ChatClient != null)
+ {
+ chatClient = accessor.ChatClient;
+ _chatClientCache.TryAdd(workspace, chatClient);
+ }
+ else
+ {
+ chatClient = await CreateAsync(workspace);
+ }
+ return chatClient;
+ }
+
+ public async virtual Task CreateAsync(string workspace)
+ {
+ if (_chatClientCache.TryGetValue(workspace, out var chatClient))
+ {
+ return chatClient;
+ }
+
+ var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace);
+
+ chatClient = await CreateChatClientAsync(workspaceDefine);
+
+ _chatClientCache.TryAdd(workspace, chatClient);
+
+ return chatClient;
+ }
+
+ protected async virtual Task CreateChatClientAsync(WorkspaceDefinition workspace)
+ {
+ foreach (var provider in ChatClientProviderManager.Providers)
+ {
+ if (!string.Equals(provider.Name, workspace.Provider))
+ {
+ continue;
+ }
+
+ return await provider.CreateAsync(workspace);
+ }
+
+ throw new AbpException($"The ChatClient provider implementation named {workspace.Provider} was not found");
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProviderManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProviderManager.cs
new file mode 100644
index 000000000..8d3cc80ec
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProviderManager.cs
@@ -0,0 +1,45 @@
+using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Volo.Abp;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.AI;
+public class ChatClientProviderManager : IChatClientProviderManager, ISingletonDependency
+{
+ public List Providers => _lazyProviders.Value;
+
+ protected AbpAICoreOptions Options { get; }
+ protected IServiceProvider ServiceProvider { get; }
+ private readonly Lazy> _lazyProviders;
+
+ public ChatClientProviderManager(
+ IServiceProvider serviceProvider,
+ IOptions options)
+ {
+
+ Options = options.Value;
+ ServiceProvider = serviceProvider;
+
+ _lazyProviders = new Lazy>(GetProviders, true);
+ }
+
+ protected virtual List GetProviders()
+ {
+ var providers = Options
+ .ChatClientProviders
+ .Select(type => (ServiceProvider.GetRequiredService(type) as IChatClientProvider)!)
+ .ToList();
+
+ var multipleProviders = providers.GroupBy(p => p.Name).FirstOrDefault(x => x.Count() > 1);
+ if (multipleProviders != null)
+ {
+ throw new AbpException($"Duplicate ChatClient provider name detected: {multipleProviders.Key}. Providers:{Environment.NewLine}{multipleProviders.Select(p => p.GetType().FullName!).JoinAsString(Environment.NewLine)}");
+ }
+
+ return providers;
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs
new file mode 100644
index 000000000..3ab6e99bb
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs
@@ -0,0 +1,13 @@
+using JetBrains.Annotations;
+using Microsoft.Extensions.AI;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.AI;
+public interface IChatClientFactory
+{
+ [NotNull]
+ Task CreateAsync();
+
+ [NotNull]
+ Task CreateAsync(string workspace);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs
new file mode 100644
index 000000000..bdf5aeeb7
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs
@@ -0,0 +1,11 @@
+using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.Extensions.AI;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.AI;
+public interface IChatClientProvider
+{
+ string Name { get; }
+
+ Task CreateAsync(WorkspaceDefinition workspace);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProviderManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProviderManager.cs
new file mode 100644
index 000000000..f3f6536ca
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProviderManager.cs
@@ -0,0 +1,7 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.AI;
+public interface IChatClientProviderManager
+{
+ List Providers { get; }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IKernelFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IKernelFactory.cs
new file mode 100644
index 000000000..c25d272bf
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IKernelFactory.cs
@@ -0,0 +1,13 @@
+using JetBrains.Annotations;
+using Microsoft.SemanticKernel;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.AI;
+public interface IKernelFactory
+{
+ [NotNull]
+ Task CreateAsync();
+
+ [NotNull]
+ Task CreateAsync(string workspace);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IKernelProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IKernelProvider.cs
new file mode 100644
index 000000000..56b34a4ca
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IKernelProvider.cs
@@ -0,0 +1,11 @@
+using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.SemanticKernel;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.AI;
+public interface IKernelProvider
+{
+ string Name { get; }
+
+ Task CreateAsync(WorkspaceDefinition workspace);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IKernelProviderManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IKernelProviderManager.cs
new file mode 100644
index 000000000..a726f7073
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IKernelProviderManager.cs
@@ -0,0 +1,7 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.AI;
+public interface IKernelProviderManager
+{
+ List Providers { get; }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs
new file mode 100644
index 000000000..5709d9127
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs
@@ -0,0 +1,82 @@
+using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.SemanticKernel;
+using System;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.AI;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.AI;
+public class KernelFactory : IKernelFactory, ISingletonDependency
+{
+ private readonly static ConcurrentDictionary _kernelCache = new();
+ protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; }
+ protected IKernelProviderManager KernelProviderManager { get; }
+ protected IServiceProvider ServiceProvider { get; }
+
+ public KernelFactory(
+ IWorkspaceDefinitionManager workspaceDefinitionManager,
+ IKernelProviderManager kernelProviderManager,
+ IServiceProvider serviceProvider)
+ {
+ WorkspaceDefinitionManager = workspaceDefinitionManager;
+ KernelProviderManager = kernelProviderManager;
+ ServiceProvider = serviceProvider;
+ }
+
+ public async virtual Task CreateAsync()
+ {
+ var workspace = WorkspaceNameAttribute.GetWorkspaceName();
+ if (_kernelCache.TryGetValue(workspace, out var kernel))
+ {
+ return kernel;
+ }
+
+ var kernelAccessorType = typeof(IKernelAccessor<>).MakeGenericType(typeof(TWorkspace));
+ var kernelAccessor = ServiceProvider.GetService(kernelAccessorType);
+ if (kernelAccessor != null &&
+ kernelAccessor is IKernelAccessor accessor
+ && accessor.Kernel != null)
+ {
+ kernel = accessor.Kernel;
+ _kernelCache.TryAdd(workspace, kernel);
+ }
+ else
+ {
+ kernel = await CreateAsync(workspace);
+ }
+ return kernel;
+ }
+
+ public async virtual Task CreateAsync(string workspace)
+ {
+ if (_kernelCache.TryGetValue(workspace, out var kernel))
+ {
+ return kernel;
+ }
+
+ var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace);
+
+ kernel = await CreateKernelAsync(workspaceDefine);
+
+ _kernelCache.TryAdd(workspace, kernel);
+
+ return kernel;
+ }
+
+ protected async virtual Task CreateKernelAsync(WorkspaceDefinition workspace)
+ {
+ foreach (var provider in KernelProviderManager.Providers)
+ {
+ if (!string.Equals(provider.Name, workspace.Provider))
+ {
+ continue;
+ }
+
+ return await provider.CreateAsync(workspace);
+ }
+
+ throw new AbpException($"The Kernel provider implementation named {workspace.Provider} was not found");
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelProviderManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelProviderManager.cs
new file mode 100644
index 000000000..f2beb0025
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelProviderManager.cs
@@ -0,0 +1,44 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Volo.Abp;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.AI;
+public class KernelProviderManager : IKernelProviderManager, ISingletonDependency
+{
+ public List Providers => _lazyProviders.Value;
+
+ protected AbpAICoreOptions Options { get; }
+ protected IServiceProvider ServiceProvider { get; }
+ private readonly Lazy> _lazyProviders;
+
+ public KernelProviderManager(
+ IServiceProvider serviceProvider,
+ IOptions options)
+ {
+
+ Options = options.Value;
+ ServiceProvider = serviceProvider;
+
+ _lazyProviders = new Lazy>(GetProviders, true);
+ }
+
+ protected virtual List GetProviders()
+ {
+ var providers = Options
+ .ChatClientProviders
+ .Select(type => (ServiceProvider.GetRequiredService(type) as IKernelProvider)!)
+ .ToList();
+
+ var multipleProviders = providers.GroupBy(p => p.Name).FirstOrDefault(x => x.Count() > 1);
+ if (multipleProviders != null)
+ {
+ throw new AbpException($"Duplicate Kernel provider name detected: {multipleProviders.Key}. Providers:{Environment.NewLine}{multipleProviders.Select(p => p.GetType().FullName!).JoinAsString(Environment.NewLine)}");
+ }
+
+ return providers;
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/AbpAIResource.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/AbpAIResource.cs
new file mode 100644
index 000000000..9566818d4
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/AbpAIResource.cs
@@ -0,0 +1,8 @@
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.AI.Localization;
+
+[LocalizationResourceName("AbpAI")]
+public class AbpAIResource
+{
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs
new file mode 100644
index 000000000..f8d145334
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs
@@ -0,0 +1,36 @@
+using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.Extensions.AI;
+using OpenAI;
+using System;
+using System.ClientModel;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.AI;
+public class OpenAIChatClientProvider : IChatClientProvider, ITransientDependency
+{
+ private const string DefaultEndpoint = "https://api.openai.com/v1";
+ public const string ProviderName = "OpenAI";
+
+ public virtual string Name => ProviderName;
+
+ public virtual Task CreateAsync(WorkspaceDefinition workspace)
+ {
+ Check.NotNull(workspace, nameof(workspace));
+ Check.NotNullOrWhiteSpace(workspace.ApiKey, nameof(WorkspaceDefinition.ApiKey));
+
+ var openAIClient = new OpenAIClient(
+ new ApiKeyCredential(workspace.ApiKey),
+ new OpenAIClientOptions
+ {
+ Endpoint = new Uri(workspace.ApiBaseUrl ?? DefaultEndpoint),
+ });
+
+ var chatClient = openAIClient
+ .GetChatClient(workspace.ModelName)
+ .AsIChatClient();
+
+ return Task.FromResult(chatClient);
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IDynamicWorkspaceDefinitionStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IDynamicWorkspaceDefinitionStore.cs
new file mode 100644
index 000000000..d3b9aa5e9
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IDynamicWorkspaceDefinitionStore.cs
@@ -0,0 +1,13 @@
+using JetBrains.Annotations;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.AI.Workspaces;
+public interface IDynamicWorkspaceDefinitionStore
+{
+ Task GetAsync([NotNull] string name);
+
+ Task> GetAllAsync();
+
+ Task GetOrNullAsync([NotNull] string name);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IStaticWorkspaceDefinitionStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IStaticWorkspaceDefinitionStore.cs
new file mode 100644
index 000000000..542ae0e6f
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IStaticWorkspaceDefinitionStore.cs
@@ -0,0 +1,13 @@
+using JetBrains.Annotations;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.AI.Workspaces;
+public interface IStaticWorkspaceDefinitionStore
+{
+ Task GetAsync([NotNull] string name);
+
+ Task> GetAllAsync();
+
+ Task GetOrNullAsync([NotNull] string name);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionContext.cs
new file mode 100644
index 000000000..4ab567639
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionContext.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.AI.Workspaces;
+public interface IWorkspaceDefinitionContext
+{
+ WorkspaceDefinition? GetOrNull(string name);
+
+ IReadOnlyList GetAll();
+
+ void Add(params WorkspaceDefinition[] definitions);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionManager.cs
new file mode 100644
index 000000000..82bd1c224
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionManager.cs
@@ -0,0 +1,16 @@
+using JetBrains.Annotations;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.AI.Workspaces;
+public interface IWorkspaceDefinitionManager
+{
+ [ItemNotNull]
+ Task GetAsync([NotNull] string name);
+
+ [ItemNotNull]
+ Task> GetAllAsync();
+
+ [ItemCanBeNull]
+ Task GetOrNullAsync([NotNull] string name);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionProvider.cs
new file mode 100644
index 000000000..69452746a
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionProvider.cs
@@ -0,0 +1,5 @@
+namespace LINGYUN.Abp.AI.Workspaces;
+public interface IWorkspaceDefinitionProvider
+{
+ void Define(IWorkspaceDefinitionContext context);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/NullDynamicWorkspaceDefinitionStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/NullDynamicWorkspaceDefinitionStore.cs
new file mode 100644
index 000000000..dd6953ba7
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/NullDynamicWorkspaceDefinitionStore.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.AI.Workspaces;
+public class NullDynamicWorkspaceDefinitionStore : IDynamicWorkspaceDefinitionStore, ISingletonDependency
+{
+ private readonly static Task CachedNullableWorkspaceResult = Task.FromResult((WorkspaceDefinition?)null);
+ private readonly static Task CachedWorkspaceResult = Task.FromResult((WorkspaceDefinition)null!);
+
+ private readonly static Task> CachedWorkspacesResult = Task.FromResult(
+ (IReadOnlyList)Array.Empty().ToImmutableList());
+
+ public Task GetAsync(string name)
+ {
+ return CachedWorkspaceResult;
+ }
+
+ public Task> GetAllAsync()
+ {
+ return CachedWorkspacesResult;
+ }
+
+ public Task GetOrNullAsync(string name)
+ {
+ return CachedNullableWorkspaceResult;
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/StaticWorkspaceDefinitionStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/StaticWorkspaceDefinitionStore.cs
new file mode 100644
index 000000000..2d711257d
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/StaticWorkspaceDefinitionStore.cs
@@ -0,0 +1,79 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.StaticDefinitions;
+
+namespace LINGYUN.Abp.AI.Workspaces;
+public class StaticWorkspaceDefinitionStore : IStaticWorkspaceDefinitionStore, ISingletonDependency
+{
+ protected IServiceProvider ServiceProvider { get; }
+ protected AbpAICoreOptions Options { get; }
+ protected IStaticDefinitionCache> DefinitionCache { get; }
+
+ public StaticWorkspaceDefinitionStore(
+ IServiceProvider serviceProvider,
+ IOptions options,
+ IStaticDefinitionCache> definitionCache)
+ {
+ ServiceProvider = serviceProvider;
+ Options = options.Value;
+ DefinitionCache = definitionCache;
+ }
+
+ public virtual async Task GetAsync(string name)
+ {
+ Check.NotNull(name, nameof(name));
+
+ var workspace = await GetOrNullAsync(name);
+
+ if (workspace == null)
+ {
+ throw new AbpException("Undefined workspace: " + name);
+ }
+
+ return workspace;
+ }
+
+ public virtual async Task> GetAllAsync()
+ {
+ var defs = await GetWorkspaceDefinitionsAsync();
+ return defs.Values.ToImmutableList();
+ }
+
+ public virtual async Task GetOrNullAsync(string name)
+ {
+ var defs = await GetWorkspaceDefinitionsAsync();
+ return defs.GetOrDefault(name);
+ }
+
+ protected virtual async Task> GetWorkspaceDefinitionsAsync()
+ {
+ return await DefinitionCache.GetOrCreateAsync(CreateWorkspaceDefinitionsAsync);
+ }
+
+ protected virtual Task> CreateWorkspaceDefinitionsAsync()
+ {
+ var workspaces = new Dictionary();
+
+ using (var scope = ServiceProvider.CreateScope())
+ {
+ var providers = Options
+ .DefinitionProviders
+ .Select(p => scope.ServiceProvider.GetRequiredService(p) as IWorkspaceDefinitionProvider)
+ .ToList();
+
+ foreach (var provider in providers)
+ {
+ provider?.Define(new WorkspaceDefinitionContext(workspaces));
+ }
+ }
+
+ return Task.FromResult(workspaces);
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs
new file mode 100644
index 000000000..56f37e127
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs
@@ -0,0 +1,104 @@
+using JetBrains.Annotations;
+using System.Collections.Generic;
+using Volo.Abp;
+using Volo.Abp.Localization;
+using Volo.Abp.SimpleStateChecking;
+
+namespace LINGYUN.Abp.AI.Workspaces;
+///
+/// 工作区定义
+///
+public class WorkspaceDefinition : IHasSimpleStateCheckers
+{
+ ///
+ /// 名称
+ ///
+ [NotNull]
+ public string Name { get; }
+ ///
+ /// AI提供者名称
+ ///
+ [NotNull]
+ public string Provider { get; }
+ ///
+ /// 模型名称
+ ///
+ [NotNull]
+ public string ModelName { get; }
+ ///
+ /// 显示名称
+ ///
+ [NotNull]
+ public ILocalizableString DisplayName {
+ get => _displayName;
+ set => _displayName = Check.NotNull(value, nameof(value));
+ }
+ private ILocalizableString _displayName = default!;
+ ///
+ /// 描述
+ ///
+ public ILocalizableString? Description { get; set; }
+ ///
+ /// API 身份验证密钥
+ ///
+ public string? ApiKey { get; set; }
+ ///
+ /// 自定义端点 URL
+ ///
+ public string? ApiBaseUrl { get; set; }
+ ///
+ /// 系统提示词
+ ///
+ public string? SystemPrompt { get; set; }
+ ///
+ /// 启用/禁用工作区
+ ///
+ public bool IsEnabled { get; set; }
+
+ [NotNull]
+ public Dictionary Properties { get; }
+
+ public List> StateCheckers { get; }
+
+ public WorkspaceDefinition(
+ string name,
+ string provider,
+ string modelName,
+ ILocalizableString displayName,
+ ILocalizableString? description = null)
+ {
+ Name = name;
+ Provider = provider;
+ ModelName = modelName;
+ _displayName = displayName;
+ _displayName = displayName;
+ Description = description;
+
+ IsEnabled = true;
+ Properties = new Dictionary();
+ StateCheckers = new List>();
+ }
+
+ public virtual WorkspaceDefinition WithApiBaseUrl(string apiBaseUrl)
+ {
+ ApiBaseUrl = apiBaseUrl;
+ return this;
+ }
+
+ public virtual WorkspaceDefinition WithApiKey(string apiKey)
+ {
+ ApiKey = apiKey;
+ return this;
+ }
+
+ public virtual WorkspaceDefinition WithProperty(string key, object value)
+ {
+ Properties[key] = value;
+ return this;
+ }
+
+ public override string ToString()
+ {
+ return $"[{nameof(WorkspaceDefinition)} {Name}]";
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionContext.cs
new file mode 100644
index 000000000..73919b9c5
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionContext.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+
+namespace LINGYUN.Abp.AI.Workspaces;
+public class WorkspaceDefinitionContext : IWorkspaceDefinitionContext
+{
+ protected Dictionary Workspaces { get; }
+
+ public WorkspaceDefinitionContext(Dictionary workspaces)
+ {
+ Workspaces = workspaces;
+ }
+
+ public virtual WorkspaceDefinition? GetOrNull(string name)
+ {
+ return Workspaces.GetOrDefault(name);
+ }
+
+ public virtual IReadOnlyList GetAll()
+ {
+ return Workspaces.Values.ToImmutableList();
+ }
+
+ public virtual void Add(params WorkspaceDefinition[] definitions)
+ {
+ if (definitions.IsNullOrEmpty())
+ {
+ return;
+ }
+
+ foreach (var definition in definitions)
+ {
+ Workspaces[definition.Name] = definition;
+ }
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionManager.cs
new file mode 100644
index 000000000..8b5b8048d
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionManager.cs
@@ -0,0 +1,52 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.AI.Workspaces;
+public class WorkspaceDefinitionManager : IWorkspaceDefinitionManager, ISingletonDependency
+{
+ protected readonly IStaticWorkspaceDefinitionStore StaticStore;
+ protected readonly IDynamicWorkspaceDefinitionStore DynamicStore;
+
+ public WorkspaceDefinitionManager(
+ IStaticWorkspaceDefinitionStore staticStore,
+ IDynamicWorkspaceDefinitionStore dynamicStore)
+ {
+ StaticStore = staticStore;
+ DynamicStore = dynamicStore;
+ }
+
+ public virtual async Task GetAsync(string name)
+ {
+ var workspace = await GetOrNullAsync(name);
+ if (workspace == null)
+ {
+ throw new AbpException("Undefined Workspace: " + name);
+ }
+
+ return workspace;
+ }
+
+ public virtual async Task GetOrNullAsync(string name)
+ {
+ Check.NotNull(name, nameof(name));
+
+ return await StaticStore.GetOrNullAsync(name) ?? await DynamicStore.GetOrNullAsync(name);
+ }
+
+ public virtual async Task> GetAllAsync()
+ {
+ var staticWorkspaces = await StaticStore.GetAllAsync();
+ var staticWorkspaceNames = staticWorkspaces
+ .Select(p => p.Name)
+ .ToImmutableHashSet();
+
+ var dynamicWorkspaces = await DynamicStore.GetAllAsync();
+
+ return staticWorkspaces.Concat(dynamicWorkspaces.Where(d => !staticWorkspaceNames.Contains(d.Name)))
+ .ToImmutableList();
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionProvider.cs
new file mode 100644
index 000000000..5f9e662d0
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionProvider.cs
@@ -0,0 +1,8 @@
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.AI.Workspaces;
+
+public abstract class WorkspaceDefinitionProvider : IWorkspaceDefinitionProvider, ITransientDependency
+{
+ public abstract void Define(IWorkspaceDefinitionContext context);
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/README.md b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/README.md
new file mode 100644
index 000000000..4a4fa8227
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/README.md
@@ -0,0 +1,164 @@
+# LINGYUN.Abp.AI.Core
+
+[Abp AI Module](https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence) 扩展.
+
+## 功能特性
+
+
+## 模块引用
+
+```csharp
+[DependsOn(typeof(AbpAICoreModule))]
+public class YouProjectModule : AbpModule
+{
+ // other
+}
+```
+
+## 依赖模块
+
+* [AbpAIModule](https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence)
+* [AbpLocalizationModule](https://abp.io/docs/latest/framework/fundamentals/localization)
+
+## 基本用法
+
+> 定义系统工作区
+```csharp
+[DependsOn(typeof(AbpAICoreModule))]
+public class YouProjectModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ PreConfigure(options =>
+ {
+ options.Workspaces.Configure(workspace =>
+ {
+ workspace.ConfigureChatClient(config =>
+ {
+ config.Builder = new ChatClientBuilder(
+ sp => new OpenAIClient(
+ new ApiKeyCredential("YouApiKey"),
+ new OpenAIClientOptions
+ {
+ Endpoint = new Uri("https://api.openai.com/v1"),
+ }).GetChatClient("GPT-4").AsIChatClient());
+ });
+
+ workspace.ConfigureKernel(config =>
+ {
+ config.Builder = Kernel.CreateBuilder()
+ .AddOpenAIChatClient(
+ modelId: "GPT-4",
+ openAIClient: new OpenAIClient(
+ new ApiKeyCredential("YouApiKey"),
+ new OpenAIClientOptions
+ {
+ Endpoint = new Uri("https://api.openai.com/v1"),
+ }));
+ });
+ });
+ });
+ }
+}
+```
+
+> 兼容 `IKernelAccessor` 与 `IChatClientAccessor` 的语法.
+```csharp
+using LINGYUN.Abp.AI;
+using LINGYUN.Abp.AI.Agent;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.SemanticKernel;
+using Volo.Abp;
+
+var application = await AbpApplicationFactory.CreateAsync(options =>
+{
+ options.UseAutofac();
+});
+
+await application.InitializeAsync();
+
+// 使用 IKernelFactory
+Console.WriteLine("Use Microsoft.SemanticKernel:");
+var kernelFactory = application.ServiceProvider.GetRequiredService();
+
+var kernel = await kernelFactory.CreateAsync();
+var kernelResponse = kernel.InvokePromptStreamingAsync("如何优化 C# 代码性能?");
+await foreach (var item in kernelResponse)
+{
+ Console.Write(item);
+}
+Console.WriteLine();
+
+// 使用 IKernelAccessor
+var kernelWithAbp = application.ServiceProvider.GetRequiredService>();
+var kernelWithAbpResponse = kernelWithAbp.InvokePromptStreamingAsync("如何优化 C# 代码性能?");
+await foreach (var item in kernelWithAbpResponse)
+{
+ Console.Write(item);
+}
+Console.WriteLine();
+
+await application.ShutdownAsync();
+
+Console.WriteLine();
+Console.WriteLine("AI Console completed!");
+
+Console.ReadKey();
+```
+
+> 定义动态工作区
+```csharp
+public class YouWorkspaceDefinitionProvider : WorkspaceDefinitionProvider
+{
+ public override void Define(IWorkspaceDefinitionContext context)
+ {
+ context.Add(
+ new WorkspaceDefinition(
+ "YouWorkspace",
+ OpenAIChatClientProvider.ProviderName,
+ "GPT-4",
+ new FixedLocalizableString("YouWorkspace"))
+ .WithApiKey("YouApiKey")
+ .WithApiBaseUrl("https://api.openai.com/v1"));
+ }
+}
+
+```
+
+> 支持动态工作区语法.
+```csharp
+using LINGYUN.Abp.AI;
+using LINGYUN.Abp.AI.Agent;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.SemanticKernel;
+using Volo.Abp;
+
+var application = await AbpApplicationFactory.CreateAsync(options =>
+{
+ options.UseAutofac();
+});
+
+await application.InitializeAsync();
+
+// Microsoft.SemanticKernel
+Console.WriteLine("Use Microsoft.SemanticKernel:");
+var kernelFactory = application.ServiceProvider.GetRequiredService();
+
+var kernel = await kernelFactory.CreateAsync("YouWorkspace");
+var kernelResponse = kernel.InvokePromptStreamingAsync("如何优化 C# 代码性能?");
+await foreach (var item in kernelResponse)
+{
+ Console.Write(item);
+}
+Console.WriteLine();
+
+await application.ShutdownAsync();
+
+Console.WriteLine();
+Console.WriteLine("AI Console completed!");
+
+Console.ReadKey();
+
+```
\ No newline at end of file