编辑
2025-11-24
C#
00

目录

💡 问题分析:传统数据查询的三大痛点
🔍 痛点一:SQL门槛高
📊 痛点二:数据结构复杂
⚡ 痛点三:重复工作多
🛠️ 解决方案:AI驱动的智能查询系统
🏗️ 系统架构图
🔥 代码实战:手把手实现智能数据助手
第一步:搭建SCADA数据库结构
第二步:智能插件注册核心
第三步:流式对话体验优化
完整代码
🎮 实际应用场景演示
场景一:设备状态查询
场景二:复杂统计分析
⚠️ 常见坑点提醒
坑点一:SQL注入风险
坑点二:AI幻觉问题
坑点三:性能问题
🚀 性能优化技巧
技巧一:智能缓存机制
技巧二:连接池优化
🎯 结尾总结
💬 互动时间

这个算是一个简单应用,前面文章我写过ai插件开发,结合着看最好了,可能为你提供更多想法。在工业自动化领域,SCADA系统每天产生海量的设备数据、报警信息和历史记录。作为开发者,你是否遇到过这样的痛点:业务人员总是找你写SQL查询各种数据报表,而你又要花大量时间理解他们的需求?

今天我要分享一个革命性的解决方案:结合SQLite数据库和AI大模型,打造一个能"听懂人话"的智能数据助手。用户只需用自然语言描述需求,系统就能自动生成SQL并返回结果!

让我们一起看看如何用C#实现这个酷炫的功能,彻底解放你的双手!

💡 问题分析:传统数据查询的三大痛点

🔍 痛点一:SQL门槛高

业务人员不懂SQL语法,每次查询都要找开发人员,效率低下。

📊 痛点二:数据结构复杂

SCADA系统表结构复杂,外键关系多,即使是开发人员也容易写错查询语句。

⚡ 痛点三:重复工作多

类似的查询需求反复出现,开发人员疲于应付各种"临时需求"。

🛠️ 解决方案:AI驱动的智能查询系统

我们的解决方案包含三个核心组件:

  1. SQLite数据库 - 轻量级、高性能的数据存储
  2. Semantic Kernel - 微软的AI编排框架
  3. DeepSeek API - 强大的中文语言模型

🏗️ 系统架构图

Markdown
用户自然语言输入 → AI理解需求 → 自动生成SQL → 执行查询 → 格式化输出

🔥 代码实战:手把手实现智能数据助手

第一步:搭建SCADA数据库结构

我们首先创建一个完整的SCADA数据库,包含设备、测点、实时数据、历史数据和报警等核心表:

C#
public static class DatabaseInitializer { /// <summary> /// 创建SCADA相关数据表 /// </summary> private static void CreateTables(SQLiteConnection connection) { // 设备表 - 存储所有监控设备信息 string createDevicesTable = @" CREATE TABLE Devices ( DeviceId INTEGER PRIMARY KEY AUTOINCREMENT, DeviceName TEXT NOT NULL, DeviceType TEXT NOT NULL, -- PLC、HMI、传感器等 Location TEXT, -- 设备位置 Status TEXT, -- 运行、停机、维护、故障 InstallDate TEXT, LastMaintenance TEXT );"; // 测点表 - 存储监控点位信息 string createTagsTable = @" CREATE TABLE Tags ( TagId INTEGER PRIMARY KEY AUTOINCREMENT, TagName TEXT NOT NULL UNIQUE, DeviceId INTEGER, TagType TEXT, -- AI/AO/DI/DO Unit TEXT, -- 单位:℃、MPa等 MinValue REAL, MaxValue REAL, AlarmHigh REAL, -- 高报警阈值 AlarmLow REAL, -- 低报警阈值 Description TEXT, FOREIGN KEY (DeviceId) REFERENCES Devices(DeviceId) );"; // 实时数据表 - 当前最新数据 string createRealTimeDataTable = @" CREATE TABLE RealTimeData ( Id INTEGER PRIMARY KEY AUTOINCREMENT, TagId INTEGER, TagName TEXT, Value REAL, -- 当前值 Quality INTEGER, -- 数据质量 0-100 Timestamp TEXT, AlarmStatus TEXT, -- 正常、预警、报警、严重 FOREIGN KEY (TagId) REFERENCES Tags(TagId) );"; // 更多表结构... } }

💡 设计亮点:

