系统提示词的分层设计:如何通过静态与动态拼接最大化利用大模型缓存
一、引言
系统提示词是Agent开发中最关键的环节。它定义了Agent系统的整体行为框架,涵盖大模型的语气与态度、需执行的职责范围、可调用的工具接口等。本质上,系统提示词是在无状态的大模型接口之上,注入的一套行为指令和上下文约束——我们通过提示词来引导模型的输出方向,使其按照预期方式回应。
然而,随着Agent能力不断增强,系统提示词往往变得冗长复杂(动辄数千甚至上万tokens)。如果每次请求都完整发送,不仅延迟升高,API调用费用也会迅速攀升。如何在不牺牲提示词丰富度的前提下,降低延迟和成本?答案在于大模型服务端的缓存机制。
二、大模型的缓存机制(KV Cache)
2.1 什么是KV Cache?
大模型在生成文本时,需要“记住”此前所有词对应的Key和Value向量,以便在生成下一个词时计算注意力权重。这些KV向量被暂存于GPU内存中,这就是KV Cache。
2.2 缓存复用原理
如果两次请求的前N个词完全相同,则这前N个词计算出的KV向量也完全相同。服务商会将这段“前缀文本”的KV向量暂存于GPU内存或高速磁盘中。当下次请求的前缀匹配(命中缓存)时,模型直接读取缓存的KV结果,仅需计算新增部分的内容,从而:
- 大幅提升响应速度(跳过重复计算)
- 降低API调用费用(服务商通常对缓存命中的部分减免计费)
2.3 对提示词设计的启示
缓存命中的前提是前缀完全一致。这意味着:
- 固定不变的内容(如角色定义、行为准则)应放在提示词最前面
- 动态变化的内容(如工具列表、用户信息)应放在后面
如果顺序颠倒,动态内容前置会导致每次请求的前缀都不同,缓存将形同虚设。
三、分层设计:静态 + 动态
基于上述缓存原理,系统提示词被自然地划分为两部分:
-
静态提示词:包含Agent的身份定位、核心职责、行为准则和输出格式等固定内容,始终保持不变。置于最前,作为缓存命中的“锚点”。
-
动态提示词:根据上下文实时生成,包括当前可用的工具列表、用户信息、输出风格、对话记忆(Memory)、会话轮次提示等。置于静态部分之后,每次请求可根据实际情况灵活变化。
这种分层设计的核心目的,在于最大化复用服务端的缓存结果,在不牺牲灵活性的前提下显著降低延迟和成本。
四、代码实现:以股市智能分析Agent为例
下面以股市智能分析Agent为例,用完整的伪代码示意提示词的分层加载与拼接流程。
4.1 静态提示词定义
const STATIC_PROMPT = `
# 角色定义
你是一位专业的股市智能分析助手,拥有20年证券市场经验,擅长技术分析、基本面分析和市场情绪研判。
# 核心职责
1. 提供A股、港股、美股市场的行情解读与趋势分析
2. 基于技术指标(MACD、KDJ、RSI、均线系统等)给出买卖信号研判
3. 结合财务数据(PE、PB、ROE、营收增长率等)评估公司价值
4. 识别市场风险(政策风险、流动性风险、行业周期风险)并给出警示
5. 回答用户关于股票、ETF、板块、大盘指数的各类问题
# 行为准则
1. 始终保持客观中立,不承诺收益,不诱导交易,明确提示"投资有风险,决策需谨慎"
2. 所有分析必须基于提供的数据,不凭空编造,不确定时明确告知"信息不足"
3. 技术分析需注明时间周期(日线/周线/月线),基本面分析需注明数据来源和时间
4. 多空观点需给出至少2个支撑理由,并同时提示潜在的反向风险
5. 遇到风险事件(财报季、政策发布、黑天鹅)时主动提高风险提示等级
# 输出格式
【结论】- 一句话总结核心观点
【分析依据】- 分点列出支撑理由(技术面/基本面/消息面)
【风险提示】- 列出主要风险因素
【操作建议】- 仅供参考的建议(买入/持有/观望/卖出,附仓位建议)
【免责声明】- 标准投资风险提示
# 语言风格
专业、严谨、数据驱动,使用金融行业标准术语,避免情绪化表达。对于新手用户,在专业术语后附简要解释。
`;
4.2 动态提示词生成
4.2.1 工具列表格式化
/**
* 格式化工具列表,供模型识别可调用的外部能力
*/
const formatTools = (tools) => {
const toolDescriptions = tools.map(
(tool) =>
`- \`${tool.name}\`: ${tool.description} | 参数: ${JSON.stringify(tool.parameters)}`
);
return `# 可用工具\n${toolDescriptions.join('\n')}`;
};
4.2.2 用户信息与风格适配
/**
* 格式化用户上下文信息
*/
const formatUserInfo = (context) => {
return `
# 用户信息
- 用户等级:${context.level}
- 风险偏好:${context.riskTolerance}
- 持仓情况:${context.positions || '未提供'}
- 关注板块:${context.focusSectors || '未提供'}
`;
};
/**
* 根据用户等级返回对应的输出风格指令
*/
const getStyleByLevel = (level) => {
const styleMap = {
'初级用户': '请用通俗易懂的语言,对每个专业术语附上简要解释,多使用比喻帮助理解。',
'普通用户': '保持专业与通俗的平衡,术语适当解释,侧重结论的可操作性。',
'专业用户': '使用专业术语无需解释,侧重数据细节、技术指标参数和策略逻辑推演。',
'机构用户': '提供深度分析,关注宏观逻辑、估值模型和风险敞口,输出格式可包含表格。',
};
return `# 输出风格\n${styleMap[level] || styleMap['普通用户']}`;
};
4.2.3 对话记忆压缩
为控制上下文长度并保留关键信息,对历史对话进行压缩摘要。此处为短期记忆压缩,实际生产环境可结合向量数据库实现长期记忆检索。
/**
* 从消息中提取股票代码或名称(示例实现,实际应接入股票代码库或NER服务)
*/
const extractStocksFromMessages = (messages) => {
// 示例正则:匹配6位数字(A股代码)或2-4位字母(美股代码),以及常见股票名称
const stockPattern = /([0-9]{6}|[A-Z]{2,4})|(茅台|宁德|腾讯|阿里)/g;
const stocks = new Set();
for (const msg of messages) {
if (!msg.content) continue;
const matches = msg.content.match(stockPattern);
if (matches) {
matches.forEach((m) => stocks.add(m));
}
}
return Array.from(stocks);
};
/**
* 从历史消息中提取已给出的关键结论
*/
const extractKeyJudgments = (messages) => {
const judgments = [];
for (const msg of messages) {
if (!msg.content) continue;
if (msg.role === 'assistant') {
const match = msg.content.match(/【结论】\s*(.+?)(?:\n|$)/);
if (match) {
judgments.push(match[1]);
}
}
}
return judgments.length > 0 ? judgments.join(';') : '无';
};
/**
* 压缩对话历史,提取关键信息供模型参考
*/
const summarizeHistory = (history) => {
const maxTurns = 10;
const recent = history.slice(-maxTurns);
const stocks = extractStocksFromMessages(recent);
const stockSummary = stocks.length > 0 ? stocks.join('、') : '无';
const judgments = extractKeyJudgments(recent);
return `
# 对话记忆
此前讨论过的标的:${stockSummary}
已给出的关键判断:${judgments || '无'}
`;
};
4.2.4 市场状态构建
/**
* 构建当前市场状态信息(时间、交易状态、大盘概况等)
*/
const buildMarketContext = () => {
const now = new Date();
const dateStr = now.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
const dayOfWeek = now.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', weekday: 'long' });
const hour = now.getHours();
// 判断交易状态(A股:工作日 9:30-11:30, 13:00-15:00)
let tradingStatus = '休市';
const isWeekday = now.getDay() >= 1 && now.getDay() <= 5;
if (isWeekday) {
if ((hour >= 9 && now.getMinutes() >= 30 && hour < 11) ||
(hour >= 11 && hour < 11) ||
(hour >= 13 && hour < 15)) {
tradingStatus = '交易中';
} else if (hour < 9 || hour >= 15) {
tradingStatus = '已收盘';
} else {
tradingStatus = '休市中';
}
}
return `
# 当前市场状态
- 当前时间:${dateStr}(${dayOfWeek})
- A股交易状态:${tradingStatus}
- 提示:请基于当前时间判断数据时效性,如非交易时间,历史行情数据仅作参考
`;
};
4.2.5 动态提示词组装
/**
* 组装所有动态模块
*/
const buildDynamicPrompt = (
userContext,
conversationHistory,
tools
) => {
const parts = [];
// 1. 工具列表(放在动态部分最前,因工具列表相对稳定,可提升子前缀缓存命中率)
if (tools && tools.length > 0) {
parts.push(formatTools(tools));
}
// 2. 用户上下文信息
parts.push(formatUserInfo(userContext));
// 3. 根据用户等级调整输出风格
parts.push(getStyleByLevel(userContext.level));
// 4. 对话记忆压缩
if (conversationHistory && conversationHistory.length > 0) {
parts.push(summarizeHistory(conversationHistory));
}
// 5. 多轮对话提示
const turnCount = conversationHistory ? conversationHistory.length : 0;
if (turnCount > 10) {
parts.push(`
# 注意
当前对话已进行多轮,请优先基于此前已讨论的标的和逻辑进行分析,避免重复介绍基础概念。
`);
}
// 6. 当前市场状态
parts.push(buildMarketContext());
return parts.join('\n\n');
};
4.3 系统提示词总拼接
关键决策:静态提示词固定不变,置于最前;动态提示词置于其后。这确保了每次请求的前缀完全一致,从而最大化缓存命中率。
/**
* 构建完整的系统提示词:静态部分 + 动态部分
*/
const buildSystemPrompt = (
userContext,
conversationHistory,
availableTools
) => {
const staticPrompt = STATIC_PROMPT;
const dynamicPrompt = buildDynamicPrompt(
userContext,
conversationHistory,
availableTools
);
// 静态在前 → 前缀固定 → 最大化缓存命中
return `${staticPrompt}\n\n${dynamicPrompt}`;
};
4.4 运行示例
// 注册可用工具
const DEFAULT_TOOLS = [
{
name: 'get_stock_price',
description: '获取实时股价',
parameters: { code: 'string' },
},
{
name: 'get_financials',
description: '获取财务报表',
parameters: { code: 'string', year: 'int' },
},
{
name: 'get_technical_indicators',
description: '计算技术指标',
parameters: { code: 'string', period: 'string' },
},
{
name: 'get_news_sentiment',
description: '获取新闻舆情',
parameters: { keyword: 'string' },
},
{
name: 'get_sector_performance',
description: '获取板块表现排名',
parameters: { market: 'string' },
},
];
// 用户上下文
const userContext = {
level: '普通用户',
riskTolerance: '中',
positions: '茅台、宁德时代',
focusSectors: '新能源、消费',
};
// 对话历史
const history = [
{
role: 'user',
content: '帮我分析一下茅台最近的走势',
},
{
role: 'assistant',
content: '【结论】茅台近期处于震荡整理阶段,多空分歧加大。\n【分析依据】1. 技术面:日线MACD金叉但量能不足...',
},
];
// 构建完整提示词
const fullPrompt = buildSystemPrompt(
userContext,
history,
DEFAULT_TOOLS
);
console.log(fullPrompt);
五、总结与扩展
以上示例以股市智能分析Agent为场景,展示了系统提示词分层设计的基本思路:
| 设计要点 | 说明 |
|---|---|
| 静态前置 | 固定内容置于最前,确保前缀一致,最大化缓存命中 |
| 动态后置 | 可变内容放在后面,保持灵活性 |
| 记忆压缩 | 对历史对话提取摘要,控制上下文长度 |
| 风格适配 | 根据用户等级动态调整输出风格 |
在实际生产环境中,提示词的构成往往更加复杂——例如从项目中加载agent.md作为基础人格设定,通过配置文件管理大模型参数,或引入SubAgent协同机制和Hook回调来实现更细粒度的行为控制。
本文侧重于核心的分层设计与缓存优化策略,这些扩展模块的加载与组织方式,可以在后续的Agent框架设计中进一步探讨。核心原则始终不变:不变的放前面,可变的放后面,让缓存为你省时省钱。
文章为个人加AI协同完成,我提供基础加思想,AI负责优化润色,受限于个人水平,文章中若有理解不当之处,恳请各位读者批评指正,不胜感激。
关于我
7年前端开发经验
借此专栏,我希望系统记录自己在 AI Agent 开发领域的思考与实践,以此为媒,寻找下一份工作机会。
📍 坐标:成都
如有合适的岗位或合作机会,欢迎各位大佬推荐或联系,不胜感激!