diff --git a/黄永兴学习笔记/4.1黄永兴-项目案例笔记.docx b/黄永兴学习笔记/4.1黄永兴-项目案例笔记.docx new file mode 100644 index 0000000..158b0f1 Binary files /dev/null and b/黄永兴学习笔记/4.1黄永兴-项目案例笔记.docx differ diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/.mvn/wrapper/maven-wrapper.properties b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..c595b00 --- /dev/null +++ b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.14/apache-maven-3.9.14-bin.zip diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/SpringAIAlibaba.iml b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/SpringAIAlibaba.iml new file mode 100644 index 0000000..d62c586 --- /dev/null +++ b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/SpringAIAlibaba.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/pom.xml b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/pom.xml new file mode 100644 index 0000000..2f3e575 --- /dev/null +++ b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/pom.xml @@ -0,0 +1,127 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.13 + + + com.example + SpringAIAlibaba + 1.1.2.0 + SpringAIAlibaba + SpringAIAlibaba + + + 17 + + 2.22.13 + + 2.2.21 + + + + + + + + + + + + + + + + + + + + com.alibaba.cloud.ai + spring-ai-alibaba-bom + 1.1.2.0 + pom + import + + + org.springframework.ai + spring-ai-bom + 1.1.2 + pom + import + + + com.alibaba.cloud.ai + spring-ai-alibaba-extensions-bom + 1.1.2.1 + pom + import + + + + + + + + + + com.alibaba.cloud.ai + spring-ai-alibaba-agent-framework + 1.1.2.0 + + + + + com.alibaba.cloud.ai + spring-ai-alibaba-starter-dashscope + 1.1.2.0 + + + + org.springframework.boot + spring-boot-starter-web + + + + io.projectreactor.addons + reactor-adapter + 3.5.0 + + + + org.projectlombok + lombok + true + + + + + com.alibaba + dashscope-sdk-java + ${dashscope.version} + + + + + io.reactivex.rxjava2 + rxjava + ${rxjava.version} + + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/main/java/com/example/springaialibaba/SpringAiAlibabaApplication.java b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/main/java/com/example/springaialibaba/SpringAiAlibabaApplication.java new file mode 100644 index 0000000..3b60de0 --- /dev/null +++ b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/main/java/com/example/springaialibaba/SpringAiAlibabaApplication.java @@ -0,0 +1,13 @@ +package com.example.springaialibaba; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringAiAlibabaApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringAiAlibabaApplication.class, args); + } + +} diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/main/java/com/example/springaialibaba/controller/CustomerServiceController.java b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/main/java/com/example/springaialibaba/controller/CustomerServiceController.java new file mode 100644 index 0000000..d42acb0 --- /dev/null +++ b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/main/java/com/example/springaialibaba/controller/CustomerServiceController.java @@ -0,0 +1,177 @@ +package com.example.springaialibaba.controller; + + +import com.alibaba.cloud.ai.dashscope.agent.DashScopeAgent; +import com.alibaba.cloud.ai.dashscope.agent.DashScopeAgentOptions; +import com.alibaba.dashscope.app.Application; +import com.alibaba.dashscope.app.ApplicationParam; +import com.alibaba.dashscope.app.ApplicationResult; +import com.alibaba.dashscope.app.FlowStreamMode; +import com.alibaba.dashscope.exception.InputRequiredException; +import com.alibaba.dashscope.exception.NoApiKeyException; +import io.reactivex.Flowable; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import reactor.adapter.rxjava.RxJava2Adapter; +import reactor.core.publisher.Flux; + +import java.nio.charset.StandardCharsets; + +@Slf4j +@RestController +@RequiredArgsConstructor //使用RequiredArgsConstructor注解,自动注入final类型的字段 +@RequestMapping("/customer") +public class CustomerServiceController { + + //注入Spring AI Alibaba Agent + //@Autowired + private final DashScopeAgent dashScopeAgent; + + /** + * 智能客服接口(非流式,同步获取结果) + * @param question 用户问题 + * @return 知识库标准答案 + */ + @GetMapping("/service") + public String customerService(@RequestParam("question") String question) { + + try { + /* + DashScopeAgentOptions 是工作流 / 智能体的调用参数,它只负责「触发工作流」,不负责「修改大模型的推理参数」。 + 因为 Spring AI Alibaba 的自动配置在工作流(Agent)模式下不生效! + */ + + // + /* ChatResponse response = dashScopeAgent.call( + new Prompt(question, DashScopeAgentOptions.builder() + .appId("df04e1abf24a416c8702260e88863ac4") + .build() + ) + ); + + return response.getResult().getOutput().getText();*/ + + ApplicationParam param = ApplicationParam.builder() + // 若没有配置环境变量,可用百炼API Key将下行替换为:.apiKey("sk-xxx")。但不建议在生产环境中直接将API Key硬编码到代码中,以减少API Key泄露风险。 + .apiKey(System.getenv("sk-f7d2253302b547c7a2fe257673cb854b")) + .appId("df04e1abf24a416c8702260e88863ac4") + .prompt(question) + .build(); + + Application application = new Application(); + ApplicationResult result = application.call(param); + return result.getOutput().getText(); + + } catch (Exception e) { + log.error("客服调用失败", e); + return "抱歉,智能客服暂时无法响应,请联系人工客服。"; + } + } + + // 官方原生流式调用(1:1对照文档) + @GetMapping(value = "/service2", produces = MediaType.TEXT_HTML_VALUE + ";charset=UTF-8") + public Flux customerService2( + @RequestParam("question") String question, + HttpServletResponse response + ) { + //设置字符编码为UTF-8,解决中文乱码问题 + response.setCharacterEncoding("UTF-8"); + + try { + // ============================================== + // 【官方原版写法】 + // ============================================== + ApplicationParam param = ApplicationParam.builder() + .apiKey("sk-f7d2253302b547c7a2fe257673cb854b") // 你的API Key + .appId("df04e1abf24a416c8702260e88863ac4") + .prompt(question) + .incrementalOutput(true) // 官方:流式必须开 + .flowStreamMode(FlowStreamMode.MESSAGE_FORMAT) // 设置为流模式 + .build(); + + Application application = new Application(); + + // 官方流式调用 + Flowable resultFlowable = application.streamCall(param); + + // 把 RxJava Flowable → Spring Flux(用于SSE输出) + // 正确的流式处理逻辑 + System.out.println(resultFlowable); + +/* return Flux.create(sink -> { + resultFlowable.subscribe( + res -> { + try { + if (res != null && res.getOutput() != null) { + String content = null; + + if ("stop".equals(res.getOutput().getFinishReason())) { + // 流结束 + content = res.getOutput().getText(); + if (content != null && !content.trim().isEmpty()) { + sink.next("data: " + content + "\n\n"); + } + } else { + // 流式输出 + if (res.getOutput().getWorkflowMessage() != null && + res.getOutput().getWorkflowMessage().getMessage() != null) { + + content = res.getOutput().getWorkflowMessage() + .getMessage().getContent(); + + if (content != null && !content.trim().isEmpty()) { + sink.next("data: " + content + "\n\n"); + } + } + } + } + } catch (Exception e) { + log.error("处理响应异常", e); + } + }, + error -> { + log.error("流式调用异常", error); + sink.next("data: 服务调用异常\n\n"); + sink.complete(); + }, + () -> { + log.info("流式调用完成"); + sink.complete(); + } + ); + });*/ + + // RxJava适配器 flowable 转换为 flux + return RxJava2Adapter.flowableToFlux(resultFlowable) + .map(this::extractContent) + .filter(content -> content != null && !content.trim().isEmpty()) + .map(content -> "data: " + content + "\n\n"); + + + } catch (Exception e) { + log.error("客服调用失败", e); + return Flux.error(e); + } + } + + // 从 ApplicationResult 中提取内容 + private String extractContent(ApplicationResult res) { + if (res == null || res.getOutput() == null) return null; + + if ("stop".equals(res.getOutput().getFinishReason())) { + return res.getOutput().getText(); + } else if (res.getOutput().getWorkflowMessage() != null && + res.getOutput().getWorkflowMessage().getMessage() != null) { + return res.getOutput().getWorkflowMessage().getMessage().getContent(); + } + return null; + } +} \ No newline at end of file diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/main/resources/application.yml b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/main/resources/application.yml new file mode 100644 index 0000000..fecb5d1 --- /dev/null +++ b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/main/resources/application.yml @@ -0,0 +1,13 @@ +server: + port: 8080 + +spring: + ai: + dashscope: + # 核心:工作流应用的AppId + agent: + app-id: df04e1abf24a416c8702260e88863ac4 + # 核心:你的API-Key + api-key: sk-f7d2253302b547c7a2fe257673cb854b + # 业务空间ID + workspace-id: llm-fuczq7vkh8vyz716 diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/test/java/com/example/springaialibaba/SpringAiAlibabaApplicationTests.java b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/test/java/com/example/springaialibaba/SpringAiAlibabaApplicationTests.java new file mode 100644 index 0000000..ac8a730 --- /dev/null +++ b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/src/test/java/com/example/springaialibaba/SpringAiAlibabaApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.springaialibaba; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringAiAlibabaApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/classes/application.yml b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/classes/application.yml new file mode 100644 index 0000000..fecb5d1 --- /dev/null +++ b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/classes/application.yml @@ -0,0 +1,13 @@ +server: + port: 8080 + +spring: + ai: + dashscope: + # 核心:工作流应用的AppId + agent: + app-id: df04e1abf24a416c8702260e88863ac4 + # 核心:你的API-Key + api-key: sk-f7d2253302b547c7a2fe257673cb854b + # 业务空间ID + workspace-id: llm-fuczq7vkh8vyz716 diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/classes/com/example/springaialibaba/SpringAiAlibabaApplication.class b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/classes/com/example/springaialibaba/SpringAiAlibabaApplication.class new file mode 100644 index 0000000..a7758aa Binary files /dev/null and b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/classes/com/example/springaialibaba/SpringAiAlibabaApplication.class differ diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/classes/com/example/springaialibaba/controller/CustomerServiceController.class b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/classes/com/example/springaialibaba/controller/CustomerServiceController.class new file mode 100644 index 0000000..94e874d Binary files /dev/null and b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/classes/com/example/springaialibaba/controller/CustomerServiceController.class differ diff --git a/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/test-classes/com/example/springaialibaba/SpringAiAlibabaApplicationTests.class b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/test-classes/com/example/springaialibaba/SpringAiAlibabaApplicationTests.class new file mode 100644 index 0000000..bdf7b50 Binary files /dev/null and b/黄永兴学习笔记/智能客服demo/SpringAIAlibaba/target/test-classes/com/example/springaialibaba/SpringAiAlibabaApplicationTests.class differ