ai-robot-core/docs/prompt-template-analysis.md

627 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Prompt 模板管理生效机制与占位符使用分析
## 1. 核心架构
Prompt 模板管理系统由以下核心组件构成:
### 1.1 数据模型
**PromptTemplate模板实体**
- `id`: UUID模板唯一标识
- `tenant_id`: 租户 ID实现多租户隔离
- `name`: 模板名称
- `scene`: 场景标识(如 "default"、"customer_service"
- `description`: 模板描述
- `is_default`: 是否为默认模板
**PromptTemplateVersion模板版本**
- `template_id`: 关联的模板 ID
- `version`: 版本号(整数,自增)
- `status`: 版本状态draft/published/archived
- `system_instruction`: 系统指令内容(包含占位符)
- `variables`: 自定义变量定义列表
### 1.2 核心服务
**PromptTemplateService** - 模板管理服务
- 位置:`ai-service/app/services/prompt/template_service.py`
- 功能:模板 CRUD、版本管理、发布/回滚、缓存
**VariableResolver** - 变量解析器
- 位置:`ai-service/app/services/prompt/variable_resolver.py`
- 功能:占位符替换、变量验证
**OrchestratorService** - 编排服务
- 位置:`ai-service/app/services/orchestrator.py`
- 功能:在对话生成流程中加载和应用模板
---
## 2. 生效机制详解
### 2.1 模板加载流程12 步 Pipeline 中的第 7 步)
```
用户请求 → Orchestrator._build_system_prompt() → 加载模板 → 解析变量 → 注入行为规则 → 传递给 LLM
```
**详细步骤**
1. **触发时机**:每次对话请求到达时,在 Step 7PromptBuilder执行
2. **加载逻辑**
```python
# orchestrator.py:632-638
template_service = PromptTemplateService(session)
template_version = await template_service.get_published_template(
tenant_id=ctx.tenant_id,
scene="default", # 场景可配置
)
```
3. **缓存机制**
- 首次加载:从数据库查询 `status=published` 的版本
- 后续请求从内存缓存读取TTL 300 秒)
- 缓存失效:发布/回滚操作会自动清除缓存
4. **降级策略**
- 如果没有已发布的模板 → 使用硬编码的 `SYSTEM_PROMPT`
- 如果数据库查询失败 → 使用硬编码的 `SYSTEM_PROMPT`
### 2.2 版本管理机制
**版本状态流转**
```
draft草稿→ published已发布→ archived已归档
```
**发布流程**
```python
# template_service.py:248-287
async def publish_version(tenant_id, template_id, version):
1. 查询模板是否存在租户隔离
2. 将当前 published 版本改为 archived
3. 将目标版本改为 published
4. 清除缓存并预热新版本
5. 记录日志
```
**回滚流程**
```python
# template_service.py:289-298
async def rollback_version(tenant_id, template_id, version):
# 实际上就是调用 publish_version
# 将历史版本重新标记为 published
```
**热更新保证**
- 发布/回滚后立即清除缓存:`self._cache.invalidate(tenant_id, scene)`
- 下次请求会从数据库加载最新版本
- 无需重启服务
### 2.3 租户隔离机制
所有操作都强制进行租户隔离:
```python
# 查询时必须带 tenant_id
stmt = select(PromptTemplate).where(
PromptTemplate.tenant_id == tenant_id,
PromptTemplate.scene == scene,
)
```
不同租户的模板完全独立,互不影响。
---
## 3. 占位符使用详解
### 3.1 占位符语法
**格式**`{{variable_name}}`
**示例**
```
你是 {{persona_name}},当前时间是 {{current_time}}。
你正在为 {{tenant_name}} 提供 {{channel_type}} 渠道的客服服务。
```
### 3.2 内置变量
**VariableResolver** 提供以下内置变量:
| 变量名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `persona_name` | string | "小N" | AI 人设名称 |
| `current_time` | function | 动态生成 | 当前时间格式YYYY-MM-DD HH:MM |
| `channel_type` | string | "default" | 渠道类型wechat/douyin/jd |
| `tenant_name` | string | "平台" | 租户名称 |
| `session_id` | string | "" | 会话 ID |
**动态变量示例**
```python
# variable_resolver.py:15-21
BUILTIN_VARIABLES = {
"persona_name": "小N",
"current_time": lambda: datetime.now().strftime("%Y-%m-%d %H:%M"),
"channel_type": "default",
"tenant_name": "平台",
"session_id": "",
}
```
### 3.3 自定义变量
**定义方式**:在模板的 `variables` 字段中定义
```json
{
"variables": [
{
"name": "company_name",
"default": "XX科技有限公司",
"description": "公司名称"
},
{
"name": "service_hours",
"default": "9:00-18:00",
"description": "服务时间"
}
]
}
```
**使用示例**
```
欢迎咨询 {{company_name}},我们的服务时间是 {{service_hours}}。
```
### 3.4 变量解析流程
```python
# variable_resolver.py:45-75
def resolve(template, variables, extra_context):
1. 构建上下文内置变量 + 自定义变量 + 额外上下文
2. 正则匹配找到所有 {{variable}} 占位符
3. 替换逻辑
- 如果变量存在 替换为值函数则调用
- 如果变量不存在 保留原占位符 + 记录警告
4. 返回解析后的字符串
```
**正则表达式**
```python
VARIABLE_PATTERN = re.compile(r"\{\{(\w+)\}\}")
```
### 3.5 变量优先级
变量解析的优先级(从高到低):
1. **extra_context**(运行时传入的额外上下文)
2. **自定义变量**(模板定义的 variables
3. **实例化时的上下文**VariableResolver 构造函数传入)
4. **内置变量**BUILTIN_VARIABLES
```python
# variable_resolver.py:77-101
def _build_context(variables, extra_context):
context = {}
# 1. 加载内置变量
for key, value in BUILTIN_VARIABLES.items():
if key in self._context:
context[key] = self._context[key] # 实例化时的上下文
else:
context[key] = value # 内置默认值
# 2. 加载自定义变量
if variables:
for var in variables:
context[var["name"]] = var.get("default", "")
# 3. 加载额外上下文(优先级最高)
if extra_context:
context.update(extra_context)
return context
```
---
## 4. 实际使用示例
### 4.1 创建模板(通过 API
```bash
POST /admin/prompt-templates
X-Tenant-Id: szmp@ash@2026
X-API-Key: your_api_key
{
"name": "客服模板 v1",
"scene": "default",
"description": "标准客服对话模板",
"system_instruction": "你是 {{persona_name}},一位专业的客服助手。\n当前时间{{current_time}}\n渠道{{channel_type}}\n\n你需要遵循以下原则\n- 礼貌、专业、耐心\n- 优先使用知识库内容回答\n- 无法回答时建议转人工\n\n公司信息{{company_name}}\n服务时间{{service_hours}}",
"variables": [
{
"name": "company_name",
"default": "XX科技",
"description": "公司名称"
},
{
"name": "service_hours",
"default": "9:00-21:00",
"description": "服务时间"
}
],
"is_default": true
}
```
**响应**
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "客服模板 v1",
"scene": "default",
"description": "标准客服对话模板",
"is_default": true,
"created_at": "2026-02-27T12:00:00Z",
"updated_at": "2026-02-27T12:00:00Z"
}
```
此时模板已创建,但版本状态为 `draft`,尚未生效。
### 4.2 发布模板
```bash
POST /admin/prompt-templates/{tpl_id}/publish
X-Tenant-Id: szmp@ash@2026
X-API-Key: your_api_key
{
"version": 1
}
```
**响应**
```json
{
"success": true,
"message": "Version 1 published successfully"
}
```
**生效时间**:立即生效(缓存已清除)
### 4.3 模板生效后的实际效果
**用户请求**
```json
POST /ai/chat
X-Tenant-Id: szmp@ash@2026
X-API-Key: your_api_key
{
"sessionId": "kf_001_wx123_1708765432000",
"currentMessage": "你好",
"channelType": "wechat"
}
```
**Orchestrator 内部处理**
1. **加载模板**Step 7
```python
# 从缓存或数据库加载已发布的模板
template_version = await template_service.get_published_template(
tenant_id="szmp@ash@2026",
scene="default"
)
# 返回system_instruction + variables
```
2. **解析变量**
```python
resolver = VariableResolver(
channel_type="wechat",
tenant_name="深圳某项目",
session_id="kf_001_wx123_1708765432000"
)
system_prompt = resolver.resolve(
template=template_version.system_instruction,
variables=template_version.variables,
extra_context={"persona_name": "AI助手"}
)
```
3. **解析结果**
```
你是 AI助手一位专业的客服助手。
当前时间2026-02-27 20:18
渠道wechat
你需要遵循以下原则:
- 礼貌、专业、耐心
- 优先使用知识库内容回答
- 无法回答时建议转人工
公司信息XX科技
服务时间9:00-21:00
```
4. **注入行为规则**(如果有):
```python
# 从数据库加载行为规则
rules = await behavior_service.get_enabled_rules(tenant_id)
# 拼接到 system_prompt
behavior_text = "\n".join([f"- {rule}" for rule in rules])
system_prompt += f"\n\n行为约束\n{behavior_text}"
```
5. **传递给 LLM**
```python
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": "你好"}
]
response = await llm_client.generate(messages)
```
### 4.4 更新模板
```bash
PUT /admin/prompt-templates/{tpl_id}
X-Tenant-Id: szmp@ash@2026
X-API-Key: your_api_key
{
"system_instruction": "你是 {{persona_name}},一位专业且友好的客服助手。\n...",
"variables": [
{
"name": "company_name",
"default": "XX科技有限公司", # 修改了默认值
"description": "公司全称"
}
]
}
```
**效果**
- 创建新版本version=2status=draft
- 旧版本version=1仍然是 published 状态
- **模板不会立即生效**,需要发布 version 2
### 4.5 回滚模板
```bash
POST /admin/prompt-templates/{tpl_id}/rollback
X-Tenant-Id: szmp@ash@2026
X-API-Key: your_api_key
{
"version": 1
}
```
**效果**
- version 2 变为 archived
- version 1 重新变为 published
- 缓存清除,立即生效
---
## 5. 高级特性
### 5.1 变量验证
```python
# variable_resolver.py:115-144
def validate_variables(template, defined_variables):
"""
验证模板中的所有变量是否已定义
返回:
{
"valid": True/False,
"missing": ["未定义的变量列表"],
"used_variables": ["模板中使用的所有变量"]
}
"""
```
**使用场景**
- 前端编辑模板时实时验证
- 发布前检查是否有未定义的变量
### 5.2 变量提取
```python
# variable_resolver.py:103-113
def extract_variables(template):
"""
从模板中提取所有变量名
返回:["persona_name", "current_time", "company_name"]
"""
```
**使用场景**
- 前端显示模板使用的变量列表
- 自动生成变量定义表单
### 5.3 缓存策略
**TemplateCache** 实现:
```python
# template_service.py:32-72
class TemplateCache:
def __init__(self, ttl_seconds=300):
self._cache = {} # key: (tenant_id, scene)
self._ttl = 300 # 5 分钟
def get(self, tenant_id, scene):
# 检查是否过期
if time.time() - cached_at < self._ttl:
return version
else:
del self._cache[key] # 自动清理过期缓存
def invalidate(self, tenant_id, scene=None):
# 发布/回滚时清除缓存
```
**缓存失效时机**
- 发布新版本
- 回滚到旧版本
- 更新模板(创建新版本时不清除,因为新版本是 draft
- TTL 过期5 分钟)
---
## 6. 最佳实践
### 6.1 模板设计建议
1. **使用语义化的变量名**
```
✅ {{company_name}}、{{service_hours}}
❌ {{var1}}、{{x}}
```
2. **为所有自定义变量提供默认值**
```json
{
"name": "company_name",
"default": "XX公司", // 必须提供
"description": "公司名称"
}
```
3. **避免在模板中硬编码业务数据**
```
❌ 你是小明,为 XX 公司提供服务
✅ 你是 {{persona_name}},为 {{company_name}} 提供服务
```
4. **合理使用内置变量**
- `current_time`:适用于时间敏感的场景
- `channel_type`:适用于多渠道差异化话术
- `session_id`:适用于调试和追踪
### 6.2 版本管理建议
1. **小步迭代**
- 每次修改创建新版本
- 在测试环境验证后再发布
- 保留历史版本以便回滚
2. **版本命名规范**(在 description 中):
```
v1.0 - 初始版本
v1.1 - 优化语气,增加公司信息变量
v1.2 - 修复变量引用错误
```
3. **灰度发布**(未来扩展):
- 可以为不同租户发布不同版本
- 可以按百分比逐步切换版本
### 6.3 性能优化建议
1. **利用缓存**
- 模板内容很少变化,缓存命中率高
- 5 分钟 TTL 平衡了实时性和性能
2. **避免频繁发布**
- 发布操作会清除缓存
- 建议批量修改后统一发布
3. **监控缓存命中率**
```python
logger.debug(f"Cache hit for template: tenant={tenant_id}, scene={scene}")
```
---
## 7. 故障排查
### 7.1 模板未生效
**症状**:发布后仍使用旧模板或硬编码 SYSTEM_PROMPT
**排查步骤**
1. 检查版本状态:`GET /admin/prompt-templates/{tpl_id}`
- 确认目标版本的 status 是否为 `published`
2. 检查缓存:等待 5 分钟或重启服务
3. 检查日志:
```
[AC-AISVC-51] Cache hit for template: tenant=xxx, scene=default
[AC-AISVC-51] Loaded published template from DB: tenant=xxx, scene=default
[AC-AISVC-51] No published template found, using fallback
```
### 7.2 变量未替换
**症状**:生成的 system_prompt 中仍有 `{{variable}}` 占位符
**排查步骤**
1. 检查变量定义:确认变量在 `variables` 中定义
2. 检查变量名拼写:必须完全匹配(区分大小写)
3. 检查日志:
```
WARNING: Unknown variable in template: xxx
```
### 7.3 租户隔离问题
**症状**:租户 A 看到了租户 B 的模板
**排查步骤**
1. 检查请求头:确认 `X-Tenant-Id` 正确
2. 检查数据库:
```sql
SELECT * FROM prompt_templates WHERE tenant_id = 'xxx';
```
3. 检查代码:所有查询必须带 `tenant_id` 过滤
---
## 8. 总结
### 8.1 核心流程
```
创建模板 → 编辑内容 → 发布版本 → 缓存加载 → 变量解析 → 传递给 LLM
↓ ↓ ↓ ↓ ↓ ↓
draft draft published 内存缓存 占位符替换 生成回复
```
### 8.2 关键特性
-**版本管理**:支持多版本、发布/回滚、历史追溯
-**热更新**:发布后立即生效,无需重启
-**租户隔离**:多租户数据完全隔离
-**缓存优化**5 分钟 TTL减少数据库查询
-**变量系统**:内置变量 + 自定义变量,支持动态值
-**降级策略**:模板不可用时自动回退到硬编码
### 8.3 扩展方向
- 🔄 **场景路由**:根据 intent 或 channel 自动选择不同 scene
- 🔄 **A/B 测试**:同一场景支持多个模板并行测试
- 🔄 **模板继承**:子模板继承父模板并覆盖部分内容
- 🔄 **变量类型**:支持 string/number/boolean/array 等类型
- 🔄 **条件渲染**:支持 `{{#if}}...{{/if}}` 等逻辑控制
---
**文档生成时间**2026-02-27 20:18
**相关代码版本**v0.6.0
**维护状态**:✅ 活跃维护