实现AI和BI整合的初步思路和探索-Part2
在上一篇中我提到了一个B站最新的一个LangChain 1.0的SQL Agent的视频,通过提供给SQL Agent的tools,可以引导大模型先查看数据库下都有哪些表,然后查看指定表的结构,最后再将这些相关表关联在一起生成查询,完全符合我们在处理相应问题时的处理逻辑。
这种场景在处理简单问题或者数据结构比较简单的时候,没有问题,但是当我们的相应表的关联逻辑如果比较复杂,我们很难相信也很难指定让大模型能生成正确的查询时,有什么解决方案吗?
这个时候我们就要结合AI+BI的能力,也就是让大模型和DWH一起协同来解决。
在DWH中,除了标准的维度模型,在此基础之上面我们还可以创建一个data mart层,这个是DWH领域常见的一种方式,以前都是供report,dashboard和data mining用的,这里我们也可以供LLM使用。
接下来我们创建这个DWH中的data mart层,这个过程很类似于Power BI中的数据建模,就是将所有相关的表JOIN在一起,然后生成一个宽表,里面只保留可能用到的字段,这样一来,就解决了大模型自己JOIN可能丢失关键逻辑,以及提示词空间的问题。如果大模型理解不了相应的字段内容,我们还可以将字段重新命名成更容易理解的方式来帮助大模型,把可操作和调整的范围完全掌握在自己手里。
首先,回顾一下相应的表结构。
在SQLServer中,通过以下代码将相关事实表和维度表整合在一起,也就是通过人工的方法把相应的数据进行整合,避免大模型只根据字面的意思无法加入特定的逻辑。(BTW:以下代码简单粗暴,纯做演示用,实际项目中仅供大家参考。)- SELECT p1.EnglishProductName, p2.EnglishProductSubcategoryName, p3.EnglishProductCategoryName,
- cust.AddressLine1 as CustomerAddressLine, cust.BirthDate as CustomerBirthDate,
- cust.CommuteDistance as CustomerCommuteDistance, cust.DateFirstPurchase as CustomerDateFirstPurchase,
- cust.EnglishEducation as CustomerEducation, cust.TotalChildren as CustomerTotalChildren,
- cust.YearlyIncome as CustomerYearlyIncome,cust.FirstName as CustomerFirstName, cust.LastName as CustomerLastName,
- promotion.EnglishPromotionName, promotion.EnglishPromotionCategory, promotion.EnglishPromotionType, promotion.DiscountPct,
- terr.SalesTerritoryRegion, terr.SalesTerritoryGroup, terr.SalesTerritoryCountry
- ,[CurrencyKey]
- ,[SalesOrderNumber]
- ,[SalesOrderLineNumber]
- ,[RevisionNumber]
- ,[OrderQuantity]
- ,[UnitPrice]
- ,[ExtendedAmount]
- ,[UnitPriceDiscountPct]
- ,[DiscountAmount]
- ,[ProductStandardCost]
- ,[TotalProductCost]
- ,[SalesAmount]
- ,[TaxAmt]
- ,[Freight]
- ,[OrderDate]
- ,[DueDate]
- ,[ShipDate] INTO [AdventureWorksDW2016].[dbo].[DataMartInternetSales]
- FROM [AdventureWorksDW2016].[dbo].[FactInternetSales] fact
- LEFT JOIN [dbo].[DimProduct] p1 on fact.ProductKey=p1.ProductKey
- LEFT JOIN [dbo].[DimProductSubCategory] p2 on p1.ProductSubcategoryKey=p2.ProductSubcategoryKey
- LEFT JOIN [dbo].[DimProductCategory] p3 on p2.ProductCategoryKey=p3.ProductCategoryKey
- LEFT JOIN [dbo].[DimCustomer] cust on fact.CustomerKey=cust.CustomerKey
- LEFT JOIN [dbo].[DimPromotion] promotion on fact.PromotionKey=promotion.PromotionKey
- LEFT JOIN [dbo].[DimSalesTerritory] terr on fact.SalesTerritoryKey=terr.SalesTerritoryKey
复制代码 然后生成新建的表生成CREATE TABLE的脚本,以备后续的大模型提示词用。可以看到通过这种方式,提供给大模型的schema信息就短了很多。- CREATE TABLE [dbo].[DataMartInternetSales](
- [EnglishProductName] [nvarchar](50) NULL,
- [EnglishProductSubcategoryName] [nvarchar](50) NULL,
- [EnglishProductCategoryName] [nvarchar](50) NULL,
- [CustomerAddressLine] [nvarchar](120) NULL,
- [CustomerBirthDate] [date] NULL,
- [CustomerCommuteDistance] [nvarchar](15) NULL,
- [CustomerDateFirstPurchase] [date] NULL,
- [CustomerEducation] [nvarchar](40) NULL,
- [CustomerTotalChildren] [tinyint] NULL,
- [CustomerYearlyIncome] [money] NULL,
- [CustomerFirstName] [nvarchar](50) NULL,
- [CustomerLastName] [nvarchar](50) NULL,
- [EnglishPromotionName] [nvarchar](255) NULL,
- [EnglishPromotionCategory] [nvarchar](50) NULL,
- [EnglishPromotionType] [nvarchar](50) NULL,
- [DiscountPct] [float] NULL,
- [SalesTerritoryRegion] [nvarchar](50) NULL,
- [SalesTerritoryGroup] [nvarchar](50) NULL,
- [SalesTerritoryCountry] [nvarchar](50) NULL,
- [CurrencyKey] [int] NOT NULL,
- [SalesOrderNumber] [nvarchar](20) NOT NULL,
- [SalesOrderLineNumber] [tinyint] NOT NULL,
- [RevisionNumber] [tinyint] NOT NULL,
- [OrderQuantity] [smallint] NOT NULL,
- [UnitPrice] [money] NOT NULL,
- [ExtendedAmount] [money] NOT NULL,
- [UnitPriceDiscountPct] [float] NOT NULL,
- [DiscountAmount] [float] NOT NULL,
- [ProductStandardCost] [money] NOT NULL,
- [TotalProductCost] [money] NOT NULL,
- [SalesAmount] [money] NOT NULL,
- [TaxAmt] [money] NOT NULL,
- [Freight] [money] NOT NULL,
- [OrderDate] [datetime] NULL,
- [DueDate] [datetime] NULL,
- [ShipDate] [datetime] NULL
- ) ON [PRIMARY]
复制代码 把以上脚本加入到提示词中:- {{#sys.query#}}
- 你是一个数据库专家,专注SQLServer的SQL语句生成。
- 以下是相关表结构:
- ***考虑到篇幅排版,这里省略建表脚本,实际使用请粘贴建表脚本至此。
- 请将用户的请求转换成相应的SQL查询。
- 直接生成SQL查询,只返回SQL查询,不要生成其它任何内容。
- {{#sys.files#}}
复制代码 这样用户就可以针对这张表来进行提问。
可以看到基于我的这几个测试用例,整个过程还算靠谱。
接下来做进一步的功能改进,我们尝试加入Row Level Security因素。
这里我们通过Territory进行筛选,也就是模拟一个让一个销售岗位的人只能看到其岗位的数据。
为了模拟这个功能,首先在工作流开始部分加一个下拉列表参数,里面加入DimSalesTerritory表的所有值,并加一个ALL在列表中。
其中ELSE部分的LLM模块的提示词大概如下所示,提示词中加入了对销售区域的筛选:- {{#sys.query#}}
- 你是一个数据库专家,专注SQLServer的SQL语句生成。
- 以下是相关表结构:
- ***考虑到篇幅排版,这里省略建表脚本,实际使用请粘贴建表脚本至此。
- 请将用户的请求转换成相应的SQL查询。
- 直接生成SQL查询,只返回SQL查询,不要生成其它任何内容。
- 将{x}SalesTerritory加入到对SalesTerritoryRegion的筛选。
- {{#sys.files#}}
复制代码 接下来首先看我的工作流:
可以看到我加了一个IF条件分支,当用户选择ALL的时候走一个LLM,否则走另外一支的LLM。这样做是因为在告诉大模型处理ALL和指定值的逻辑时,LLM返回的SQL语句总是莫名其妙。所以这里将逻辑通过IF分支做了简化。(BTW:也是为了自己的血压,所以真不想跟大模型扛下去了,脾气不好的真的会被气到,赶上辅导孩子写作业了。。。)
接下来看下运行结果。
一些其它的细节:
有时大模型对字段的理解会有问题,比如销售区域会去找字段Sales Promotion。
为了辅助大模型的理解,在系统提示词里,我加入了以下内容:- 销售区域和销售岗位对应的字段是SalesTerritoryRegion。
- 促销名称对应的字段是EnglishPromotionName。
复制代码 从大模型的推理能看到这些内容是有被参考到的。
此外还需要留意,不同大模型的反馈是不同的,除了他们可能会以不同的方式气到你之外,后续的处理代码也会不同,比如,如果替换成了DeepSeek V3.1,那么就没有Think部分的输出,也就是后面就不需要去Split了。
其它问题:
实际应用中,如果Dify是被嵌入到其它平台中的,那么如何实现RLS是个问题,主要是其它平台的用户信息如何传递到Dify的工作流。这个会在后续慢慢探索。当然如果是同样的逻辑在LangChain里去实现就灵活的多了。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |