当前位置: 首页 > news >正文

14【.NET10 实战--孢子记账--产品智能化】--智能生成预算

经过前面三篇文章的铺垫,我们先后完成了硅基流动平台的接入配置、大模型接入方式的技术选型,以及 LLM 调用层的统一封装,孢子记账的智能化基础设施已经基本就绪。从现在开始,我们的目光将从"怎么接入 AI"转向"用 AI 做什么",将大模型能力真正嵌入到具体的业务场景中,让用户能够实实在在地感受到智能化带来的体验提升。而我们将要攻克的第一个智能化功能,就是智能生成预算

一、痛点

预算管理是孢子记账的核心功能模块之一,也是个人财务管理中承上启下的关键环节。如果说记账是对过去的记录,那么预算就是对未来的规划,它帮助用户在消费发生之前建立一道理性防线,避免冲动支出,从而真正实现"管住钱"的目标。然而在传统的记账流程中,这道防线恰恰是最容易被用户绕过的。

制定预算这件事,本质上需要用户完成一次小型的"自我财务审计"。用户需要回顾自己过去一段时间在各个分类上的支出情况,结合当月的收入变化、季节性消费波动以及计划内的大额支出等因素,综合判断每个分类该分配多少额度。对于本身就具备财务意识、对自己的消费习惯了然于胸的用户来说,这或许只是几分钟的事情。但对于占绝大多数的普通用户而言,这已经构成了一道不低的认知门槛。很多用户打开预算设置页面之后,面对一排空白输入框和一串分类名称,脑子里只有一个念头 "我该填多少?"当这个问题没有明确答案时,放弃就成了最自然的选择。

二、智能生成预算的设计思路

针对用户在制定预算时面临的认知挑战,我们的设计思路可以概括为一句话:让 AI 来帮用户算账。用户不再需要自己对着空白表单绞尽脑汁,而是由系统自动拉取历史消费数据,交给大模型进行分析和推理,最终产出一份可直接使用或微调的预算方案。整个功能的交互流程可以拆解为数据采集、数据聚合、智能推理和结果确认四个阶段。

在数据采集阶段,用户首先选择想要制定的预算类型(月度、季度、年度),这个选择决定了后续预算的时间粒度。系统收到请求之后,会从数据库中拉取用户过去十二个月的支出明细数据。之所以选择十二个月作为数据窗口,是因为一个完整的年度周期能够覆盖工资调整、节假日消费高峰、季节性支出波动等各类周期性因素,为大模型提供足够丰富的分析素材,避免因数据窗口过窄而导致的判断偏差。

接下来进入数据聚合阶段。原始的交易明细是零散的、逐条的,直接将这些数据丢给大模型不仅会超出上下文窗口的限制,也会让模型难以从中提取有意义的规律。因此我们需要在服务端对数据进行一次预处理,将过去十二个月的所有支出按照分类进行分组,统计出每个分类在每个月的支出总额,以及该分类在过去十二个月的总支出。这样一来,原本杂乱无章的交易流水就被整理成了一张结构清晰的"支出结构图谱",大模型拿到的不再是一堆数字,而是一份能够直接用于推理的财务画像。

有了这份支出结构图谱作为输入,流程进入最核心的智能推理阶段。我们将聚合后的数据连同用户选择的预算类型一起,构造为结构化的提示词发送给大模型。大模型的任务不是简单地计算平均数然后原样返回,而是需要结合数据中体现的消费趋势、周期性波动以及各分类之间的比例关系,综合判断出一个既不过于激进也不过于保守的预算方案。

最后一个阶段是结果确认。大模型生成的预算方案会以前端可视化的方式呈现给用户,用户可以逐一查看每个分类的建议额度,也可以对比系统建议与历史均值之间的差异。如果用户对方案整体满意,点击保存即可将预算写入数据库,如果方案不合适,用户也可以直接拒绝并重新生成。在整个流程中,AI 扮演的是"参谋"角色,最终的决策权始终掌握在用户手中,这种"人机协同"的交互模式既发挥了 AI 的数据分析能力,也保留了用户对自身财务的掌控感。

三、核心代码实现

在讲解了智能生成预算的设计思路之后,接下来我们来看看这个功能的核心代码实现,其他部分的代码由于不是核心逻辑代码,因此在此不再赘述。以下是核心代码:

usingSP.FinanceService.Models.Request;usingSP.FinanceService.Models.Response;usingSP.FinanceService.Models.Enumeration;usingSystem.Text.Json;usingSP.Common.LLM;usingSP.Common.Redis;usingSP.Common;usingSP.Common.ExceptionHandling.Exceptions;namespaceSP.FinanceService.Service.Impl;/// <summary>/// 预算生成服务实现类/// </summary>publicclassBudgetGenerationServerImpl:IBudgetGenerationServer{/// <summary>/// 预算服务接口/// </summary>privatereadonlyIBudgetServer_budgetService;/// <summary>/// 记账服务接口/// </summary>privatereadonlyIAccountingServer_accountingServer;/// <summary>/// 记账分类服务接口/// </summary>privatereadonlyITransactionCategoryServer_transactionCategoryServer;/// <summary>/// 大模型服务/// </summary>privatereadonlyIOpenAIService_openAIService;/// <summary>/// Redis缓存服务接口/// </summary>privatereadonlyIRedisService_redisService;/// <summary>/// 上下文服务/// </summary>privatereadonlyContextSession_contextSession;/// <summary>// redis缓存键前缀// </summary>privateconststringRedisCacheKeyPrefix="budget_generation_preview:";/// <summary>/// 构造函数/// </summary>/// <param name="budgetService">预算服务接口</param>/// <param name="accountingServer">记账服务接口</param>/// <param name="transactionCategoryServer">记账分类服务接口</param>/// <param name="openAIService">大模型服务接口</param>/// <param name="redisService">Redis缓存服务接口</param>/// <param name="contextSession">上下文服务接口</param>publicBudgetGenerationServerImpl(IBudgetServerbudgetService,IAccountingServeraccountingServer,ITransactionCategoryServertransactionCategoryServer,IOpenAIServiceopenAIService,IRedisServiceredisService,ContextSessioncontextSession){_budgetService=budgetService;_accountingServer=accountingServer;_transactionCategoryServer=transactionCategoryServer;_openAIService=openAIService;_redisService=redisService;_contextSession=contextSession;}/// <summary>/// AI生成预算,返回预览数据/// </summary>/// <param name="request">生成请求</param>/// <returns>预览数据</returns>publicasyncTask<List<BudgetGenerationResponse>>GenerateBudget(BudgetGenerationRequestrequest){// 1. 构建AI输入数据varaiInputData=BuildAiInputData();if(aiInputData==null)returnnewList<BudgetGenerationResponse>();// 2. 获取预算周期信息var(month,beginDate,endDate)=GetBudgetPeriodInfo(request.Period);// 3. 构建提示词并调用AI服务varprompt=BuildBudgetPrompt(aiInputData,month,beginDate,endDate);varbudgetResponses=await_openAIService.ChatStructuredAsync<List<BudgetGenerationResponse>>(prompt);// 4. 缓存预览数据awaitCacheBudgetPreviewAsync(budgetResponses);// 5. 返回预览数据returnbudgetResponses??newList<BudgetGenerationResponse>();}/// <summary>/// 确认/取消生成预算,返回生成结果/// </summary>/// <param name="id">AI生成预算id</param>/// <param name="confirm">是否确认生成</param>/// <returns>任务</returns>publicasyncSystem.Threading.Tasks.TaskConfirmBudget(longid,boolconfirm){// 查询缓存中的预算预览数据varcacheKey=$"{RedisCacheKeyPrefix}{_contextSession.UserId}";varcachedData=await_redisService.GetAsync<List<BudgetGenerationResponse>>(cacheKey);if(cachedData==null){thrownewBusinessException("未找到预算预览数据,请重新生成预算");}if(!confirm){// 取消生成,删除缓存并返回0await_redisService.RemoveAsync(cacheKey);return;}// 确认生成,将预览数据保存为正式预算varbudgetAdds=newList<BudgetAddRequest>();foreach(varitemincachedData){budgetAdds.Add(newBudgetAddRequest{TransactionCategoryId=item.TransactionCategoryId,Amount=item.Amount,Period=(PeriodEnum)item.Period,Remark=item.Remark,StartTime=item.StartTime,EndTime=item.EndTime});}await_budgetService.Adds(budgetAdds);}/// <summary>/// 构建AI生成预算所需的输入数据/// </summary>/// <returns>输入数据,若无数据则返回null</returns>privateobject?BuildAiInputData(){// 查询用户最近12个月的记账数据varaccountingResponses=_accountingServer.GetAccountingsByTimeRange(DateTime.Now.AddMonths(-12),DateTime.Now);if(accountingResponses==null)returnnull;// 获取支出分类数据varcategoryResponses=_transactionCategoryServer.QueryByType(TransactionCategoryEnmu.Expenditure);if(categoryResponses==null)returnnull;varcategoryIds=categoryResponses.Select(c=>c.Id).ToList();// 过滤出支出的记账数据varcategorySummaries=accountingResponses.Where(a=>categoryIds.Contains(a.TransactionCategoryId));// 按支出分类分组,计算每个支出分类的总额varcategoryTotalAmounts=categorySummaries.GroupBy(a=>a.TransactionCategoryId).Select(g=>new{CategoryId=g.Key,TotalAmount=g.Sum(a=>a.Amount)}).ToList();// 按支出分类和月份分组,计算每个月的支出分类总额varmonthlyCategoryAmounts=categorySummaries.GroupBy(a=>new{a.TransactionCategoryId,Month=a.RecordDate.Month}).Select(g=>new{CategoryId=g.Key.TransactionCategoryId,Month=g.Key.Month,TotalAmount=g.Sum(a=>a.Amount)}).ToList();// 组装为AI输入格式returncategoryTotalAmounts.Select(c=>new{CategoryId=c.CategoryId,CategoryName=categoryResponses.FirstOrDefault(r=>r.Id==c.CategoryId)?.Name,TotalAmount=c.TotalAmount,MonthlyAmounts=monthlyCategoryAmounts.Where(m=>m.CategoryId==c.CategoryId).Select(m=>new{m.Month,m.TotalAmount}).ToList()}).ToList();}/// <summary>/// 根据预算周期获取月份数、起始日期和结束日期/// </summary>/// <param name="period">预算周期</param>/// <returns>月份数、起始日期、结束日期</returns>privatestatic(intmonth,DateTime beginDate,DateTime endDate)GetBudgetPeriodInfo(PeriodEnumperiod){varbeginDate=DateTime.Now;varendDate=periodswitch{PeriodEnum.Month=>DateTime.Now.AddMonths(1),PeriodEnum.Quarter=>DateTime.Now.AddMonths(3),PeriodEnum.Year=>DateTime.Now.AddMonths(12),_=>DateTime.Now};varmonth=periodswitch{PeriodEnum.Month=>1,PeriodEnum.Quarter=>3,PeriodEnum.Year=>12,_=>0};return(month,beginDate,endDate);}/// <summary>/// 构建AI生成预算的提示词/// </summary>/// <param name="aiInputData">AI输入数据</param>/// <param name="month">预算月数</param>/// <param name="beginDate">预算起始日期</param>/// <param name="endDate">预算结束日期</param>/// <returns>提示词字符串</returns>privatestaticstringBuildBudgetPrompt(objectaiInputData,intmonth,DateTimebeginDate,DateTimeendDate){return$"请根据以下数据生成未来{month}个月的预算,预算起止日期为{beginDate:yyyy-MM-dd}{endDate:yyyy-MM-dd},要求按照支出分类进行预算分配,并且给出每个支出分类的预算金额:{JsonSerializer.Serialize(aiInputData)}";}/// <summary>/// 将生成的预算预览数据缓存到Redis,过期时间30分钟/// </summary>/// <param name="data">预算预览数据</param>privateasyncSystem.Threading.Tasks.TaskCacheBudgetPreviewAsync(List<BudgetGenerationResponse>data){varcacheKey=$"{RedisCacheKeyPrefix}{_contextSession.UserId}";await_redisService.SetAsync(cacheKey,JsonSerializer.Serialize(data),30*60);}}

