上集回顾:老张用Spring AI搭了个客服助手原型,能对话但语气生硬,用户反馈"像跟Siri聊天"。总监老李皱眉:“这哪像我们公司的客服?”


一、故事继续:用户的吐槽

周四上午,老张打开客服系统后台,看到一条用户留言:

“你们这个AI怎么回事?我问的是退款流程,它给我背了一堆条款,跟念经似的。我要的是步骤!”

老张点开AI对话记录,果然:

用户:怎么退款?
AI:根据《售后服务管理条例》第三条规定,退货需满足以下条件:
一、商品完好无损;
二、原始包装齐全;
三、在七日内申请...

老张的内心独白:“这AI是不是没受过客服培训?光背法条不管用啊!”

他想起自己写代码时,如果给新员工发的"员工手册"写得太官方,新员工肯定也学不会。AI也一样——你得先告诉它"你是谁"“你该怎么做”。


二、技术引入:System Message是什么?

2.1 类比理解

System Message就像给AI的"岗位说明书"

岗位 System Message内容
客服 “你是XX公司的资深客服,语气亲切,回答简洁,不背法条”
技术支持 “你是高级Java工程师,能用代码示例解释概念”
销售 “你是金牌销售,擅长挖掘需求、促成成交”

2.2 代码实现

// 之前:没有System Message
String answer = chatClient.prompt()
        .user("怎么退款?")
        .call()
        .content();

// 之后:加上System Message
String answer = chatClient.prompt()
        .system("你是XX公司的资深客服小王,有5年客服经验。")
        .system("你的职责:1)快速回答用户问题 2)语气亲切友好 3)不用官方术语")
        .system("当用户问流程时,用步骤列表回答,不要太长")
        .user("怎么退款?")
        .call()
        .content();

效果对比

【改前】AI:根据《售后服务管理条例》第三条规定...
【改后】AI:亲,退款很简单哦~ 

1️⃣ 登录APP → 订单详情
2️⃣ 点击「申请退款」
3️⃣ 选择退款原因
4️⃣ 提交后3天内原路退回

有问题随时问我哈!😊

三、Prompt模板:别让AI猜你的意思

3.1 问题:每次手写Prompt太累

老张发现,每个功能都要写一大段System Message,重复代码很多:

// 客服对话
.system("你是XX公司的客服小王...")
.system("语气要亲切...")
.system("回答要简洁...")

// 技术支持对话
.system("你是XX公司的技术支持工程师...")
.system("语气要专业...")
.system("回答要有代码示例...")

3.2 解决方案:PromptTemplate

Prompt模板就像"填空游戏"

@Service
public class AiService {
    
    // 定义客服角色模板
    private final PromptTemplate客服模板 = PromptTemplate.builder()
        .template("""
            你是{company}公司的资深客服{name}。
            
            你的背景:{experience}年客服经验,熟悉{productLine}产品。
            
            你的职责:
            1. 快速回答用户问题
            2. 语气亲切友好,多用emoji
            3. 不用官方术语,用大白话
            
            回答格式:
            - 流程类问题:用步骤列表(1️⃣2️⃣3️⃣)
            - 咨询类问题:简洁回答,不超过3句话
            - 投诉类问题:先道歉,再给解决方案
            
            当前对话:
            {conversation}
            
            用户问题:{question}
            """)
        .build();
    
    public String chat(String question, String userId) {
        // 获取用户历史对话
        String conversation = getHistory(userId);
        
        // 渲染模板
        Map<String, Object> vars = new HashMap<>();
        vars.put("company", "XX科技");
        vars.put("name", "小王");
        vars.put("experience", "5");
        vars.put("productLine", "软件服务");
        vars.put("conversation", conversation);
        vars.put("question", question);
        
        Prompt prompt = 客服模板.create(vars);
        
        return chatClient.prompt(prompt).call().content();
    }
}

老张的感悟:“Prompt模板就是『标准化作业流程』的AI版——同样的场景,同样的回答风格,不靠AI临场发挥。”


四、Few-shot示例:给AI看"标准答案"

4.1 问题:光说不够,要让AI看样例

老张发现,光写System Message,AI还是掌握不好"简洁"的度。有时候太长,有时候又太短。

解决方案:Few-shot Learning(少样本学习)

.system("""
    你是资深客服小王。
    
    回答示例:
    
    用户:退款流程是什么?
    AI:亲,退款3步走:
    1️⃣ APP→我的订单→申请退款
    2️⃣ 选原因→提交
    3️⃣ 等审核(通常3天)
    
    用户:发票怎么开?
    AI:电子发票在订单详情页点「开具发票」,填邮箱就行~
    
    现在请你回答:{question}
    """)

4.2 Few-shot的核心逻辑

