在大语言模型(LLM)交互中,Prompt 工程的本质是通过明确角色分工实现 "人机协作" 的高效闭环。无论是简单问答还是复杂的工具调用场景,都离不开四大核心角色的协同:系统角色(System) 、用户角色(User) 、工具角色(Tool) 和响应处理角色(Response Handler)。这四大角色共同构成了 LLM 应用的交互骨架,决定了 AI 行为的边界、交互的流畅度和结果的可用性。
一、系统角色(System):定义 AI 的 "行为准则"
系统角色是 LLM 的 "隐形指挥官",通过预设指令定义 AI 的身份、能力边界和输出规范。它决定了 AI"是什么样的存在",直接影响交互的专业性和可靠性。
核心职责
- 明确 AI 的身份定位(如法律助手、翻译官)
- 划定能力边界(如 "只回答法律问题")
- 规范输出格式(如 "以 HTML 格式返回")
- 设定交互风格(如 "简洁明了"、"详细严谨")
代码实现与解析
在 Spring AI 中,系统角色通过SystemMessage或prompt().system()方法定义,是 Prompt 中优先级最高的指令。
java
// 场景1:法律助手角色定义(链式API)
@GetMapping("/prompt/chat")
public Flux<String> chat(String question) {
return deepseekChatClient.prompt()
// 系统角色:明确身份和边界
.system("你是一个法律助手,只回答法律相关问题。" +
"回答需基于中国现行法律条文,避免模糊表述。" +
"若问题超出法律范围,直接回复'该问题不属于法律范畴'")
.user(question) // 用户输入
.stream()
.content();
}
java
// 场景3:格式约束的系统角色(手动构建Prompt)
@GetMapping("/prompt/chat3")
public Flux<String> chat3(String question) {
// 系统角色:强制输出格式
SystemMessage systemMessage = new SystemMessage(
"你是内容排版助手,需将用户问题的答案以HTML格式返回。" +
"要求:标题用<h3>,段落用<p>,重点用<strong>,列表用<ul><li>"
);
UserMessage userMessage = new UserMessage(question);
Prompt prompt = new Prompt(userMessage, systemMessage); // 组合系统指令与用户输入
// ...
}
关键设计原则
- 指令具体化:避免模糊表述(如不说 "回答专业",而说 "基于中国现行法律条文")
- 边界清晰化:明确 "不能做什么"(如 "超出法律范围直接拒绝")
- 格式标准化:对输出格式的约束要可执行(如明确 HTML 标签规则)
二、用户角色(User):交互的 "发起者与需求方"
用户角色是交互的源头,通过输入传递需求。它的核心价值是清晰、准确地表达意图,为 AI 提供足够的上下文信息。
核心职责
- 传递原始需求(如 "解释《民法典》第 1043 条")
- 提供必要上下文(如 "我是企业 HR,想了解劳动合同解除的条件")
- 反馈交互结果(如 "请用更通俗的语言解释")
代码实现与解析
用户角色的输入通过UserMessage或prompt().user()方法传递,在多轮对话中还需要携带历史上下文。
java
// 单轮对话:简单用户输入
@GetMapping("/prompt/chat4")
public String chat4(String question) {
// 用户角色输入直接通过user()方法传递
AssistantMessage assistantMessage = deepseekChatClient.prompt()
.user(question) // 用户需求:question参数
.call()
.chatResponse()
.getResult()
.getOutput();
return assistantMessage.getText();
}
java
// 多轮对话:携带上下文的用户角色(伪代码)
public String multiTurnChat(String newQuestion, List<Message> history) {
// 构建包含历史的Prompt:历史消息+新用户输入
List<Message> messages = new ArrayList<>(history);
messages.add(new UserMessage(newQuestion)); // 新的用户需求
Prompt prompt = new Prompt(messages);
return qwenChatModel.call(prompt)
.getResult()
.getOutput()
.getText();
}
最佳实践
- 上下文完整性:复杂问题需提供背景(如 "我公司员工连续旷工 3 天,能否解除合同?" 比 "如何解除合同" 更有效)
- 需求明确化:避免歧义(如不说 "解释这个法律条文",而说 "解释《民法典》第 1043 条在婚姻纠纷中的应用")
- 渐进式提问:复杂需求分步骤提出(先问 "什么是劳动合同",再问 "解除条件")
三、工具角色(Tool):AI 的 "能力扩展器"
工具角色是 LLM 突破自身知识局限的 "外挂",通过调用外部系统(如 API、数据库、计算器)获取实时信息或执行复杂操作,让 AI 从 "纯文本交互" 升级为 "实际问题解决者"。
核心职责
- 提供实时数据(如天气查询、股票行情)
- 执行计算逻辑(如财务计算、数据分析)
- 操作外部系统(如发送邮件、查询数据库)
- 将工具结果格式化回传给 AI
代码实现与解析
Spring AI 通过ToolResponseMessage封装工具调用结果,实现 "AI - 工具 - AI" 的闭环。
java
@GetMapping("/prompt/chat5")
public String chat5(String city) {
// 1. 第一步:AI判断是否需要调用工具(模拟场景)
String initialAnswer = deepseekChatClient.prompt()
.user(city + "未来3天天气情况如何?") // 用户问天气(AI无实时数据)
.call()
.chatResponse()
.getResult()
.getOutput()
.getText();
// (实际场景中,AI会返回工具调用指令,如{"tool":"weatherApi","params":{"city":"北京"}})
// 2. 第二步:调用工具获取数据(模拟天气API调用)
String weatherData = mockWeatherApiCall(city); // 工具角色:返回实时天气
// 3. 第三步:工具结果封装为ToolResponseMessage回传给AI
ToolResponseMessage toolResponse = new ToolResponseMessage(
List.of(new ToolResponseMessage.ToolResponse(
"weather_api_123", // 工具调用ID(用于多工具区分)
"获取天气数据", // 工具描述
weatherData // 工具返回结果
))
);
// 4. 第四步:AI基于工具结果生成最终回答
String finalAnswer = deepseekChatClient.prompt()
.messages(
new UserMessage(city + "未来3天天气情况如何?"),
new AssistantMessage(initialAnswer), // 历史:AI的初步响应
toolResponse // 工具返回结果
)
.call()
.chatResponse()
.getResult()
.getOutput()
.getText();
return finalAnswer;
}
// 模拟天气工具(工具角色的具体实现)
private String mockWeatherApiCall(String city) {
return city + "未来3天天气:晴/25℃,多云/23℃,小雨/20℃";
}
工具调用流程
- 触发判断:AI 接收用户需求后,判断是否需要工具(基于自身知识局限性)
- 工具调用:应用程序解析 AI 的工具调用指令,调用对应外部系统
- 结果回传 :将工具返回的原始数据用
ToolResponseMessage封装,作为新消息传入 Prompt - 二次处理:AI 基于工具结果生成自然语言回答
四、响应处理角色(Response Handler):结果的 "最终塑造者"
响应处理角色负责将 AI 的原始输出转换为应用所需的形式,确保结果可用、易用。它是连接 AI 输出与用户体验的关键环节。
核心职责
- 格式转换(如将纯文本转为 HTML、JSON)
- 流式处理(实时返回长文本结果)
- 结果过滤(去除敏感信息、修正格式错误)
- 元数据处理(提取 token 统计、调用耗时等信息)
代码实现与解析
在 Spring AI 中,响应处理通过响应式操作(如map、filter)或同步处理实现,适配不同的交互场景。
java
// 场景2:获取完整元数据(响应处理:保留原始信息)
@GetMapping("/prompt/chat2")
public Flux<ChatResponse> chat2(String question) {
SystemMessage systemMessage = new SystemMessage("你是一个讲故事的助手,擅长创作童话");
UserMessage userMessage = new UserMessage(question);
Prompt prompt = new Prompt(userMessage, systemMessage);
// 响应处理:直接返回完整ChatResponse(包含元数据)
return deepseekChatModel.stream(prompt);
// ChatResponse包含:模型名称、token使用量、响应时间、完整消息等
}
// 场景3:格式转换(响应处理:提取并转换文本)
@GetMapping("/prompt/chat3")
public Flux<String> chat3(String question) {
// ... 省略Prompt构建 ...
// 响应处理:从ChatResponse中提取文本,并通过map转换
return deepseekChatModel.stream(prompt)
.map(response -> {
String rawText = response.getResults().get(0).getOutput().getText();
// 额外处理:去除可能的格式错误(如多余的HTML标签)
return rawText.replaceAll("<[^>]*>", "");
});
}
// 场景1:流式响应(响应处理:实时推送)
@GetMapping("/prompt/chat")
public Flux<String> chat(String question) {
// ... 省略Prompt构建 ...
// 响应处理:通过stream()实现流式返回,前端可逐段渲染
return deepseekChatClient.prompt()
.system(...)
.user(question)
.stream() // 触发流式处理
.content(); // 提取文本内容
}
响应处理策略
- 实时性优先 :长文本场景用流式响应(
Flux),避免用户等待 - 结构化优先:需存储或二次处理的场景,转换为 JSON/XML 等格式
- 透明性优先:调试或成本监控场景,保留完整元数据(token 数、模型信息)
四大角色的协同逻辑:从需求到结果的闭环
四大角色并非孤立存在,而是形成了完整的交互链路:
plaintext
用户角色(提需求)→ 系统角色(定规则)→ AI(初处理)
→ [工具角色(补能力)→ AI(再处理)] → 响应处理角色(塑结果)→ 用户(获答案)
- 系统角色贯穿全程,为 AI 提供 "行为指南"
- 用户角色是起点,需求的质量决定交互效率
- 工具角色是扩展,解决 AI"做不到" 的问题
- 响应处理角色是终点,决定用户最终体验
例如在 "法律查询 + 案例检索" 场景中:
- 用户角色提出 "劳动合同解除的赔偿标准"(需求)
- 系统角色定义 "法律助手,需引用法条和案例"(规则)
- AI 初步回答法条内容,但发现需要最新案例(触发工具调用)
- 工具角色调用 "裁判文书网 API" 获取案例(扩展能力)
- 响应处理角色将法条 + 案例整合成 HTML 格式(结果塑造)
- 用户获得结构化的法律参考(最终体验)
总结:角色设计是 Prompt 工程的核心
四大角色的设计质量直接决定 LLM 应用的效果:系统角色定义 AI 的 "专业性",用户角色决定交互的 "准确性",工具角色扩展 AI 的 "能力边界",响应处理角色保障结果的 "可用性"。
在实际开发中,需根据业务场景灵活调整角色权重:
- 客服场景:强化系统角色的 "服务语气" 和响应处理的 "实时性"
- 数据分析场景:强化工具角色的 "计算能力" 和响应处理的 "结构化"
- 创作场景:弱化系统角色的约束,强化用户角色的 "创意引导"
理解并掌握这四大角色的协同逻辑,是从 "简单调用 LLM" 到 "构建企业级 LLM 应用" 的关键一步。