BudgetGenerationServerImpl是整个智能生成预算功能的核心实现类,它通过依赖注入持有了六个关键服务接口。IAccountingServer负责从数据库中拉取用户的记账明细,ITransactionCategoryServer提供支出分类的元数据(分类 ID 与名称的映射),这两者共同构成了数据采集层的基础。IOpenAIService是我们在上一篇文章中封装的 LLM 调用层,本次直接以ChatStructuredAsync<T>的方式使用,调用大模型并自动将返回的 JSON 反序列化为强类型的List<BudgetGenerationResponse>IRedisService承担了预览数据的临时存储职责,而IBudgetServer则在用户确认后将预览数据正式持久化到预算表。ContextSession从当前请求上下文中提取用户 ID,用于构造 Redis 缓存键的隔离前缀,确保不同用户之间的预览数据互不干扰。

整个生成流程的入口是GenerateBudget方法,它按照五个步骤依次执行。首先调用BuildAiInputData构建大模型所需的输入数据,这一步会查询用户过去十二个月的支出明细,并按分类进行两次分组聚合:一次仅按分类汇总出该分类的十二个月总支出,另一次按分类加月份汇总出每个分类每个月的支出额,最终组装成一个同时包含分类名称、总支出和月度明细的结构化对象。如果用户没有任何历史记账数据,方法直接返回空列表。接下来通过GetBudgetPeriodInfo根据用户选择的周期类型(月度、季度、年度)计算出预算覆盖的月份数和起止日期,这里利用 C# 的switch表达式将枚举值映射为具体的月份偏移量,代码简洁且不易出错。然后调用BuildBudgetPrompt将聚合后的数据与预算周期信息拼接为一段自然语言提示词,核心指令是"根据以下数据生成未来 N 个月的预算,按照支出分类进行预算分配,并给出每个分类的预算金额"。提示词末尾附上 JSON 序列化后的数据,大模型拿到这段提示词后就可以同时理解"任务指令"和"数据上下文",从而产出结构化的预算建议。

