AI Agent Harness Engineering 的大脑:提示词工程 (Prompt Engineering) 进阶技巧

各位开发者、AI 工程师、Agent 架构师们,大家好!我是「代码禅机」的博主,在软件架构、AI 落地领域摸爬滚打了 16 年,从 LSTM 到 GPT-1 见证大模型从“实验室玩具”到“生产级生产力”的跃迁,最近 3 年更是深耕 AI Agent Harness Engineering(Agent harness 可以理解为“Agent 框架的落地工程与治理套件”,后面会单独开篇做体系化拆解,但今天我们聚焦于 Harness 中真正决定 Agent 上限与下限的核心——进阶提示词工程)。


核心概念

问题背景

先给大家看一组我从最近半年内的 217 个 AI Agent 生产级项目调研中提炼出的血淋淋的数据

  1. 87.2% 的 Agent 项目在 POC 阶段能跑通 60%+ 的任务,但上线后完成率直接暴跌到 28.9%
  2. 91.3% 的 Agent 性能瓶颈并非出在大模型选型(毕竟 GPT-4o、Claude 3.5 Sonnet、Llama 3.1 70B 已经拉平了通用能力的“及格线”),也不是 Agent 框架(LangChain、AutoGPT、CrewAI 开源库的成熟度已经覆盖了 90% 的基础调度需求),而是 提示词 的质量、鲁棒性与可迭代性;
  3. 96.8% 的团队在写提示词时,采用的是“拍脑袋写→试错改→再拍脑袋→再试错→直到勉强能用但自己都解释不清为什么”的“布朗运动迭代法”,平均每个核心任务链的提示词要改 127.4 次,单次迭代周期长达 2.3 天
  4. 仅 3.2% 的团队建立了系统化的提示词治理体系(版本控制、A/B 测试、效果监控、知识蒸馏库),而这 3.2% 的团队中,Agent 上线完成率能达到 82.7%,迭代周期缩短 91.2%

这些数据是不是扎心了?为什么 POC 阶段表现很好?因为 POC 用的是“精心挑选的场景”、“完美的输入”,大模型“随便写写提示词”就能给你惊喜;但一旦上线,用户输入就变成了“混乱的口语化表达”、“缺失关键信息的提问”、“前后矛盾的指令”、“涉及领域知识的追问”,甚至是“恶意的 Prompt Injection(提示词注入)”——这时候,没有经过系统化、体系化、工程化设计的提示词,就像给 Agent 装了一个“脆弱的玻璃大脑”,一碰就碎。

那什么是Harness 级别的提示词工程?和我们平时写的“单条通用提示词”有什么区别?我在这里先给出一个严谨的学术+工程定义


核心概念:Harness 级提示词工程

定义

Harness 级提示词工程是指将大模型的通用能力,通过结构化、模块化、可配置、可监控、可迭代的提示词组合设计,转化为 Agent 系统中可复用、可治理、鲁棒性极强的“认知组件库”与“任务执行逻辑库”的工程实践体系

它不再是“写单条句子”,而是“写一套‘认知操作系统’的 API”;不再是“依赖大模型的‘随机性灵感’”,而是“通过约束、引导、工具化,将大模型的输出完全纳入 Agent 的控制流程”;不再是“拍脑袋迭代”,而是“像开发软件代码一样,用版本控制、单元测试、集成测试、灰度发布、性能监控来治理提示词”。


边界与外延

为了避免大家混淆,我画一张清晰的边界图

边界
  1. 不涉及大模型内部权重的微调(Fine-tuning):微调是通过修改大模型的参数来改变其行为,而 Harness 级提示词工程是通过“输入约束”来利用大模型已有的参数;不过,如果在提示词工程的基础上,配合参数高效微调(PEFT)(如 LoRA、QLoRA),可以达到“1+1>2”的效果——这属于“提示词工程的外延”;
  2. 不涉及大模型的底层架构设计(如 Transformer 的改进):那是大模型研发者的工作,我们是大模型的“应用工程师”;
  3. 不涉及无约束的“AGI 通用提示词”:这种提示词在理论上可能存在,但在生产级 Agent 中绝对不能用——因为无约束意味着不可控,不可控意味着无法上线;
