diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d2a221f --- /dev/null +++ b/.vscode/launch.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d546372 --- /dev/null +++ b/.vscode/tasks.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/MilvusDemo/MilvusDemo.csproj b/MilvusDemo/MilvusDemo.csproj new file mode 100644 index 0000000..7b77bb2 --- /dev/null +++ b/MilvusDemo/MilvusDemo.csproj @@ -0,0 +1,14 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + diff --git a/MilvusDemo/MilvusService.cs b/MilvusDemo/MilvusService.cs new file mode 100644 index 0000000..f72bcb5 --- /dev/null +++ b/MilvusDemo/MilvusService.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Milvus.Client; + +namespace MilvusDemo +{ + /// + /// 提供与Milvus向量数据库交互的服务类 + /// + public class MilvusService + { + private readonly MilvusClient _milvusClient; + private readonly string _collectionName; + private readonly int _dim; + + /// + /// 初始化MilvusService的新实例 + /// + /// Milvus服务器主机名 + /// Milvus服务器端口 + /// 集合名称 + /// 向量维度 + public MilvusService(string host, int port, string collectionName, int dim) + { + _milvusClient = new MilvusClient(host, port); + _collectionName = collectionName; + _dim = dim; + } + + /// + /// 初始化集合,如果存在则删除并重新创建 + /// + 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("id", isPrimaryKey: true), + FieldSchema.CreateVarchar("varchar", 256), + FieldSchema.CreateFloatVector("vector", _dim) + }); + Console.WriteLine($"Collection '{_collectionName}' created."); + } + + /// + /// 创建索引并加载集合 + /// + 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."); + } + + /// + /// 插入随机向量数据 + /// + /// 要插入的向量数量 + public async Task InsertRandomDataAsync(int count) + { + var collection = _milvusClient.GetCollection(_collectionName); + var random = new Random(); + + // 生成随机向量 + var vectors = new List>(); + 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.Create("id", ids), + FieldData.Create("varchar", varchars), + FieldData.CreateFloatVector("vector", vectors) + }; + + // 执行插入 + await collection.InsertAsync(insertData); + Console.WriteLine("Data inserted."); + } + + /// + /// 执行向量相似度搜索 + /// + /// 返回最相似的结果数量 + /// 搜索结果 + public async Task 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)targetVector }, + SimilarityMetricType.L2, + topK, + searchParameters); + + return searchResults; + } + } +} \ No newline at end of file diff --git a/MilvusDemo/MilvusServiceTests.cs b/MilvusDemo/MilvusServiceTests.cs new file mode 100644 index 0000000..1f65522 --- /dev/null +++ b/MilvusDemo/MilvusServiceTests.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Milvus.Client; + +namespace MilvusDemo +{ + /// + /// 用于测试MilvusService的模拟类 + /// + 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); + } + + /// + /// 模拟初始化集合的方法 + /// + public async Task MockInitializeCollectionAsync() + { + InitializeCollectionCalled = true; + Console.WriteLine("[TEST] InitializeCollectionAsync called"); + + // 模拟操作延迟 + await Task.Delay(100); + + Console.WriteLine("[TEST] Collection dropped and created"); + } + + /// + /// 模拟创建索引并加载集合的方法 + /// + 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"); + } + + /// + /// 模拟插入随机数据的方法 + /// + 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"); + } + + /// + /// 模拟向量搜索的方法 + /// + public async Task MockSearchVectorAsync(int topK = 5) + { + SearchVectorCalled = true; + SearchTopK = topK; + Console.WriteLine($"[TEST] SearchVectorAsync called with topK: {topK}"); + + // 模拟操作延迟 + await Task.Delay(100); + + // 创建模拟的搜索结果 + var mockIds = new List(); + for (int i = 0; i < topK; i++) + { + mockIds.Add(1000 + i); // 使用1000+i作为模拟ID + } + + Console.WriteLine("[TEST] Search completed"); + + // 注意:这里我们无法直接创建SearchResults实例,因为它可能是内部类 + // 在实际测试中,您可能需要使用模拟框架如Moq来模拟这个返回值 + // 这里我们返回null,实际使用时需要处理这种情况 + return null; + } + + /// + /// 运行完整的测试流程 + /// + 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 ==="); + } + } +} \ No newline at end of file diff --git a/MilvusDemo/Program.cs b/MilvusDemo/Program.cs new file mode 100644 index 0000000..e83b03c --- /dev/null +++ b/MilvusDemo/Program.cs @@ -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(); + } + } +} diff --git a/MilvusDemo/TestRunner.cs b/MilvusDemo/TestRunner.cs new file mode 100644 index 0000000..8b190c9 --- /dev/null +++ b/MilvusDemo/TestRunner.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading.Tasks; + +namespace MilvusDemo +{ + /// + /// 用于运行MilvusService测试的类 + /// + public class TestRunner + { + /// + /// 运行测试 + /// + public static async Task RunTests() + { + Console.WriteLine("开始运行MilvusService测试..."); + + var tester = new MilvusServiceTests(); + await tester.RunFullTestAsync(); + + Console.WriteLine("测试完成!"); + } + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f884d7a --- /dev/null +++ b/README.md @@ -0,0 +1,109 @@ +# 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`。 + +### 在解决方案根目录运行(多项目) +本仓库包含多个示例项目。可在解决方案根目录使用 `--project` 指定要运行的项目: + +```powershell +# 运行 Milvus 示例(默认交互菜单 1/2) +dotnet run --project MilvusDemo + +# 运行语音到函数示例 +dotnet run --project VoiceToFunctionDemo +``` + +### 启动 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`)。 + +### 项目结构 +- `MilvusDemo/`:Milvus 向量数据库最小示例,支持实际连接与模拟测试两种模式。 +- `VoiceToFunctionDemo/`:语音(文本)解析为函数调用的示例,展示如何从自然语言中抽取参数并调用预定义函数。 + +--- + +## VoiceToFunctionDemo + +一个将中文自然语言指令解析为函数调用的控制台示例。内置函数库包含: +- `PRODUCT(double[], double[])`:计算两列数值的逐项乘积 +- `FILTER(List, Func)`:按条件筛选用户(支持年龄阈值与姓名包含) +- `COUNTIF(double[], Func)`:按范围统计数量 + +### 运行 +```powershell +dotnet run --project VoiceToFunctionDemo +``` + +程序内置了示例输入,启动后会依次演示: +- 计算两列数值的乘积(如 `[1,3,5]` 与 `[2,4,6]`) +- 筛选年龄大于某值且姓名包含关键字的用户 +- 统计销售额处于指定区间内的订单数 + +如需扩展,请修改 `VoiceToFunctionDemo/Program.cs`: +- 新增函数:在 `functionLibrary` 中增加 `FunctionInfo`,定义参数类型与执行逻辑。 +- 扩展解析:在 `ParseVoiceCommand` 中为新函数添加参数解析与类型校验。 + +--- + +### 常见问题(FAQ) +- 无法连接到 Milvus:请确认 Milvus 是否已启动并监听 `19530` 端口,以及本机/容器网络连通性。 +- 还原/编译失败:请确认已安装 .NET 9 SDK,并允许联网下载 NuGet 依赖;必要时执行 `dotnet restore` 后再构建。 +- 端口占用:如 `19530` 已被占用,请修改容器映射或程序连接端口并保持一致。 + diff --git a/VectorDBDemo.slnx b/VectorDBDemo.slnx new file mode 100644 index 0000000..72029d1 --- /dev/null +++ b/VectorDBDemo.slnx @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/VoiceToFunctionDemo/FunctionInfo.cs b/VoiceToFunctionDemo/FunctionInfo.cs new file mode 100644 index 0000000..8774fed --- /dev/null +++ b/VoiceToFunctionDemo/FunctionInfo.cs @@ -0,0 +1,13 @@ +namespace VoiceToFunction +{ + // 函数信息模型(新增参数类型描述,用于校验) + public class FunctionInfo + { + public string Name { get; set; } + public string Description { get; set; } + public List Synonyms { get; set; } + public int MinParams { get; set; } + public List ParamTypes { get; set; } // 参数类型列表(如double[], List) + public Func Execute { get; set; } + } +} diff --git a/VoiceToFunctionDemo/ParsedResult.cs b/VoiceToFunctionDemo/ParsedResult.cs new file mode 100644 index 0000000..13af2af --- /dev/null +++ b/VoiceToFunctionDemo/ParsedResult.cs @@ -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; } + } +} diff --git a/VoiceToFunctionDemo/Program.cs b/VoiceToFunctionDemo/Program.cs new file mode 100644 index 0000000..3a3318a --- /dev/null +++ b/VoiceToFunctionDemo/Program.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace VoiceToFunction +{ + + class Program + { + // 初始化函数库(明确参数类型要求) + private static List functionLibrary = new List + { + // 1. 乘积函数(参数:两个double数组) + new FunctionInfo + { + Name = "PRODUCT", + Description = "计算多列对应行的乘积", + Synonyms = new List { "计算乘积", "相乘", "乘积" }, + MinParams = 2, + ParamTypes = new List { 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 { "筛选", "过滤", "选出" }, + MinParams = 2, + ParamTypes = new List { typeof(List), typeof(Func) }, + Execute = (args) => + { + List users = (List)args[0]; + Func condition = (Func)args[1]; + return users.Where(condition).ToList(); + } + }, + + // 3. 统计函数(参数:销售额数组 + 统计条件) + new FunctionInfo + { + Name = "COUNTIF", + Description = "统计满足条件的记录数", + Synonyms = new List { "统计数量", "计数", "数一下" }, + MinParams = 2, + ParamTypes = new List { typeof(double[]), typeof(Func) }, + Execute = (args) => + { + double[] sales = (double[])args[0]; + Func condition = (Func)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() + .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 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 + { + 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", "Func" }; + 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)(s => s >= min && s <= max) }; + result.ParamTypes = new[] { "Double[]", "Func" }; + 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 users) return string.Join(", ", users.Select(u => $"{u.Name}({u.Age})")); + if (result is int count) return count.ToString(); + return "未知结果"; + } + } +} diff --git a/VoiceToFunctionDemo/User.cs b/VoiceToFunctionDemo/User.cs new file mode 100644 index 0000000..cd60f8e --- /dev/null +++ b/VoiceToFunctionDemo/User.cs @@ -0,0 +1,8 @@ +namespace VoiceToFunction +{ + public class User + { + public string Name { get; set; } + public int Age { get; set; } + } +} diff --git a/VoiceToFunctionDemo/VoiceToFunctionDemo.csproj b/VoiceToFunctionDemo/VoiceToFunctionDemo.csproj new file mode 100644 index 0000000..cc9a686 --- /dev/null +++ b/VoiceToFunctionDemo/VoiceToFunctionDemo.csproj @@ -0,0 +1,13 @@ + + + + Exe + net9.0 + enable + enable + + + + + +