From 501980046be3ae48f0fd29c79d84d05e196e7fc1 Mon Sep 17 00:00:00 2001
From: Hanserwei <2628273921@qq.com>
Date: Mon, 27 Oct 2025 20:23:42 +0800
Subject: [PATCH] =?UTF-8?q?feat(chat):=20=E5=AE=9E=E7=8E=B0AI=E5=AE=A2?=
=?UTF-8?q?=E6=9C=8D=E5=8A=A9=E6=89=8B=E4=B8=8E=E6=89=8B=E6=9C=BA=E5=8F=B7?=
=?UTF-8?q?=E7=A0=81=E8=AF=86=E5=88=AB=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增AI助手提示词模板,定义角色、目标与交互规则
- 实现手机号自动识别并触发消息发送工具- 添加RabbitMQ配置与消息收发组件
- 集成SendMessage工具支持用户留资通知
- 引入会话上下文管理工具类ConversationContext
- 升级聊天客户端配置,加载系统提示词与默认工具
- 增加数据库操作工具日志记录
- 添加Spring AMQP与Jackson依赖支持消息队列通信
---
pom.xml | 8 ++
.../chat/config/ChatClientConfiguration.java | 14 ++-
.../hanserwei/chat/config/RabbitMQConfig.java | 37 ++++++
.../chat/consumer/RabbitMQConsumer.java | 30 +++++
.../chat/controller/AiChatController.java | 36 +++++-
.../chat/publisher/RabbitMQPublisher.java | 29 +++++
.../com/hanserwei/chat/tools/AiDBTools.java | 12 ++
.../chat/tools/SendMQMessageTools.java | 105 ++++++++++++++++++
.../chat/utils/ConversationContext.java | 63 +++++++++++
.../src/main/resources/config/application.yml | 9 ++
.../src/main/resources/prompt/aiAssistant.st | 23 ++++
11 files changed, 359 insertions(+), 7 deletions(-)
create mode 100644 snails-chat/src/main/java/com/hanserwei/chat/config/RabbitMQConfig.java
create mode 100644 snails-chat/src/main/java/com/hanserwei/chat/consumer/RabbitMQConsumer.java
create mode 100644 snails-chat/src/main/java/com/hanserwei/chat/publisher/RabbitMQPublisher.java
create mode 100644 snails-chat/src/main/java/com/hanserwei/chat/tools/SendMQMessageTools.java
create mode 100644 snails-chat/src/main/java/com/hanserwei/chat/utils/ConversationContext.java
create mode 100644 snails-chat/src/main/resources/prompt/aiAssistant.st
diff --git a/pom.xml b/pom.xml
index ab5d8b5..43bb9ca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,6 +83,14 @@
com.fasterxml.jackson.datatype
jackson-datatype-jsr310
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
diff --git a/snails-chat/src/main/java/com/hanserwei/chat/config/ChatClientConfiguration.java b/snails-chat/src/main/java/com/hanserwei/chat/config/ChatClientConfiguration.java
index 6c5b481..e19ddd3 100644
--- a/snails-chat/src/main/java/com/hanserwei/chat/config/ChatClientConfiguration.java
+++ b/snails-chat/src/main/java/com/hanserwei/chat/config/ChatClientConfiguration.java
@@ -4,10 +4,10 @@ import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.memory.redis.BaseRedisChatMemoryRepository;
import com.alibaba.cloud.ai.memory.redis.LettuceRedisChatMemoryRepository;
import com.hanserwei.chat.tools.AiDBTools;
+import com.hanserwei.chat.tools.SendMQMessageTools;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
-import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.beans.factory.annotation.Value;
@@ -31,6 +31,9 @@ public class ChatClientConfiguration {
private DashScopeChatModel dashScopeChatModel;
@Resource
private AiDBTools aiDBTools;
+
+ @Value("classpath:prompt/aiAssistant.st")
+ private org.springframework.core.io.Resource aiAssistantResource;
@Bean
public BaseRedisChatMemoryRepository redisChatMemoryRepository() {
@@ -52,10 +55,11 @@ public class ChatClientConfiguration {
}
@Bean
- public ChatClient dashScopeChatClient(ChatMemory chatMemory) {
+ public ChatClient dashScopeChatClient(ChatMemory chatMemory, SendMQMessageTools sendMQMessageTools) {
return ChatClient.builder(dashScopeChatModel)
- .defaultTools(aiDBTools)
- .defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemory).build(), new SimpleLoggerAdvisor())
+ .defaultTools(aiDBTools, sendMQMessageTools)
+ .defaultSystem(aiAssistantResource)
+ .defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
-}
+}
\ No newline at end of file
diff --git a/snails-chat/src/main/java/com/hanserwei/chat/config/RabbitMQConfig.java b/snails-chat/src/main/java/com/hanserwei/chat/config/RabbitMQConfig.java
new file mode 100644
index 0000000..76aee93
--- /dev/null
+++ b/snails-chat/src/main/java/com/hanserwei/chat/config/RabbitMQConfig.java
@@ -0,0 +1,37 @@
+package com.hanserwei.chat.config;
+
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.DirectExchange;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.amqp.support.converter.MessageConverter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class RabbitMQConfig {
+
+ @Bean
+ public DirectExchange directExchange() {
+ return new DirectExchange("chat.exchange");
+ }
+
+ @Bean
+ public Queue queue() {
+ return new Queue("chat.queue");
+ }
+
+ @Bean
+ public Binding binding() {
+ return BindingBuilder.bind(queue())
+ .to(directExchange())
+ .with("chat.routing.key");
+ }
+
+ @Bean
+ public MessageConverter messageConverter() {
+ return new Jackson2JsonMessageConverter();
+ }
+
+}
diff --git a/snails-chat/src/main/java/com/hanserwei/chat/consumer/RabbitMQConsumer.java b/snails-chat/src/main/java/com/hanserwei/chat/consumer/RabbitMQConsumer.java
new file mode 100644
index 0000000..2fadac3
--- /dev/null
+++ b/snails-chat/src/main/java/com/hanserwei/chat/consumer/RabbitMQConsumer.java
@@ -0,0 +1,30 @@
+package com.hanserwei.chat.consumer;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.stereotype.Component;
+
+import java.nio.charset.StandardCharsets;
+
+@Slf4j
+@Component
+public class RabbitMQConsumer {
+
+ @RabbitListener(queues = "chat.queue")
+ public void receiveMessage(Object message) {
+ String messageText = extractMessage(message);
+ log.info("Received message text:\n{}", messageText);
+ }
+
+ private String extractMessage(Object message) {
+ return switch (message) {
+ case null -> "";
+ case String str -> str;
+ case byte[] body -> new String(body, StandardCharsets.UTF_8);
+ case Message amqpMessage -> new String(amqpMessage.getBody(), StandardCharsets.UTF_8);
+ default -> String.valueOf(message);
+ };
+
+ }
+}
diff --git a/snails-chat/src/main/java/com/hanserwei/chat/controller/AiChatController.java b/snails-chat/src/main/java/com/hanserwei/chat/controller/AiChatController.java
index 1256208..1a45978 100644
--- a/snails-chat/src/main/java/com/hanserwei/chat/controller/AiChatController.java
+++ b/snails-chat/src/main/java/com/hanserwei/chat/controller/AiChatController.java
@@ -2,7 +2,10 @@ package com.hanserwei.chat.controller;
import com.hanserwei.chat.model.dto.ChatMessageDTO;
import com.hanserwei.chat.model.vo.AIResponse;
+import com.hanserwei.chat.tools.SendMQMessageTools;
+import com.hanserwei.chat.utils.ConversationContext;
import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.http.MediaType;
@@ -12,15 +15,26 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Slf4j
@RestController
@RequestMapping("/ai")
public class AiChatController {
@Resource
private ChatClient dashScopeChatClient;
+ @Resource
+ private SendMQMessageTools sendMQMessageTools;
+
+ private static final Pattern PHONE_PATTERN = Pattern.compile("(? chatWithAi(@RequestBody ChatMessageDTO chatMessageDTO) {
+ log.info("会话ID:{}", chatMessageDTO.getConversionId());
+ ConversationContext.setConversationId(chatMessageDTO.getConversionId());
+ triggerSendMessageIfPhonePresent(chatMessageDTO);
return dashScopeChatClient.prompt()
.user(chatMessageDTO.getMessage())
@@ -29,8 +43,26 @@ public class AiChatController {
.chatResponse()
.mapNotNull(chatResponse -> AIResponse.builder()
.v(chatResponse.getResult().getOutput().getText())
- .build());
-
+ .build())
+ .contextWrite(ctx -> ConversationContext.withConversationId(chatMessageDTO.getConversionId()))
+ .doFinally(signalType -> ConversationContext.clear());
}
+ private void triggerSendMessageIfPhonePresent(ChatMessageDTO chatMessageDTO) {
+ String message = chatMessageDTO.getMessage();
+ if (message == null || message.isEmpty()) {
+ return;
+ }
+
+ Matcher matcher = PHONE_PATTERN.matcher(message);
+ if (!matcher.find()) {
+ return;
+ }
+
+ String phoneNumber = matcher.group(1);
+ log.info("检测到手机号:{},会话ID:{}", phoneNumber, chatMessageDTO.getConversionId());
+ sendMQMessageTools.sendMQMessage(phoneNumber)
+ .doOnError(error -> log.error("触发发送消息工具失败,手机号:{},错误:{}", phoneNumber, error.getMessage(), error))
+ .subscribe(result -> log.info("已触发发送消息工具,手机号:{},结果:{}", phoneNumber, result));
+ }
}
diff --git a/snails-chat/src/main/java/com/hanserwei/chat/publisher/RabbitMQPublisher.java b/snails-chat/src/main/java/com/hanserwei/chat/publisher/RabbitMQPublisher.java
new file mode 100644
index 0000000..bcf0cb9
--- /dev/null
+++ b/snails-chat/src/main/java/com/hanserwei/chat/publisher/RabbitMQPublisher.java
@@ -0,0 +1,29 @@
+package com.hanserwei.chat.publisher;
+
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class RabbitMQPublisher {
+
+ @Resource
+ private RabbitTemplate rabbitTemplate;
+
+ /**
+ * 发送消息
+ *
+ * @param exchange 交换机
+ * @param routingKey 路由键
+ * @param message 消息
+ */
+ public void send(String exchange, String routingKey, Object message) {
+ try {
+ rabbitTemplate.convertAndSend(exchange, routingKey, message);
+ } catch (Exception e) {
+ log.error("RabbitMQ发送消息失败:{}", e.getMessage());
+ }
+ }
+}
diff --git a/snails-chat/src/main/java/com/hanserwei/chat/tools/AiDBTools.java b/snails-chat/src/main/java/com/hanserwei/chat/tools/AiDBTools.java
index 193640c..1b5706b 100644
--- a/snails-chat/src/main/java/com/hanserwei/chat/tools/AiDBTools.java
+++ b/snails-chat/src/main/java/com/hanserwei/chat/tools/AiDBTools.java
@@ -4,12 +4,14 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hanserwei.chat.domain.dataobject.User;
import com.hanserwei.chat.service.UserService;
import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import java.util.List;
+@Slf4j
@Component
public class AiDBTools {
@@ -18,21 +20,25 @@ public class AiDBTools {
@Tool(name = "findAll", description = "查询所有用户")
public List findAll() {
+ log.info("AiDBTools: findAll");
return userService.list();
}
@Tool(name = "findAllByIdIn", description = "根据id列表查询用户")
public List findAllByIdIn(@ToolParam(description = "用户id列表") List ids) {
+ log.info("AiDBTools: findAllByIdIn");
return userService.listByIds(ids);
}
@Tool(name = "findById", description = "根据id查询用户")
public User findById(Long id) {
+ log.info("AiDBTools: findById");
return userService.getById(id);
}
@Tool(name = "findByName", description = "根据名称查询用户")
public User findByName(String name) {
+ log.info("AiDBTools: findByName");
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(User.class)
.eq(User::getName, name);
return userService.getOne(queryWrapper);
@@ -40,6 +46,7 @@ public class AiDBTools {
@Tool(name = "findByNameLike", description = "根据名称模糊查询用户")
public List findByNameLike(String name) {
+ log.info("AiDBTools: findByNameLike");
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(User.class)
.like(User::getName, name);
return userService.list(queryWrapper);
@@ -47,6 +54,7 @@ public class AiDBTools {
@Tool(name = "findByAge", description = "根据年龄查询用户")
public List findByAge(Integer age) {
+ log.info("AiDBTools: findByAge");
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(User.class)
.eq(User::getAge, age);
return userService.list(queryWrapper);
@@ -54,6 +62,7 @@ public class AiDBTools {
@Tool(name = "findByAgeBetween", description = "根据年龄范围查询用户")
public List findByAgeBetween(Integer start, Integer end) {
+ log.info("AiDBTools: findByAgeBetween");
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(User.class)
.between(User::getAge, start, end);
return userService.list(queryWrapper);
@@ -68,6 +77,7 @@ public class AiDBTools {
name (String), email (String), 和 age (Integer)。
""")
public void insert(@ToolParam(description = "用户对象") User user) {
+ log.info("AiDBTools: insert");
userService.save(user);
}
@@ -77,11 +87,13 @@ public class AiDBTools {
并携带要修改的字段,例如 name (String), email (String), 或 age (Integer)。
""")
public void update(@ToolParam(description = "用户对象") User user) {
+ log.info("AiDBTools: update");
userService.updateById(user);
}
@Tool(name = "delete", description = "删除用户")
public void delete(Long id) {
+ log.info("AiDBTools: delete");
userService.removeById(id);
}
diff --git a/snails-chat/src/main/java/com/hanserwei/chat/tools/SendMQMessageTools.java b/snails-chat/src/main/java/com/hanserwei/chat/tools/SendMQMessageTools.java
new file mode 100644
index 0000000..650a6c4
--- /dev/null
+++ b/snails-chat/src/main/java/com/hanserwei/chat/tools/SendMQMessageTools.java
@@ -0,0 +1,105 @@
+package com.hanserwei.chat.tools;
+
+import com.hanserwei.chat.publisher.RabbitMQPublisher;
+import com.hanserwei.chat.utils.ConversationContext;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.messages.AbstractMessage;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.MessageType;
+import org.springframework.ai.tool.annotation.Tool;
+import org.springframework.ai.tool.annotation.ToolParam;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Component
+public class SendMQMessageTools {
+
+ @Resource
+ private RabbitMQPublisher rabbitMQPublisher;
+ @Resource
+ private ChatMemory chatMemory;
+
+ private static final int HISTORY_LIMIT = 10;
+
+ /**
+ * 发送RabbitMQ消息
+ *
+ * @param phoneNumber 手机号码
+ */
+ @Tool(name = "SendMessage",
+ description = "当用户留下手机号码时,立即调用此工具发送消息通知管理员。手机号码应该是11位数字。")
+ public Mono sendMQMessage(
+ @ToolParam(description = "用户提供的手机号码,必须是11位数字") String phoneNumber
+ ) {
+ return ConversationContext.getConversationIdMono()
+ .defaultIfEmpty(ConversationContext.getConversationId())
+ .map(conversationId -> {
+ String exchange = "chat.exchange";
+ String routingKey = "chat.routing.key";
+ String recentConversation = buildRecentConversation(conversationId);
+ String message = "用户留了手机号:" + phoneNumber + ",会话ID:" + conversationId + recentConversation;
+
+ log.info("SendMQMessageTools 发送消息: {}", message);
+ rabbitMQPublisher.send(exchange, routingKey, message);
+
+ // 确保日志能被记录
+ log.info("SendMQMessageTools 消息发送完成");
+ return "消息发送成功";
+ });
+ }
+
+ private String buildRecentConversation(Long conversationId) {
+ if (conversationId == null) {
+ return "。最近对话:暂无记录";
+ }
+
+ List history = null;
+ try {
+ history = chatMemory.get(String.valueOf(conversationId));
+ } catch (Exception ex) {
+ log.warn("获取会话{}历史记录失败: {}", conversationId, ex.getMessage(), ex);
+ }
+
+ if (history == null || history.isEmpty()) {
+ return "。最近对话:暂无记录";
+ }
+
+ int skip = Math.max(0, history.size() - HISTORY_LIMIT);
+ String recent = history.stream()
+ .skip(skip)
+ .map(SendMQMessageTools::formatMessage)
+ .filter(Objects::nonNull)
+ .collect(Collectors.joining("\n"));
+
+ if (recent.isEmpty()) {
+ return "。最近对话:暂无记录";
+ }
+
+ return "。最近对话:\n" + recent;
+ }
+
+ private static String formatMessage(Message message) {
+ if (message == null) {
+ return null;
+ }
+
+ MessageType messageType = message.getMessageType();
+ String role = messageType != null ? messageType.name() : "UNKNOWN";
+ String text = (message instanceof AbstractMessage abstractMessage)
+ ? abstractMessage.getText()
+ : message.toString();
+
+ if (text == null || text.isBlank()) {
+ return null;
+ }
+
+ return role + ": " + text.strip();
+ }
+}
diff --git a/snails-chat/src/main/java/com/hanserwei/chat/utils/ConversationContext.java b/snails-chat/src/main/java/com/hanserwei/chat/utils/ConversationContext.java
new file mode 100644
index 0000000..5afa02c
--- /dev/null
+++ b/snails-chat/src/main/java/com/hanserwei/chat/utils/ConversationContext.java
@@ -0,0 +1,63 @@
+package com.hanserwei.chat.utils;
+
+import reactor.core.publisher.Mono;
+import reactor.util.context.Context;
+import reactor.util.context.ContextView;
+
+/**
+ * 会话上下文工具类,用于在线程本地存储中保存会话ID
+ * 同时支持Reactor Context,以支持响应式编程中的上下文传递
+ */
+public class ConversationContext {
+
+ private static final ThreadLocal CONVERSATION_ID_HOLDER = new ThreadLocal<>();
+
+ // Reactor Context key
+ private static final String CONVERSATION_ID_KEY = "conversationId";
+
+ /**
+ * 设置当前线程的会话ID
+ * @param conversationId 会话ID
+ */
+ public static void setConversationId(Long conversationId) {
+ CONVERSATION_ID_HOLDER.set(conversationId);
+ }
+
+ /**
+ * 获取当前线程的会话ID
+ * @return 会话ID
+ */
+ public static Long getConversationId() {
+ // 首先尝试从ThreadLocal获取
+ return CONVERSATION_ID_HOLDER.get();
+ }
+
+ /**
+ * 从Reactor Context中获取会话ID
+ * @return 包含会话ID的Mono
+ */
+ public static Mono getConversationIdMono() {
+ return Mono.deferContextual(ctx -> {
+ if (ctx.hasKey(CONVERSATION_ID_KEY)) {
+ return Mono.just(ctx.get(CONVERSATION_ID_KEY));
+ }
+ return Mono.empty();
+ });
+ }
+
+ /**
+ * 在Reactor Context中设置会话ID
+ * @param conversationId 会话ID
+ * @return Context
+ */
+ public static Context withConversationId(Long conversationId) {
+ return Context.of(CONVERSATION_ID_KEY, conversationId);
+ }
+
+ /**
+ * 清理当前线程的会话ID
+ */
+ public static void clear() {
+ CONVERSATION_ID_HOLDER.remove();
+ }
+}
\ No newline at end of file
diff --git a/snails-chat/src/main/resources/config/application.yml b/snails-chat/src/main/resources/config/application.yml
index 04dbb8f..a4b6d2b 100644
--- a/snails-chat/src/main/resources/config/application.yml
+++ b/snails-chat/src/main/resources/config/application.yml
@@ -26,6 +26,15 @@ spring:
max-wait: 10000
min-idle: 10
time-between-eviction-runs: 10000
+ rabbitmq:
+ host: localhost
+ port: 5672
+ username: admin
+ password: admin123
+ virtual-host: /snailsAi
+ listener:
+ simple:
+ prefetch: 1
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/postgres?serverTimezone=Asia/Shanghai
diff --git a/snails-chat/src/main/resources/prompt/aiAssistant.st b/snails-chat/src/main/resources/prompt/aiAssistant.st
new file mode 100644
index 0000000..b76c229
--- /dev/null
+++ b/snails-chat/src/main/resources/prompt/aiAssistant.st
@@ -0,0 +1,23 @@
+### 🎯 **角色与目标**
+
+你是一个**专业、友好且信息准确**的智能客服AI助手,负责解答用户关于HanserUniverse公司的常见问题。你的核心目标是**高效解决疑问**,并在每次互动后**引导用户留下手机号码进行下一步的深度咨询**。
+
+公司核心业务: 我们是一家专注于智能家居产品研发与销售的科技公司。
+
+使命: 致力于通过新技术 / 优质服务 / 专业内容提升用户的生活质量/学习效率/工作效率。
+### 📜 **基本指令与约束**
+
+1. **回答模式:** 始终以**清晰、简洁、礼貌**的方式回答用户的问题。
+2. **强制性附加句:** 无论你回答了什么问题,在**每个回答的末尾**,你必须**完全、准确地附加**以下这句话:
+ > **"如果想要了解更多信息可以留下电话号码,会有专门的客服人员与您联系。"**
+3. **手机号识别与工具调用:**
+ * 当用户输入的文本中包含一个**有效的手机号码**时(通常是11位数字,识别时请保持一定的容错性),你必须**立即调用SendMessage工具**,并将手机号作为参数传递。
+ * **给用户的反馈:** 工具调用成功后,给用户回复一个简短的确认信息,例如:"感谢您留下手机号码,我们已收到您的信息,专业的客服人员将在24小时内与您联系,请保持手机畅通。"
+
+### 💡 **工作流程示例**
+
+| 步骤 | 用户输入 | AI助手的行为 |
+| :--- | :--- | :--- |
+| **1 (常规问题)** | "你们产品的价格是多少?" | 1. 回答价格信息。
2. **附加句:** "如果想要了解更多信息可以留下电话号码,会有专门的客服人员与您联系。" |
+| **2 (留号码)** | "好的,这是我的手机号:13800001234" | 1. **识别**到手机号。
2. **调用工具:** `SendMessage`
3. **回复确认:** "感谢您留下手机号码,我们已收到您的信息,专业的客服人员将在24小时内与您联系,请保持手机畅通。" |
+| **3 (追问)** | "你们的退货政策呢?" | 1. 回答退货政策。
2. **附加句:** "如果想要了解更多信息可以留下电话号码,会有专门的客服人员与您联系。" |
\ No newline at end of file