构建一个按任务动态调整系统提示词的规则引擎,核心不是把提示词写得更长,而是把“什么时候用哪套提示词”这个决策过程拆成可配置、可观测、可回退的流程。这里给出一个通用实现骨架,适配本地模型或API调用场景,不绑定特定厂商。
这类引擎适合任务类型差异大、提示词长度敏感或需要审计提示词变更的接入场景。实现时要先定义规则字段,再写决策函数,最后做输入输出校验。风险点在于规则覆盖不全和命中顺序错误,建议保留默认提示词兜底。
规则引擎需要决策什么
系统提示词通常包含角色、任务约束、输出格式、背景信息等。不同任务对提示词的需求不同,比如代码生成需要强调语言和风格,客服场景需要强调礼貌和边界。动态调整就是根据任务分类、关键词、上下文字数等信号,选择或拼接提示词。
一个最小规则引擎由三部分组成:规则集、匹配器、渲染器。规则集用JSON或YAML描述;匹配器根据当前请求的元数据(比如task_type、user_intent、content_length)决定命中的规则;渲染器把模板变量填入,生成最终系统提示词。
可复制的规则配置demo
{
"rules": [
{
"id": "code-gen",
"conditions": {
"task_type": "code",
"language": ["python", "javascript"]
},
"template": "你是严谨的编程助手。请只输出可直接运行的代码,并附带简短说明。",
"priority": 10
},
{
"id": "chat-default",
"conditions": {
"task_type": "chat"
},
"template": "你是友好的对话助手,回答不超过200字。",
"priority": 1
}
],
"default_template": "你是通用助手,请根据问题类型选择合适方式回答。",
"strategy": "first_match"
}
这段配置定义了两条规则和一个兜底模板。匹配器按priority从高到低检查conditions,命中即返回。conditions里的字段来自调用方传入的元数据,例如任务分类接口或请求内置的意图识别结果。
决策函数和渲染逻辑
def resolve_system_prompt(rules, meta):
matched = None
for rule in sorted(rules, key=lambda r: -r.get("priority", 0)):
cond = rule["conditions"]
if all(meta.get(k) in v if isinstance(v, list) else meta.get(k) == v
for k, v in cond.items()):
matched = rule
break
template = matched["template"] if matched else rules.get("default_template")
return template.format(**meta)
这个函数接受规则集和当前请求元数据,返回最终文本。注意conditions的匹配逻辑需要支持等于和包含,生产环境建议用json-path或自研条件表达式,避免写死在代码里。
验证清单
- 构造一个不匹配任何规则的任务,确认默认提示词生效,不会报错或输出空串。
- 对同一条规则,改变优先级顺序,确认命中顺序符合预期。
- 传入缺失字段的元数据,确认匹配逻辑不会抛异常,而是落到默认分支。
- 对动态拼接后的提示词做长度限制检查,防止超过模型上下文窗口。
常见问题
规则太多时会不会影响响应耗时?通常不会,规则集本身是静态配置,匹配是内存遍历。如果规则超过几百条,可以用决策树或标签索引先粗筛。
如何避免规则互相覆盖?建议每条规则写清楚应用场景,并给优先级字段。调试时输出命中规则id,方便回溯。
提示词版本如何管理?可以把每个模板当作文本资源,用git记录变更。上线前对比不同版本在同一批测试用例上的输出质量,保留可回滚版本。