添加项目文件。
This commit is contained in:
26
.vscode/launch.json
vendored
Normal file
26
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// 使用 IntelliSense 找出 C# 调试存在哪些属性
|
||||
// 将悬停用于现有属性的说明
|
||||
// 有关详细信息,请访问 https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
|
||||
"name": ".NET Core Launch (console)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// 如果已更改目标框架,请确保更新程序路径。
|
||||
"program": "${workspaceFolder}/MilvusDemo/bin/Debug/net9.0/MilvusDemo.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/MilvusDemo",
|
||||
// 有关“控制台”字段的详细信息,请参阅 https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
||||
41
.vscode/tasks.json
vendored
Normal file
41
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/MilvusDemo/MilvusDemo.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/MilvusDemo/MilvusDemo.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/MilvusDemo/MilvusDemo.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
14
MilvusDemo/MilvusDemo.csproj
Normal file
14
MilvusDemo/MilvusDemo.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Milvus.Client" Version="2.3.0-preview.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
144
MilvusDemo/MilvusService.cs
Normal file
144
MilvusDemo/MilvusService.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Milvus.Client;
|
||||
|
||||
namespace MilvusDemo
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供与Milvus向量数据库交互的服务类
|
||||
/// </summary>
|
||||
public class MilvusService
|
||||
{
|
||||
private readonly MilvusClient _milvusClient;
|
||||
private readonly string _collectionName;
|
||||
private readonly int _dim;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化MilvusService的新实例
|
||||
/// </summary>
|
||||
/// <param name="host">Milvus服务器主机名</param>
|
||||
/// <param name="port">Milvus服务器端口</param>
|
||||
/// <param name="collectionName">集合名称</param>
|
||||
/// <param name="dim">向量维度</param>
|
||||
public MilvusService(string host, int port, string collectionName, int dim)
|
||||
{
|
||||
_milvusClient = new MilvusClient(host, port);
|
||||
_collectionName = collectionName;
|
||||
_dim = dim;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化集合,如果存在则删除并重新创建
|
||||
/// </summary>
|
||||
public async Task InitializeCollectionAsync()
|
||||
{
|
||||
// 检查集合是否存在,如果存在则删除
|
||||
if (await _milvusClient.HasCollectionAsync(_collectionName))
|
||||
{
|
||||
var collectionToDrop = _milvusClient.GetCollection(_collectionName);
|
||||
await collectionToDrop.DropAsync();
|
||||
Console.WriteLine($"Collection '{_collectionName}' dropped.");
|
||||
}
|
||||
|
||||
// 创建新的集合
|
||||
await _milvusClient.CreateCollectionAsync(
|
||||
_collectionName,
|
||||
new[]
|
||||
{
|
||||
FieldSchema.Create<long>("id", isPrimaryKey: true),
|
||||
FieldSchema.CreateVarchar("varchar", 256),
|
||||
FieldSchema.CreateFloatVector("vector", _dim)
|
||||
});
|
||||
Console.WriteLine($"Collection '{_collectionName}' created.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建索引并加载集合
|
||||
/// </summary>
|
||||
public async Task CreateIndexAndLoadAsync()
|
||||
{
|
||||
var collection = _milvusClient.GetCollection(_collectionName);
|
||||
|
||||
// 创建索引
|
||||
await collection.CreateIndexAsync("vector", IndexType.Flat, SimilarityMetricType.L2);
|
||||
Console.WriteLine("Index created.");
|
||||
|
||||
// 加载集合
|
||||
await collection.LoadAsync();
|
||||
Console.WriteLine("Collection loaded.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插入随机向量数据
|
||||
/// </summary>
|
||||
/// <param name="count">要插入的向量数量</param>
|
||||
public async Task InsertRandomDataAsync(int count)
|
||||
{
|
||||
var collection = _milvusClient.GetCollection(_collectionName);
|
||||
var random = new Random();
|
||||
|
||||
// 生成随机向量
|
||||
var vectors = new List<ReadOnlyMemory<float>>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var vector = new float[_dim];
|
||||
for (int j = 0; j < _dim; j++)
|
||||
{
|
||||
vector[j] = (float)random.NextDouble();
|
||||
}
|
||||
vectors.Add(vector);
|
||||
}
|
||||
|
||||
// 生成ID和字符串字段
|
||||
var ids = Enumerable.Range(0, count).Select(i => (long)i).ToList();
|
||||
var varchars = ids.Select(i => $"varchar_{i}").ToList();
|
||||
|
||||
// 打包数据
|
||||
var insertData = new List<FieldData>
|
||||
{
|
||||
FieldData.Create("id", ids),
|
||||
FieldData.Create("varchar", varchars),
|
||||
FieldData.CreateFloatVector("vector", vectors)
|
||||
};
|
||||
|
||||
// 执行插入
|
||||
await collection.InsertAsync(insertData);
|
||||
Console.WriteLine("Data inserted.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行向量相似度搜索
|
||||
/// </summary>
|
||||
/// <param name="topK">返回最相似的结果数量</param>
|
||||
/// <returns>搜索结果</returns>
|
||||
public async Task<SearchResults> SearchVectorAsync(int topK = 5)
|
||||
{
|
||||
var collection = _milvusClient.GetCollection(_collectionName);
|
||||
var random = new Random();
|
||||
|
||||
// 生成随机目标向量
|
||||
var targetVector = new float[_dim];
|
||||
for (int j = 0; j < _dim; j++)
|
||||
{
|
||||
targetVector[j] = (float)random.NextDouble();
|
||||
}
|
||||
|
||||
// 设置搜索参数
|
||||
var searchParameters = new SearchParameters();
|
||||
searchParameters.OutputFields.Add("id");
|
||||
searchParameters.OutputFields.Add("varchar");
|
||||
|
||||
// 执行搜索
|
||||
var searchResults = await collection.SearchAsync(
|
||||
"vector",
|
||||
new[] { (ReadOnlyMemory<float>)targetVector },
|
||||
SimilarityMetricType.L2,
|
||||
topK,
|
||||
searchParameters);
|
||||
|
||||
return searchResults;
|
||||
}
|
||||
}
|
||||
}
|
||||
125
MilvusDemo/MilvusServiceTests.cs
Normal file
125
MilvusDemo/MilvusServiceTests.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Milvus.Client;
|
||||
|
||||
namespace MilvusDemo
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于测试MilvusService的模拟类
|
||||
/// </summary>
|
||||
public class MilvusServiceTests
|
||||
{
|
||||
// 模拟的MilvusService实例
|
||||
private readonly MilvusService _milvusService;
|
||||
|
||||
// 标记各个方法是否被调用
|
||||
public bool InitializeCollectionCalled { get; private set; }
|
||||
public bool CreateIndexAndLoadCalled { get; private set; }
|
||||
public bool InsertRandomDataCalled { get; private set; }
|
||||
public bool SearchVectorCalled { get; private set; }
|
||||
|
||||
// 记录传入的参数
|
||||
public int InsertDataCount { get; private set; }
|
||||
public int SearchTopK { get; private set; }
|
||||
|
||||
public MilvusServiceTests()
|
||||
{
|
||||
// 创建真实的MilvusService实例,但我们将拦截其方法调用
|
||||
_milvusService = new MilvusService("localhost", 19530, "test_collection", 8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟初始化集合的方法
|
||||
/// </summary>
|
||||
public async Task MockInitializeCollectionAsync()
|
||||
{
|
||||
InitializeCollectionCalled = true;
|
||||
Console.WriteLine("[TEST] InitializeCollectionAsync called");
|
||||
|
||||
// 模拟操作延迟
|
||||
await Task.Delay(100);
|
||||
|
||||
Console.WriteLine("[TEST] Collection dropped and created");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟创建索引并加载集合的方法
|
||||
/// </summary>
|
||||
public async Task MockCreateIndexAndLoadAsync()
|
||||
{
|
||||
CreateIndexAndLoadCalled = true;
|
||||
Console.WriteLine("[TEST] CreateIndexAndLoadAsync called");
|
||||
|
||||
// 模拟操作延迟
|
||||
await Task.Delay(100);
|
||||
|
||||
Console.WriteLine("[TEST] Index created");
|
||||
Console.WriteLine("[TEST] Collection loaded");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟插入随机数据的方法
|
||||
/// </summary>
|
||||
public async Task MockInsertRandomDataAsync(int count)
|
||||
{
|
||||
InsertRandomDataCalled = true;
|
||||
InsertDataCount = count;
|
||||
Console.WriteLine($"[TEST] InsertRandomDataAsync called with count: {count}");
|
||||
|
||||
// 模拟操作延迟
|
||||
await Task.Delay(100);
|
||||
|
||||
Console.WriteLine("[TEST] Data inserted");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟向量搜索的方法
|
||||
/// </summary>
|
||||
public async Task<SearchResults> MockSearchVectorAsync(int topK = 5)
|
||||
{
|
||||
SearchVectorCalled = true;
|
||||
SearchTopK = topK;
|
||||
Console.WriteLine($"[TEST] SearchVectorAsync called with topK: {topK}");
|
||||
|
||||
// 模拟操作延迟
|
||||
await Task.Delay(100);
|
||||
|
||||
// 创建模拟的搜索结果
|
||||
var mockIds = new List<long>();
|
||||
for (int i = 0; i < topK; i++)
|
||||
{
|
||||
mockIds.Add(1000 + i); // 使用1000+i作为模拟ID
|
||||
}
|
||||
|
||||
Console.WriteLine("[TEST] Search completed");
|
||||
|
||||
// 注意:这里我们无法直接创建SearchResults实例,因为它可能是内部类
|
||||
// 在实际测试中,您可能需要使用模拟框架如Moq来模拟这个返回值
|
||||
// 这里我们返回null,实际使用时需要处理这种情况
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行完整的测试流程
|
||||
/// </summary>
|
||||
public async Task RunFullTestAsync()
|
||||
{
|
||||
Console.WriteLine("=== Starting MilvusService Test ===");
|
||||
|
||||
await MockInitializeCollectionAsync();
|
||||
await MockCreateIndexAndLoadAsync();
|
||||
await MockInsertRandomDataAsync(1000);
|
||||
await MockSearchVectorAsync(5);
|
||||
|
||||
// 打印测试结果摘要
|
||||
Console.WriteLine("\n=== Test Summary ===");
|
||||
Console.WriteLine($"InitializeCollection Called: {InitializeCollectionCalled}");
|
||||
Console.WriteLine($"CreateIndexAndLoad Called: {CreateIndexAndLoadCalled}");
|
||||
Console.WriteLine($"InsertRandomData Called: {InsertRandomDataCalled} (Count: {InsertDataCount})");
|
||||
Console.WriteLine($"SearchVector Called: {SearchVectorCalled} (TopK: {SearchTopK})");
|
||||
|
||||
Console.WriteLine("=== Test Completed ===");
|
||||
}
|
||||
}
|
||||
}
|
||||
74
MilvusDemo/Program.cs
Normal file
74
MilvusDemo/Program.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MilvusDemo
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Milvus 演示程序");
|
||||
Console.WriteLine("1. 运行实际的 Milvus 操作");
|
||||
Console.WriteLine("2. 运行模拟测试");
|
||||
Console.Write("请选择 (1/2): ");
|
||||
|
||||
string choice = Console.ReadLine();
|
||||
|
||||
if (choice == "2")
|
||||
{
|
||||
await RunTests();
|
||||
}
|
||||
else
|
||||
{
|
||||
await RunActualMilvusOperations();
|
||||
}
|
||||
}
|
||||
|
||||
static async Task RunActualMilvusOperations()
|
||||
{
|
||||
// 初始化MilvusService
|
||||
const string host = "localhost";
|
||||
const int port = 19530;
|
||||
const string collectionName = "csharp_demo_collection";
|
||||
const int dim = 8; // 向量维度
|
||||
|
||||
// 创建MilvusService实例
|
||||
var milvusService = new MilvusService(host, port, collectionName, dim);
|
||||
|
||||
try
|
||||
{
|
||||
// 初始化集合(如果存在则删除并重新创建)
|
||||
await milvusService.InitializeCollectionAsync();
|
||||
|
||||
// 创建索引并加载集合
|
||||
await milvusService.CreateIndexAndLoadAsync();
|
||||
|
||||
// 插入随机数据
|
||||
await milvusService.InsertRandomDataAsync(1000);
|
||||
|
||||
// 执行向量搜索
|
||||
var searchResults = await milvusService.SearchVectorAsync(5);
|
||||
|
||||
// 显示搜索结果
|
||||
Console.WriteLine("\nSearch results:");
|
||||
foreach (var result in searchResults.Ids.LongIds)
|
||||
{
|
||||
Console.WriteLine($"- ID: {result}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Console.WriteLine($"Inner error: {ex.InnerException.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async Task RunTests()
|
||||
{
|
||||
await TestRunner.RunTests();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
MilvusDemo/TestRunner.cs
Normal file
24
MilvusDemo/TestRunner.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MilvusDemo
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于运行MilvusService测试的类
|
||||
/// </summary>
|
||||
public class TestRunner
|
||||
{
|
||||
/// <summary>
|
||||
/// 运行测试
|
||||
/// </summary>
|
||||
public static async Task RunTests()
|
||||
{
|
||||
Console.WriteLine("开始运行MilvusService测试...");
|
||||
|
||||
var tester = new MilvusServiceTests();
|
||||
await tester.RunFullTestAsync();
|
||||
|
||||
Console.WriteLine("测试完成!");
|
||||
}
|
||||
}
|
||||
}
|
||||
69
README.md
Normal file
69
README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# MilvusDemo
|
||||
|
||||
一个使用 C#(.NET 9)与 `Milvus.Client` 的极简示例项目,演示:
|
||||
- 初始化集合(存在则删除并重建)
|
||||
- 创建向量索引与加载集合
|
||||
- 批量插入随机向量
|
||||
- 执行向量相似度搜索
|
||||
- 通过内置测试流程验证调用链
|
||||
|
||||
### 模块划分
|
||||
- **App(入口层)**:`Program.cs`
|
||||
- 提供交互式入口:输入 1=实际连接 Milvus 执行;输入 2=运行内置测试(无需 Milvus 实例)。
|
||||
- **Core/Service(业务层)**:`MilvusService.cs`
|
||||
- 封装与 Milvus 的连接、集合初始化、索引创建/加载、数据插入与搜索。
|
||||
- **Test Harness(测试层)**:`TestRunner.cs`、`MilvusServiceTests.cs`
|
||||
- `TestRunner` 负责编排测试流程;`MilvusServiceTests` 提供无外部依赖的模拟测试(验证调用链与参数)。
|
||||
|
||||
### 环境要求
|
||||
- .NET SDK 9.0+
|
||||
- 仅当选择“实际运行(选项 1)”时需要:可访问的 Milvus Server(默认 `localhost:19530`)。
|
||||
|
||||
### 快速开始
|
||||
1. 构建
|
||||
```powershell
|
||||
dotnet build
|
||||
```
|
||||
2. 运行(测试模式,无需 Milvus)
|
||||
- PowerShell:
|
||||
```powershell
|
||||
echo 2 | dotnet run
|
||||
```
|
||||
- Bash:
|
||||
```bash
|
||||
echo 2 | dotnet run
|
||||
```
|
||||
3. 运行(实际连接 Milvus)
|
||||
- 确保本地或可达的 Milvus 实例运行在 `localhost:19530`。
|
||||
- 程序启动后输入 `1`。
|
||||
|
||||
### 启动 Milvus(可选)
|
||||
若需在本机快速启动 Milvus,可参考官方快速开始,或使用 Docker(示例):
|
||||
|
||||
```bash
|
||||
# 仅供参考,建议按官方文档选择合适的版本与持久化配置
|
||||
docker pull milvusdb/milvus:latest
|
||||
docker run -d --name milvus-standalone -p 19530:19530 milvusdb/milvus:latest
|
||||
```
|
||||
|
||||
文档:`https://milvus.io/docs`(查看“Get Started / Install”)。
|
||||
|
||||
### 主要参数(实际运行模式)
|
||||
- 主机与端口:`localhost:19530`
|
||||
- 集合名:`csharp_demo_collection`
|
||||
- 向量维度:`8`
|
||||
|
||||
> 提示:若需修改连接地址或集合名,请在代码中相应位置调整(例如在连接初始化与集合定义处)。
|
||||
|
||||
### 文件说明
|
||||
- `Program.cs`:控制台入口与菜单;调用服务或测试。
|
||||
- `MilvusService.cs`:核心服务,封装 Milvus 客户端操作。
|
||||
- `MilvusServiceTests.cs`:简单的模拟测试实现(不依赖 Milvus)。
|
||||
- `TestRunner.cs`:运行测试流程的编排类。
|
||||
- `MilvusDemo.csproj`:项目与依赖定义(包含 `Milvus.Client`)。
|
||||
|
||||
### 常见问题(FAQ)
|
||||
- 无法连接到 Milvus:请确认 Milvus 是否已启动并监听 `19530` 端口,以及本机/容器网络连通性。
|
||||
- 还原/编译失败:请确认已安装 .NET 9 SDK,并允许联网下载 NuGet 依赖;必要时执行 `dotnet restore` 后再构建。
|
||||
- 端口占用:如 `19530` 已被占用,请修改容器映射或程序连接端口并保持一致。
|
||||
|
||||
10
VectorDBDemo.slnx
Normal file
10
VectorDBDemo.slnx
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
<Solution>
|
||||
<Projects>
|
||||
<Project type="FAE04EC0-301F-11D3-BF4B-00C04F79EFBC" name="MilvusDemo" path="MilvusDemo\MilvusDemo.csproj" />
|
||||
</Projects>
|
||||
<Folder Name="/src/">
|
||||
<Project Path="MilvusDemo/MilvusDemo.csproj" />
|
||||
<Project Path="VoiceToFunctionDemo/VoiceToFunctionDemo.csproj" />
|
||||
</Folder>
|
||||
</Solution>
|
||||
13
VoiceToFunctionDemo/FunctionInfo.cs
Normal file
13
VoiceToFunctionDemo/FunctionInfo.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace VoiceToFunction
|
||||
{
|
||||
// 函数信息模型(新增参数类型描述,用于校验)
|
||||
public class FunctionInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public List<string> Synonyms { get; set; }
|
||||
public int MinParams { get; set; }
|
||||
public List<Type> ParamTypes { get; set; } // 参数类型列表(如double[], List<User>)
|
||||
public Func<object[], object> Execute { get; set; }
|
||||
}
|
||||
}
|
||||
11
VoiceToFunctionDemo/ParsedResult.cs
Normal file
11
VoiceToFunctionDemo/ParsedResult.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace VoiceToFunction
|
||||
{
|
||||
// 语音解析结果(新增参数类型信息)
|
||||
public class ParsedResult
|
||||
{
|
||||
public string FunctionName { get; set; }
|
||||
public object[] Parameters { get; set; }
|
||||
public string[] ParamTypes { get; set; } // 实际提取的参数类型
|
||||
public string Error { get; set; }
|
||||
}
|
||||
}
|
||||
210
VoiceToFunctionDemo/Program.cs
Normal file
210
VoiceToFunctionDemo/Program.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace VoiceToFunction
|
||||
{
|
||||
|
||||
class Program
|
||||
{
|
||||
// 初始化函数库(明确参数类型要求)
|
||||
private static List<FunctionInfo> functionLibrary = new List<FunctionInfo>
|
||||
{
|
||||
// 1. 乘积函数(参数:两个double数组)
|
||||
new FunctionInfo
|
||||
{
|
||||
Name = "PRODUCT",
|
||||
Description = "计算多列对应行的乘积",
|
||||
Synonyms = new List<string> { "计算乘积", "相乘", "乘积" },
|
||||
MinParams = 2,
|
||||
ParamTypes = new List<Type> { typeof(double[]), typeof(double[]) },
|
||||
Execute = (args) =>
|
||||
{
|
||||
double[] col1 = (double[])args[0];
|
||||
double[] col2 = (double[])args[1];
|
||||
return col1.Zip(col2, (a, b) => a * b).ToArray();
|
||||
}
|
||||
},
|
||||
|
||||
// 2. 筛选函数(参数:用户列表 + 筛选条件)
|
||||
new FunctionInfo
|
||||
{
|
||||
Name = "FILTER",
|
||||
Description = "筛选满足条件的数据",
|
||||
Synonyms = new List<string> { "筛选", "过滤", "选出" },
|
||||
MinParams = 2,
|
||||
ParamTypes = new List<Type> { typeof(List<User>), typeof(Func<User, bool>) },
|
||||
Execute = (args) =>
|
||||
{
|
||||
List<User> users = (List<User>)args[0];
|
||||
Func<User, bool> condition = (Func<User, bool>)args[1];
|
||||
return users.Where(condition).ToList();
|
||||
}
|
||||
},
|
||||
|
||||
// 3. 统计函数(参数:销售额数组 + 统计条件)
|
||||
new FunctionInfo
|
||||
{
|
||||
Name = "COUNTIF",
|
||||
Description = "统计满足条件的记录数",
|
||||
Synonyms = new List<string> { "统计数量", "计数", "数一下" },
|
||||
MinParams = 2,
|
||||
ParamTypes = new List<Type> { typeof(double[]), typeof(Func<double, bool>) },
|
||||
Execute = (args) =>
|
||||
{
|
||||
double[] sales = (double[])args[0];
|
||||
Func<double, bool> condition = (Func<double, bool>)args[1];
|
||||
return sales.Count(condition);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// 测试带复杂参数的语音指令
|
||||
TestVoiceCommand("计算C列和D列的乘积,C列是[1,3,5],D列是[2,4,6]");
|
||||
TestVoiceCommand("计算A列和B列的乘积,A列是[1,3,5],B列是[2,4,6]");
|
||||
TestVoiceCommand("筛选出年龄大于28且姓名包含'李'的用户");
|
||||
TestVoiceCommand("统计销售额在5000到20000之间的订单数,数据是[3000, 8000, 15000, 25000]");
|
||||
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
static void TestVoiceCommand(string voiceText)
|
||||
{
|
||||
Console.WriteLine($"=== 语音指令:{voiceText} ===");
|
||||
ParsedResult result = ParseVoiceCommand(voiceText);
|
||||
|
||||
if (!string.IsNullOrEmpty(result.Error))
|
||||
{
|
||||
Console.WriteLine($"错误:{result.Error}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验参数类型是否匹配函数要求
|
||||
var function = functionLibrary.First(f => f.Name == result.FunctionName);
|
||||
bool paramTypeMatch = function.ParamTypes.Zip(result.ParamTypes,
|
||||
(required, actual) => required.Name == actual).All(match => match);
|
||||
if (!paramTypeMatch)
|
||||
{
|
||||
Console.WriteLine("错误:参数类型与函数要求不匹配");
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行函数并输出结果
|
||||
var executionResult = function.Execute(result.Parameters);
|
||||
Console.WriteLine($"匹配函数:{result.FunctionName}");
|
||||
Console.WriteLine($"执行结果:{GetResultString(executionResult)}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
// 核心:解析语音文本,提取函数+参数(强化入参处理)
|
||||
static ParsedResult ParseVoiceCommand(string text)
|
||||
{
|
||||
var result = new ParsedResult();
|
||||
|
||||
// 1. 匹配函数(同之前逻辑)
|
||||
FunctionInfo matchedFunction = functionLibrary.FirstOrDefault(
|
||||
func => func.Synonyms.Any(syn => text.Contains(syn)));
|
||||
if (matchedFunction == null)
|
||||
{
|
||||
result.Error = "未找到匹配的函数";
|
||||
return result;
|
||||
}
|
||||
result.FunctionName = matchedFunction.Name;
|
||||
|
||||
// 2. 提取并解析参数(根据函数类型动态处理)
|
||||
try
|
||||
{
|
||||
switch (matchedFunction.Name)
|
||||
{
|
||||
case "PRODUCT":
|
||||
// 提取列数据(支持显式输入如"[1,3,5]")
|
||||
var colDataMatches = Regex.Matches(text, @"\[([\d, ]+)\]")
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Groups[1].Value.Split(',').Select(double.Parse).ToArray())
|
||||
.ToList();
|
||||
if (colDataMatches.Count < 2)
|
||||
{
|
||||
result.Error = "PRODUCT需要至少两列数据,格式如'[1,2,3]'";
|
||||
return result;
|
||||
}
|
||||
result.Parameters = new object[] { colDataMatches[0], colDataMatches[1] };
|
||||
result.ParamTypes = new[] { "Double[]", "Double[]" };
|
||||
break;
|
||||
|
||||
case "FILTER":
|
||||
// 提取年龄条件(如"大于28")和姓名条件(如"包含'李'")
|
||||
int ageThreshold = 0;
|
||||
string nameKeyword = null;
|
||||
|
||||
var ageMatch = Regex.Match(text, @"年龄(大于|小于|等于)(\d+)");
|
||||
if (ageMatch.Success)
|
||||
{
|
||||
string op = ageMatch.Groups[1].Value;
|
||||
ageThreshold = int.Parse(ageMatch.Groups[2].Value);
|
||||
}
|
||||
|
||||
var nameMatch = Regex.Match(text, @"姓名包含'([^']+)'");
|
||||
if (nameMatch.Success)
|
||||
{
|
||||
nameKeyword = nameMatch.Groups[1].Value;
|
||||
}
|
||||
|
||||
// 组合多条件筛选器
|
||||
Func<User, bool> condition = u =>
|
||||
(ageMatch.Success ? (ageMatch.Groups[1].Value == "大于" ? u.Age > ageThreshold : u.Age < ageThreshold) : true) &&
|
||||
(nameMatch.Success ? u.Name.Contains(nameKeyword) : true);
|
||||
|
||||
// 模拟用户数据(实际应从数据源获取)
|
||||
var users = new List<User>
|
||||
{
|
||||
new User { Name = "张三", Age = 25 },
|
||||
new User { Name = "李四", Age = 30 },
|
||||
new User { Name = "李白", Age = 29 },
|
||||
new User { Name = "王五", Age = 35 }
|
||||
};
|
||||
|
||||
result.Parameters = new object[] { users, condition };
|
||||
result.ParamTypes = new[] { "List<User>", "Func<User, Boolean>" };
|
||||
break;
|
||||
|
||||
case "COUNTIF":
|
||||
// 提取数值范围(如"5000到20000")和数据
|
||||
var rangeMatch = Regex.Match(text, @"(\d+)到(\d+)");
|
||||
if (!rangeMatch.Success)
|
||||
{
|
||||
result.Error = "COUNTIF需要范围条件,如'5000到20000'";
|
||||
return result;
|
||||
}
|
||||
double min = double.Parse(rangeMatch.Groups[1].Value);
|
||||
double max = double.Parse(rangeMatch.Groups[2].Value);
|
||||
|
||||
// 提取销售额数据
|
||||
var salesData = Regex.Match(text, @"\[([\d, ]+)\]")
|
||||
.Groups[1].Value.Split(',').Select(double.Parse).ToArray();
|
||||
|
||||
result.Parameters = new object[] { salesData, (Func<double, bool>)(s => s >= min && s <= max) };
|
||||
result.ParamTypes = new[] { "Double[]", "Func<Double, Boolean>" };
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Error = $"参数解析失败:{ex.Message}";
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static string GetResultString(object result)
|
||||
{
|
||||
if (result is double[] nums) return string.Join(", ", nums);
|
||||
if (result is List<User> users) return string.Join(", ", users.Select(u => $"{u.Name}({u.Age})"));
|
||||
if (result is int count) return count.ToString();
|
||||
return "未知结果";
|
||||
}
|
||||
}
|
||||
}
|
||||
8
VoiceToFunctionDemo/User.cs
Normal file
8
VoiceToFunctionDemo/User.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace VoiceToFunction
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Age { get; set; }
|
||||
}
|
||||
}
|
||||
13
VoiceToFunctionDemo/VoiceToFunctionDemo.csproj
Normal file
13
VoiceToFunctionDemo/VoiceToFunctionDemo.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user