  • 表结构完全参照工业标准SCADA系统设计
  • 合理的外键关系确保数据一致性
  • 中文字段注释便于理解业务含义

第二步:智能插件注册核心

这是整个系统的灵魂部分 - 将数据库操作包装成AI可调用的函数:

C#
static void RegisterDatabaseQueryPlugin(Kernel kernel) { // 🔍 函数1:获取数据库结构信息 var getSchemaFunction = kernel.CreateFunctionFromMethod( method: () => { using var connection = new SQLiteConnection($"Data Source=test.db;Version=3;"); connection.Open(); var schema = GetDatabaseSchema(connection); return schema; }, functionName: "GetDatabaseSchema", description: "获取数据库的完整结构信息,包括所有表、字段、数据类型等" ); // 🧠 函数2:智能分析查询需求 var smartQueryFunction = kernel.CreateFunctionFromMethod( method: (string userQuestion) => { using var connection = new SQLiteConnection($"Data Source=test.db;Version=3;"); connection.Open(); // 获取数据库结构供AI参考 var schema = GetDatabaseSchema(connection); return $"数据库结构信息:\n{schema}\n\n用户问题:{userQuestion}\n\n请根据上述数据库结构生成合适的SQL查询语句。"; }, functionName: "AnalyzeQueryRequest", description: "分析用户查询需求并提供数据库结构信息,帮助生成正确的SQL语句" ); // ⚡ 函数3:安全执行SQL查询 var executeQueryFunction = kernel.CreateFunctionFromMethod( method: (string sql) => { // 安全检查:只允许SELECT语句 if (!sql.Trim().ToUpper().StartsWith("SELECT")) { return "⚠️ 出于安全考虑,只允许执行SELECT查询语句。"; } using var connection = new SQLiteConnection($"Data Source=test.db;Version=3;"); connection.Open(); using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); var dt = new DataTable(); dt.Load(reader); return FormatQueryResult(dt); }, functionName: "ExecuteSQL", description: "执行SQL查询语句并返回格式化的结果" ); // 注册为AI插件 kernel.ImportPluginFromFunctions("DatabasePlugin", [getSchemaFunction, smartQueryFunction, executeQueryFunction]); }

🔥 关键技术点:

  1. 函数式编程思想 - 将数据库操作封装为纯函数
  2. 安全防护机制 - 只允许SELECT查询,防止数据被误删
  3. 智能结构感知 - AI能够理解完整的数据库结构

第三步:流式对话体验优化

为了提供更好的用户体验,我们实现了类似ChatGPT的流式输出

C#
static async Task ProcessAIResponse(Kernel kernel, ChatHistory chatHistory) { var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>(); // 🎯 配置自动函数调用 var executionSettings = new OpenAIPromptExecutionSettings { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, MaxTokens = 2000, Temperature = 0.7 // 适中的创造性,确保SQL准确性 }; // 🌊 流式响应处理 var streamResponse = chatCompletionService.GetStreamingChatMessageContentsAsync( chatHistory: chatHistory, executionSettings: executionSettings, kernel: kernel ); string fullResponse = ""; bool isFirstToken = true; await foreach (var content in streamResponse) { if (!string.IsNullOrEmpty(content.Content)) { // ⏱️ 模拟思考延迟,提升用户体验 if (isFirstToken) { await Task.Delay(100); isFirstToken = false; } Console.Write(content.Content); fullResponse += content.Content; await Task.Delay(20); // 自然的打字效果 } } // 保存完整对话历史 if (!string.IsNullOrWhiteSpace(fullResponse)) { chatHistory.AddAssistantMessage(fullResponse); } }

完整代码

C#
using System.Text; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; using Microsoft.SemanticKernel; using System.Data; using System.Data.SQLite; namespace AppDbAiAgent { internal class Program { static async Task Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; // 检查是否需要初始化数据库 if (!File.Exists("test.db")) { Console.WriteLine("🔧 正在初始化SCADA数据库..."); DatabaseInitializer.InitializeScadaDatabase(); Console.WriteLine(); } // 显示数据库统计信息 DatabaseInitializer.ShowDatabaseStatistics(); Console.WriteLine(); // Kernel 初始化及AI服务连接 var kernel = InitializeKernel(); // 插件注册 RegisterDatabaseQueryPlugin(kernel); // 启动聊天循环 await StartChatLoop(kernel); } /// <summary> /// 初始化Kernel /// </summary> static Kernel InitializeKernel() { var kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatCompletion( modelId: "deepseek-chat", apiKey: Environment.GetEnvironmentVariable("DEEPSEEK_API_KEY") ?? "sk-xxxx", endpoint: new Uri("https://api.deepseek.com/v1") ); return kernelBuilder.Build(); } /// <summary> /// 启动聊天循环 /// </summary> static async Task StartChatLoop(Kernel kernel) { // 聊天历史管理 var chatHistory = new ChatHistory(); chatHistory.AddSystemMessage(@"你是一个智能学习助手,具备数据查询与分析能力, 请根据用户需求选择合适的工具来协助用户。"); Console.WriteLine("🤖 欢迎使用智能AI数据助手!"); Console.WriteLine("📝 输入 'exit' 可随时退出\n"); while (true) { Console.Write("👤 您: "); string userInput = Console.ReadLine(); if (string.IsNullOrWhiteSpace(userInput)) continue; if (userInput.Trim().ToLower() == "exit") break; // 添加用户消息到历史 chatHistory.AddUserMessage(userInput); try { Console.Write("🤖 AI助手: "); // 处理AI响应 await ProcessAIResponse(kernel, chatHistory); } catch (Exception ex) { Console.WriteLine($"❌ 发生错误: {ex.Message}\n"); } } Console.WriteLine("👋 感谢使用,再见!"); } /// <summary> /// 处理AI响应 /// </summary> static async Task ProcessAIResponse(Kernel kernel, ChatHistory chatHistory) { // 获取聊天完成服务 var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>(); // 配置执行设置 - 启用自动函数调用 var executionSettings = new OpenAIPromptExecutionSettings { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, MaxTokens = 2000, Temperature = 0.7 }; // 流式响应输出 var streamResponse = chatCompletionService.GetStreamingChatMessageContentsAsync( chatHistory: chatHistory, executionSettings: executionSettings, kernel: kernel ); string fullResponse = ""; bool isFirstToken = true; await foreach (var content in streamResponse) { if (!string.IsNullOrEmpty(content.Content)) { // 第一个token前添加一个小延迟,模拟思考 if (isFirstToken) { await Task.Delay(100); isFirstToken = false; } Console.Write(content.Content); fullResponse += content.Content; // 添加微小延迟,让输出更自然 await Task.Delay(20); } } Console.WriteLine("\n"); // 将完整响应添加到聊天历史 if (!string.IsNullOrWhiteSpace(fullResponse)) { chatHistory.AddAssistantMessage(fullResponse); } } static void RegisterDatabaseQueryPlugin(Kernel kernel) { // 1. 获取数据库结构的函数 var getSchemaFunction = kernel.CreateFunctionFromMethod( method: () => { try { var dbFilePath = "test.db"; if (!File.Exists(dbFilePath)) return "数据库文件不存在。"; using var connection = new SQLiteConnection($"Data Source={dbFilePath};Version=3;"); connection.Open(); var schema = GetDatabaseSchema(connection); return schema; } catch (Exception ex) { return $"获取数据库结构失败: {ex.Message}"; } }, functionName: "GetDatabaseSchema", description: "获取数据库的完整结构信息,包括所有表、字段、数据类型等" ); // 2. 智能SQL查询函数(带结构感知) var smartQueryFunction = kernel.CreateFunctionFromMethod( method: (string userQuestion) => { try { var dbFilePath = "test.db"; if (!File.Exists(dbFilePath)) return "数据库文件不存在。"; using var connection = new SQLiteConnection($"Data Source={dbFilePath};Version=3;"); connection.Open(); // 获取数据库结构 var schema = GetDatabaseSchema(connection); // 返回结构信息供AI参考 return $"数据库结构信息:\n{schema}\n\n用户问题:{userQuestion}\n\n请根据上述数据库结构生成合适的SQL查询语句。"; } catch (Exception ex) { return $"查询失败: {ex.Message}"; } }, functionName: "AnalyzeQueryRequest", description: "分析用户查询需求并提供数据库结构信息,帮助生成正确的SQL语句" ); // 3. 执行SQL的函数 var executeQueryFunction = kernel.CreateFunctionFromMethod( method: (string sql) => { try { var dbFilePath = "test.db"; if (!File.Exists(dbFilePath)) return "数据库文件不存在。"; using var connection = new SQLiteConnection($"Data Source={dbFilePath};Version=3;"); connection.Open(); // 安全检查:只允许SELECT语句 if (!sql.Trim().ToUpper().StartsWith("SELECT")) { return "⚠️ 出于安全考虑,只允许执行SELECT查询语句。"; } using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); var dt = new DataTable(); dt.Load(reader); if (dt.Rows.Count == 0) return "查询结果为空。"; return FormatQueryResult(dt); } catch (Exception ex) { return $"SQL执行出错: {ex.Message}"; } }, functionName: "ExecuteSQL", description: "执行SQL查询语句并返回格式化的结果" ); kernel.ImportPluginFromFunctions("DatabasePlugin", [getSchemaFunction, smartQueryFunction, executeQueryFunction]); } /// <summary> /// 获取数据库结构信息 /// </summary> private static string GetDatabaseSchema(SQLiteConnection connection) { var schema = new StringBuilder(); schema.AppendLine("📊 SCADA数据库结构信息"); schema.AppendLine("=" + new string('=', 50)); // 获取所有表名 var tables = GetTableNames(connection); foreach (var tableName in tables) { schema.AppendLine($"\n🔹 表名: {tableName}"); schema.AppendLine($" 字段信息:"); // 获取表结构 var tableInfo = GetTableStructure(connection, tableName); foreach (var column in tableInfo) { schema.AppendLine($" - {column.Name} ({column.Type}) {(column.IsPrimaryKey ? "[主键]" : "")} {(column.IsNotNull ? "[非空]" : "")}"); } // 获取记录数 var count = GetTableRecordCount(connection, tableName); schema.AppendLine($" 📋 记录数: {count}"); // 获取外键信息 var foreignKeys = GetForeignKeys(connection, tableName); if (foreignKeys.Any()) { schema.AppendLine($" 🔗 外键关系:"); foreach (var fk in foreignKeys) { schema.AppendLine($" - {fk.Column} -> {fk.RefTable}.{fk.RefColumn}"); } } } // 这里有用处,如果是sqlserver,我们可以用自动功能提取常用sql脚本,效果会更好 schema.AppendLine($"\n💡 使用示例:"); schema.AppendLine($" - 查询所有设备: SELECT * FROM Devices"); schema.AppendLine($" - 查询运行状态的设备: SELECT * FROM Devices WHERE Status = '运行'"); schema.AppendLine($" - 查询最近的报警: SELECT * FROM Alarms ORDER BY AlarmTime DESC LIMIT 10"); schema.AppendLine($" - 统计设备类型: SELECT DeviceType, COUNT(*) FROM Devices GROUP BY DeviceType"); return schema.ToString(); } /// <summary> /// 获取所有表名 /// </summary> private static List<string> GetTableNames(SQLiteConnection connection) { var tables = new List<string>(); string sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"; using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); while (reader.Read()) { tables.Add(reader.GetString(0)); } return tables; } /// <summary> /// 获取表结构信息 /// </summary> private static List<ColumnInfo> GetTableStructure(SQLiteConnection connection, string tableName) { var columns = new List<ColumnInfo>(); string sql = $"PRAGMA table_info({tableName})"; using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); while (reader.Read()) { columns.Add(new ColumnInfo { Name = reader.GetString("name"), Type = reader.GetString("type"), IsNotNull = reader.GetBoolean("notnull"), IsPrimaryKey = reader.GetBoolean("pk") }); } return columns; } /// <summary> /// 获取表记录数 /// </summary> private static int GetTableRecordCount(SQLiteConnection connection, string tableName) { string sql = $"SELECT COUNT(*) FROM {tableName}"; using var command = new SQLiteCommand(sql, connection); return Convert.ToInt32(command.ExecuteScalar()); } /// <summary> /// 获取外键信息 /// </summary> private static List<ForeignKeyInfo> GetForeignKeys(SQLiteConnection connection, string tableName) { var foreignKeys = new List<ForeignKeyInfo>(); string sql = $"PRAGMA foreign_key_list({tableName})"; using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); while (reader.Read()) { foreignKeys.Add(new ForeignKeyInfo { Column = reader.GetString("from"), RefTable = reader.GetString("table"), RefColumn = reader.GetString("to") }); } return foreignKeys; } /// <summary> /// 格式化查询结果 /// </summary> private static string FormatQueryResult(DataTable dt) { var result = new StringBuilder(); result.AppendLine($"📋 查询结果 (共 {dt.Rows.Count} 条记录):"); result.AppendLine(new string('=', 60)); // 列标题 foreach (DataColumn col in dt.Columns) { result.Append($"{col.ColumnName,-15} | "); } result.AppendLine(); result.AppendLine(new string('-', 60)); // 数据行(限制显示前20行) int maxRows = Math.Min(20, dt.Rows.Count); for (int i = 0; i < maxRows; i++) { foreach (var item in dt.Rows[i].ItemArray) { string value = item?.ToString() ?? "NULL"; if (value.Length > 12) value = value.Substring(0, 12) + "..."; result.Append($"{value,-15} | "); } result.AppendLine(); } if (dt.Rows.Count > 20) { result.AppendLine($"... 还有 {dt.Rows.Count - 20} 条记录未显示"); } return result.ToString(); } /// <summary> /// 列信息类 /// </summary> public class ColumnInfo { public string Name { get; set; } public string Type { get; set; } public bool IsNotNull { get; set; } public bool IsPrimaryKey { get; set; } } /// <summary> /// 外键信息类 /// </summary> public class ForeignKeyInfo { public string Column { get; set; } public string RefTable { get; set; } public string RefColumn { get; set; } } } }
C#
using System.Text; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; using Microsoft.SemanticKernel; using System.Data; using System.Data.SQLite; namespace AppDbAiAgent { internal class Program { static async Task Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; // 检查是否需要初始化数据库 if (!File.Exists("test.db")) { Console.WriteLine("🔧 正在初始化SCADA数据库..."); DatabaseInitializer.InitializeScadaDatabase(); Console.WriteLine(); } // 显示数据库统计信息 DatabaseInitializer.ShowDatabaseStatistics(); Console.WriteLine(); // Kernel 初始化及AI服务连接 var kernel = InitializeKernel(); // 插件注册 RegisterDatabaseQueryPlugin(kernel); // 启动聊天循环 await StartChatLoop(kernel); } /// <summary> /// 初始化Kernel /// </summary> static Kernel InitializeKernel() { var kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatCompletion( modelId: "deepseek-chat", apiKey: Environment.GetEnvironmentVariable("DEEPSEEK_API_KEY") ?? "sk-xxxx", endpoint: new Uri("https://api.deepseek.com/v1") ); return kernelBuilder.Build(); } /// <summary> /// 启动聊天循环 /// </summary> static async Task StartChatLoop(Kernel kernel) { // 聊天历史管理 var chatHistory = new ChatHistory(); chatHistory.AddSystemMessage(@"你是一个智能学习助手,具备数据查询与分析能力, 请根据用户需求选择合适的工具来协助用户。"); Console.WriteLine("🤖 欢迎使用智能AI数据助手!"); Console.WriteLine("📝 输入 'exit' 可随时退出\n"); while (true) { Console.Write("👤 您: "); string userInput = Console.ReadLine(); if (string.IsNullOrWhiteSpace(userInput)) continue; if (userInput.Trim().ToLower() == "exit") break; // 添加用户消息到历史 chatHistory.AddUserMessage(userInput); try { Console.Write("🤖 AI助手: "); // 处理AI响应 await ProcessAIResponse(kernel, chatHistory); } catch (Exception ex) { Console.WriteLine($"❌ 发生错误: {ex.Message}\n"); } } Console.WriteLine("👋 感谢使用,再见!"); } /// <summary> /// 处理AI响应 /// </summary> static async Task ProcessAIResponse(Kernel kernel, ChatHistory chatHistory) { // 获取聊天完成服务 var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>(); // 配置执行设置 - 启用自动函数调用 var executionSettings = new OpenAIPromptExecutionSettings { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, MaxTokens = 2000, Temperature = 0.7 }; // 流式响应输出 var streamResponse = chatCompletionService.GetStreamingChatMessageContentsAsync( chatHistory: chatHistory, executionSettings: executionSettings, kernel: kernel ); string fullResponse = ""; bool isFirstToken = true; await foreach (var content in streamResponse) { if (!string.IsNullOrEmpty(content.Content)) { // 第一个token前添加一个小延迟,模拟思考 if (isFirstToken) { await Task.Delay(100); isFirstToken = false; } Console.Write(content.Content); fullResponse += content.Content; // 添加微小延迟,让输出更自然 await Task.Delay(20); } } Console.WriteLine("\n"); // 将完整响应添加到聊天历史 if (!string.IsNullOrWhiteSpace(fullResponse)) { chatHistory.AddAssistantMessage(fullResponse); } } static void RegisterDatabaseQueryPlugin(Kernel kernel) { // 1. 获取数据库结构的函数 var getSchemaFunction = kernel.CreateFunctionFromMethod( method: () => { try { var dbFilePath = "test.db"; if (!File.Exists(dbFilePath)) return "数据库文件不存在。"; using var connection = new SQLiteConnection($"Data Source={dbFilePath};Version=3;"); connection.Open(); var schema = GetDatabaseSchema(connection); return schema; } catch (Exception ex) { return $"获取数据库结构失败: {ex.Message}"; } }, functionName: "GetDatabaseSchema", description: "获取数据库的完整结构信息,包括所有表、字段、数据类型等" ); // 2. 智能SQL查询函数(带结构感知) var smartQueryFunction = kernel.CreateFunctionFromMethod( method: (string userQuestion) => { try { var dbFilePath = "test.db"; if (!File.Exists(dbFilePath)) return "数据库文件不存在。"; using var connection = new SQLiteConnection($"Data Source={dbFilePath};Version=3;"); connection.Open(); // 获取数据库结构 var schema = GetDatabaseSchema(connection); // 返回结构信息供AI参考 return $"数据库结构信息:\n{schema}\n\n用户问题:{userQuestion}\n\n请根据上述数据库结构生成合适的SQL查询语句。"; } catch (Exception ex) { return $"查询失败: {ex.Message}"; } }, functionName: "AnalyzeQueryRequest", description: "分析用户查询需求并提供数据库结构信息,帮助生成正确的SQL语句" ); // 3. 执行SQL的函数 var executeQueryFunction = kernel.CreateFunctionFromMethod( method: (string sql) => { try { var dbFilePath = "test.db"; if (!File.Exists(dbFilePath)) return "数据库文件不存在。"; using var connection = new SQLiteConnection($"Data Source={dbFilePath};Version=3;"); connection.Open(); // 安全检查:只允许SELECT语句 if (!sql.Trim().ToUpper().StartsWith("SELECT")) { return "⚠️ 出于安全考虑,只允许执行SELECT查询语句。"; } using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); var dt = new DataTable(); dt.Load(reader); if (dt.Rows.Count == 0) return "查询结果为空。"; return FormatQueryResult(dt); } catch (Exception ex) { return $"SQL执行出错: {ex.Message}"; } }, functionName: "ExecuteSQL", description: "执行SQL查询语句并返回格式化的结果" ); kernel.ImportPluginFromFunctions("DatabasePlugin", [getSchemaFunction, smartQueryFunction, executeQueryFunction]); } /// <summary> /// 获取数据库结构信息 /// </summary> private static string GetDatabaseSchema(SQLiteConnection connection) { var schema = new StringBuilder(); schema.AppendLine("📊 SCADA数据库结构信息"); schema.AppendLine("=" + new string('=', 50)); // 获取所有表名 var tables = GetTableNames(connection); foreach (var tableName in tables) { schema.AppendLine($"\n🔹 表名: {tableName}"); schema.AppendLine($" 字段信息:"); // 获取表结构 var tableInfo = GetTableStructure(connection, tableName); foreach (var column in tableInfo) { schema.AppendLine($" - {column.Name} ({column.Type}) {(column.IsPrimaryKey ? "[主键]" : "")} {(column.IsNotNull ? "[非空]" : "")}"); } // 获取记录数 var count = GetTableRecordCount(connection, tableName); schema.AppendLine($" 📋 记录数: {count}"); // 获取外键信息 var foreignKeys = GetForeignKeys(connection, tableName); if (foreignKeys.Any()) { schema.AppendLine($" 🔗 外键关系:"); foreach (var fk in foreignKeys) { schema.AppendLine($" - {fk.Column} -> {fk.RefTable}.{fk.RefColumn}"); } } } // 这里有用处,如果是sqlserver,我们可以用自动功能提取常用sql脚本,效果会更好 schema.AppendLine($"\n💡 使用示例:"); schema.AppendLine($" - 查询所有设备: SELECT * FROM Devices"); schema.AppendLine($" - 查询运行状态的设备: SELECT * FROM Devices WHERE Status = '运行'"); schema.AppendLine($" - 查询最近的报警: SELECT * FROM Alarms ORDER BY AlarmTime DESC LIMIT 10"); schema.AppendLine($" - 统计设备类型: SELECT DeviceType, COUNT(*) FROM Devices GROUP BY DeviceType"); return schema.ToString(); } /// <summary> /// 获取所有表名 /// </summary> private static List<string> GetTableNames(SQLiteConnection connection) { var tables = new List<string>(); string sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"; using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); while (reader.Read()) { tables.Add(reader.GetString(0)); } return tables; } /// <summary> /// 获取表结构信息 /// </summary> private static List<ColumnInfo> GetTableStructure(SQLiteConnection connection, string tableName) { var columns = new List<ColumnInfo>(); string sql = $"PRAGMA table_info({tableName})"; using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); while (reader.Read()) { columns.Add(new ColumnInfo { Name = reader.GetString("name"), Type = reader.GetString("type"), IsNotNull = reader.GetBoolean("notnull"), IsPrimaryKey = reader.GetBoolean("pk") }); } return columns; } /// <summary> /// 获取表记录数 /// </summary> private static int GetTableRecordCount(SQLiteConnection connection, string tableName) { string sql = $"SELECT COUNT(*) FROM {tableName}"; using var command = new SQLiteCommand(sql, connection); return Convert.ToInt32(command.ExecuteScalar()); } /// <summary> /// 获取外键信息 /// </summary> private static List<ForeignKeyInfo> GetForeignKeys(SQLiteConnection connection, string tableName) { var foreignKeys = new List<ForeignKeyInfo>(); string sql = $"PRAGMA foreign_key_list({tableName})"; using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); while (reader.Read()) { foreignKeys.Add(new ForeignKeyInfo { Column = reader.GetString("from"), RefTable = reader.GetString("table"), RefColumn = reader.GetString("to") }); } return foreignKeys; } /// <summary> /// 格式化查询结果 /// </summary> private static string FormatQueryResult(DataTable dt) { var result = new StringBuilder(); result.AppendLine($"📋 查询结果 (共 {dt.Rows.Count} 条记录):"); result.AppendLine(new string('=', 60)); // 列标题 foreach (DataColumn col in dt.Columns) { result.Append($"{col.ColumnName,-15} | "); } result.AppendLine(); result.AppendLine(new string('-', 60)); // 数据行(限制显示前20行) int maxRows = Math.Min(20, dt.Rows.Count); for (int i = 0; i < maxRows; i++) { foreach (var item in dt.Rows[i].ItemArray) { string value = item?.ToString() ?? "NULL"; if (value.Length > 12) value = value.Substring(0, 12) + "..."; result.Append($"{value,-15} | "); } result.AppendLine(); } if (dt.Rows.Count > 20) { result.AppendLine($"... 还有 {dt.Rows.Count - 20} 条记录未显示"); } return result.ToString(); } /// <summary> /// 列信息类 /// </summary> public class ColumnInfo { public string Name { get; set; } public string Type { get; set; } public bool IsNotNull { get; set; } public bool IsPrimaryKey { get; set; } } /// <summary> /// 外键信息类 /// </summary> public class ForeignKeyInfo { public string Column { get; set; } public string RefTable { get; set; } public string RefColumn { get; set; } } } }

🎮 实际应用场景演示

让我们看看这个系统在实际使用中的表现:

image.png

场景一:设备状态查询

image.png

场景二:复杂统计分析

image.png

⚠️ 常见坑点提醒

坑点一:SQL注入风险

问题: 直接拼接用户输入到SQL语句中

解决: 使用参数化查询 + 白名单验证

坑点二:AI幻觉问题

问题: AI可能生成不存在的表名或字段

解决: 提供完整的数据库结构信息给AI参考

坑点三:性能问题

问题: 大表查询可能导致超时

解决: 限制返回行数 + 添加合适的索引

C#
// 🛡️ 安全的查询限制 if (dt.Rows.Count > 1000) { result.AppendLine("⚠️ 结果集过大,仅显示前1000条记录"); }

🚀 性能优化技巧

技巧一:智能缓存机制

C#
private static readonly MemoryCache _schemaCache = new MemoryCache(new MemoryCacheOptions()); public static string GetCachedSchema() { return _schemaCache.GetOrCreate("db_schema", factory => { factory.SetAbsoluteExpiration(TimeSpan.FromHours(1)); return GetDatabaseSchema(); }); }

技巧二:连接池优化

C#
// 使用连接字符串配置连接池 string connectionString = "Data Source=test.db;Version=3;Pooling=true;Max Pool Size=10;";

🎯 结尾总结

通过本文的实战演示,我们成功打造了一个能听懂人话的智能数据助手。这个方案的三大核心优势:

  1. 🧠 智能化:用自然语言替代复杂SQL,降低使用门槛
  2. 🔒 安全性:多重防护机制,确保数据安全
  3. ⚡ 高性能:SQLite轻量级设计 + 智能优化策略

金句总结:

  • "让AI成为你的SQL专家,把复杂查询变成自然对话"
  • "好的架构设计,让功能扩展变得轻而易举"
  • "安全永远是第一位,便利性是第二位"

💬 互动时间

你在项目中是否遇到过类似的数据查询痛点?或者你有更好的AI+数据库结合方案?欢迎在评论区分享你的经验和想法!

如果这篇文章对你有帮助,别忘了转发给更多需要的同行朋友!让我们一起推动C#技术社区的发展!

相关信息

通过网盘分享的文件:AppDbAiAgent.zip 链接: https://pan.baidu.com/s/1_qZtb72ZGKF_ECKm5zI_fg?pwd=wwgs 提取码: wwgs --来自百度网盘超级会员v9的分享

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!