电商智能化实战:从搜索推荐到AI客服的全栈解决方案
电商智能化实战:从搜索推荐到AI客服的全栈解决方案
开篇故事:一次AI改造,省了2600万
2024年双11前夕,陈磊所在的电商平台做了一次彻底的AI化改造。
那一年他们的平台有1200万SKU,每天活跃用户80万。搜索团队有12个人,客服团队有160人,但效果一直不理想:搜索"夏季连衣裙"搜出来的第一页有3件不是连衣裙;客服的平均响应时间是4分12秒;用户的人均浏览商品数是4.7,转化率卡在了2.3%。
改造完成后的数字让老板直接在全公司大会上点了陈磊的名:
- 搜索转化率从2.3%提升到了4.1%,增幅78%
- 客服成本降低65%(160人客服团队缩减到55人,AI处理74%的常规咨询)
- 人均浏览商品数从4.7提升到7.2,客单价提升19%
折算下来,一年为平台创造的直接价值超过2600万。
陈磊用了什么技术?没有自研大模型,没有专门的AI团队,整个改造的核心是:把现有的Java电商系统和Spring AI框架对接,用合适的大模型能力解决具体业务问题。
这篇文章就是把他们的改造方案拆解给你看。
一、电商AI化全景:哪些环节值得改造
改造优先级建议(按ROI排序):
- AI客服:成本降低最直接,效果最可量化
- 语义搜索:转化率提升,直接影响GMV
- 商品文案生成:运营效率,释放人力
- 个性化推荐:客单价提升,但需要用户行为数据积累
- 欺诈检测:降低损失,但需要历史异常数据
二、智能搜索:语义搜索替代关键词搜索
2.1 关键词搜索 vs 语义搜索
关键词搜索的痛点:
用户输入:"男朋友送给女朋友的礼物"
关键词系统理解:包含"男朋友"OR"女朋友"OR"礼物"的商品
实际结果:乱成一锅粥
语义搜索的理解:
→ 这是节日/纪念日礼物场景
→ 可能需要:饰品、包、香水、花束、护肤品等品类
→ 价格段:通常100-500元
→ 女性偏好商品2.2 商品向量化入库
package com.ecommerce.ai.search;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.stereotype.Service;
import org.springframework.scheduling.annotation.Async;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class ProductIndexingService {
private final VectorStore vectorStore;
private final EmbeddingModel embeddingModel;
/**
* 将商品信息向量化并入库
* 将结构化商品信息转为语义丰富的文本,再向量化
*/
public void indexProduct(Product product) {
// 构建语义丰富的商品文本描述
String semanticText = buildSemanticText(product);
Document doc = new Document(
semanticText,
Map.of(
"product_id", product.getId(),
"title", product.getTitle(),
"category", product.getCategory(),
"price", product.getPrice(),
"brand", product.getBrand(),
"tags", String.join(",", product.getTags()),
"rating", product.getRating(),
"sales_volume", product.getSalesVolume()
)
);
vectorStore.add(List.of(doc));
log.debug("商品入库成功: {}", product.getId());
}
/**
* 构建语义文本的关键方法
* 比单纯的标题/描述拼接效果好得多
*/
private String buildSemanticText(Product product) {
StringBuilder sb = new StringBuilder();
sb.append("商品名称:").append(product.getTitle()).append("\n");
sb.append("品牌:").append(product.getBrand()).append("\n");
sb.append("品类:").append(product.getCategory()).append("\n");
// 关键:将规格参数转为自然语言
if (product.getSpecs() != null) {
sb.append("商品特点:");
product.getSpecs().forEach((k, v) ->
sb.append(k).append("为").append(v).append(",")
);
sb.append("\n");
}
// 适用场景(这是关键的语义信息)
if (product.getScenes() != null && !product.getScenes().isEmpty()) {
sb.append("适用场景:").append(String.join("、", product.getScenes())).append("\n");
}
// 适用人群
if (product.getTargetUsers() != null) {
sb.append("适合人群:").append(product.getTargetUsers()).append("\n");
}
// 热门买家评价关键词
if (product.getReviewKeywords() != null) {
sb.append("买家评价:").append(String.join("、", product.getReviewKeywords())).append("\n");
}
sb.append("价格区间:").append(product.getPriceRange()).append("\n");
return sb.toString();
}
/**
* 批量异步入库(新品上线时使用)
*/
@Async
public void batchIndexProducts(List<Product> products) {
log.info("开始批量商品入库,数量: {}", products.size());
// 分批处理,每批100个
List<List<Product>> batches = partition(products, 100);
for (int i = 0; i < batches.size(); i++) {
List<Document> docs = batches.get(i).stream()
.map(p -> new Document(buildSemanticText(p), buildMetadata(p)))
.collect(Collectors.toList());
vectorStore.add(docs);
log.info("批次 {}/{} 入库完成", i + 1, batches.size());
}
}
private Map<String, Object> buildMetadata(Product product) {
return Map.of(
"product_id", product.getId(),
"title", product.getTitle(),
"category", product.getCategory(),
"price", product.getPrice()
);
}
private <T> List<List<T>> partition(List<T> list, int size) {
List<List<T>> result = new ArrayList<>();
for (int i = 0; i < list.size(); i += size) {
result.add(list.subList(i, Math.min(i + size, list.size())));
}
return result;
}
}2.3 语义搜索服务
package com.ecommerce.ai.search;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class SemanticSearchService {
private final VectorStore vectorStore;
private final ChatClient chatClient;
private final ProductRepository productRepository;
/**
* 混合搜索:语义搜索 + 传统过滤
* 语义搜索找相关商品,传统过滤处理价格/品类等精确条件
*/
public SearchResult search(SearchQuery query) {
log.info("语义搜索请求: {}, 页码: {}", query.getKeyword(), query.getPage());
// 1. 意图理解:解析搜索词中的结构化条件
SearchIntent intent = parseSearchIntent(query.getKeyword());
// 2. 向量检索(召回候选集)
SearchRequest request = SearchRequest.query(intent.getSemanticQuery())
.withTopK(50) // 先召回50个,再过滤排序
.withSimilarityThreshold(0.6);
if (intent.getCategoryFilter() != null) {
request = request.withFilterExpression(
"category == '" + intent.getCategoryFilter() + "'"
);
}
List<Document> candidates = vectorStore.similaritySearch(request);
// 3. 精确过滤(价格范围等)
List<SearchResultItem> filtered = candidates.stream()
.filter(doc -> matchesPriceRange(doc, intent))
.map(this::toSearchResultItem)
.collect(Collectors.toList());
// 4. 重排序(融合语义相似度 + 销量 + 评分)
List<SearchResultItem> reranked = rerank(filtered, query);
// 5. 分页
int total = reranked.size();
int start = query.getPage() * query.getPageSize();
List<SearchResultItem> pageItems = reranked.stream()
.skip(start)
.limit(query.getPageSize())
.collect(Collectors.toList());
return SearchResult.builder()
.items(pageItems)
.total(total)
.intentSummary(intent.getSummary())
.build();
}
/**
* 搜索意图解析
* 将自然语言搜索词结构化
*/
private SearchIntent parseSearchIntent(String keyword) {
// 简单的规则提取(生产环境可用AI增强)
SearchIntent intent = new SearchIntent();
intent.setSemanticQuery(keyword);
// 价格区间提取
if (keyword.contains("百元") || keyword.contains("100元以内")) {
intent.setMaxPrice(100.0);
} else if (keyword.contains("千元") || keyword.contains("1000以内")) {
intent.setMaxPrice(1000.0);
}
// 这里可以接入AI进行更精准的意图理解
// 如果关键词比较复杂,调用chatClient解析
if (keyword.length() > 10 && !isSimpleKeyword(keyword)) {
intent = parseWithAI(keyword);
}
return intent;
}
private SearchIntent parseWithAI(String keyword) {
String prompt = String.format("""
分析以下电商搜索词,提取结构化信息:
搜索词:"%s"
请以JSON格式输出:
{
"semanticQuery": "用于语义搜索的核心描述",
"categoryHint": "品类提示(如有)",
"priceRange": {"min": null, "max": null},
"targetUser": "目标用户(如有)",
"scene": "使用场景(如有)",
"summary": "搜索意图一句话摘要"
}
""", keyword);
String response = chatClient.prompt()
.user(prompt)
.call()
.content();
return SearchIntent.fromJson(response);
}
private boolean isSimpleKeyword(String keyword) {
// 不含空格和特殊场景词的短词认为是简单关键词
return !keyword.contains(" ") && !keyword.contains("送给") &&
!keyword.contains("适合") && !keyword.contains("什么");
}
private boolean matchesPriceRange(Document doc, SearchIntent intent) {
Object price = doc.getMetadata().get("price");
if (price == null) return true;
double p = Double.parseDouble(price.toString());
if (intent.getMinPrice() != null && p < intent.getMinPrice()) return false;
if (intent.getMaxPrice() != null && p > intent.getMaxPrice()) return false;
return true;
}
/**
* 多因子重排序
* 融合:语义相似度(0.5) + 销量得分(0.3) + 评分(0.2)
*/
private List<SearchResultItem> rerank(
List<SearchResultItem> items,
SearchQuery query) {
return items.stream()
.sorted(Comparator.comparingDouble(item ->
-calculateRankScore(item)
))
.collect(Collectors.toList());
}
private double calculateRankScore(SearchResultItem item) {
double semanticScore = item.getSimilarity(); // 0-1
// 销量对数归一化
double salesScore = Math.log1p(item.getSalesVolume()) / 20.0;
salesScore = Math.min(salesScore, 1.0);
// 评分归一化
double ratingScore = (item.getRating() - 1.0) / 4.0; // 1-5 → 0-1
return semanticScore * 0.5 + salesScore * 0.3 + ratingScore * 0.2;
}
private SearchResultItem toSearchResultItem(Document doc) {
Map<String, Object> meta = doc.getMetadata();
return SearchResultItem.builder()
.productId((String) meta.get("product_id"))
.title((String) meta.get("title"))
.price(Double.parseDouble(meta.get("price").toString()))
.similarity((Double) meta.getOrDefault("distance", 0.8))
.salesVolume(Long.parseLong(meta.getOrDefault("sales_volume", "0").toString()))
.rating(Double.parseDouble(meta.getOrDefault("rating", "5").toString()))
.build();
}
}三、个性化推荐:基于用户行为的AI推荐引擎
3.1 用户行为数据采集
package com.ecommerce.ai.recommendation;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
/**
* 用户行为事件采集
* 所有用户行为统一入Kafka,异步处理
*/
@Service
@RequiredArgsConstructor
public class UserBehaviorTracker {
private final KafkaTemplate<String, UserBehaviorEvent> kafkaTemplate;
public void trackView(String userId, String productId, long dwellTimeMs) {
publish(UserBehaviorEvent.view(userId, productId, dwellTimeMs));
}
public void trackClick(String userId, String productId, String source) {
publish(UserBehaviorEvent.click(userId, productId, source));
}
public void trackAddToCart(String userId, String productId, int quantity) {
publish(UserBehaviorEvent.addToCart(userId, productId, quantity));
}
public void trackPurchase(String userId, String productId, double amount) {
publish(UserBehaviorEvent.purchase(userId, productId, amount));
}
public void trackSearch(String userId, String keyword, List<String> clickedProducts) {
publish(UserBehaviorEvent.search(userId, keyword, clickedProducts));
}
private void publish(UserBehaviorEvent event) {
kafkaTemplate.send("user-behavior-events", event.getUserId(), event);
}
}3.2 推荐服务
package com.ecommerce.ai.recommendation;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class PersonalizedRecommendationService {
private final ChatClient chatClient;
private final VectorStore vectorStore;
private final UserProfileRepository userProfileRepository;
private final ProductRepository productRepository;
/**
* 首页个性化推荐
* 缓存5分钟(用户偏好不会变化太快)
*/
@Cacheable(value = "homepage-recommendations", key = "#userId")
public List<RecommendedProduct> getHomepageRecommendations(
String userId,
int limit) {
// 1. 获取用户画像
UserProfile profile = userProfileRepository.findByUserId(userId);
if (profile == null) {
// 新用户:返回热门商品
return getHotProducts(limit);
}
// 2. 基于用户偏好构建查询
String preferenceQuery = buildPreferenceQuery(profile);
// 3. 向量检索相关商品
List<Document> candidates = vectorStore.similaritySearch(
SearchRequest.query(preferenceQuery)
.withTopK(limit * 3) // 召回3倍,再过滤已购/已浏览
.withSimilarityThreshold(0.6)
);
// 4. 过滤已购买商品
Set<String> purchasedIds = new HashSet<>(profile.getPurchasedProductIds());
Set<String> recentViewedIds = new HashSet<>(profile.getRecentViewedIds());
return candidates.stream()
.filter(doc -> !purchasedIds.contains(doc.getMetadata().get("product_id")))
.filter(doc -> !recentViewedIds.contains(doc.getMetadata().get("product_id")))
.limit(limit)
.map(this::toRecommendedProduct)
.collect(Collectors.toList());
}
/**
* 商品详情页"猜你喜欢"(基于当前商品 + 用户历史)
*/
public List<RecommendedProduct> getSimilarProducts(
String currentProductId,
String userId,
int limit) {
Product current = productRepository.findById(currentProductId)
.orElseThrow(() -> new ProductNotFoundException(currentProductId));
// 构建"相似商品"查询
String query = current.getTitle() + " " + current.getCategory() +
" " + String.join(" ", current.getTags());
List<Document> similar = vectorStore.similaritySearch(
SearchRequest.query(query)
.withTopK(limit + 5) // 多取5个用于过滤当前商品
.withSimilarityThreshold(0.65)
);
return similar.stream()
.filter(doc -> !currentProductId.equals(doc.getMetadata().get("product_id")))
.limit(limit)
.map(this::toRecommendedProduct)
.collect(Collectors.toList());
}
private String buildPreferenceQuery(UserProfile profile) {
StringBuilder query = new StringBuilder();
// 偏好品类(按频次排序,取前3)
profile.getCategoryPreferences().entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.limit(3)
.forEach(e -> query.append(e.getKey()).append(" "));
// 偏好价格区间
if (profile.getAvgOrderAmount() > 0) {
query.append(String.format("价格约%d元 ",
(int) profile.getAvgOrderAmount()));
}
// 最近浏览的商品标签
if (profile.getRecentSearchKeywords() != null) {
profile.getRecentSearchKeywords().stream()
.limit(3)
.forEach(kw -> query.append(kw).append(" "));
}
return query.toString().trim();
}
private List<RecommendedProduct> getHotProducts(int limit) {
return productRepository.findTopByOrderBySalesVolumeDesc(limit)
.stream()
.map(p -> RecommendedProduct.fromProduct(p, "热销推荐", null))
.collect(Collectors.toList());
}
private RecommendedProduct toRecommendedProduct(Document doc) {
Map<String, Object> meta = doc.getMetadata();
return RecommendedProduct.builder()
.productId((String) meta.get("product_id"))
.title((String) meta.get("title"))
.price(Double.parseDouble(meta.get("price").toString()))
.reason("猜你喜欢")
.score((Double) meta.getOrDefault("distance", 0.75))
.build();
}
}四、商品描述生成:从规格参数自动生成卖点文案
4.1 痛点分析
一个电商平台每天上新商品约3000个,每个商品需要写100-300字的卖点文案。如果用人工,一个运营每天最多处理50个。有了AI,一个人可以处理500个,效率提升10倍。
4.2 实现
package com.ecommerce.ai.content;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
public class ProductCopywritingService {
private final ChatClient chatClient;
private static final String COPYWRITING_SYSTEM_PROMPT = """
你是一名电商文案专家,擅长写出让消费者心动的商品卖点文案。
写作原则:
1. 突出用户最关心的痛点和需求,而不是堆砌参数
2. 使用具体数字和对比,比"效果好"更有说服力
3. 语气亲切自然,像朋友推荐而非广告
4. 字数控制在150-250字
5. 结尾可以有一个行动引导(但不要用"立即购买"这类生硬词)
不要使用的词:极致、卓越、颠覆、引领、革命性、黑科技
""";
/**
* 生成商品卖点文案
*
* @param product 商品信息
* @param style 文案风格:professional/casual/youth
*/
public String generateProductCopy(Product product, String style) {
String styleGuide = switch (style) {
case "casual" -> "风格:轻松口语化,像闺蜜/好友推荐";
case "youth" -> "风格:年轻活泼,可以用一点网络用语,但不要过度";
default -> "风格:专业可信,适合高客单价商品";
};
String prompt = String.format("""
请为以下商品写一段卖点文案:
商品名称:%s
品类:%s
品牌:%s
核心规格:%s
价格:%s元
目标人群:%s
该商品最大的优势:%s
%s
""",
product.getTitle(),
product.getCategory(),
product.getBrand(),
formatSpecs(product.getSpecs()),
product.getPrice(),
product.getTargetUsers(),
product.getKeyBenefits(),
styleGuide
);
return chatClient.prompt()
.system(COPYWRITING_SYSTEM_PROMPT)
.user(prompt)
.call()
.content();
}
/**
* 批量生成(带进度回调)
*/
public void batchGenerateCopy(
List<Product> products,
String style,
BatchProgressCallback callback) {
int total = products.size();
for (int i = 0; i < total; i++) {
Product product = products.get(i);
try {
String copy = generateProductCopy(product, style);
product.setAiCopy(copy);
callback.onProgress(i + 1, total, product.getId(), copy);
} catch (Exception e) {
log.warn("商品文案生成失败: {}", product.getId(), e);
callback.onError(product.getId(), e.getMessage());
}
}
}
/**
* 多版本A/B测试文案生成
*/
public List<String> generateAbTestCopies(Product product, int versions) {
List<String> copies = new ArrayList<>();
String[] styles = {"professional", "casual", "youth"};
for (int i = 0; i < Math.min(versions, styles.length); i++) {
copies.add(generateProductCopy(product, styles[i]));
}
return copies;
}
private String formatSpecs(Map<String, String> specs) {
if (specs == null || specs.isEmpty()) return "无详细规格";
return specs.entrySet().stream()
.map(e -> e.getKey() + ":" + e.getValue())
.collect(java.util.stream.Collectors.joining("、"));
}
@FunctionalInterface
public interface BatchProgressCallback {
void onProgress(int current, int total, String productId, String copy);
default void onError(String productId, String error) {}
}
}五、智能客服:电商场景的对话机器人
5.1 架构设计
5.2 客服机器人实现
package com.ecommerce.ai.customerservice;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
public class EcommerceCustomerServiceBot {
private final ChatClient chatClient;
private final OrderService orderService;
private final ProductKnowledgeService productKnowledgeService;
private final EscalationService escalationService;
private final ChatMemory chatMemory = new InMemoryChatMemory();
private static final String CS_SYSTEM_PROMPT = """
你是【优购商城】的智能客服小优,专业、友善、高效。
你的能力范围:
- 查询订单状态和物流信息
- 解答商品相关问题
- 受理退换货申请(7天无理由)
- 解答平台规则和活动问题
处理规则:
1. 用户情绪激动时,先表示理解,再解决问题
2. 退换货问题:先判断是否在7天内,在则直接指引,不在则说明规则并请用户提供证据
3. 物流查询:直接调用工具获取实时信息
4. 无法解决的问题:主动提出转人工
5. 回复简洁,用分点列表,不超过200字
当前用户ID:{userId}
当前会话ID:{sessionId}
""";
/**
* 处理用户消息
*/
public CustomerServiceResponse handleMessage(
String sessionId,
String userId,
String message) {
log.info("客服消息,用户: {}, 内容: {}", userId, message);
// 1. 意图分类
ServiceIntent intent = classifyIntent(message);
log.debug("意图识别结果: {}", intent);
// 2. 预处理(订单查询等需要先获取数据)
String contextualInfo = gatherContextualInfo(intent, userId, message);
// 3. 检查是否需要转人工
if (shouldEscalate(intent, message)) {
return escalationService.escalate(sessionId, userId, message);
}
// 4. AI生成回复(带会话记忆)
String systemPrompt = CS_SYSTEM_PROMPT
.replace("{userId}", userId)
.replace("{sessionId}", sessionId);
String userMessage = contextualInfo.isEmpty() ? message
: message + "\n\n[系统信息:" + contextualInfo + "]";
String response = chatClient.prompt()
.system(systemPrompt)
.user(userMessage)
.advisors(new MessageChatMemoryAdvisor(chatMemory, sessionId, 10))
.call()
.content();
// 5. 检查回复是否包含转人工信号
if (response.contains("为您转接人工客服")) {
escalationService.flagForEscalation(sessionId);
}
return CustomerServiceResponse.builder()
.content(response)
.intent(intent.name())
.requiresHuman(false)
.sessionId(sessionId)
.build();
}
/**
* 意图分类(基于规则+AI兜底)
*/
private ServiceIntent classifyIntent(String message) {
// 关键词快速分类
if (message.contains("快递") || message.contains("物流") ||
message.contains("到哪") || message.contains("发货")) {
return ServiceIntent.LOGISTICS_INQUIRY;
}
if (message.contains("退款") || message.contains("退货") || message.contains("换货")) {
return ServiceIntent.RETURN_REFUND;
}
if (message.contains("质量") || message.contains("坏了") || message.contains("破损")) {
return ServiceIntent.QUALITY_COMPLAINT;
}
if (message.contains("订单")) {
return ServiceIntent.ORDER_INQUIRY;
}
// AI兜底分类
return classifyWithAI(message);
}
private ServiceIntent classifyWithAI(String message) {
String prompt = String.format("""
对以下客服消息进行意图分类,只返回分类名称:
消息:"%s"
分类选项(只选一个):
- LOGISTICS_INQUIRY(物流查询)
- ORDER_INQUIRY(订单查询)
- RETURN_REFUND(退换货)
- QUALITY_COMPLAINT(质量投诉)
- PRODUCT_INQUIRY(商品咨询)
- GENERAL_INQUIRY(一般咨询)
- NEGATIVE_EMOTION(情绪激动/投诉)
""", message);
String result = chatClient.prompt().user(prompt).call().content().trim();
try {
return ServiceIntent.valueOf(result);
} catch (IllegalArgumentException e) {
return ServiceIntent.GENERAL_INQUIRY;
}
}
private String gatherContextualInfo(
ServiceIntent intent,
String userId,
String message) {
return switch (intent) {
case LOGISTICS_INQUIRY, ORDER_INQUIRY -> {
// 自动获取用户最近订单信息
List<Order> recentOrders = orderService.getRecentOrders(userId, 3);
yield formatOrderInfo(recentOrders);
}
case RETURN_REFUND -> {
// 获取订单信息和退换货政策
List<Order> orders = orderService.getRecentOrders(userId, 5);
yield formatOrderInfo(orders) + "\n退换货政策:7天无理由退换货";
}
default -> "";
};
}
private boolean shouldEscalate(ServiceIntent intent, String message) {
// 强情绪词检测
String[] escalationKeywords = {"投诉", "举报", "律师", "起诉", "媒体", "曝光",
"太差了", "骗人", "恶心"};
for (String kw : escalationKeywords) {
if (message.contains(kw)) return true;
}
return intent == ServiceIntent.QUALITY_COMPLAINT &&
message.contains("严重");
}
private String formatOrderInfo(List<Order> orders) {
if (orders.isEmpty()) return "未找到相关订单";
StringBuilder sb = new StringBuilder("用户最近订单:\n");
orders.forEach(o -> sb.append(String.format(
"订单号:%s,商品:%s,状态:%s,下单时间:%s\n",
o.getId(), o.getProductName(), o.getStatus(), o.getCreatedAt()
)));
return sb.toString();
}
public enum ServiceIntent {
LOGISTICS_INQUIRY, ORDER_INQUIRY, RETURN_REFUND,
QUALITY_COMPLAINT, PRODUCT_INQUIRY, GENERAL_INQUIRY, NEGATIVE_EMOTION
}
}六、订单异常检测:AI识别刷单和欺诈
6.1 欺诈特征工程
package com.ecommerce.ai.fraud;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
@Service
@RequiredArgsConstructor
public class OrderFraudDetectionService {
private final OrderRepository orderRepository;
private final UserRepository userRepository;
/**
* 订单欺诈风险评分
* 分值越高风险越大
*/
public FraudRiskScore evaluateOrder(Order order) {
List<RiskFactor> factors = new ArrayList<>();
// 规则1:账号注册时间 < 7天但下单金额 > 500元
User user = userRepository.findById(order.getUserId()).orElse(null);
if (user != null) {
long daysSinceRegistered = Duration.between(
user.getCreatedAt(), LocalDateTime.now()).toDays();
if (daysSinceRegistered < 7 && order.getTotalAmount() > 500) {
factors.add(RiskFactor.of("新账号大金额", 0.3));
}
}
// 规则2:同一收货地址24小时内3次及以上下单
long recentOrderCount = orderRepository.countByShippingAddressAndCreatedAtAfter(
order.getShippingAddress(),
LocalDateTime.now().minusHours(24)
);
if (recentOrderCount >= 3) {
factors.add(RiskFactor.of("同地址高频下单", 0.25));
}
// 规则3:IP地址与收货地址城市不匹配
if (!isIpCityMatchShipping(order.getClientIp(), order.getShippingAddress())) {
factors.add(RiskFactor.of("IP与收货地不匹配", 0.15));
}
// 规则4:下单到支付时间 < 10秒(可能是脚本)
if (order.getPaymentTime() != null) {
long secondsToPayment = Duration.between(
order.getCreatedAt(), order.getPaymentTime()).toSeconds();
if (secondsToPayment < 10) {
factors.add(RiskFactor.of("极速付款(疑似脚本)", 0.2));
}
}
// 规则5:同一用户历史退款率 > 30%
double refundRate = calculateUserRefundRate(order.getUserId());
if (refundRate > 0.30) {
factors.add(RiskFactor.of("历史高退款率:" + String.format("%.0f%%", refundRate * 100), 0.25));
}
// 汇总风险分
double totalScore = factors.stream()
.mapToDouble(RiskFactor::getScore)
.sum();
totalScore = Math.min(totalScore, 1.0);
FraudRiskLevel level;
String action;
if (totalScore >= 0.7) {
level = FraudRiskLevel.HIGH;
action = "BLOCK"; // 拦截订单
} else if (totalScore >= 0.4) {
level = FraudRiskLevel.MEDIUM;
action = "REVIEW"; // 人工审核
} else {
level = FraudRiskLevel.LOW;
action = "PASS";
}
return FraudRiskScore.builder()
.orderId(order.getId())
.totalScore(totalScore)
.level(level)
.action(action)
.factors(factors)
.build();
}
private boolean isIpCityMatchShipping(String ip, String address) {
// 接入IP地理位置API实现,此处简化
return true;
}
private double calculateUserRefundRate(String userId) {
long totalOrders = orderRepository.countByUserId(userId);
if (totalOrders < 5) return 0.0; // 订单太少,不判断
long refundedOrders = orderRepository.countByUserIdAndRefunded(userId, true);
return (double) refundedOrders / totalOrders;
}
}七、营销文案生成:活动页面和推送文案的自动化
7.1 多场景文案生成
package com.ecommerce.ai.marketing;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class MarketingCopyService {
private final ChatClient chatClient;
/**
* 活动Banner文案
*/
public BannerCopy generateBannerCopy(MarketingActivity activity) {
String prompt = String.format("""
为以下促销活动生成Banner文案:
活动名称:%s
活动类型:%s(满减/折扣/秒杀/包邮)
优惠力度:%s
活动时间:%s
主推品类:%s
请生成:
1. 主标题(8字以内,有冲击力)
2. 副标题(15字以内,说明优惠)
3. 按钮文字(4字以内)
4. 紧迫感文案("仅剩X小时"类型,10字以内)
输出为JSON格式。
""",
activity.getName(),
activity.getType(),
activity.getDiscount(),
activity.getTimeRange(),
activity.getMainCategory()
);
String response = chatClient.prompt().user(prompt).call().content();
return BannerCopy.fromJson(response);
}
/**
* 推送通知文案(App Push / 短信)
*/
public PushCopy generatePushCopy(
PushType type,
Product product,
UserProfile userProfile) {
String personalization = userProfile != null
? "该用户偏好:" + String.join("、", userProfile.getTopCategories())
: "无个性化信息,面向大众用户";
String prompt = String.format("""
生成一条%s推送文案:
商品:%s
原价:%s元,现价:%s元,折扣:%s折
%s
要求:
- Push通知:标题20字以内,内容40字以内
- 短信:70字以内,包含退订提示占用4字符
- 不要使用敏感词(免费、中奖、恭喜等)
输出JSON格式:
{"pushTitle": "...", "pushContent": "...", "smsContent": "..."}
""",
type.name(),
product.getTitle(),
product.getOriginalPrice(),
product.getCurrentPrice(),
String.format("%.1f", product.getCurrentPrice() / product.getOriginalPrice() * 10),
personalization
);
String response = chatClient.prompt().user(prompt).call().content();
return PushCopy.fromJson(response);
}
public enum PushType {
FLASH_SALE, PERSONALIZED_RECOMMEND, CART_REMINDER, REPURCHASE
}
}八、数据看板:AI功能效果的实时监控
8.1 指标体系
package com.ecommerce.ai.monitoring;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
public class AIEffectivenessMonitor {
private final MetricsCollector metricsCollector;
private final AlertService alertService;
/**
* 每小时统计AI功能效果指标
*/
@Scheduled(fixedRate = 3600000)
public void collectHourlyMetrics() {
AIEffectivenessReport report = AIEffectivenessReport.builder()
// 搜索效果
.semanticSearchCtr(metricsCollector.getSearchCtr("semantic"))
.keywordSearchCtr(metricsCollector.getSearchCtr("keyword"))
.searchConversionRate(metricsCollector.getSearchConversion())
// 推荐效果
.recommendationCtr(metricsCollector.getRecommendCtr())
.recommendationConversionRate(metricsCollector.getRecommendConversion())
.averageOrderValueFromRecommend(metricsCollector.getRecommendAov())
// 客服效果
.botHandleRate(metricsCollector.getBotHandleRate())
.botResolutionRate(metricsCollector.getBotResolutionRate())
.botSatisfactionScore(metricsCollector.getBotCsatScore())
.humanEscalationRate(metricsCollector.getEscalationRate())
// AI系统健康度
.aiApiSuccessRate(metricsCollector.getAiApiSuccessRate())
.aiAvgResponseTimeMs(metricsCollector.getAiAvgLatency())
.build();
log.info("AI效果小时报告: {}", report);
// 异常告警
checkAlerts(report);
}
private void checkAlerts(AIEffectivenessReport report) {
if (report.getAiApiSuccessRate() < 0.95) {
alertService.sendAlert("AI API成功率低于95%,当前: " +
String.format("%.1f%%", report.getAiApiSuccessRate() * 100));
}
if (report.getAiAvgResponseTimeMs() > 3000) {
alertService.sendAlert("AI平均响应时间超过3秒,当前: " +
report.getAiAvgResponseTimeMs() + "ms");
}
if (report.getBotHandleRate() < 0.60) {
alertService.sendAlert("客服机器人处理率低于60%,需要检查意图识别模型");
}
}
}九、数据与效果
| 指标 | 改造前 | 改造后 | 变化 |
|---|---|---|---|
| 搜索转化率 | 2.3% | 4.1% | +78% |
| 人均浏览商品数 | 4.7 | 7.2 | +53% |
| 客服AI处理率 | 0% | 74% | - |
| 客服平均响应时间 | 4分12秒 | 8秒 | -97% |
| 客服人员数量 | 160人 | 55人 | -66% |
| 商品文案生产效率 | 50件/人/天 | 500件/人/天 | +900% |
| 欺诈订单识别率 | 手工抽检30% | AI识别85% | +183% |
FAQ
Q:向量数据库选哪个?Milvus、Weaviate、Pinecone哪个适合电商?
中小规模(1000万SKU以下)用Milvus单机版足够;大规模用Milvus集群或Elasticsearch的向量检索插件(前者性能更好,后者与现有ES集群整合更方便)。Pinecone是SaaS服务,数据在海外,不适合国内电商。
Q:推荐系统冷启动怎么解决?新用户没有行为数据怎么推?
三步走:第一步看注册时填的兴趣标签(如果有);第二步推平台热销榜;第三步在用户第一次浏览/点击后立即更新画像,通常用户3次行为后推荐就能较精准了。
Q:AI客服机器人准确率不够高怎么办?用户投诉怎么处理?
设置兜底策略比提高准确率更重要:(1) 连续2次用户反馈"没有解决",自动转人工;(2) 情绪检测(不满意词汇出现时)主动询问"是否需要转人工";(3) 做好人工接管的上下文传递(把对话历史给人工客服)。
Q:营销文案AI生成后还需要人工审核吗?
建议:低价低风险商品(200元以下)可以直接发布;高价商品(500元以上)、生鲜/医疗类建议人工过审;促销文案(涉及优惠力度)必须人工确认数字准确性。
