0 简介
在大模型对话 API 中,messages 一般会有以下 4 个角色:system、user、assistant、tool。(本文主要参考 deepseek API)
1 不同 role 作用介绍
1.1 System
1.1.1 作用
为整个对话设定基调、规则和身份。
1.1.2 应用场景
- 角色扮演:如“你是一位资深Java开发工程师,请用专业但易懂的语言回答”。
- 格式化输出:如“始终以 JSON 格式返回结果”。
- 多轮对话引导:如“在每轮回答末尾,询问用户是否需要进一步帮助”。
- 安全限制:如“拒绝回答任何涉及隐私的问题”。
1.1.3 最佳实践
-
放在消息数组的最前面: 始终将 system 消息作为第一条,确保模型在生成任何回复前就理解全局指令。
-
清晰、简洁、正面表述: 用肯定句说明“你应该做什么”,而非否定句“不要做什么”。例如:
- ✅ “你是一个专业的 Java 编程助手,回答时优先提供代码示例。”
- ❌ “不要给出错误的代码,不要啰嗦。”
-
控制长度: system 内容会消耗上下文窗口,建议不超过 500~800 个 token(视模型上下文大小调整)。过长会挤占后续对话空间。
-
避免与 assistant 混淆: 不要在 system 中放置本该由 assistant 输出的内容(如示例回答),否则模型可能误以为那是历史对话。
-
使用分隔符: 如果 system 包含多条指令,可以用 Markdown 标题、编号或 XML 标签分隔,提高模型解析准确率。
1.2 user
1.2.1 作用
承载用户的输入,是模型需要响应的主要对象。
1.2.2 最佳实践
- 保持自然语言交互: 直接使用用户的原话或清晰的问题表述,避免过度修饰。
- 多轮对话中保留完整历史: 将每一轮用户消息都追加到 messages 数组中,使模型能理解上下文。
- 单次处理长输入: 礼貌回复输入过长,或者调用其他模型先对长文本进行摘要,然后再将摘要作为用户输入
- 历史消息积累过长: 常见的做法是保留 topN, 然后摘要历史消息。
1.3 assistant
1.3.1 作用
记录模型的回答,并可用于发起工具调用
1.3.2 应用场景
- 常规对话: 模型回答用户问题。
- 工具调用请求: 模型判断需要外部数据时,返回 tool_calls 而非直接文本。
- 多角色模拟: 通过 name 字段模拟多个 AI 助手(如“MathBot”和“CodeBot”)在同一个对话中轮流回答,常应用于多角色聊天室。
普通回答示例
{
"role": "assistant",
"content": "可以使用 pandas 库:pd.read_csv('file.csv')"
}
带工具调用示例
{
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{"city": "Beijing"}"
}
}]
}
针对多角色聊天示例,下面会有一个完整的示例介绍,这边不过多描述。
1.3.3 最佳实践
- 完整保存历史中的 assistant 消息: 每次模型的回答都应存入对话历史,以便后续轮次参考。不要只保留最新一条。
- 处理 tool_calls: 当模型返回 tool_calls 时,必须将该条 assistant 消息原样保存(即使 content 为 null),否则后续无法正确匹配 tool 响应
- 使用 name 区分多助手: 如果有多角色,需要显示指名当前 assistant 的 name
- 不要手动篡改 assistant 内容: 保持模型输出的原始性,除非做必要的过滤或安全处理。修改后可能破坏模型对上下文的连贯理解。
1.4 tool
1.4.1 作用
将外部工具的执行结果返回给模型,通常用于函数调用场景。
1.4.2 最佳实践
当前 tool call,最大的问题在于:每次请求大模型时,都会将当前应用存在的 MCP 完整定义传给大模型,导致每次请求都需要额外消耗大量 token。
针对该问题最常见的解决方案:
- 动态工具发现: 不加载所有工具,先让 AI 理解用户意图,在通过关键字进行向量搜索,搜索到最匹配的 topN 工具
2 多角色聊天室 DEMO
以下仅通过模拟消息的方式,来模拟多角色聊天,后续看情况用代码举例。
2.1 通过 system 定基调
{
"role": "system",
"content": "你是一个多助手系统,包含 MathBot 和 CodeBot 两个角色。\n\n- MathBot:数学专家,回答风格简洁,直接给出结果和公式,不添加额外解释。\n- CodeBot:编程专家,回答时优先提供可运行的代码示例,并简要说明。\n\n当用户明确指定助手时(例如“MathBot,...”),你必须以该助手的身份回复,并在回复消息中使用对应的 name 字段。如果用户未指定,默认使用 CodeBot。"
}
在 system prompt 中,我们定义了 2 个角色,并要求大模型回消息时,也需要带上当前对应的回复人。当然如果要程序可以更方便的解析的话,我们还可以在 system prompt 中要求大模型以 json 形式返回 2 个内容。1 个字段存放当前说话的角色,1 个字段存放说话内容。
2.2 用户与 MathBot 对话
// 用户消息
{
"role": "user",
"content": "MathBot,解方程 x^2 - 4 = 0"
}
按照上面的最佳实践,用户内容、大模型返回的内容,我们都要原样不动的追加到 messages 中。其中因为我们声明了 assistant name,因此提交 assistant 消息给大模型时,我们还得给 name 附上对应的值。因此现在的 messages 的消息为
{
"role": "system",
"content": "..."
}
{
"role": "user",
"content": "MathBot,解方程 x^2 - 4 = 0"
}
{
"role": "assistant",
"content": "xx",
"name": "MathBot"
}
2.3 用户与 CodeBot 对话
// 追加用户消息
{
"role": "user",
"content": "CodeBot,用 Python 打印这两个解。"
}
类似,下一轮提交给大模型的消息列表,就会变成如下所示:
{
"role": "system",
"content": "..."
}
{
"role": "user",
"content": "MathBot,解方程 x^2 - 4 = 0"
}
{
"role": "assistant",
"content": "xx",
"name": "MathBot"
}
{
"role": "user",
"content": "CodeBot,用 Python 打印这两个解。"
}
{
"role": "assistant",
"content": "xx",
"name": "CodeBot"
}
大体上,这就是实现多角色聊天的核心了。