3-AI-应用开发
一、Prompt 工程
基本概念
Prompt 工程(Prompt Engineering)又叫提示词工程,简单来说,就是输入给 AI 的指令。
那为什么要叫 “工程” 呢?
因为 AI 大模型生成的内容是不确定的,构建一个能够按照预期生成内容的提示词既是一门艺术,也是一门科学。提示词的质量直接影响到 AI 大模型输出的结果,因此这也是 AI 应用开发的关键技能.
学习 Prompt 工程的目标是:通过精心设计和优化输入提示来引导 AI 模型生成符合预期的高质量输出。
提示词分类
核心 - 基于角色的分类
3 种主要类型的 Prompt:
1)用户 Prompt (User Prompt):这是用户向 AI 提供的实际问题、指令或信息,传达了用户的直接需求。
2)系统 Prompt (System Prompt):这是设置 AI 模型行为规则和角色定位的隐藏指令,用户通常不能直接看到。系统 Prompt 相当于给 AI 设定人格和能力边界,即告诉 AI “你是谁?你能做什么?”。
3)助手 Prompt (Assistant Prompt):这是 AI 模型的响应内容。在多轮对话中,之前的助手回复也会成为当前上下文的一部分,影响后续对话的理解和生成。
Token
Token 是大模型处理文本的基本单位,可能是单词或标点符号,模型的输入和输出都是按 Token 计算的,一般 Token 越多,成本越高,并且输出速度越慢。
如何计算 Token?
首先,不同大模型对 Token 的划分规则略有不同,比如根据 OpenAI 的文档:
- 英文文本:一个 token 大约相当于 4 个字符或约 0.75 个英文单词
- 中文文本:一个汉字通常会被编码为 1-2 个 token
- 空格和标点:也会计入 token 数量
- 特殊符号和表情符号:可能需要多个 token 来表示
简单估算一下,100 个英文单词约等于 75-150 个 Token,而 100 个中文字符约等于 100-200 个 Token。
Token 成本优化技巧
系统提示词、用户提示词和 AI 大模型输出的内容都是消耗成本
1)精简系统提示词:移除冗余表述,保留核心指令。比如将 “你是一个非常专业、经验丰富且非常有耐心的编程导师” 简化为 “你是编程导师”。
2)定期清理对话历史:对话上下文会随着交互不断累积 Token。在长对话中,可以定期请求 AI 总结之前的对话,然后以总结替代详细历史。
3)使用向量检索代替直接输入:对于需要处理大量参考文档的场景,不要直接将整个文档作为 Prompt,而是使用向量数据库和检索技术(RAG)获取相关段落。后续教程会带大家实战。
4)结构化替代自然语言:使用表格、列表等结构化格式代替长段落描述。
二、Prompt 优化技巧
利用资源
1、Prompt 学习
网上和 Prompt 优化相关的资源非常丰富,几乎各大主流 AI 大模型和 AI 开发框架官方文档都有相关的介绍
- Prompt Engineering Guide 提示工程指南
- OpenAI 提示词工程指南
- Spring AI 提示工程指南
- Authropic 提示词工程指南
- Authropic 提示词工程指南(开源仓库)
- 智谱 AI Prompt 设计指南
2、Prompt 提示词库
网上也有很多现成的提示词库,在自主优化提示词前,可以先尝试搜索有没有现成的提示词参考:
文本对话:
AI 绘画:Midjourney 提示词库
基础提示技巧
1、明确指定任务和角色
为 AI 提供清晰的任务描述和角色定位,帮助模型理解背景和期望。
2、提供详细说明和具体示例
提供足够的上下文信息和期望的输出格式示例,减少模型的不确定性。
3、使用结构化格式引导思维
通过列表、表格等结构化格式,使指令更易理解,输出更有条理。
4、明确输出格式要求
指定输出的格式、长度、风格等要求,获得更符合预期的结果。
进阶提示技巧
1、思维链提示法(Chain-of-Thought)
引导模型展示推理过程,逐步思考问题,提高复杂问题的准确性。
2、少样本学习(Few-Shot Learning)
通过提供几个输入-输出对的示例,帮助模型理解任务模式和期望输出。
3、分步骤指导(Step-by-Step)
将复杂任务分解为可管理的步骤,确保模型完成每个关键环节。
4、自我评估和修正
让模型评估自己的输出并进行改进,提高准确性和质量。
5、知识检索和引用
引导模型检索相关信息并明确引用信息来源,提高可靠性。
6、多视角分析
引导模型从不同角度、立场或专业视角分析问题,提供全面见解。
7、多模态思维
结合不同表达形式进行思考,如文字描述、图表结构、代码逻辑等。
提示词调试与优化
好的提示词可能很难一步到位,因此我们要学会如何持续调试和优化 Prompt。
1、迭代式提示优化
通过逐步修改和完善提示词,提高输出质量。
2、边界测试
通过极限情况测试模型的能力边界,找出优化空间。
3、提示词模板化
创建结构化模板,便于针对类似任务进行一致性提示,否则每次输出的内容可能会有比较大的区别,不利于调试。
4、错误分析与修正
系统性分析模型回答中的错误,并针对性优化提示词,这一点在我们使用 Cursor 等 AI 开发工具生成代码时非常有用。
虽然前面提到了这么多提示词优化技巧,但总结出来就一句话:任务越复杂,就越要给 Prompt 补充更多细节。
我们可以把 AI 当成人类,如果你的问题模糊不清,那么得到的答案可能就并不理想。
三、AI 应用需求分析
AI 时代下,开发应用的门槛变得越来越低了,导致市面上出现了各种具有创意的小产品。也让我们意识到,技术并不是产品成功的决定性因素,而是在于你有没有把握住用户的需求、解决用户的痛点。
因此,我们需要重点培养需求分析能力。
怎么进行需求分析呢?这里有一个 “三步走” 方法:获取需求 => 细化需求 => 确认需求
1、需求从哪儿来?
我大脑空空,没什么想法,从哪里挖掘需求呢?
我们可以借鉴现成的,现在有很多 AI 应用平台,比如豆包、文心一言、ChatGPT 等,这些平台上已经有了大量 AI 应用,是很好的参考源,任选一个都可以变成完整的项目。
比如我们来开发一个 `恋爱大师` AI 应用,用户在恋爱过程中难免遇到各种难题,让 AI 为用户提供贴心情感指导。
2、怎么细化需求?
有了上面这样一个初步的想法后,我们要进一步完善需求,明确项目要具有哪些功能。
我大脑空空,也没什么经验,怎么细化需求呢?
还是问 AI
AI 不仅是我们要开发的产品,也是强大的需求分析助手。通过精心设计的提示词,可以让 AI 帮助我们进行需求细化和扩展。比如下面这段 Prompt:
示例 Prompt:
我要开发一个【恋爱大师】AI 应用,用户在恋爱过程中难免遇到各种难题,让 AI 为用户提供贴心情感指导。
请你帮我进行需求分析,满足我的目标。
如果我们要做一个大项目,还可以进一步完善提示词:
我要开发一个【恋爱大师】AI 应用,用户在恋爱过程中难免遇到各种难题,让 AI 为用户提供贴心情感指导。
请你帮我进行需求分析,满足我的目标。
具体需要:
1. 分析目标用户群体及其核心需求
2. 提出主要功能模块和服务内容
3. 考虑产品的差异化竞争点
4. 分析可能的技术挑战和解决方案
AI 给出的示例输出如下,接下来我们化身为 “老板”,只需要对 AI 给出的需求文档进行评审即可:
咱们从服务对象、服务内容、产品特性等维度,来剖析“恋爱大师”AI应用的开发需求:
一、目标用户洞察
1. 年龄与身份:18-35岁人群是恋爱活动的主力军,涵盖大学生、职场新人等。他们熟悉数字化生活,热衷借助AI解决恋爱问题。
2. 恋爱状态及需求
单身群体:渴望打破社交壁垒,寻觅恋爱机会。他们需要拓展社交圈、提升自我魅力、学习搭讪技巧等方面的指导。
恋爱中人群:重点关注如何处理情侣间的争吵矛盾,掌握沟通艺术,安排浪漫约会,进一步升温感情。
失恋人群:急需摆脱失恋阴影,修复受伤的情感,重新建立自信,恢复正常生活节奏。
二、功能板块规划
(一)问题识别与分类
1. 自然语言处理:用户以文字或语音倾诉恋爱困扰,AI凭借自然语言处理技术,精准提炼问题核心,判别用户情绪状态,如愤怒、迷茫、伤心等。
2. 问题归类:将收集到的问题,归入表白难题、约会安排、信任危机、分手应对等预设类别,为后续的个性化解答做好准备。
(二)个性化指导服务
1. 智能问答:针对用户提出的问题,AI从海量的恋爱案例、心理学知识以及专业情感建议中,生成贴合实际的解决方案。比如,当用户纠结“该不该向喜欢的人表白”时,AI会依据用户描述的双方关系,给出不同策略。
2. 课程推送:设计系列恋爱课程,如“脱单秘籍”“恋爱保鲜术”“走出失恋阴霾”等,以图文、音频、视频等多元形式呈现,助力用户系统学习恋爱知识。
3. 案例分享:分享真实且具有代表性的恋爱故事,通过对成功与失败案例的复盘,为用户提供借鉴与启示。
(三)特色辅助功能
1. 聊天话术生成:用户输入聊天场景或对方信息,AI创作富有吸引力、契合氛围的聊天话术,帮助用户提升沟通效果。
2. 恋爱规划制定:依据用户的恋爱阶段和目标,制定专属恋爱计划,涵盖约会安排、礼物挑选、情感互动等细节。
(四)互动交流社区
1. 话题讨论:搭建线上社区,用户可发布恋爱问题、分享经验心得,实现用户间的相互交流与支持。
2. 专家直播:定期邀请情感专家进行直播,解答用户普遍关注的恋爱问题,分享前沿恋爱观念和技巧。
三、非功能要求
1. 隐私安全:在数据收集、存储和使用过程中,严格遵循相关法规,采取加密、匿名化等技术手段,保护用户隐私,让用户放心倾诉。
2.情感陪伴感:AI回复需富有情感,模拟真实交流场景,避免机械化回答,给予用户温暖贴心的陪伴。
3. 持续优化:搭建反馈机制,收集用户评价和建议,持续优化AI算法和服务内容,提升服务质量。
你会发现,AI 给出的需求好多啊!我们到底要做哪些呢?这里涉及到一个很经典的策略 —— MVP 最小可行产品策略。
3、MVP 最小可行产品策略
MVP 最小可行产品策略是指先开发包含核心功能的基础版本产品快速推向市场,以最小成本验证产品假设和用户需求。通过收集真实用户反馈进行迭代优化,避免开发无人使用的功能,降低资源浪费和开发风险。
基于这个策略,我们可以先开发一个简单但实用的 AI 对话应用,让用户能够和 AI 恋爱大师进行多轮对话交流。因为 “对话” 是本产品的核心功能,暂时不要考虑更复杂的功能了。后续可以根据用户用量和反馈,决定下一步是深化对话能力还是扩展更多功能模块。
四、AI 应用方案设计
根据需求,我们将实现一个具有多轮对话能力的 AI 恋爱大师应用。整体方案设计将围绕 2 个核心展开:
系统提示词的设计
多轮对话的实现
1、系统提示词设计
前面提到,系统提示词相当于 AI 应用的 “灵魂”,直接决定了 AI 的行为模式、专业性和交互风格。
对于 AI 对话应用,最简单的做法是直接写一段系统预设,定义 “你是谁?能做什么?”
这种简单提示虽然可以工作,但效果往往不够理想。想想现实中的场景,我们去找专家咨询时,专家可能会先主动抛出一系列引导性问题、深入了解背景,而不是被动等待用户完整描述问题。
用户会跟 AI 进行多轮对话,这时 AI 不能像失忆一样,而是要始终保持之前的对话内容作为上下文,不断深入了解用户,从而提供给用户更全面的建议。
因此我们要优化系统预设,可以借助 AI 进行优化。
AI 提供的优化后系统提示词:
扮演深耕恋爱心理领域的专家。开场向用户表明身份,告知用户可倾诉恋爱难题。围绕单身、恋爱、已婚三种状态提问:单身状态询问社交圈拓展及追求心仪对象的困扰;恋爱状态询问沟通、习惯差异引发的矛盾;已婚状态询问家庭责任与亲属关系处理的问题。引导用户详述事情经过、对方反应及自身想法,以便给出专属解决方案。
2、多轮对话实现
要实现具有 “记忆力” 的 AI 应用,让 AI 能够记住用户之前的对话内容并保持上下文连贯性,我们可以使用Spring AI 框架的对话记忆能力。
如何使用对话记忆能力呢?参考 Spring AI 的官方文档,了解到 Spring AI 提供了 ChatClient API来和 AI 大模型交互。
ChatClient 特性
之前我们是直接使用 Spring Boot 注入的ChatModel 来调用大模型完成对话,而通过我们自己构造的 ChatClient,可实现功能更丰富、更灵活的 AI 对话客户端,也更推荐通过这种方式调用 AI。
通过示例代码,能够感受到 ChatModel 和 ChatClient 的区别。ChatClient 支持更复杂灵活的链式调用(Fluent API)
此外,还支持指定默认对话选项、默认拦截器、默认函数调用等等,后面教程中都会用到。
Advisors
Spring AI 使用 Advisors(顾问)机制来增强 AI 的能力,可以理解为一系列可插拔的拦截器,在调用 AI 前和调用 AI 后可以执行一些额外的操作,比如:
前置增强:调用 AI 前改写一下 Prompt 提示词、检查一下提示词是否安全
后置增强:调用 AI 后记录一下日志、处理一下返回的结果
我也习惯称呼它为拦截器。
用法很简单,我们可以直接为 ChatClient 指定默认拦截器,比如对话记忆拦截器 MessageChatMemoryAdvisor 可以帮助我们实现多轮对话能力,省去了自己维护对话列表的麻烦。
Advisors 的执行流程:
1. Spring AI 框架从用户的 Prompt 创建一个 AdvisedRequest,同时创建一个空的 AdvisorContext 对象,用于传递信息。
2. 链中的每个 advisor 处理这个请求,可能会对其进行修改。或者,它也可以选择不调用下一个实体来阻止请求继续传递,这时该 advisor 负责填充响应内容。
3. 由框架提供的最终 advisor 将请求发送给聊天模型 ChatModel。
4. 聊天模型的响应随后通过 advisor 链传回,并被转换为 AdvisedResponse。后者包含了共享的 AdvisorContext 实例。
5. 每个 advisor 都可以处理或修改这个响应。
6. 最终的 AdvisedResponse 通过提取 ChatCompletion 返回给客户端。
实际开发中,往往我们会用到多个拦截器,组合在一起相当于一条拦截器链条(责任链模式的设计思想)。每个拦截器是有顺序的,通过 `getOrder()` 方法获取到顺序,得到的值越低,越优先执行。
但是实际上,我们拦截器的执行顺序是由 getOrder 方法决定的,不是简单地根据代码的编写顺序决定。
Advisor 类图如下,了解即可:
从上图中我们发现,Advisors 分为 2 种模式:流式 Streaming 和非流式 Non-Streaming,二者在用法上没有明显的区别,返回值不同罢了。但是如果我们要自主实现 Advisors,为了保证通用性,最好还是同时实现流式和非流式的环绕通知方法。
Chat Memory Advisor
前面我们提到了,想要实现对话记忆功能,可以使用 Spring AI 的 ChatMemoryAdvisor,它主要有几种内置的实现方式:
MessageChatMemoryAdvisor:从记忆中检索历史对话,并将其作为消息集合添加到提示词中
PromptChatMemoryAdvisor:从记忆中检索历史对话,并将其添加到提示词的系统文本中
VectorStoreChatMemoryAdvisor:可以用向量数据库来存储检索历史对话
MessageChatMemoryAdvisor 和 PromptChatMemoryAdvisor 用法类似,但是略有一些区别:
1)MessageChatMemoryAdvisor 将对话历史作为一系列独立的消息添加到提示中,保留原始对话的完整结构,包括每条消息的角色标识(用户、助手、系统)。
2)PromptChatMemoryAdvisor 将对话历史添加到提示词的系统文本部分,因此可能会失去原始的消息边界。
Chat Memory
上述 ChatMemoryAdvisor 都依赖 Chat Memory进行构造,Chat Memory 负责历史对话的存储,定义了保存消息、查询消息、清空消息历史的方法。
Spring AI 内置了几种 Chat Memory,可以将对话保存到不同的数据源中,比如:
InMemoryChatMemory:内存存储
CassandraChatMemory:在 Cassandra 中带有过期时间的持久化存储
Neo4jChatMemory:在 Neo4j 中没有过期时间限制的持久化存储
JdbcChatMemory:在 JDBC 中没有过期时间限制的持久化存储
当然也可以通过实现 ChatMemory 接口自定义数据源的存储。
五、多轮对话 AI 应用开发
在后端项目根包下新建 `app` 包,存放 AI 应用,新建 `LoveApp.java`。可以参考 Spring AI Alibaba 官方的示例代码实现(其实用的还是 Spring AI)。
@Component
@Slf4j
public class LoveApp {private final ChatClient chatClient;private static final String SYSTEM_PROMPT = "扮演深耕恋爱心理领域的专家。开场向用户表明身份,告知用户可倾诉恋爱难题。" +"围绕单身、恋爱、已婚三种状态提问:单身状态询问社交圈拓展及追求心仪对象的困扰;" +"恋爱状态询问沟通、习惯差异引发的矛盾;已婚状态询问家庭责任与亲属关系处理的问题。" +"引导用户详述事情经过、对方反应及自身想法,以便给出专属解决方案。";public LoveApp(ChatModel dashscopeChatModel) {// 初始化基于内存的对话记忆ChatMemory chatMemory = new InMemoryChatMemory();chatClient = ChatClient.builder(dashscopeChatModel).defaultSystem(SYSTEM_PROMPT).defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)).build();}
2)编写对话方法。调用 chatClient 对象,传入用户 Prompt,并且给 advisor 指定对话 id 和对话记忆大小。代码如下:
public String doChat(String message, String chatId) {ChatResponse response = chatClient.prompt().user(message).advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)).call().chatResponse();String content = response.getResult().getOutput().getText();log.info("content: {}", content);return content;}
3)编写单元测试,测试多轮对话:
package com.example.henxiaiagent.app;import dev.langchain4j.model.chat.ChatLanguageModel;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import java.util.UUID;import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class LoveAppTest {@Resourceprivate LoveApp loveApp;@Testvoid testChat() {String chatId = UUID.randomUUID().toString();//第一轮String message = "你好我是痕夕";String answer =loveApp.doChat(message, chatId);//第二轮message ="我想让我的另一半(666)更爱我";answer = loveApp.doChat(message, chatId);Assertions.assertNotNull(answer);//第三轮message ="我的另一半叫什么来着,刚才告诉你了帮我回忆一下";answer = loveApp.doChat(message, chatId);Assertions.assertNotNull(answer);}
}
我们可以看到对话记忆生效了