基于 Spring AI 和 Claude 构建企业智能客服系统:架构与实践
基于 Spring AI 框架与 Claude 大语言模型构建企业级智能客服系统。方案涵盖分层架构设计、RAG 知识库检索增强生成、Maven 依赖配置及核心业务代码实现。包含 REST API 开发、Docker 容器化部署、Kubernetes 编排以及性能优化策略如缓存与异步处理。通过向量数据库存储知识片段,实现精准问答与多轮对话管理,确保数据安全与企业级稳定性。

基于 Spring AI 框架与 Claude 大语言模型构建企业级智能客服系统。方案涵盖分层架构设计、RAG 知识库检索增强生成、Maven 依赖配置及核心业务代码实现。包含 REST API 开发、Docker 容器化部署、Kubernetes 编排以及性能优化策略如缓存与异步处理。通过向量数据库存储知识片段,实现精准问答与多轮对话管理,确保数据安全与企业级稳定性。

随着人工智能技术的快速发展,越来越多的企业开始构建内部智能客服系统来提升客户服务效率和质量。本文将详细介绍如何使用 Spring AI 框架结合 Claude 大语言模型,构建一个功能完善的企业级智能客服系统。
Spring AI 是 Spring 生态系统中专门为 AI 应用开发设计的框架,它具有以下核心优势:
1. 天然的 Spring 生态集成
2. 简化的 AI 开发体验
3. 企业级特性
Claude 作为 Anthropic 开发的大语言模型,在企业应用场景中表现出色:
我们的智能客服系统采用分层架构设计,主要包含以下组件:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 前端界面 │────│ Spring Boot │────│ Claude API │
│ (Web/Mobile) │ │ 应用服务 │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
┌─────────────────┐
│ 知识库系统 │
│ (Vector Store) │
└─────────────────┘
1. 对话管理引擎
2. 知识检索系统
3. Claude 集成层
首先,让我们配置项目的基础依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.company</groupId>
<artifactId>intelligent-customer-service</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Boot 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring AI 相关依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-anthropic-spring-boot-starter</artifactId>
<version>1.0.0-M1</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
<version>1.0.0-M1</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
<version>1.0.0-M1</version>
</dependency>
<!-- 数据库相关 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- 其他工具库 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</project>
在 application.yml 中配置相关参数:
spring:
application:
name: intelligent-customer-service
# 数据库配置
datasource:
url: jdbc:postgresql://localhost:5432/customer_service
username: ${DB_USERNAME:postgres}
password: ${DB_PASSWORD:password}
driver-class-name: org.postgresql.Driver
# JPA 配置
jpa:
hibernate:
ddl-auto: update
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
# Spring AI 配置
ai:
anthropic:
api-key: ${ANTHROPIC_API_KEY}
chat:
options:
model: claude-sonnet-4-20250514
temperature: 0.3
max-tokens: 2000
vectorstore:
pgvector:
index-type: HNSW
distance-type: COSINE_DISTANCE
dimensions: 1536
# 应用自定义配置
app:
knowledge-base:
max-file-size: 10MB
@Service
@Slf4j
public class IntelligentCustomerService {
private final AnthropicChatModel chatModel;
private final VectorStore vectorStore;
private final ChatMemory chatMemory;
private final ConversationService conversationService;
public IntelligentCustomerService(AnthropicChatModel chatModel,
VectorStore vectorStore,
ChatMemory chatMemory,
ConversationService conversationService) {
this.chatModel = chatModel;
this.vectorStore = vectorStore;
this.chatMemory = chatMemory;
this.conversationService = conversationService;
}
/**
* 处理用户问题的核心方法
*/
public ChatResponse handleUserQuery(ChatRequest request) {
try {
String userId = request.getUserId();
String question = request.getMessage();
log.info("处理用户 {} 的问题:{}", userId, question);
// 1. 从知识库检索相关信息
List<Document> relevantDocs = retrieveRelevantKnowledge(question);
// 2. 构建系统提示词
String systemPrompt = buildSystemPrompt(relevantDocs, request.getUserContext());
// 3. 获取对话历史
List<Message> conversationHistory = chatMemory.get(userId, 10);
List<Message> messages = buildMessageList(systemPrompt, conversationHistory, question);
(messages, buildChatOptions());
org.springframework.ai.chat.model. chatModel.call(prompt);
aiResponse.getResult().getOutput().getContent();
saveConversationHistory(userId, question, answer);
ChatResponse.builder()
.message(answer)
.conversationId(request.getConversationId())
.timestamp(LocalDateTime.now())
.sources(extractSources(relevantDocs))
.build();
} (Exception e) {
log.error(, e);
ChatResponse.builder()
.message()
.error()
.build();
}
}
List<Document> {
SearchRequest.query(question).withTopK().withSimilarityThreshold();
vectorStore.similaritySearch(searchRequest);
}
String {
();
contextBuilder.append();
( ; i < relevantDocs.size(); i++) {
relevantDocs.get(i);
contextBuilder.append(String.format(, i + , doc.getContent()));
}
(userContext != ) {
contextBuilder.append(String.format(, userContext.getDepartment(), userContext.getRole()));
}
contextBuilder.append();
contextBuilder.append();
contextBuilder.append();
contextBuilder.append();
contextBuilder.append();
contextBuilder.toString();
}
List<Message> {
List<Message> messages = <>();
messages.add( (systemPrompt));
messages.addAll(history);
messages.add( (currentQuestion));
messages;
}
AnthropicChatOptions {
AnthropicChatOptions.builder()
.withModel()
.withTemperature()
.withMaxTokens()
.build();
}
{
chatMemory.add(userId, (question));
chatMemory.add(userId, (answer));
conversationService.saveConversation(userId, question, answer);
}
List<String> {
documents.stream()
.map(doc -> doc.getMetadata().get())
.filter(Objects::nonNull)
.map(Object::toString)
.distinct()
.collect(Collectors.toList());
}
}
@Service
@Slf4j
public class KnowledgeBaseService {
private final VectorStore vectorStore;
private final KnowledgeDocumentRepository documentRepository;
private final TextSplitter textSplitter;
@Value("${app.knowledge-base.max-file-size:10MB}")
private String maxFileSize;
public KnowledgeBaseService(VectorStore vectorStore, KnowledgeDocumentRepository documentRepository) {
this.vectorStore = vectorStore;
this.documentRepository = documentRepository;
this.textSplitter = new TokenTextSplitter(500, 50);
}
/**
* 添加文档到知识库
*/
@Transactional
public void addDocument(MultipartFile file, String category, String uploadedBy) {
try {
// 1. 验证文件
validateFile(file);
// 2. 读取文档内容
List<Document> documents = readDocument(file);
// 3. 文档分块
List<Document> chunks = splitDocuments(documents);
// 4. 添加元数据
enrichDocuments(chunks, file.getOriginalFilename(), category, uploadedBy);
// 5. 向量化并存储
vectorStore.add(chunks);
// 6. 保存文档记录
saveDocumentRecord(file, category, uploadedBy, chunks.size());
log.info("成功添加文档到知识库:{}, 分块数:{}", file.getOriginalFilename(), chunks.size());
} catch (Exception e) {
log.error("添加文档到知识库失败:{}", file.getOriginalFilename(), e);
( + e.getMessage());
}
}
List<Document> IOException {
file.getOriginalFilename();
getFileExtension(filename);
(extension.toLowerCase()) {
-> (file.getResource());
-> (file.getResource());
, -> (file.getResource());
-> ( + extension);
};
reader.get();
}
List<Document> {
List<Document> allChunks = <>();
(Document document : documents) {
List<Document> chunks = textSplitter.split(document);
allChunks.addAll(chunks);
}
allChunks;
}
{
( ; i < chunks.size(); i++) {
chunks.get(i);
Map<String, Object> metadata = chunk.getMetadata();
metadata.put(, filename);
metadata.put(, category);
metadata.put(, uploadedBy);
metadata.put(, i);
metadata.put(, chunks.size());
metadata.put(, LocalDateTime.now().toString());
}
}
List<KnowledgeSearchResult> {
SearchRequest.query(query).withTopK(limit).withSimilarityThreshold();
List<Document> results = vectorStore.similaritySearch(searchRequest);
results.stream().map(::convertToSearchResult).collect(Collectors.toList());
}
{
documentRepository.findById(documentId)
.orElseThrow(() -> ());
vectorStore.delete(List.of(document.getFilename()));
documentRepository.delete(document);
log.info(, document.getFilename());
}
KnowledgeBaseStats {
documentRepository.count();
vectorStore.similaritySearch(SearchRequest.query().withTopK(Integer.MAX_VALUE)).size();
Map<String, Long> categoryStats = documentRepository.findCategoryStatistics();
KnowledgeBaseStats.builder()
.totalDocuments(totalDocuments)
.totalChunks(totalChunks)
.categoryStatistics(categoryStats)
.lastUpdated(LocalDateTime.now())
.build();
}
{
(file.isEmpty()) {
();
}
file.getOriginalFilename();
(filename == || filename.trim().isEmpty()) {
();
}
}
String {
(filename == || !filename.contains()) {
;
}
filename.substring(filename.lastIndexOf() + );
}
{
KnowledgeDocument.builder()
.filename(file.getOriginalFilename())
.fileSize(file.getSize())
.category(category)
.uploadedBy(uploadedBy)
.chunkCount(chunkCount)
.uploadTime(LocalDateTime.now())
.build();
documentRepository.save(document);
}
KnowledgeSearchResult {
KnowledgeSearchResult.builder()
.content(document.getContent())
.source(document.getMetadata().get().toString())
.category(document.getMetadata().get().toString())
.relevanceScore()
.build();
}
}
@RestController
@RequestMapping("/api/chat")
@Slf4j
@Validated
public class ChatController {
private final IntelligentCustomerService customerService;
private final KnowledgeBaseService knowledgeBaseService;
public ChatController(IntelligentCustomerService customerService, KnowledgeBaseService knowledgeBaseService) {
this.customerService = customerService;
this.knowledgeBaseService = knowledgeBaseService;
}
/**
* 处理聊天消息
*/
@PostMapping("/message")
public ResponseEntity<ApiResponse<ChatResponse>> sendMessage(@Valid @RequestBody ChatRequest request, HttpServletRequest httpRequest) {
try {
// 从请求中获取用户信息
String userId = getUserIdFromRequest(httpRequest);
request.setUserId(userId);
// 处理用户问题
ChatResponse response = customerService.handleUserQuery(request);
return ResponseEntity.ok(ApiResponse.success(response));
} catch (Exception e) {
log.error("处理聊天消息失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error("服务暂时不可用,请稍后重试"));
}
}
/**
* 获取对话历史
*/
@GetMapping("/history/{conversationId}")
public ResponseEntity<ApiResponse<List<ConversationHistory>>> {
{
List<ConversationHistory> history = customerService.getConversationHistory(conversationId, page, size);
ResponseEntity.ok(ApiResponse.success(history));
} (Exception e) {
log.error(, e);
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error());
}
}
ResponseEntity<ApiResponse<Void>> {
{
customerService.clearConversationHistory(conversationId);
ResponseEntity.ok(ApiResponse.success());
} (Exception e) {
log.error(, e);
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error());
}
}
String {
request.getHeader();
}
}
{
KnowledgeBaseService knowledgeBaseService;
{
.knowledgeBaseService = knowledgeBaseService;
}
ResponseEntity<ApiResponse<String>> {
{
getUserIdFromRequest(request);
knowledgeBaseService.addDocument(file, category, uploadedBy);
ResponseEntity.ok(ApiResponse.success());
} (Exception e) {
log.error(, e);
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error( + e.getMessage()));
}
}
ResponseEntity<ApiResponse<List<KnowledgeSearchResult>>> {
{
List<KnowledgeSearchResult> results = knowledgeBaseService.searchKnowledge(query, limit);
ResponseEntity.ok(ApiResponse.success(results));
} (Exception e) {
log.error(, e);
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error());
}
}
ResponseEntity<ApiResponse<KnowledgeBaseStats>> {
{
knowledgeBaseService.getStatistics();
ResponseEntity.ok(ApiResponse.success(stats));
} (Exception e) {
log.error(, e);
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error());
}
}
String {
request.getHeader();
}
}
缓存机制
异步处理
资源优化
数据安全
API 安全
应用监控
@Component
public class ChatServiceMetrics {
private final MeterRegistry meterRegistry;
private final Counter chatRequestCounter;
private final Timer responseTimeTimer;
public ChatServiceMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.chatRequestCounter = Counter.builder("chat.requests.total")
.description("Total number of chat requests")
.register(meterRegistry);
this.responseTimeTimer = Timer.builder("chat.response.time")
.description("Chat response time")
.register(meterRegistry);
}
public void recordChatRequest() {
chatRequestCounter.increment();
}
public Timer.Sample startTimer() {
return Timer.start(meterRegistry);
}
}
健康检查
@Component
public class ChatServiceHealthIndicator implements HealthIndicator {
private final AnthropicChatModel chatModel;
private final VectorStore vectorStore;
@Override
public Health health() {
try {
// 检查 Claude API 连接
checkClaudeConnection();
// 检查向量数据库连接
checkVectorStoreConnection();
return Health.up()
.withDetail("claude", "Available")
.withDetail("vectorStore", "Available")
.build();
} catch (Exception e) {
return Health.down().withDetail("error", e.getMessage()).build();
}
}
private void checkClaudeConnection() {
// 简单的健康检查请求
chatModel.call(new Prompt("Hello"));
}
private void checkVectorStoreConnection() {
// 检查向量数据库连接
vectorStore.similaritySearch(SearchRequest.query("test").withTopK(1));
}
}
FROM openjdk:17-jdk-slim
COPY target/intelligent-customer-service-1.0.0.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- DB_USERNAME=postgres
- DB_PASSWORD=password
depends_on:
- postgres
- redis
postgres:
image: pgvector/pgvector:pg16
environment:
- POSTGRES_DB=customer_service
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-customer-service
labels:
app: customer-service
spec:
replicas: 3
selector:
matchLabels:
app: customer-service
template:
metadata:
labels:
app: customer-service
spec:
containers:
- name: customer-service
image: company/intelligent-customer-service:1.0.0
ports:
- containerPort: 8080
env:
- name: ANTHROPIC_API_KEY
valueFrom:
secretKeyRef:
name: api-secrets
key: anthropic-api-key
- name: DB_USERNAME
valueFrom:
configMapKeyRef:
name: app-config
key: db-username
-
@ExtendWith(MockitoExtension.class)
class IntelligentCustomerServiceTest {
@Mock
private AnthropicChatModel chatModel;
@Mock
private VectorStore vectorStore;
@Mock
private ChatMemory chatMemory;
@Mock
private ConversationService conversationService;
@InjectMocks
private IntelligentCustomerService customerService;
@Test
void shouldHandleUserQuerySuccessfully() {
// Given
ChatRequest request = ChatRequest.builder()
.userId("user123")
.message("如何申请年假?")
.conversationId("conv456")
.build();
List<Document> mockDocs = Arrays.asList(new Document("年假申请需要提前 2 周提交申请表..."));
when(vectorStore.similaritySearch(any(SearchRequest.class))).thenReturn(mockDocs);
when(chatMemory.get(eq("user123"), eq(10))).thenReturn(Arrays.asList());
org.springframework.ai.chat.model.ChatResponse mockResponse = new org.springframework.ai.chat.model.ChatResponse(
Arrays.asList(new Generation(new AssistantMessage("根据公司政策,年假申请需要..."))));
when(chatModel.call(any(Prompt.class))).thenReturn(mockResponse);
// When
customerService.handleUserQuery(request);
assertThat(response).isNotNull();
assertThat(response.getMessage()).contains();
assertThat(response.isError()).isFalse();
verify(vectorStore).similaritySearch(any(SearchRequest.class));
verify(chatModel).call(any(Prompt.class));
verify(chatMemory, times()).add(eq(), any(Message.class));
}
{
ChatRequest.builder()
.userId()
.message()
.build();
(vectorStore.similaritySearch(any(SearchRequest.class))).thenReturn(Arrays.asList());
customerService.handleUserQuery(request);
assertThat(response).isNotNull();
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class CustomerServiceIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("pgvector/pgvector:pg16")
.withDatabaseName("test_customer_service")
.withUsername("test")
.withPassword("test");
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private KnowledgeBaseService knowledgeBaseService;
@MockBean
private AnthropicChatModel chatModel;
@Test
void shouldCompleteFullChatFlow() throws Exception {
// 1. 准备测试数据 - 添加知识文档
MockMultipartFile testFile = new MockMultipartFile("file", "test-doc.txt", "text/plain",
"这是一个测试文档,包含公司政策信息。".getBytes());
knowledgeBaseService.addDocument(testFile, "policy", "test-user");
// 2. 模拟 Claude 响应
org.springframework.ai.chat.model.ChatResponse mockResponse = new org.springframework.ai.chat.model.ChatResponse(
Arrays.asList(new Generation( ())));
(chatModel.call(any(Prompt.class))).thenReturn(mockResponse);
ChatRequest.builder()
.message()
.userId()
.conversationId()
.build();
();
headers.set(, );
HttpEntity<ChatRequest> request = <>(chatRequest, headers);
ResponseEntity<ApiResponse> response = restTemplate.postForEntity(, request, ApiResponse.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().isSuccess()).isTrue();
}
}
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
RedisCacheManager.Builder builder = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory())
.cacheDefaults(cacheConfiguration());
return builder.build();
}
private RedisCacheConfiguration cacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
}
}
@Service
public class CachedKnowledgeService {
@Cacheable(value = "knowledge-search", key = "#query + '-' + #limit")
public List<KnowledgeSearchResult> searchWithCache(String query, int limit) {
return knowledgeBaseService.searchKnowledge(query, limit);
}
@CacheEvict(value = "knowledge-search", allEntries = true)
public void clearSearchCache() {
// 当知识库更新时清除缓存
}
}
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "documentProcessingExecutor")
public TaskExecutor documentProcessingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("doc-processing-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Bean(name = "chatProcessingExecutor")
public TaskExecutor chatProcessingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8);
executor.setMaxPoolSize(16);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("chat-processing-");
executor.initialize();
return executor;
}
}
@Service
public class AsyncDocumentProcessor {
@Async("documentProcessingExecutor")
public CompletableFuture<Void> processDocumentAsync(MultipartFile file, String category, String uploadedBy) {
{
knowledgeBaseService.addDocument(file, category, uploadedBy);
notificationService.sendProcessingComplete(uploadedBy, file.getOriginalFilename());
CompletableFuture.completedFuture();
} (Exception e) {
log.error(, e);
notificationService.sendProcessingError(uploadedBy, file.getOriginalFilename(), e.getMessage());
(e);
}
}
}
@RestController
public class StreamingChatController {
@GetMapping(value = "/api/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamChat(@RequestParam String message, @RequestParam String userId) {
return Flux.create(sink -> {
try {
// 构建流式请求
ChatRequest request = ChatRequest.builder()
.message(message)
.userId(userId)
.build();
// 调用支持流式响应的服务
customerService.handleUserQueryStream(request).subscribe(
chunk -> sink.next(ServerSentEvent.<String>builder().data(chunk).build()),
error -> sink.error(error),
() -> sink.complete()
);
} catch (Exception e) {
sink.error(e);
}
});
}
}
基于 Spring AI 和 Claude 构建企业智能客服系统,我们获得了以下核心优势:
随着 AI 技术的快速发展,我们的智能客服系统还可以在以下方面进行增强:
多模态支持
智能化升级
性能优化
通过本文的详细介绍,相信您已经掌握了使用 Spring AI 和 Claude 构建企业智能客服系统的核心技术和实践方法。这套方案不仅技术先进,而且具有良好的工程实践性,能够满足企业级应用的各种需求。
在实际项目中,建议根据具体的业务场景和技术栈情况,对架构和实现细节进行适当调整。同时,持续关注 Spring AI 和 Claude 的更新动态,及时采用新功能来进一步提升系统能力。
企业智能客服系统的建设是一个持续迭代的过程,通过不断优化和完善,必将为企业带来更大的价值和竞争优势。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online