diff --git a/SpringAI/0_使用SpringAI接入AI模型.md b/SpringAI/0_使用SpringAI接入AI模型.md index 8c9d3d1..1e99747 100644 --- a/SpringAI/0_使用SpringAI接入AI模型.md +++ b/SpringAI/0_使用SpringAI接入AI模型.md @@ -206,7 +206,7 @@ ChatClient.create(chatModel) .advisors(new MessageChatMemoryAdvisor(chatMemory, sessionId, 6)) .stream() .content() - .map(chatResponse -> ServerSentEvent.builder(chatResponse).event("message").build()); + .map(chatResponse -> ServerSentEvent.builder(JSONUtil.toJsonStr(chatResponse)).event("message").build()); ``` ### ETL @@ -394,4 +394,127 @@ vectorStore.add(splitDocuments); ```java // query可以是用户输入的文本字符串 List list = vectorStore.similaritySearch(query); -``` \ No newline at end of file +``` + +### RAG + +RAG就是当文档等数据ETL到向量数据库以后,用户提问时拿用户的prompt检索向量数据库里的相关资料,然后跟用户的prompt合并提交给大模型,再让大模型生成答案给用户。 + +#### QuestionAnswerAdvisor + +在SpringAI中,QuestionAnswerAdvisor这一组件实现了上述的能力。我们可以定义一个prompt模板,来做这件事: + +``` +下面是上下文信息 +--------------------- +{question_answer_context} +--------------------- +给定的上下文和提供的历史信息,而不是事先的知识,回复用户的意见。如果答案不在上下文中,告诉用户你不能回答这个问题。 +``` + +这个模板的内容可以从数据库、Redis、配置文件加载,**{question_answer_context}**是一个模板的变量占位符。 + +```java +ChatClient.create(chatModel) + .prompt() + .user(prompt) + // 会自动从向量数据库查询资料,并替换到 question_answer_context 变量,结合用户的 prompt 一起发送给大模型API + // 假设 promptTemplate 这个字符串就是上面的模板 + .advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults(), promptTemplate)) + .stream() + .content() + .map(chatResponse -> ServerSentEvent.builder(JSONUtil.toJsonStr(chatResponse)).event("message").build()); +``` + +### FunctionCall + +Function Call旨在解决一些复杂场景的需求,比如我希望AI读取本机某个文件,然后回答文件里面的内容,读取文件这个操作就可以通过FunctionCall来实现。 + +#### 读取文档可以通过tika实现 + +```xml + + org.springframework.ai + spring-ai-tika-document-reader + +``` + +#### 使用JSON描述Function + +AI模型是不知道你代码里的Function位置在哪儿的,我们需要用JSON声明Function的结构,让AI去找并调用,简单示例: + +```json +{ + "type": "function", + "function": { + "name": "documentAnalyzer", // 这个是 spring bean 的名称 + "description": "文档解析", // 描述函数的用途 + "parameters": { // 这个函数要传递的参数 + "properties": { + "path": { // 参数的名称 + "type": "string", // 参数的类型 + "description": "被解析的文档路径" // 参数的描述 + }, + }, + "required":["path"], // 必传的参数 + "type": "object" + } + } +} +``` + +#### 编写函数 + +我们需要实现**java.util.function.Function**接口里的**apply**方法,并将其注入到spring bean中就可以实现自动调用。 + +```java +@Description("文档解析") +@Service +public class DocumentAnalyzer implements Function { + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class Request { + // 这2个jackson注解的目的是让 spring ai 提取参数和描述 + @JsonPropertyDescription(value = "需要解析的文档路径") + @JsonProperty(value = "path", required = true) + private String path; + } + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class Response { + private String result; + } + + @Override + public Response apply(Request request) { + var resource = new FileSystemResource(request.path); + TikaDocumentReader reader = new TikaDocumentReader(resource); + return new Response(reader.read().get(0).getContent()); + } +} +``` + +#### 使用函数 + +```java +ChatClient.create(chatModel) + .prompt() + .messages(new UserMessage(prompt)) + // spring ai会根据beanName查找fucntion + .functions(functionName) + .stream() + .chatResponse() + .map(chatResponse -> ServerSentEvent.builder(JSONUtil.toJsonStr(chatResponse)).event("message").build()); +``` + +可以用封装一个http接口,接收`prompt`和`functionName`完成函数调用。 + +## 总结 + +Spring AI框架帮助我们实现了各大主流AI大模型的API封装和常用的一些工具组件,依托spring强大的生态,各大主流的AI大模型供应商也在积极适配中。它简化了我们的开发工作量,同类型的框架还有LangChain4J。 \ No newline at end of file