🎤 面试官:你们的退款系统是怎么设计的?有没有审核流程?
我: 有!我们不是“用户一申请就退钱”,而是分了自动退款 + 人工审核两套流程,根据订单金额、用户等级、风险等级来决定走哪条路。
核心目标就三个:
- 不能多退、错退(钱退错人,公司兜不住)
- 不能拖太久(用户投诉“退了半个月还没到账”)
- 能防欺诈(比如黑产买低价商品然后恶意退款)
✅ 一、退款流程整体是怎样的?
用户申请退款
↓
系统判断:走自动?还是人工?
↓
┌─────────────┴─────────────┐
↓(小额/低风险) ↓(大额/高风险)
自动退款审批 人工审核(运营/客服)
↓ ↓
调第三方支付退钱 审核通过 → 调支付退钱
↓ ↓
更新订单状态 更新状态 + 通知用户✅ 二、什么情况下走“自动退款”?
我们设了一套自动通过规则,满足就秒退:
| 条件 | 说明 |
|---|---|
| 退款金额 ≤ 50元 | 小额默认信任 |
| 用户是“高信用等级” | 比如老用户、无历史欺诈记录 |
| 商品是“虚拟类” or “未发货” | 比如电子书、充值卡、刚下单还没出库 |
| 未使用优惠券 or 可返还 | 避免优惠券白送 |
| 非敏感品类 | 比如不是手机、黄金等高价值商品 |
满足以上条件 → 系统自动审批 → 调用微信/支付宝的 退款接口 → 钱原路返回 → 更新订单状态。
用户感知就是:“点了申请,3秒到账”。
✅ 三、什么情况下要“人工审核”?
以下情况必须人工介入:
| 场景 | 为什么? |
|---|---|
| 退款金额 > 500元 | 防资损,必须看一眼 |
| 用户是新用户 or 有退款欺诈记录 | 黑产常用小号 |
| 已发货但用户说“没收到” | 可能物流丢件 or 恶意白嫖 |
| 使用了大额优惠券 | 要判断是否要追回 |
| 同一用户近期频繁退款 | 比如7天退了5笔,疑似职业退款党 |
| 敏感商品(手机、奢侈品) | 高价值,怕调包 |
这时候系统会把退款申请推到运营后台,由客服或风控人员审核。
✅ 四、人工审核流程怎么做?
我们做了个“退款审核工作台”,运营能看到:
| 信息 | 用途 |
|---|---|
| 订单详情 | 买了啥、多少钱、啥时候下单 |
| 退款原因 | 用户填的“不想要了”、“发错货”等 |
| 用户信息 | 是否新用户、历史订单数、退款率 |
| 物流信息 | 是否已签收?签收时间? |
| 商品类型 | 是否高价值、是否易损 |
| 历史行为 | 该用户是否频繁退款? |
| 图片证据 | 用户上传的“破损照” or “发错图” |
审核员看完后,点“通过” or “拒绝”。
- 通过 → 系统调支付接口退款;
- 拒绝 → 给用户发通知,可申诉。
✅ 五、调第三方支付退款是怎么做的?
和支付一样,不能直接退,要调接口:
// 调用微信退款接口
WeChatRefundRequest request = new WeChatRefundRequest();
request.setOutTradeNo(order.getOutTradeNo());
request.setOutRefundNo("refund_" + orderId);
request.setTotalFee(order.getAmount());
request.setRefundFee(refundAmount);
WeChatRefundResponse resp = weChatClient.refund(request);关键点:
- 必须用原支付单号;
- 退款金额 ≤ 支付金额(支持部分退);
- 微信/支付宝会异步回调我们
refund_notify_url;- 我们收到回调后,才把订单退款状态改为“已退款”。
不能只信前端返回!必须等支付平台的回调才算数。
✅ 六、怎么防止“重复退款”?
这是大坑!我们踩过:
有一次网络超时,系统以为退款失败,重试了3次,结果钱退了3遍,资损严重。
现在我们加了三道锁:
🔒 1. 添加数据库唯一索引
ALTER TABLE refund_record
ADD UNIQUE uk_out_refund_no (out_refund_no); -- 退款单号out_refund_no唯一🔒 2. Redis 分布式锁
String lockKey = "refund:lock:" + orderNo;
Boolean locked = redisTemplate.setIfAbsent(lockKey, "1", 5, TimeUnit.MINUTES);
if (!locked) {
throw new BizException("正在处理中,请勿重复提交");
}🔒 3. 幂等处理
每次退款请求都带一个业务幂等ID(如
refund_1001_1), 系统先查是否已处理过,有就直接返回结果,不再调支付。
✅ 七、退款后,优惠券、积分怎么处理?
不能光退钱,关联权益也要还原:
| 权益 | 处理方式 |
|---|---|
| 优惠券 | 如果是“平台券”,退回券池(状态变“未使用”); 如果是“活动券”,可能不退(看规则) |
| 积分 | 扣掉的积分还给用户 |
| 成长值 | 会员等级相关的,也要扣回 |
| 赠品 | 如果赠品已发,要提示用户退回 |
这些都在退款成功后,发个 MQ 消息去异步处理。
✅ 八、怎么监控和对账?
为了保证退款业务的完整性,每天凌晨定时跑一个退款对账任务:
- 查我们数据库里“已退款”的订单;
- 调微信/支付宝的
refund.query(退款查询)接口,查真实退款状态; - 如果发现“我们记了退款,支付平台没成功”,就告警 + 补发;
- 如果“支付平台退了,我们没记”,就手动补单(修改订单状态)。
防止出现“钱退了但订单状态还是待退款”。
🎤 面试官:什么是“幂等”?
一句话:同一个操作,做一次和做一百次,结果都一样。
比如:
- 你给用户退 100 块,退一次是 -100,退十次变成 -1000,这就不幂等;
- 正确做法:退一次成功,再调十次也只退一次,其他都返回“已处理”,这就叫幂等。
🌰 举个真实场景:退款接口被重复调用
用户申请退款 → 我们调微信退款接口 → 网络超时 → 我们以为失败了 → 重试 → 结果微信那边其实成功了 → 钱退了两次!
这就是资损,公司要赔钱的!
✅ 幂等的核心思路:每笔业务请求,都带一个“唯一标识”
这个标识叫:幂等ID(idempotent_id)
比如:
{
"orderId": "1001",
"refundAmount": 9900,
"idempotentId": "refund_1001_20240601" // ← 关键!
}这个
idempotentId是前端 or 后端生成的,同一个退款请求,ID 必须一样。
✅ 幂等处理的 3 种实战做法(从简单到高级)
🔹 方法一:数据库唯一索引(最简单,推荐)
在
refund_record表加个字段:
ALTER TABLE refund_record
ADD COLUMN idempotent_id VARCHAR(64) UNIQUE COMMENT '幂等ID';插入退款记录时:
INSERT INTO refund_record
(order_id, amount, idempotent_id, status)
VALUES (1001, 9900, 'refund_1001_20240601', 'PROCESSING'); -- 退款中如果这个
idempotentId已经存在,数据库直接报错:Duplicate entry 'refund_1001_20240601' for key 'idempotent_id'
我们捕获这个异常,返回:
json{ "code": "SUCCESS", "msg": "请求已处理", "data": { "status": "REFUNDED" } } //已退款
优点:简单、可靠、不依赖外部服务 缺点:得写数据库,高并发时可能锁表
🔹 方法二:Redis 先查后写(高性能)
用 Redis 记录“这个幂等ID是否已处理”:
String key = "idempotent:" + idempotentId; // eg: idempotent:refund_1001_20240601
// 1. 先查是否已存在
Boolean exists = redisTemplate.hasKey(key);
if (exists) {
// 已处理,直接返回结果
return getRefundResultFromDB(idempotentId);
}
// 2. 加锁,防止并发
String lockKey = "lock:" + key;
Boolean locked = redisTemplate.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (!locked) {
throw new BizException("处理中,请勿重复提交");
}
try {
// 3. 再次确认(防锁住后别人已处理)
if (redisTemplate.hasKey(key)) {
return getRefundResultFromDB(idempotentId);
}
// 4. 走正常退款流程(调微信、写DB)
WeChatRefundResponse resp = weChatClient.refund(request);
// 5. 记录结果到 Redis,有效期30天
redisTemplate.set(key, JSON.toJSONString(resp), 30, TimeUnit.DAYS);
return resp;
} finally {
redisTemplate.delete(lockKey);
}优点:快,适合高并发 缺点:Redis 故障可能丢数据,得配合对账
🔹 方法三:状态机 + 条件更新(最安全)
不光看幂等ID,还要看订单当前状态。
UPDATE refund_record
SET status = 'REFUNDED',
refund_no = 'wx123456',
update_time = NOW()
WHERE order_id = 1001
AND status = 'PENDING' -- 必须是“待退款”状态
AND idempotent_id = 'refund_1001_20240601';如果
UPDATE影响行数为 0,说明:
- 要么已经退过了(状态不是 PENDING)
- 要么幂等ID不对
这时就返回“已处理”,不再调支付。
优点:双重校验,最安全 缺点:逻辑复杂点
✅ 幂等ID 谁来生成?怎么生成?
一般由前端 or 网关层生成,规则要唯一且可读:
| 场景 | 幂等ID 示例 |
|---|---|
| 退款 | refund_{orderId}_{timestamp} |
| 支付 | pay_{orderId}_{timestamp} |
| 发券 | coupon_{userId}_{templateId} |
也可以用 UUID,但不好查日志。 我们用“业务前缀 + 订单ID + 时间”,方便排查。
🎤 面试官:现在AI这么火,能不能用AI来自动处理退款?
我: 能,但不是“AI一键退钱”,而是“用AI辅助决策”。
真实情况是:
- AI不能直接调支付接口退钱(这太危险了);
- 但AI可以帮你判断“这笔退款该不该过”,然后让系统自动执行 or 推给人工。
我们现在就在用AI做“智能初审”。
✅ 一、AI在退款里能干啥?(实战场景)
🔹 1. 自动判断“是否合理退款”
比如用户申请退款,理由是:“衣服有色差”。
AI可以:
- 分析用户上传的照片(是不是真有色差?还是光线问题?);
- 对比商品详情页的图片;
- 判断“色差是否在合理范围内”。
输出: ✅ 合理 → 系统自动通过 ❌ 不合理 → 转人工 or 拒绝
这种我们叫“图像识别 + 规则引擎”,本质是AI辅助质检。
🔹 2. 识别“高风险用户” or “职业退款党”
AI模型可以分析用户历史行为:
- 近30天退款率 > 80%?
- 退款理由总是“没收到货”但物流显示已签收?
- 同一设备注册多个账号?
模型打个“欺诈分”:
- 分数 > 90 → 高风险,必须人工审;
- 分数 < 30 → 低风险,可走自动退款;
- 中间值 → AI建议 + 人工复核。
这种我们叫“风控模型”,大厂都在用。
🔹 3. 自动分类退款原因
用户填的退款理由五花八门:
- “不喜欢”
- “和图片不一样”
- “太慢了”
- “客服态度差”
AI可以用NLP(自然语言处理) 把这些归类:
- 商品问题
- 物流问题
- 服务问题
- 主观原因
方便运营做数据分析,优化供应链 or 客服流程。
🔹 4. 自动生成拒绝理由 or 客服话术
如果AI判断“不能退”,可以:
自动生成拒绝文案:
“亲,您上传的图片显示商品无破损,且已签收,不符合退货规则。”
推荐客服标准回复话术,提升效率。
✅ 二、AI不能干啥?(别想太多)
| 想法 | 现实 |
|---|---|
| AI直接退钱 | ❌ 不可能!资金操作必须有严格流程 |
| AI完全替代人工 | ❌ 复杂纠纷(如调包、真假货)还得人来判 |
| AI保证100%准确 | ❌ 模型会误判,必须有人兜底 |
| AI自己学习规则 | ❌ 得靠人工标注数据 + 持续调优 |
AI是“助手”,不是“老板”。
✅ 三、真实项目里怎么用AI做退款?
我们是这么设计的:
用户申请退款
↓
AI模型实时打分(欺诈分、合理性分)
↓
┌─────────────┴─────────────┐
↓(低风险 + 高置信) ↓(高风险 or 模型不确定)
系统自动通过 推给人工审核
↓ ↓
调支付退款 运营结合AI建议做最终判断AI在这里的角色是:第一道过滤器。
✅ 四、谁在用AI做退款?
| 公司 | 做法 |
|---|---|
| 淘宝/天猫 | 用AI识别“仅退款”是否合理,减少商家纠纷 |
| 京东 | 用模型判断“是否到货拒收”,优化物流成本 |
| 拼多多 | AI自动处理大量小额退款,提升效率 |
| 抖音电商 | 用图像识别判断“商品破损”真伪 |
小公司可能用不起,但大厂基本都在用。
✅ 五、要上AI,得有什么前提?
不是所有公司都能上,得有:
- 足够的历史数据(几千条标注好的退款记录);
- 基础的风控系统(用户行为埋点、订单、物流数据);
- 工程能力(能接AI模型服务,做AB测试);
- 人工复核机制(防AI出错);
否则就是“为了AI而AI”,反而增加成本。
🎤 面试官:如果让你来设计一个AI智能退款客服你该怎么设计呢?
我的目标是:做一个“懂规则、会查数据、能调系统”的AI客服
不再是“固定话术机器人”,而是:
用户说:“我买的iPhone碎屏了,能退货吗?” AI 能:
- 理解你是“高价值商品破损”;
- 查公司退货政策(是否支持碎屏退货);
- 查你的订单是否已签收;
- 调图像识别API分析照片;
- 综合判断后回复:“符合退货条件,已为您安排上门取件”。
这就需要:角色 + RAG + 向量库 + Function Calling + MCP 五件套。
✅ 第一步:定义“角色”(Role)——让AI知道它是谁
在大模型请求中,设置
system消息,明确角色和职责:
// AI 请求的 Message 对象
public class AiMessage {
private String role; // system, user, assistant, function
private String content;
private String name; // function name
private Object functionCall; // 用于输出 function call
// getter/setter
}// 构建带角色的 prompt
List<AiMessage> messages = new ArrayList<>();
messages.add(new AiMessage()
.setRole("system")
.setContent('''
你是一个电商平台的AI退货客服,名叫‘小退’。
你的任务是:
1. 理解用户退货请求;
2. 查询退货政策;
3. 调用工具检查订单状态、图像;
4. 给出专业、合规的回复;
5. 复杂问题转人工。
你说话要礼貌、清晰、不承诺无法确定的事。
'''
));✅ 作用:让大模型知道自己是“客服”,不是“搜索引擎”。
✅ 第二步:用 RAG + 向量数据库——让AI“懂公司规则”
问题是:大模型不知道你们公司的“碎屏是否支持退货”。
解法:RAG(检索增强生成) + 向量数据库
🔹 1. 引入依赖(pom.xml)
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-cloud-opensearch</artifactId>
<version>1.0.0</version>
</dependency>
<!-- 或 Milvus -->
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.3.0</version>
</dependency>🔹 2. 把公司规则文档存进去
比如:
- 《7天无理由退货规则》
- 《电子商品特殊退货政策》
- 《破损商品处理流程》
🔹 3. 调用 Qwen Embedding API 生成向量
用 embedding 模型(如 text-embedding-v3)把文档切片并转成向量,存入向量数据库:
- Milvus
- Pinecone
- 阿里云 OpenSearch(支持向量检索)
@Service
public class EmbeddingService {
@Value("${qwen.api.key}")
private String apiKey;
public float[] getEmbedding(String text) {
String url = "https://dashscope.aliyuncs.com/api/v1/services/embeddings/text-embedding-v3/text-embedding";
OkHttpClient client = new OkHttpClient();
MediaType JSON = MediaType.get("application/json");
// 请求体
JsonObject requestJson = new JsonObject();
requestJson.addProperty("input", text);
requestJson.addProperty("model", "text-embedding-v3");
RequestBody body = RequestBody.create(JSON, requestJson.toString());
Request request = new Request.Builder()
.url(url)
.addHeader("Authorization", "Bearer " + apiKey)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
String responseBody = response.body().string();
// 解析 JSON 获取 embedding
JsonElement embeddingElement = JsonParser.parseString(responseBody)
.getAsJsonObject()
.get("output")
.getAsJsonObject()
.get("embeddings")
.getAsJsonArray()
.get(0)
.getAsJsonObject()
.get("embedding");
JsonArray embArray = embeddingElement.getAsJsonArray();
float[] vector = new float[embArray.size()];
for (int i = 0; i < embArray.size(); i++) {
vector[i] = embArray.get(i).getAsFloat();
}
return vector;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}🔹 3. 用户提问时,先检索相关规则
@Service
public class VectorSearchService {
@Autowired
private EmbeddingService embeddingService;
public List<RuleDocument> searchRules(String query, int topK) {
float[] queryVector = embeddingService.getEmbedding(query);
// 伪代码:调用 OpenSearch 向量检索
SearchRequest searchRequest = SearchRequest.newBuilder()
.index("return_policy_index")
.size(topK)
.vectorQuery("vector_field", queryVector, 0.8f) // 相似度阈值
.build();
SearchResponse response = openSearchClient.search(searchRequest);
List<RuleDocument> rules = new ArrayList<>();
for (Hit hit : response.getHits()) {
rules.add(new RuleDocument(
hit.getSource().get("text").toString(),
hit.getScore()
));
}
return rules;
}
}返回最相关的规则,拼到 prompt 里:
【用户问题】
我买的iPhone碎屏了,能退货吗?
【检索到的规则】
手机碎屏属于严重破损,签收后48小时内可申请退货。✅ 作用:让AI基于最新、准确的内部规则回答,而不是靠“训练数据猜”。
✅ 第三步:Function Calling——让AI“能调系统”
AI 知道规则还不够,还得查“这个用户是不是真买了iPhone?是不是已签收?”
解法:Function Calling(函数调用)
🔹 1. 定义可调用的函数
public class FunctionDefinition {
private String name;
private String description;
private Map<String, Object> parameters;
// 构造函数、getter/setter
}// 构建函数列表
List<FunctionDefinition> functions = new ArrayList<>();
FunctionDefinition checkOrder = new FunctionDefinition();
checkOrder.setName("check_order_status");
checkOrder.setDescription("查询订单状态");
checkOrder.setParameters(Map.of(
"type", "object",
"properties", Map.of(
"order_id", Map.of("type", "string")
),
"required", List.of("order_id")
));
functions.add(checkOrder);
FunctionDefinition analyzeImage = new FunctionDefinition();
analyzeImage.setName("analyze_image");
analyzeImage.setDescription("分析商品图片是否破损");
analyzeImage.setParameters(Map.of(
"type", "object",
"properties", Map.of(
"image_url", Map.of("type", "string")
),
"required", "image_url")
));
functions.add(analyzeImage);🔹 2. 调用大模型(Qwen + Function Calling)
@Service
public class AiService {
@Value("${qwen.api.key}")
private String apiKey;
public Object callWithFunctions(List<AiMessage> messages, List<FunctionDefinition> functions) {
String url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation";
OkHttpClient client = new OkHttpClient();
MediaType JSON = MediaType.get("application/json");
JsonObject requestJson = new JsonObject();
requestJson.addProperty("model", "qwen-max");
// 消息
JsonArray messagesJson = new JsonArray();
for (AiMessage msg : messages) {
JsonObject m = new JsonObject();
m.addProperty("role", msg.getRole());
m.addProperty("content", msg.getContent());
if ("function".equals(msg.getRole())) {
m.addProperty("name", msg.getName());
}
messagesJson.add(m);
}
requestJson.add("input", messagesJson);
// 函数
JsonArray functionsJson = new JsonArray();
for (FunctionDefinition f : functions) {
JsonObject func = new JsonObject();
func.addProperty("name", f.getName());
func.addProperty("description", f.getDescription());
func.add("parameters", JsonParser.parseString(new Gson().toJson(f.getParameters())));
functionsJson.add(func);
}
requestJson.add("functions", functionsJson);
RequestBody body = RequestBody.create(JSON, requestJson.toString());
Request request = new Request.Builder()
.url(url)
.addHeader("Authorization", "Bearer " + apiKey)
.addHeader("Content-Type", "application/json")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
String responseBody = response.body().string();
// 解析是否返回 function_call
JsonObject result = JsonParser.parseString(responseBody).getAsJsonObject();
JsonObject choice = result.getAsJsonArray("output").get(0).getAsJsonObject();
if (choice.has("function_call")) {
return choice.get("function_call");
} else {
return choice.get("text");
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}✅ 作用:AI 从“只会说”变成“能做事”。
✅ 第四步:MCP(Model Context Protocol)——让AI“记住上下文”
问题:用户聊了5轮,AI 忘了前面说的订单号、图片链接。
解法:MCP 思想(不是协议,是一种设计模式)
@Component
public class SessionContextManager {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String SESSION_PREFIX = "ai:session:";
public void saveContext(String sessionId, Map<String, Object> context) {
String key = SESSION_PREFIX + sessionId;
String json = new Gson().toJson(context);
redisTemplate.opsForValue().set(key, json, Duration.ofHours(2));
}
public Map<String, Object> getContext(String sessionId) {
String key = SESSION_PREFIX + sessionId;
String json = redisTemplate.opsForValue().get(key);
if (json != null) {
Type type = new TypeToken<Map<String, Object>>(){}.getType();
return new Gson().fromJson(json, type);
}
return new HashMap<>();
}
}🔹 1. 维护一个“上下文状态机”
{
"session_id": "sess_123",
"user_id": "u1001",
"current_intent": "apply_return",
"order_id": "1001",
"image_urls": ["https://xxx.jpg"],
"function_results": {
"check_order_status": { "sign_time": "2024-06-01 14:30" },
"analyze_image": { "label": "broken", "confidence": 0.96 }
}
}🔹 2. 每次请求,把上下文拼进 prompt
【用户历史】
- 提供了订单号:1001
- 上传了碎屏照片
- 已调用 check_order_status → 已签收
- 已调用 analyze_image → 确认为破损
【当前问题】
现在能退货吗?✅ 作用:AI 不再是“失忆患者”,能做复杂决策。
✅ 第五步:整合流程——完整工作流
@RestController
@RequestMapping("/api/ai")
public class AiRefundController {
@Autowired
private AiService aiService;
@Autowired
private VectorSearchService vectorSearchService;
@Autowired
private SessionContextManager contextManager;
@Autowired
private OrderService orderService;
@Autowired
private ImageAnalysisService imageAnalysisService;
@PostMapping("/chat")
public String chat(@RequestBody ChatRequest request) {
String sessionId = request.getSessionId();
String userInput = request.getMessage();
// 1. 获取上下文
Map<String, Object> context = contextManager.getContext(sessionId);
List<AiMessage> messages = (List<AiMessage>) context.get("messages");
if (messages == null) messages = new ArrayList<>();
// 加入用户输入
messages.add(new AiMessage().setRole("user").setContent(userInput));
// 2. RAG:检索规则
List<RuleDocument> rules = vectorSearchService.searchRules(userInput, 3);
String ragContext = rules.stream()
.map(r -> "【规则】" + r.getText())
.collect(Collectors.joining("\n"));
// 加入规则到 prompt
messages.add(new AiMessage()
.setRole("system")
.setContent(ragContext));
// 3. 调大模型 + Function Calling
List<FunctionDefinition> functions = buildFunctions(); // 上面定义的
Object result = aiService.callWithFunctions(messages, functions);
if (result instanceof JsonObject functionCall) {
// 4. 执行函数
String funcName = functionCall.get("name").getAsString();
JsonObject args = functionCall.getAsJsonObject("arguments");
if ("check_order_status".equals(funcName)) {
OrderStatus status = orderService.getOrderStatus(args.get("order_id").getAsString());
String funcResult = new Gson().toJson(status);
// 5. 记录函数结果(MCP)
messages.add(new AiMessage()
.setRole("function")
.setName("check_order_status")
.setContent(funcResult));
}
if ("analyze_image".equals(funcName)) {
ImageAnalysisResult analysis = imageAnalysisService.analyze(args.get("image_url").getAsString());
messages.add(new AiMessage()
.setRole("function")
.setName("analyze_image")
.setContent(new Gson().toJson(analysis)));
}
// 再次调用大模型,让它基于函数结果回复
Object finalResponse = aiService.callWithFunctions(messages, functions);
messages.add(new AiMessage().setRole("assistant").setContent(finalResponse.toString()));
contextManager.saveContext(sessionId, Map.of("messages", messages));
return finalResponse.toString();
}
// 纯文本回复
messages.add(new AiMessage().setRole("assistant").setContent(result.toString()));
contextManager.saveContext(sessionId, Map.of("messages", messages));
return result.toString();
}
}结果如下:
用户:iPhone碎屏了,能退吗?[图片]
↓
1. Role:AI 明白自己是“退货客服”
↓
2. RAG:检索“电子商品碎屏退货政策” → 找到“48小时内可退”
↓
3. Function Calling:
→ 调 check_order_status(order_id=1001)
→ 调 analyze_image(image_url=xxx.jpg)
↓
4. MCP:把订单状态、图片结果存入上下文
↓
5. 大模型综合判断:
- 规则:碎屏48小时内可退
- 签收时间:2024-06-01 14:30
- 当前时间:2024-06-02 10:00 → 在48小时内
- 图片:确认破损
↓
AI回复:
“您好,检测到商品有破损,且在签收48小时内,符合退货条件。
已为您安排上门取件,请保持电话畅通。”