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