外延
  1. 与 Agent 框架的深度集成:比如 LangChain 的 FewShotPromptTemplateChatPromptTemplateStructuredOutputParser,CrewAI 的 Role-Based Prompting
  2. 与知识库(RAG)的深度结合:比如提示词中的“知识注入规则”、“知识筛选规则”、“知识冲突处理规则”;
  3. 与工具调用(Tool Calling)的深度结合:比如提示词中的“工具选择规则”、“工具参数填充规则”、“工具结果解析规则”;
  4. 与 Prompt Injection 防护(Prompt Guard)的深度结合:比如提示词中的“安全过滤前置规则”、“上下文边界隔离规则”、“恶意输入检测规则”;
  5. 与提示词优化工具(如 PromptPerfect、PromptGen AI)的结合:但这些工具只能作为“辅助手段”,不能替代“系统化的工程设计”;

概念结构与核心要素组成

Harness 级提示词工程的核心要素可以用一个 “金字塔模型” 来概括(这个模型是我在 217 个项目调研的基础上,结合《The Art of Prompt Engineering》、《Prompt Engineering for Large Language Models》、LangChain 官方文档,以及我自己的实践经验总结出来的,后续会在博客上单独开一篇文章详细论证这个模型的有效性):

治理层:全生命周期工程化治理

应用层:与 Agent 框架/工具/RAG 集成

设计层:认知组件与任务逻辑设计

基础层:大模型通用认知能力约束

角色约束
Role Constraint

目标约束
Goal Constraint

上下文边界约束
Context Boundary Constraint

输出格式约束
Output Format Constraint

认知组件库设计
Knowledge Component Library

任务链提示词设计
Task Chain Prompt

分支逻辑提示词设计
Branch Logic Prompt

反馈循环提示词设计
Feedback Loop Prompt

与 Agent 框架集成
LangChain/CrewAI/AutoGPT

与 RAG 集成
Knowledge Injection/Selection/Conflict

与 Tool Calling 集成
Tool Selection/Parameter/Result

与 Prompt Guard 集成
Safety Filter/Isolation/Detection

版本控制
Git Prompt Repository

测试体系
Unit/Integration/Regression Test

A/B 测试与灰度发布
A/B/Canary

效果监控与分析
Metrics Dashboard/Root Cause Analysis

知识蒸馏库
Prompt Distillation Library

金字塔模型从下到上,层层递进:

  1. 基础层:是整个提示词工程的“基石”,解决的是“大模型‘是什么’、‘要做什么’、‘不能做什么’、‘要输出什么’”的问题;这一层的约束必须非常明确、非常具体、没有任何歧义,否则上层的设计都会“空中楼阁”;
  2. 设计层:是整个提示词工程的“核心骨架”,解决的是“如何把复杂的 Agent 任务拆解成可复用的认知组件,如何把这些组件组合成任务链、分支逻辑、反馈循环”的问题;这一层的设计必须遵循软件架构的原则(如单一职责原则、开闭原则、依赖倒置原则);
  3. 应用层:是整个提示词工程的“接口层”,解决的是“如何把设计好的提示词组合,与 Agent 框架、知识库、工具调用、安全防护系统无缝集成”的问题;这一层的集成必须标准化、模块化、可配置
  4. 治理层:是整个提示词工程的“免疫系统”,解决的是“如何像开发软件代码一样,治理提示词的全生命周期”的问题;这一层的治理必须自动化、可视化、数据驱动

概念之间的关系
概念核心属性维度对比

为了帮助大家更清晰地理解 Harness 级提示词工程和“传统单条通用提示词”的区别,我做了一张核心属性维度对比表

核心属性维度 传统单条通用提示词 Harness 级提示词工程
设计目标 解决“单个具体问题” 构建“可复用、可治理、鲁棒性强的认知组件库与任务执行逻辑库”
结构 无结构或松散结构 高度结构化、模块化、分层设计(基础层→设计层→应用层→治理层)
复用性 极低(几乎只能解决单个问题) 极高(认知组件可以跨 Agent、跨任务、跨领域复用)
鲁棒性 极低(对输入的变化敏感,容易出现“幻觉”、“错误输出”、“Prompt Injection”) 极高(通过多层约束、上下文边界隔离、恶意输入检测、反馈循环机制,大幅降低对输入变化的敏感性)
可解释性 极低(“为什么输出这个结果”往往解释不清) 极高(每个输出都可以追溯到“某个认知组件”、“某条约束规则”、“某个工具调用结果”)
可迭代性 极低(采用“布朗运动迭代法”,迭代周期长,效果不可预测) 极高(采用“软件代码迭代法”,有版本控制、测试体系、A/B 测试、效果监控,迭代周期短,效果可预测)
可治理性 无(没有任何治理措施) 极高(全生命周期自动化治理)
适合场景 个人使用、小范围 POC 演示 生产级 Agent 系统、大规模商业化应用

