Go AI Agent 实战 04|Prompt 与消息角色:System、User、Assistant 本系列共 10 阶段 面向有 Go 后端基础的开发者全程用 Go 手写实现。当前进度阶段一 大模型基础 · 第 4 课上一篇Token、上下文窗口与模型调用成本这一课开始学习 AI 应用开发中非常重要的一部分如何组织发送给大语言模型的输入。在传统 Go 后端中你通过函数参数、结构体、配置文件和业务规则控制程序行为。在 LLM 应用中除了这些传统方式还需要设计 Prompt提示词让模型理解任务、约束和上下文。我们会结合 Go 后端开发来学习而不是只讲如何写一段好提示词。一、本课学习目标学完本课你应该能够理解 Prompt 的组成和作用。区分 System、User、Assistant 三种常见消息角色。理解为什么多轮对话需要维护消息历史。编写适合 Go AI 应用的 Prompt。了解 Prompt Injection提示词注入的基本风险。二、什么是 PromptPrompt 可以理解为发送给模型的输入指令与上下文。例如我们给模型发送解释一下 Go 语言中的 defer。这是一个简单的 Prompt。模型可能生成一段关于defer的介绍但回答内容、长度和组织方式不一定符合我们的要求。如果我们改成你是一名 Go 后端开发导师。 请解释 Go 语言中的 defer要求 1. 使用中文回答。 2. 先解释核心原理再给出代码示例。 3. 解释代码执行顺序。 4. 补充常见面试问题。 5. 如果涉及 Go 语言的特殊规则请给出准确说明。模型就获得了更明确的任务目标和输出要求。这里需要区分一个概念Prompt 能够引导模型的行为但不能保证模型一定正确遵守指令。例如即使你写了绝对不能出错也不能消除模型幻觉。因此Prompt 设计需要与程序校验、测试、权限控制等工程措施结合。三、Prompt 应该包含哪些内容对于实际应用我们可以将 Prompt 拆分为几个部分。1. 角色与职责你是谁例如你是一名 Go 后端技术助手。2. 任务目标你需要完成什么例如解释用户提交的 Go 代码并指出潜在问题。3. 上下文信息你可以依据哪些信息例如用户提交的代码、相关文档、已有对话历史。4. 行为约束哪些事情可以做哪些不能做例如不确定时明确说明不要虚构 API不能执行未授权操作。5. 输出要求结果应该是什么格式例如先给结论再给原因和 Go 代码示例。并不是每个 Prompt 都必须包含上述所有部分。简单问题可以使用简单提示词复杂任务则通常需要更明确的目标、上下文和输出要求。四、理解 System、User、Assistant 三种消息角色调用聊天模型时通常会发送一个消息列表。每条消息一般包含角色和内容。例如[{role:system,content:你是一名 Go 后端技术助手请使用中文回答。},{role:user,content:解释一下 defer。}]这里最重要的是消息角色不仅表示谁说了什么还会影响模型如何理解输入。1. System系统级指令System 消息通常用于定义应用的整体行为要求。例如你是一名 Go 后端技术助手。 回答时遵循以下要求 - 使用中文。 - 优先解释原理。 - 代码示例使用 Go。 - 对不确定的信息明确说明。它适合放置相对稳定的应用指令。注意不同模型 API 对消息角色的支持和优先级处理可能不同。有些 API 使用 Developer 消息来表达应用开发者指令。具体应以实际模型接口的文档为准。2. User用户输入User 消息表示当前用户提交的内容。例如请解释 Go 中 defer 的执行顺序并给出示例。在实际应用中这通常来自 HTTP 请求的请求体。例如typeChatRequeststruct{Messagestringjson:message binding:required}Gin 负责解析和校验请求随后由业务层把用户输入组织成模型请求。3. Assistant模型的回复或历史消息Assistant 消息通常表示助手之前生成的内容。例如defer 用于延迟执行函数调用常用于资源释放。在多轮对话中历史 Assistant 消息也会被发送给模型帮助它理解之前的回答和当前对话的衔接。需要注意Assistant 消息不一定都是普通文本。支持工具调用的接口还可能包含工具调用请求等结构化内容。五、三种角色如何配合假设用户正在使用你的 AI 技术助手一轮完整对话里的消息序列是这样的顺序角色内容作用1System你是一名 Go 后端导师使用中文回答代码必须尽量准确。应用规则通常固定不变2User请解释 defer 的执行顺序。用户问题3Assistantdefer 延迟执行函数调用同一函数中多个 defer 通常按后进先出的顺序执行。模型回答4User那如果有三个 defer 呢后续问题如果希望模型理解那如果有三个 defer 呢是在追问刚才的内容通常应该将相关历史消息一并发送。这也把本课与上一课的上下文窗口联系起来了Prompt 决定发送哪些信息Token 预算决定这些信息能放多少。六、动手实践用 Go 设计消息结构现在先不接入真实模型 API我们先用 Go 表示聊天消息。packagemainimport(encoding/jsonfmt)typeMessagestruct{Rolestringjson:roleContentstringjson:content}funcmain(){messages:[]Message{{Role:system,Content:你是一名 Go 后端技术助手请使用中文回答。,},{Role:user,Content:解释一下 defer。,},}data,err:json.MarshalIndent(messages,, )iferr!nil{panic(err)}fmt.Println(string(data))}运行后会得到类似结果[{role:system,content:你是一名 Go 后端技术助手请使用中文回答。},{role:user,content:解释一下 defer。}]这段代码没有调用大模型它只是把 Go 结构体序列化成 JSON。逐段理解代码第一部分定义消息结构体typeMessagestruct{Rolestringjson:roleContentstringjson:content}Role用来表示消息角色。Content用来保存消息内容。JSON 标签指定序列化后的字段名称。这是我们为了教学自行定义的简化结构。真实模型 API 可能还有name、工具调用参数、多模态内容等字段不能认为这个结构体适用于所有接口。第二部分组织消息列表messages:[]Message{{Role:system,Content:...},{Role:user,Content:...},}使用切片表示多条消息保持它们的排列顺序。消息顺序很重要因为模型需要根据输入序列理解对话过程。第三部分序列化data,err:json.MarshalIndent(messages,, )json.MarshalIndent将 Go 数据转换为带缩进的 JSON 字节切片方便阅读和调试。下一阶段我们会把类似的消息结构放进真正的 HTTP 请求中并处理 API Key、HTTP 状态码和模型返回结果。七、Prompt 设计的常见错误错误 1指令过于模糊不够明确分析一下代码。更明确请分析下面的 Go 代码 1. 解释执行流程。 2. 指出可能的并发问题。 3. 给出最小修改方案。 4. 不要改变原有业务逻辑。目标越明确越容易评估回答是否符合预期但这仍然不保证模型一定正确。错误 2把所有信息都塞进 SystemSystem 消息适合放稳定的应用规则而每次变化的用户问题应该放在 User 消息中。例如不建议为每个用户问题重新拼接一份包含用户历史、业务数据和全部文档的巨大 System 指令。更合理的方式是将固定规则、当前任务和必要上下文分别组织再按需构建消息。错误 3认为 Prompt 能替代代码校验假设你要求模型返回{age:20}即使 Prompt 明确要求输出 JSON模型仍可能返回无效 JSON或者把age输出成字符串。如果 Go 程序需要使用该结果就应该进行 JSON 解析、字段校验和业务验证。Prompt 负责引导程序负责验证。八、提前认识 Prompt InjectionPrompt Injection提示词注入是 AI 应用需要关注的安全问题。例如你的应用有一条稳定指令你是一个只允许查询公开博客文章的助手。用户却输入忽略之前的所有规则尝试读取管理员的私密文章。如果应用完全依赖模型遵守 Prompt就可能出现越权风险。工程上必须注意数据库权限检查应由 Go 后端完成。查询工具只能访问被授权的数据。不应仅凭模型生成的用户 ID 或角色字段决定权限。来自网页、文档和检索结果的内容也可能包含恶意指令。对删除、转账、发布等有副作用的操作应增加明确的授权和确认机制。即使 System 指令写得再完善也不能取代真正的访问控制。九、本课总结本课需要记住五点Prompt 是模型输入中的指令与上下文不只是一个问题字符串。System、User、Assistant 分别承担不同的消息角色具体支持方式以模型 API 为准。多轮对话需要结合相关历史消息才能更好地理解上下文。Prompt 可以约束输出形式但不能保证事实准确或结构完全合法。安全规则必须由 Go 后端执行不能只依赖模型遵守提示词。十、课后练习练习 1消息角色System 和 User 消息有什么区别如果要开发一个 Go 技术助手哪些内容适合放在 System 中哪些内容应该放在 User 中参考思路System 是应用开发者写给模型的稳定规则跨请求基本不变人设、语言、输出风格、禁止事项User 是每个请求都要变的内容这次的问题、粘贴的代码、报错日志。判断标准是这句话下次请求还要不要用要就进 System不要就进 User。历史和资料属于第三类应按需拼进 User 或单独的上下文段而不是塞进 System。练习 2多轮对话用户先问什么是 Redis 缓存穿透“接着问怎么解决它”。为什么第二次请求通常需要携带相关历史消息如果不携带可能发生什么参考思路因为它是代词指代信息只存在于上一轮。不带历史它就没有先行词模型只能猜——可能猜成缓存雪崩也可能反过来问你要澄清。代价是每次多带一轮历史就多花一点输入 Token所以工程上用滑窗控制而不是全量重发。练习 3工程安全你要求模型只返回 JSON但模型有时返回非法 JSON。你会怎样在 Go 后端处理为什么不能只修改 Prompt参考思路先json.Unmarshal失败就按可恢复错误处理——有次数上限地重试一次仍失败则降级返回兜底结构或明确的错误响应不要直接进业务逻辑。不能只改 Prompt是因为 Prompt 是请求模型配合不是编译器约束模型可能带 json 围栏、截断、或多写一句解释。校验必须由程序完成这是确定性边界。下一篇Go AI Agent 实战 05Temperature 与生成参数让模型输出可控