┌─────────────────────────────────────┐
│  System Message                      │
│  ├─ 角色定义                         │
│  ├─ 回答规则                         │
│  └─ 示例(关键!)                   │
│       ├─ 示例1:流程类问题           │
│       ├─ 示例2:咨询类问题           │
│       └─ 示例3:投诉类问题           │
└─────────────────────────────────────┘
           ↓
      AI学习你的"风格"

老张的发现:“原来AI也是『看样例学做事』的——就像新员工培训,给几个优秀案例比说一堆规则管用。”


五、结构化输出:让AI返回「可解析的数据」

5.1 问题:AI的回答太自由

某天,老李要求"把AI回答存进数据库"。老张写了代码,结果AI一会儿返回纯文本,一会儿返回JSON,一会儿还带表情符号…

用户:这个订单什么时候发货?
AI:亲亲,您的订单已经在路上啦~预计明天送到哦!📦✨

老张的困惑:“这格式,我怎么存数据库?”

5.2 解决方案:结构化输出

Spring AI提供BeanOutputConverter,让AI返回标准Java对象:

// 定义输出结构
public record OrderStatus(
    String orderId,           // 订单号
    String status,            // 状态:已发货/运输中/已签收
    String estimatedDelivery, // 预计送达
    String trackingNumber     // 快递单号
) {}

// 使用结构化输出
ChatResponse response = chatClient.prompt()
    .system("你是客服小王。请严格按照JSON格式回答订单查询。")
    .system("""
        输出格式:
        {
            "orderId": "订单号",
            "status": "状态",
            "estimatedDelivery": "预计送达时间",
            "trackingNumber": "快递单号"
        }
        """)
    .user("查一下订单ORD20240820001的物流状态")
    .call()
    .entity(OrderStatus.class);  // 关键:直接转Java对象

OrderStatus result = response.getResult().getOutput().getText();

效果

{
    "orderId": "ORD20240820001",
    "status": "运输中",
    "estimatedDelivery": "2024-08-21",
    "trackingNumber": "SF1234567890"
}

5.3 结构化输出的应用场景

场景 输出结构
订单查询 OrderStatus 对象
问题分类 {"category": "咨询/投诉/建议"}
情感分析 {"sentiment": "positive/negative/neutral"}
意图识别 {"intent": "refund/query/complaint"}

老张的总结:“以前AI是『自由发挥』,现在让它『按格式交作业』——后端开发最擅长的结构化数据,AI也能搞定。”


六、完整代码:老张的客服AI V2

@Service
public class CustomerServiceAgent {
    
    @Autowired
    private ChatClient chatClient;
    
    // 客服角色模板
    private final PromptTemplate 客服模板 = PromptTemplate.builder()
        .template("""
            你是XX科技的资深客服小王,5年经验。
            
            你的风格:
            - 亲切友好,像朋友聊天
            - 简洁明了,不啰嗦
            - 该道歉时真诚道歉
            
            回答规则:
            1. 流程类 → 步骤列表(1️⃣2️⃣3️⃣)
            2. 咨询类 → 3句话内回答
            3. 投诉类 → 先道歉再给方案
            
            示例:
            用户:怎么退款?
            你:亲,退款3步走:
            1️⃣ APP→我的订单→申请退款
            2️⃣ 选原因→提交
            3️⃣ 等审核(通常3天)
            
            现在请回答:{question}
            """)
        .build();
    
    public String chat(String userId, String question) {
        // 1. 获取历史对话
        List<Message> history = getHistory(userId);
        
        // 2. 渲染Prompt模板
        Map<String, Object> vars = Map.of("question", question);
        Prompt prompt = 客服模板.create(vars);
        
        // 3. 构建带历史的对话
        ChatResponse response = chatClient.prompt(prompt)
            .system(history)  // 注入历史
            .call()
            .getResult();
        
        // 4. 保存历史
        saveHistory(userId, question, response.getOutput());
        
        return response.getOutput().getText();
    }
}

七、效果对比:从「机器人」到「老员工」

维度 改前 改后
语气 官方、生硬 亲切、自然
格式 大段文字 列表、emoji
风格 千人一面 有个人特色
用户反馈 常被吐槽"太机械" 张姐验收时说"像那么回事了"

张姐(客服主管)的评价:“这个AI像那么回事了,比我们新培训的员工还行!”


八、下一集预告

AI学会了"说话",但总是「断片」——用户问第三句时,它忘了第一句。

“它怎么老忘事?” —— 第3集,给客服助手装上"海马体"。


📦 完整代码

GitHub分支:https://github.com/yinclei/SpringAITutorial/chapter02-prompt-engineering


💬 互动环节

老兵问答

你在Prompt编写时遇到过什么问题?
A. AI不按我说的格式回答
B. 提示词太长记不住
C. 不知道怎么写才能控制AI
D. 其他(评论区说说)

下周三晚9点,我们继续!


作者:Java老兵搞AI | 关注我,看Java如何玩转AI

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