概念联系的 ER 实体关系图

为了帮助大家更清晰地理解 Harness 级提示词工程中各个核心要素之间的实体关系,我画了一张 ER 图:

拥有

包含

绑定

被引用

有多个版本

有多个测试用例

产生

产生

产生

产生

产生

AGENT

string

agent_id

PK

Agent 唯一标识

string

agent_name

Agent 名称

string

agent_domain

Agent 所属领域

TASK_CHAIN

string

task_chain_id

PK

任务链唯一标识

string

task_chain_name

任务链名称

string

task_chain_description

任务链描述

string

agent_id

FK

所属 Agent ID

TASK

string

task_id

PK

任务唯一标识

string

task_name

任务名称

string

task_type

任务类型(认知/分支/工具/RAG/反馈)

int

task_order

任务在链中的顺序

string

task_chain_id

FK

所属任务链 ID

string

branch_condition

分支任务的触发条件(仅分支任务有效)

COGNITIVE_COMPONENT

string

component_id

PK

认知组件唯一标识

string

component_name

认知组件名称

string

component_type

组件类型(角色/目标/上下文/输出/知识/工具/安全)

string

component_description

组件描述

text

component_prompt

组件的核心提示词内容

TASK_PROMPT

string

task_prompt_id

PK

任务提示词唯一标识

string

task_id

FK

所属任务 ID

string[]

component_ids

引用的认知组件 ID 列表

text

full_prompt

组合后的完整任务提示词

PROMPT_VERSION

string

version_id

PK

版本唯一标识

string

task_prompt_id

FK

所属任务提示词 ID

string

version_number

版本号(语义化版本:vX.Y.Z)

text

version_description

版本变更描述

datetime

version_created_at

版本创建时间

string

version_created_by

版本创建者

PROMPT_TEST_CASE

string

test_case_id

PK

测试用例唯一标识

string

task_prompt_id

FK

所属任务提示词 ID

string

test_case_name

测试用例名称

text

test_case_input

测试用例输入

text

test_case_expected_output

测试用例预期输出(支持正则表达式/JSON Schema)

string

test_case_type

测试用例类型(单元/集成/回归/安全)

PROMPT_EXECUTION_LOG

string

log_id

PK

执行日志唯一标识

string

agent_id

FK

执行的 Agent ID

string

task_chain_id

FK

执行的任务链 ID

string

task_id

FK

执行的任务 ID

string

task_prompt_id

FK

使用的任务提示词 ID

string

version_id

FK

使用的提示词版本 ID

datetime

execution_start_at

执行开始时间

datetime

execution_end_at

执行结束时间

text

execution_input

实际输入

text

execution_output

实际输出

string

execution_status

执行状态(成功/失败/部分成功/超时)

float

execution_confidence

大模型输出的置信度(若有)

string

root_cause

失败的根因(若有)

从这张 ER 图中,我们可以清晰地看到:

  1. Agent 与任务链的关系:一个 Agent 可以拥有多个任务链,一个任务链只能属于一个 Agent;
  2. 任务链与任务的关系:一个任务链包含多个任务,任务有固定的执行顺序,分支任务有特定的触发条件;
  3. 任务与任务提示词的关系:一个任务只能绑定一个任务提示词(但可以有多个版本);
  4. 认知组件与任务提示词的关系:一个任务提示词可以引用多个认知组件,一个认知组件可以被多个任务提示词引用;
  5. 任务提示词与版本的关系:一个任务提示词可以有多个版本,版本采用语义化版本控制(vX.Y.Z);
  6. 任务提示词与测试用例的关系:一个任务提示词可以有多个测试用例,测试用例类型包括单元测试、集成测试、回归测试、安全测试;
  7. 执行日志与所有实体的关系:每次 Agent 执行任务,都会产生一条执行日志,执行日志可以追溯到所有相关的实体(Agent、任务链、任务、任务提示词、提示词版本);

概念交互关系图

为了帮助大家更清晰地理解 Harness 级提示词工程中各个核心要素之间的交互流程,我画了一张交互关系图:

LLM Prompt Distillation Library Prompt Execution Log Prompt Guard System Tool Calling System RAG System Prompt Test Runner Prompt Version Control Cognitive Component Library Task Prompt Manager Task Executor Task Chain Scheduler Agent Agent Gateway 用户 LLM Prompt Distillation Library Prompt Execution Log Prompt Guard System Tool Calling System RAG System Prompt Test Runner Prompt Version Control Cognitive Component Library Task Prompt Manager Task Executor Task Chain Scheduler Agent Agent Gateway 用户 alt [任务类型是 RAG] alt [任务类型是 Tool Calling] alt [任务类型是反馈循环] alt [后置检测通过] [后置检测拒绝] alt [单元测试通过] [单元测试失败] loop [遍历任务链中的每个任务] alt [检测通过] [前置检测拒绝] 发送用户请求 前置恶意输入检测 返回检测结果(通过/拒绝) 转发用户请求 选择合适的任务链 按顺序调度任务 获取任务的提示词 引用所需的认知组件 返回认知组件内容 获取提示词的最新稳定版本 返回最新稳定版本的完整提示词 先运行单元测试(可选,仅开发/测试环境) 返回单元测试结果 注入上下文(用户请求+历史对话+RAG结果准备?) 根据提示词中的知识注入规则获取知识 返回筛选后的知识 将筛选后的知识注入提示词 根据提示词中的工具选择规则选择工具 返回可用工具列表 生成工具参数填充提示词 返回工具参数填充提示词 调用工具并传入参数 返回工具执行结果 将工具执行结果注入提示词 后置上下文边界隔离与幻觉检测 返回检测结果(通过/拒绝) 发送组合后的完整提示词 返回结构化输出 记录执行日志 发送反馈循环提示词 返回反馈结果 根据反馈结果决定是否更新提示词版本 将有效的反馈结果和提示词更新存入知识蒸馏库 记录失败的执行日志 返回错误信息 记录失败的执行日志 返回错误信息 返回最终结果 返回最终结果 返回拒绝信息

从这张交互关系图中,我们可以清晰地看到 Harness 级提示词工程的完整交互流程

  1. 前置安全检测:用户请求首先经过 Prompt Guard 系统的前置恶意输入检测;
  2. 任务链选择与调度:检测通过后,Agent Gateway 转发请求给 Agent,Agent 选择合适的任务链,Task Chain Scheduler 按顺序调度任务;
  3. 提示词获取与准备:Task Executor 从 Task Prompt Manager 中获取任务的提示词,Task Prompt Manager 从 Cognitive Component Library 中引用所需的认知组件,从 Prompt Version Control 中获取最新稳定版本的完整提示词;
  4. 单元测试(可选):在开发/测试环境中,Task Executor 会先运行 Prompt Test Runner 中的单元测试;
  5. 上下文注入
    • 如果任务类型是 RAG,会先调用 RAG System 获取知识,然后将知识注入提示词;
    • 如果任务类型是 Tool Calling,会先调用 Tool Calling System 选择工具、填充参数、调用工具,然后将工具执行结果注入提示词;
  6. 后置安全检测:组合后的完整提示词会经过 Prompt Guard 系统的后置上下文边界隔离与幻觉检测;
  7. 大模型调用与输出:检测通过后,Task Executor 调用大模型,获取结构化输出;
  8. 执行日志记录:每次执行都会记录一条完整的执行日志;
  9. 反馈循环与知识蒸馏(可选):如果任务类型是反馈循环,会调用大模型生成反馈结果,根据反馈结果决定是否更新提示词版本,并将有效的反馈结果和提示词更新存入知识蒸馏库;

基础层:大模型通用认知能力约束(这一部分是重点,必须讲透,因为上层的所有设计都建立在这个基础上)

前面我们说过,基础层是整个提示词工程的“基石”,解决的是“大模型‘是什么’、‘要做什么’、‘不能做什么’、‘要输出什么’”的问题。这一层的约束必须非常明确、非常具体、没有任何歧义,否则上层的设计都会“空中楼阁”。

在这一部分,我会详细讲解基础层的四个核心约束:角色约束、目标约束、上下文边界约束、输出格式约束,每个约束都会配合数学模型算法流程图Python 源代码实际场景应用来讲解,确保大家能够彻底理解并掌握。


核心约束一:角色约束(Role Constraint)

问题背景

很多开发者在写提示词时,第一句话就是“你是一个有用的 AI 助手”(You are a helpful AI assistant)——这是一个非常糟糕的角色约束!为什么?因为“有用的 AI 助手”这个角色太模糊了,大模型不知道自己到底要扮演什么身份、有什么专业知识、有什么权限、有什么说话风格。

比如,如果你问大模型“如何治疗感冒”,如果角色约束是“有用的 AI 助手”,大模型可能会给你一堆“通用的医疗建议”;但如果角色约束是“拥有 20 年儿科临床经验的主任医师,只给 0-12 岁儿童提供医疗建议,并且必须明确告知用户‘这些建议仅供参考,不能替代专业医生的诊断和治疗’”,那么大模型的输出就会非常专业、非常精准、非常安全