大模型返回的List<BudgetGenerationResponse>并不会直接写入数据库,而是先通过CacheBudgetPreviewAsync存入 Redis,设置30分钟的过期时间。这样做的目的是将"生成"和"确认"两个操作解耦——用户可以在预览页面上仔细审视每个分类的建议金额,甚至反复对比不同周期下的方案差异,而不必担心每次查看都重新调用一次大模型(既浪费算力也增加延迟)。当用户做出最终决定后,前端调用ConfirmBudget方法,传入是否确认的布尔标记。如果用户选择取消,方法会直接清除 Redis 中的预览缓存,不留下任何数据痕迹;如果用户选择确认,则将缓存中的BudgetGenerationResponse列表转换为BudgetAddRequest列表,调用IBudgetServer.Adds批量写入预算表。这种"先预览、后确认"的两阶段提交模式,既保证了用户体验的流畅性,也为后续可能的扩展(比如在确认前支持用户手动微调某个分类的金额)预留了灵活的改造空间。

四、小结

智能生成预算功能的实现,充分体现了我们在前面几篇文章中构建的智能化基础设施的价值。通过大模型的强大分析能力,我们成功地将用户过去的消费行为转化为对未来的理性规划,帮助用户跨过了制定预算时的认知门槛,让"管住钱"不再是一个抽象的目标,而是一个具体可行的行动方案。在后续的文章中,我们还将继续挖掘大模型在个人财务管理中的更多应用场景,持续提升孢子记账的智能化水平。

http://www.gsyq.cn/news/1483981.html

相关文章:

  • 7个树莓派节点打造Docker集群:gh_mirrors/do/docker-arm项目可视化与监控方案全攻略
  • SQL Server视图的‘潜规则’:通过视图插入、更新数据时,你可能会踩的5个坑
  • STM32F103上开箱即跑的FreeRTOS串口命令行调试工程(Keil MDK + 中断驱动)
  • 2026年售后服务好的大金空调全屋空气系统/大金空调维修/大金空调工程/大金空调上海经销商怎么选比较好 - 品牌宣传支持者
  • 2026年热门的聚脲防腐/玻璃鳞片防腐精选推荐公司 - 品牌宣传支持者
  • Kali Linux 2024.2 国内源配置与DDos-Attack工具安装避坑指南
  • 日志太多看不过来?MonkeyCode帮你智能分析
  • 全网最全!GIS所有数据格式分级速查表(常用/不常用/淘汰+ArcGIS/QGIS/GDAL兼容对照表)全量喂给AI
  • 告别跳线帽!用串口助手5分钟搞定TMC2209电机驱动配置(附CRC校验避坑指南)
  • 2026年评价高的油缸定制/油缸品牌/液压油缸/油缸设备横向对比厂家推荐 - 行业平台推荐
  • Reacto安全最佳实践:保护你的React应用开发环境
  • OpenCode数据持久化完全指南:如何保存你的编程进度不丢失
  • Isaac Gym机器人强化学习训练环境预装包(含URDF/GLB模型与factory/amp/trifinger多任务示例)
  • 2026年靠谱的广东液压/液压设备/液压设备配套品牌厂家推荐 - 行业平台推荐
  • 2026年最火的 10 款 GIS 软件
  • 超长视频生成技术:LoL方案解决注意力塌陷难题
  • R语言mediation包实战:用移民数据手把手教你做中介效应分析(附完整代码)
  • EFT-CoT框架:情感聚焦疗法与多代理系统的融合应用
  • 【2027最新】基于SpringBoot+Vue的+周边游平台管理系统源码+MyBatis+MySQL
  • PDBRipper实战案例:从复杂PDB文件中提取关键信息的完整流程
  • JSONlite性能测试:大规模JSON文档存储的基准测试与优化策略
  • 视频内容去重终极指南:Vidupe智能识别重复视频的完整解决方案
  • 老旧Mac设备系统兼容性深度解析:硬件适配与性能优化全指南
  • Nginx限流实战:用limit_req和limit_conn保护你的服务器,附突发流量处理技巧
  • ESP32 ADC实战避坑:从电位器读数到电压换算,一篇搞定所有配置细节
  • 从水流到电磁场:图解环量与通量,帮你彻底理解这两个核心物理概念
  • Reactive-gRPC源码解析:核心组件与响应式流实现原理
  • 从Datasheet到可运行代码:我的W5500+LWIP驱动调试全记录(中断、缓存、信号量一个不少)
  • Godot Voxel引擎深度解析:5大架构设计让体素地形生成更高效
  • leecodecode【动态规划2】【2026.6.7打卡-java版本】