概要
本文聚焦如何使用spring-AI来开发大模型应用一些进阶技能,包含一套可落地的技术设计模式,读完你将会学习到:
- 如何使用Spring-AI 开发大模型对话应用
- 如何综合设计一套适用Spring-ai的代码结构,为应用提供更好的扩展能力
本文假设读者已经熟悉spring-ai的基本功能以及大模型开发的入门知识,如果你还不熟悉这些基础知识,可以找我仔细学习。
开发目标
我们会简单的模拟豆包的业务模型,开发一个用户与大模型对话的应用程序,我们会从领域模型开始设计,一直到应用模型和应用实现。
由于篇幅有限,我们不展开细节完成每一个功能,这里只介绍核心领域建模和应用的开发模式。
我们将会聚焦一次对话的处理流程,如下图所示:
- 本地工具集也就是function calling 可以随时添加,删除,并且根据对话上下文动态抉择
- 向量数据库搜索可以根据对话上下文选择是否使用,甚至提供多个选择
# 设计领域模型
- Agent 表示一个大模型agent,包括大模型的命名,SystemPrompt,所属用户等
- Conversation 表示一次对话
- User 表示正在使用系统的用户
- ChatMessage表示一个对话消息,一个对话消息由多个内容组成,因为一次对话可以发送包括文本和媒体多条具体内容。
至此,我们简单模拟了豆包的领域模型
设计应用模型
首先设计一个 ChatContext类,用来表示全部对话的上下文核心,这里我们分析如下:
- 对话上下文包含 when,who,what,where,how 五种元素
- When - 用户发送消息的时间
- Who - 发送消息的用户
- What - 用户发送发的消息
- Where - 用户处于哪一个对话
- How - 本次对话有哪些配置选项
- 对话上下文可以配置标记属性,以便在不同功能之间传递消息,这点类似Servlet技术中方的ServletRequest#getAttribute
- 对话上下文是只读的,不允许修改
至此,我们有了可用的对话上下文,可以围绕这个上下文开发对话逻辑了。
设计应用逻辑
首先我们来设计应用的扩展点,其实本质上应该是先设计应用逻辑,再进行重构设计扩展点,但是这里为了行文方便,直接展示下扩展点,免去重构的过程,请读者注意,真实开发的时候不可能一开始就想得到哪些地方需要扩展,一定是先做出基础逻辑,再重构出扩展点点。
我们先来分析一下可扩展的点:
- 对话模型可以切换,系统将会根据上下文推断出本次要使用的模型。
- 本地方法可以随时增加删除,系统会很久本次上下文推断出需要调用的本地工具。
- 其他spring-ai框架的的Advisor也可能根据一次对话的上下文被推断出。
由此可见对话上下文是整个应用的重点,所有的功能是否被使用都围绕着这个上下文,并且这些功能在运行的时候会根据上下文动态提供出来,不难看出,这是一个策略模式,于是我们设计如下接口:
- ChatAdvisorSupplier 用来为本次对话提供spring-ai的Advisor
- ChatClientSupplier 会根据本地对话提供可用的模型client
- ChatTool 用来表示一个包含本地放的的类,提供了name和desc两个属性,用来让大模型帮我们判断哪些工具在本次对话需要被使用到
- ChatToolSupplier则会根据当前对话给出哪些本地工具会被使用到。
下面我们将这些组件串联起来,这样一来,我们的核心交互流程不变,而具体交互流程在策略器中可随时动态增减。
实现应用逻辑
我们来看一下ChatService是如何被实现的。
- 首先ChatService注入了所有的ChatToolSupplier,ChatClientSupplier,ChatAdvisorSupplier接口实例;
- 当处理ChatCommand的时候,组装出ChatContext;
- 然后调用一系列的get方法读取相关的策略
- 最后调用大模型client与之交互
其中getTools方法相对比较复杂,它先便利了所有的本地工具,然后将用户对话和本地工具描述一起交给了大模型,大模型告诉本地应用那一套functions更适合处理这个问题,然后菜返回本地工具集。之所以这么做,是因为(例如)openai官网明确说明,建议一次对话functions不要太多,最好不要超过20个,因为更多的functions意味着更多的token,也意味着更多的处理时间,而且也没有必要。
为应用增加RAG功能
有了ChatAdvisorSupplier这个接口,我们可以轻易的为应用逻辑增加RAG的功能。
这里我们规定,只要chatOption里面开启了InternalSearch开关,则应用RAG功能。你只要看一下下面的ChatOption类的设计,就瞬间明白了这个设计。
为应用增加一组Function Calling
我们写一个示例的Tool,提供function calling的功能
再为这个tool写一个supplier
于是乎,你在没有修改主逻辑的情况下为应用增加了两个功能,这看上去真的很棒!高内聚,低耦合,并且对扩展开放,对修改封闭!
现在,你可以像下面这样,提供更多的扩展能力
# Maven
首先配置maven配置,导入spring-ai的核心包,这里我们目前只用到了openai和rag向量数据库,暂时导入这两个包即可。
代码整体结构
具体代码示例
https://github.com/aurora-ultra/aurora-spring-ai
原创作者: mrye 转载于: https://www.cnblogs.com/mrye/p/18857558