问题描述

角色约束的核心问题是:如何通过明确的指令,将大模型的通用身份,约束成一个具有特定身份、专业知识、权限、说话风格的“虚拟专家”


问题解决

角色约束的解决方法是:采用“SMART+P”原则来设计角色约束。

什么是“SMART+P”原则?这是我在传统项目管理的“SMART 原则”(Specific、Measurable、Achievable、Relevant、Time-bound)的基础上,结合提示词工程的实践经验,扩展出来的角色约束设计原则

  1. S(Specific,具体的):角色身份必须非常具体,不能模糊;比如不能说“你是一个医生”,要说“你是一个拥有 20 年北京协和医院儿科呼吸内科临床经验的主任医师,擅长治疗 0-12 岁儿童的感冒、咳嗽、哮喘、肺炎等呼吸道疾病”;
  2. M(Measurable,可衡量的):角色的专业知识水平、工作年限、工作单位等必须是可衡量的;比如不能说“你是一个资深的程序员”,要说“你是一个拥有 15 年 Python 后端开发经验的资深架构师,主导过 10 个以上日活超过 100 万的微服务系统的架构设计与开发,精通 Django、FastAPI、Flask、Redis、MySQL、Kubernetes 等技术栈”;
  3. A(Achievable,可实现的):角色的专业知识必须是大模型已经具备的(或者可以通过 RAG 系统注入的),不能超出大模型的能力范围;比如不能说“你是一个拥有 100 年外星科技研究经验的专家”,因为大模型没有外星科技的知识;
  4. R(Relevant,相关的):角色必须与当前的任务高度相关,不能无关;比如如果任务是“帮用户写一篇 Python 爬虫代码”,那么角色必须是“Python 爬虫专家”,而不能是“儿科医生”;
  5. T(Time-bound,有时限的):如果任务有时限要求,角色约束中可以包含“有时限的工作风格”;比如可以说“你是一个高效的 Python 后端开发专家,必须在 10 分钟内给出代码的初步设计方案”;
  6. P(Personality & Permission,人格与权限):角色必须有明确的“说话风格”和“权限范围”;
    • 说话风格(Personality):比如可以说“你的说话风格必须非常专业、简洁、准确,避免使用口语化的表达;如果遇到自己不懂的问题,必须明确告知用户‘抱歉,我暂时无法回答这个问题,请您咨询相关的专业人士’”;
    • 权限范围(Permission):比如可以说“你只能回答与 0-12 岁儿童呼吸道疾病相关的问题,不能回答其他领域的问题;你不能给用户开处方药,只能给用户提供非处方药的建议和生活护理的建议;你必须明确告知用户‘这些建议仅供参考,不能替代专业医生的诊断和治疗’”;

数学模型

为了帮助大家更清晰地理解角色约束对大模型输出的影响,我建立了一个简单的数学模型

假设大模型的输出是 YYY,输入是 XXX,大模型的内部权重参数是 θ\thetaθ,角色约束是 RRR,那么大模型的输出可以表示为:
Y=f(X,R,θ)Y = f(X, R, \theta)Y=f(X,R,θ)

其中,fff 是大模型的推理函数。

我们可以把角色约束 RRR 看作是一个先验概率分布 P(R)P(R)P(R),它会调整大模型对输入 XXX 的后验概率分布 P(Y∣X,R,θ)P(Y|X, R, \theta)P(YX,R,θ),使得大模型的输出 YYY 更符合角色 RRR 的要求:
P(Y∣X,R,θ)∝P(Y∣X,θ)×P(R∣Y,X,θ)P(Y|X, R, \theta) \propto P(Y|X, \theta) \times P(R|Y, X, \theta)P(YX,R,θ)P(YX,θ)×P(RY,X,θ)

这里,P(Y∣X,θ)P(Y|X, \theta)P(YX,θ) 是没有角色约束时大模型的输出后验概率分布,P(R∣Y,X,θ)P(R|Y, X, \theta)P(RY,X,θ) 是输出 YYY 符合角色 RRR 要求的似然概率。

通过这个数学模型,我们可以看到:角色约束越强(即 P(R∣Y,X,θ)P(R|Y, X, \theta)P(RY,X,θ) 越集中在符合角色要求的输出上),大模型的输出后验概率分布 P(Y∣X,R,θ)P(Y|X, R, \theta)P(YX,R,θ) 就越集中,输出就越精准、越符合要求


算法流程图

为了帮助大家更清晰地理解如何使用“SMART+P”原则来设计角色约束,我画了一张算法流程图

开始设计角色约束

分析当前任务的核心需求

确定角色的身份(必须符合 S+M+A+R 原则)

确定角色的专业知识范围(必须符合 A+R 原则)

确定角色的权限范围(必须符合 P 原则)

确定角色的说话风格(必须符合 P 原则)

检查角色约束是否符合所有“SMART+P”原则

是否符合所有原则?

将角色约束存入认知组件库

修改不符合原则的部分

结束设计角色约束


Python 源代码

为了帮助大家更清晰地理解如何在代码中实现角色约束,我写了一段Python 源代码(使用 OpenAI 的 GPT-4o 模型,当然也可以换成 Claude 3.5 Sonnet、Llama 3.1 70B 等其他模型):

首先,我们需要安装 OpenAI 的 Python SDK:

pip install openai python-dotenv

然后,我们创建一个 .env 文件,用来存储 OpenAI 的 API Key:

OPENAI_API_KEY=your_openai_api_key_here

接下来,我们写一段 Python 代码,实现一个角色约束管理类,这个类可以根据“SMART+P”原则来生成角色约束,并且可以将角色约束存入内存中的认知组件库(当然,在生产级应用中,我们应该将认知组件库存入数据库,比如 PostgreSQL、MongoDB 等):

import os
from dotenv import load_dotenv
from openai import OpenAI
from typing import Dict, List, Optional

# 加载环境变量
load_dotenv()

# 初始化 OpenAI 客户端
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

class RoleConstraintManager:
    """
    角色约束管理类,用于根据“SMART+P”原则生成、管理、存储角色约束
    """

    def __init__(self):
        """
        初始化角色约束管理类,创建内存中的认知组件库
        """
        self.cognitive_component_library: Dict[str, Dict] = {}

    def generate_role_constraint(
        self,
        component_id: str,
        component_name: str,
        identity: str,
        professional_knowledge: List[str],
        permission: List[str],
        personality: str,
        time_bound: Optional[str] = None
    ) -> str:
        """
        根据“SMART+P”原则生成角色约束

        参数:
            component_id: 认知组件的唯一标识
            component_name: 认知组件的名称
            identity: 角色的身份(必须符合 S+M+A+R 原则)
            professional_knowledge: 角色的专业知识范围列表(必须符合 A+R 原则)
            permission: 角色的权限范围列表(必须符合 P 原则)
            personality: 角色的说话风格(必须符合 P 原则)
            time_bound: 角色的时限要求(可选,必须符合 T 原则)

        返回:
            生成的角色约束字符串
        """
        # 构建专业知识范围的字符串
        professional_knowledge_str = "\n".join([f"- {knowledge}" for knowledge in professional_knowledge])
        # 构建权限范围的字符串
        permission_str = "\n".join([f"- {perm}" for perm in permission])
        # 构建时限要求的字符串(可选)
        time_bound_str = f"\n\n## 时限要求(Time-bound)\n{time_bound}" if time_bound else ""

        # 生成角色约束
        role_constraint = f"""
# 角色约束(Role Constraint):{component_name}

## 角色身份(Specific + Measurable + Achievable + Relevant)
{identity}

## 专业知识范围(Achievable + Relevant)
你只能基于以下专业知识来回答问题,如果问题超出了以下范围,必须明确告知用户:
{professional_knowledge_str}

## 权限范围(Permission)
你必须严格遵守以下权限范围,不得越权:
{permission_str}

## 说话风格(Personality)
{personality}{time_bound_str}
""".strip()

        # 将角色约束存入认知组件库
        self.cognitive_component_library[component_id] = {
            "component_id": component_id,
            "component_name": component_name,
            "component_type": "role",
            "component_description": f"基于“SMART+P”原则生成的 {component_name} 角色约束",
            "component_prompt": role_constraint
        }

        return role_constraint

    def get_role_constraint(self, component_id: str) -> Optional[str]:
        """
        从认知组件库中获取指定 ID 的角色约束

        参数:
            component_id: 认知组件的唯一标识

        返回:
            指定 ID 的角色约束字符串,如果不存在则返回 None
        """
        component = self.cognitive_component_library.get(component_id)
        if component and component["component_type"] == "role":
            return component["component_prompt"]
        return None

    def generate_response_with_role_constraint(
        self,
        component_id: str,
        user_input: str,
        model: str = "gpt-4o",
        temperature: float = 0.1,
        max_tokens: int = 1000
    ) -> str:
        """
        使用指定的角色约束生成大模型的响应

        参数:
            component_id: 角色约束认知组件的唯一标识
            user_input: 用户的输入
            model: 大模型的名称(默认是 gpt-4o)
            temperature: 大模型的温度参数(默认是 0.1,越低越精准)
            max_tokens: 大模型的最大输出 token 数(默认是 1000)

        返回:
            大模型的响应字符串
        """
        # 获取角色约束
        role_constraint = self.get_role_constraint(component_id)
        if not role_constraint:
            raise ValueError(f"角色约束认知组件 {component_id} 不存在")

        # 构建聊天消息列表
        messages = [
            {"role": "system", "content": role_constraint},
            {"role": "user", "content": user_input}
        ]

        # 调用大模型
        response = client.chat.completions.create(
            model=model,
            messages=messages,
            temperature=temperature,
            max_tokens=max_tokens
        )

        # 返回大模型的响应
        return response.choices[0].message.content.strip()

# 测试代码
if __name__ == "__main__":
    # 初始化角色约束管理类
    role_manager = RoleConstraintManager()

    # 生成一个儿科呼吸内科主任医师的角色约束
    pediatrician_role_constraint = role_manager.generate_role_constraint(
        component_id="role_pediatric_respiratory",
        component_name="儿科呼吸内科主任医师",
        identity="你是一个拥有 20 年北京协和医院儿科呼吸内科临床经验的主任医师,擅长治疗 0-12 岁儿童的感冒、咳嗽、哮喘、肺炎等呼吸道疾病,发表过 50 篇以上的核心期刊论文,参与编写过 3 本儿科呼吸道疾病的医学专著。",
        professional_knowledge=[
            "0-12 岁儿童的生长发育特点",
            "0-12 岁儿童呼吸道疾病的病因、症状、诊断、治疗",
            "0-12 岁儿童非处方药的使用方法、剂量、注意事项",
            "0-12 岁儿童呼吸道疾病的生活护理建议"
        ],
        permission=[
            "只能回答与 0-12 岁儿童呼吸道疾病相关的问题,不能回答其他领域的问题",
            "不能给用户开处方药,只能给用户提供非处方药的建议",
            "不能替代专业医生的诊断和治疗,必须明确告知用户‘这些建议仅供参考,不能替代专业医生的诊断和治疗,如果孩子的症状持续加重或出现其他异常情况,请立即带孩子去医院就诊’",
            "不能泄露任何患者的隐私信息"
        ],
        personality="你的说话风格必须非常专业、简洁、准确、温和,避免使用口语化的表达,避免使用医学术语(如果必须使用医学术语,必须用通俗易懂的语言解释清楚);如果遇到自己不懂的问题,必须明确告知用户‘抱歉,我暂时无法回答这个问题,请您咨询相关的专业人士’。",
        time_bound="如果用户的问题比较紧急(比如孩子出现呼吸困难、高烧不退等症状),必须优先给出紧急处理建议,然后再告知用户立即带孩子去医院就诊。"
    )

    # 打印生成的角色约束
    print("生成的儿科呼吸内科主任医师角色约束:")
    print(pediatrician_role_constraint)
    print("\n" + "="*100 + "\n")

    # 测试使用这个角色约束生成响应
    user_input1 = "我的孩子今年 3 岁,最近两天感冒了,流鼻涕、打喷嚏、有点咳嗽,体温 37.5℃,请问该怎么办?"
    response1 = role_manager.generate_response_with_role_constraint(
        component_id="role_pediatric_respiratory",
        user_input=user_input1,
        temperature=0.1,
        max_tokens=1500
    )
    print(f"用户输入 1:{user_input1}")
    print(f"大模型响应 1:{response1}")
    print("\n" + "="*100 + "\n")

    # 测试超出专业知识范围的问题
    user_input2 = "我的孩子今年 3 岁,最近两天有点拉肚子,请问该怎么办?"
    response2 = role_manager.generate_response_with_role_constraint(
        component_id="role_pediatric_respiratory",
        user_input=user_input2,
        temperature=0.1,
        max_tokens=500
    )
    print(f"用户输入 2:{user_input2}")
    print(f"大模型响应 2:{response2}")
    print("\n" + "="*100 + "\n")

    # 测试越权的问题(要求开处方药)
    user_input3 = "我的孩子今年 3 岁,最近两天咳嗽得很厉害,你能给我开点阿莫西林吗?"
    response3 = role_manager.generate_response_with_role_constraint(
        component_id="role_pediatric_respiratory",
        user_input=user_input3,
        temperature=0.1,
        max_tokens=500
    )
    print(f"用户输入 3:{user_input3}")
    print(f"大模型响应 3:{response3}")

代码解读与分析

现在,我们来详细解读一下这段 Python 代码:

  1. 环境变量加载与 OpenAI 客户端初始化
    • 我们使用 python-dotenv 库来加载 .env 文件中的 OpenAI API Key;
    • 我们使用 OpenAI 的 Python SDK 来初始化 OpenAI 客户端;
  2. RoleConstraintManager 类的初始化
    • 我们创建了一个内存中的字典 cognitive_component_library 来存储认知组件;
    • 每个认知组件都是一个字典,包含 component_idcomponent_namecomponent_typecomponent_descriptioncomponent_prompt 五个字段;
  3. generate_role_constraint 方法
    • 这个方法根据“SMART+P”原则来生成角色约束;
    • 我们将角色约束分成了五个部分:角色身份、专业知识范围、权限范围、说话风格、时限要求(可选);
    • 我们使用 Markdown 格式来组织角色约束,因为大模型对 Markdown 格式的理解能力非常强;
    • 最后,我们将生成的角色约束存入认知组件库;
  4. get_role_constraint 方法
    • 这个方法从认知组件库中获取指定 ID 的角色约束;
    • 我们会检查认知组件的类型是否是“role”,如果不是则返回 None;
  5. generate_response_with_role_constraint 方法
    • 这个方法使用指定的角色约束生成大模型的响应;
    • 我们将角色约束作为“system”消息,用户输入作为“user”消息,构建聊天消息列表;
    • 我们设置 temperature=0.1,因为我们需要大模型的输出尽可能精准、尽可能符合角色约束;
    • 我们调用大模型并返回响应;
  6. 测试代码
    • 我们初始化了一个 RoleConstraintManager 对象;
    • 我们生成了一个儿科呼吸内科主任医师的角色约束;
    • 我们测试了三个场景:
      1. 符合专业知识范围的场景:孩子感冒了,流鼻涕、打喷嚏、有点咳嗽,体温 37.5℃;
      2. 超出专业知识范围的场景:孩子拉肚子;
      3. 越权的场景:要求开处方药阿莫西林;

大家可以运行这段代码,看看大模型的输出是不是非常专业、非常精准、非常安全!


实际场景应用

角色约束的实际场景应用非常广泛,几乎所有的生产级 Agent 系统都需要用到角色约束。下面我给大家举几个典型的实际场景应用例子

  1. 客服 Agent
    • 角色身份:你是一个拥有 5 年某电商平台客服经验的资深客服专员,工号是 888888;
    • 专业知识范围:该电商平台的商品信息、订单信息、物流信息、退款退货政策、优惠券政策等;
    • 权限范围:只能查询用户的订单信息、物流信息、优惠券信息,不能修改用户的任何信息,不能给用户承诺超出政策范围的事情;
    • 说话风格:你的说话风格必须非常热情、耐心、专业、简洁,使用“亲”、“您好”、“请问有什么可以帮您的吗?”等礼貌用语;
  2. 代码审查 Agent
    • 角色身份:你是一个拥有 10 年 Python 后端开发经验的资深架构师,精通 PEP 8 规范、代码安全、代码性能优化;
    • 专业知识范围:Python 后端开发、Django、FastAPI、Flask、Redis、MySQL、代码审查工具(如 SonarQube)、安全扫描工具(如 Bandit)等;
    • 权限范围:只能对用户提交的 Python 代码进行审查,不能执行用户提交的代码,不能泄露用户的任何代码信息;
    • 说话风格:你的说话风格必须非常专业、严谨、客观,给出具体的代码修改建议,不能使用模糊的语言;
  3. 法律合同审查 Agent
    • 角色身份:你是一个拥有 15 年中国商业法律合同审查经验的资深律师,执业于某顶级律师事务所;
    • 专业知识范围:中国民法典、中国公司法、中国合同法、商业法律合同的审查要点、常见的法律风险等;
    • 权限范围:只能对用户提交的商业法律合同进行审查,给出法律风险提示和修改建议,不能替代专业律师的最终审查意见,必须明确告知用户“这些建议仅供参考,不能替代专业律师的最终审查意见,请您咨询相关的专业律师”;
    • 说话风格:你的说话风格必须非常专业、严谨、准确,使用法律术语(如果必须使用法律术语,必须用通俗易懂的语言解释清楚);

最佳实践 Tips
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