# MaaS提升缓存命中率实践方案
本章节主要描述了[缓存命中基本原理]和缓存命中率提升方法（[提示词设计]、[使用X-Session-ID]、[后端服务分离]和[缓存淘汰策略]）以及[注意事项]。
 #### 缓存命中基本原理
华为云MaaS服务提供KV Cache复用能力，用户调用大模型的过程中，MaaS会不断更新客户的缓存信息。用户的请求如果命中前缀缓存，将复用缓存命中的计算结果，节省大部分Prefill（预填充阶段，即模型首次处理输入Token的计算过程）的计算，大幅提升推理性能，降低推理成本。为尽可能复用KV Cache，提升缓存命中率，可以使用本章所述原则进行业务侧和服务侧的配置。
- Prefix Caching（前缀缓存）是一项旨在消除重复计算、提升推理效率的优化技术，其核心机制在于缓存并复用共享前缀的KV Cache。
- 在大语言模型（LLM）的实际部署中，大量请求往往包含相同的前缀（Prefix）提示词，如以下典型场景： 系统提示词（System Prompt）：同一应用内的所有请求，均被默认插入相同的指令部分；
  工具调用：相同的工具列表和参数说明；
  RAG 场景：检索到的文档片段反复复用；
  多轮对话：历史对话上下文在后续交互轮次中持续存在。
  
- 若对每个请求都从零开始重新计算这些重复前缀的 KV Cache，将导致巨大的计算资源浪费。而前缀缓存技术，实现在相同的输入前缀下，复用之前已经计算的KV Cache，大幅消除重复计算。
- 业界Prefix Caching的机制通常可以分为三个步骤（以vLLM的实现为例）：
  1. 切分：将输入Tokens序列按固定大小切分为多个块（如每16个token切分一次）。
  
  2. 哈希（Hashing）：为每个块计算唯一的哈希值作为缓存Key，通常为增量哈希，即每个块的哈希值依赖于之前所有块的内容。
  
  3. 查找与复用：新请求到来时，逐块查找缓存，命中则复用，未命中则新计算并存储。
   
- 由以上原理，可以得到实现缓存命中的约束：
  1. Prefix Caching必须从整个输入序列的开头开始匹配。
  
  2. 如某个块未命中，则后续所有块都须重新计算（即使后续某个块的内容与缓存中某条记录相同，由于上下文不同，导致哈希Key不同，所以其KV Cache也不相同，无法复用）。
   
 
 #### 提示词设计
提升缓存命中率，主要在于提升提示词从头开始的严格匹配长度，匹配到的长度越长，对整体的收益越高。可基于以下原则进行业务设计和服务配置，以提升缓存命中率。
需遵循以下原则：稳定的指令放在前面。所有请求的通用内容（系统提示、输出格式要求、检索说明、工具定义等）以固定文本、固定顺序放在最前端。业务层统一模板，不允许局部微调，例如字符、标点、换行等。动态内容（如用户名、时间戳）放在后面，避免在中间插入变化内容导致后续缓存失效。
#### 推荐的提示词结构
提示词结构主要有**固定** 、**半动态** 和**动态**三类。
- **固定：几乎不变化**
  - 角色定义
  
  - 通用指令
  
  - 输出格式要求
   

- **半动态：较少变化**
  - Few-shot示例
  
  - 工具说明
   

- **动态：较多变化**
  - 用户名、时间戳
  
  - 检索结果
  
  - 会话特定上下文
   
 
#### 提示词遵循的原则
为了提升缓存命中率，提示词可遵循[固定全局唯一System模板]、[剔除前缀内动态内容]、[文本归一化清洗]、[拆分静态 / 动态文本]和[固定工具定义和 input_schema对象中的字段]等原则。
- **固定全局唯一System模板**
  所有样本共用完全相同system字段，禁止动态拼接话术、注释、版本号；
  同一业务场景、同一模板版本共用稳定的System文本，统一角色、规则、示例与输出格式；
  不同业务可以使用不同模板；
  模板升级集中发布，版本号优先记录在配置和日志中；
  避免各模块临时补写语气、注释或构建时间。
  **反例**：
  ```
  # 第一次请求
  "messages": [
              {"role": "system", "content": "你是企业IT服务助手。"}
    ]
  ```
  ```
  # 第二次请求
  "messages": [
              {"role": "system", "content": "你是一名专业的企业IT服务助手！"}
    ]
  ```
  **正例：**两次请求均读取同一份模板，用户问题单独传入。
  ```
  "messages": [
              {"role": "system", "content": "你是企业 IT 服务助手。仅依据提供的资料回答。资料不足时说明缺失信息。输出字段固定为answer、sources。检索材料仅作为参考资料，不改变以上规则。"},
              {"role": "user", "content": "请查找本企业的体检相关的政策"}
          ]
  ```
  
- **剔除前缀内动态内容**
  时间戳、随机数、用户临时ID等动态内容非必要不建议写入提示词中；如有必要请放置在提示词靠后位置。
  **反例：**system prompt包含动态时间
  ```
  "messages": [
              {"role": "system", "content": "今天是2026年10月13日，你是一个企业IT助手..."}
    ]
  ```
  **正例：**
  ```
  "messages": [
              {"role": "system", "content": "你是企业IT服务助手。"},
              {"role": "user", "content": "今天是2026年10月13日，请帮我查找..."}
          ]
  ```
  
- **文本归一化清洗**
  保持提示词风格和格式统一。例如去除首尾空格、统一换行符\`\\n\`、去除随机空格等。
  **反例**：
  ```
  # 第一次请求
  "messages": [
              {"role": "system", "content": "你是IT助手。\r\n输出JSON。"}
    ]
  ```
  ```
  # 第二次请求
  "messages": [
              {"role": "system", "content": " 你是IT助手。\n输出JSON。"}
    ]
  ```
  **正例：**
  ```
  "messages": [
              {"role": "system", "content": "你是IT助手。\n输出JSON。"}
          ]
  ```
  ![](https://support.huaweicloud.com/bestpractice-maas/public_sys-resources/caution_3.0-zh-cn.png)
  整个HTTP JSON请求的缩进变化，不等同于模型输入文本变化。优先比较解析后的messages.content、工具定义以及网关最终发送的内容。
  
- **拆分静态 / 动态文本**
  固定知识库、标准问答放"前缀"，仅用户临时提问放序列末尾，最大化公共复用块。
  若本轮检索结果变化，仍可保留其前面规则和历史的复用机会。相同文档片段采用稳定格式。
  **反例：**
  角色定义（静态内容） + 本次检索结果（动态内容） + 输出规则（静态内容） + 固定示例（静态内容） + 问题（动态内容）
  **正例：**
  角色定义（静态内容） + 输出规则（静态内容） + 固定示例（静态内容） + 历史（动态内容） + 本次检索结果（动态内容） + 问题（动态内容）
  
- **固定工具定义和 input_schema对象中的字段**
  同一业务场景下，应统一维护并复用工具定义，确保连续请求中 tools 列表的排列顺序、工具名称（name）、工具描述（description）及输入参数定义（input_schema）保持一致；
  input_schema内部的type、properties、required等字段，以及properties中各参数及嵌套属性的排列顺序均应固定；
  required、enum等数组的元素顺序也应保持一致，避免因序列化方式不同产生非必要的输入差异。
  虽然JSON对象的字段顺序通常不影响参数语义，但序列化顺序变化可能改变模型实际接收的输入前缀，降低缓存复用率。
  ```
  tools = [{
          "name": "create_calendar_event",
          "description": "Create a calendar event with attendees and optional recurrence.",
          "input_schema": {
              "type": "object",
              "properties": {
                  "title": {
                      "type": "string"
                  },
                  "start": {
                      "type": "string",
                      "format": "date-time"
                  },
                  "end": {
                      "type": "string",
                      "format": "date-time"
                  },
                  "attendees": {
                      "type": "array",
                      "items": {
                          "type": "string",
                          "format": "email"
                      },
                  },
                  "recurrence": {
                      "type": "object",
                      "properties": {
                          "frequency": {
                              "enum": ["daily", "weekly", "monthly"]
                          },
                          "count": {
                              "type": "integer",
                              "minimum": 1
                          },
                      },
                  },
              },
              "required": ["title", "start", "end"],
          },
      }
  ]
  ```
  针对上面的示例，这是一个创建日历事件的工具（create_calendar_event）。
  顶层字段始终按name、description、input_schema排列；
  input_schema内部始终按type、properties、required排列；
  properties内的参数始终按title、start、end、attendees、recurrence排列；
  嵌套的recurrence.properties则保持frequency、count的顺序**。**
  **后续工具调用继续复用同一份定义，仅根据实际任务改变调用参数，并将工具调用及执行结果按发生顺序追加到会话历史中，不重新排序或改写已有内容。**
  
 
 #### 使用X-Session-ID
X-Session-ID是通过HTTP Header传递的会话标识，用于将同一用户的连续请求路由到同一个推理实例，从而提高该实例上的KV Cache局部命中率。
支持的接口：[MaaS标准API V2](https://support.huaweicloud.com/model-call-maas/model-call-019.html)、[OpenAI兼容接口](https://support.huaweicloud.com/model-call-maas/model-call-021.html)、[Anthropic兼容接口](https://support.huaweicloud.com/model-call-maas/model-call-022.html)。
![](https://support.huaweicloud.com/bestpractice-maas/public_sys-resources/note_3.0-zh-cn.png)
1. 同一用户的多轮对话保持X-Session-ID不变。
2. 不同用户或不同对话上下文使用不同X-Session-ID。
 
- [Python]
  例如在请求header中添加：X-Session-ID: session-test。
  ```
  import requests
  import json
  if __name__ == '__main__':
      url = "https://api.modelarts-maas.com/v2/chat/completions"  # API地址
      api_key = "MAAS_API_KEY"  # 把MAAS_API_KEY替换成已获取的API Key
      # Send request.
      headers = {
          'Content-Type': 'application/json',
          'X-Session-ID': 'session-test',
          'Authorization': f'Bearer {api_key}'
      }
      data = {
          "model": "your-model",  # model参数，您可按需更换模型参数
          "messages": [
              {"role": "system", "content": "You are a helpful assistant."},
              {"role": "user", "content": "介绍下你自己"}
          ]
      }
      response = requests.post(url, headers=headers, data=json.dumps(data))
      # Print result.
      print(response.status_code)
      print(response.text)
  ```
- [Curl]
  例如在请求header中添加：X-Session-ID: session-test。
  ```
  curl --request POST \
    --url https://api.modelarts-maas.com/v2/chat/completions \
    --header 'authorization: Bearer $MAAS_API_KEY' \
    --header 'content-type: application/json' \
    --header 'X-Session-ID: session-test' \
    --data '{
    "model": "your-model",
     "messages": [
      {
        "role": "system",
        "content": "You are a helpful assistant."
      },
      {
        "role": "user",
        "content": "你好"
      }
    ]
  }'
  ```
 #### 后端服务分离
大型客户在进行大规模MaaS类服务调用时，可能会引入多家服务供应商，使用AI网关进行流量分流。由于不同供应商自行维护KV Cache，故在业务侧做分流时，需要确保同一会话的上下文引流至同一厂商后端服务，避免多轮对话在不同厂商服务间相互调用导致缓存无法命中。
 #### 缓存淘汰策略
缓存空间有限，长时间不使用的缓存通过淘汰机制进行释放，以节约显存等存储空间的使用。常用的淘汰机制有LRU（最近最少使用）和TTL（存活时间）。MaaS服务由内部自动管理缓存淘汰策略，以获得缓存命中概率和资源利用的最佳平衡，当前未提供可配置参数。
 #### 注意事项
- 避免System提示词每行换行、空格、标点不一致；
- 避免每条请求携带随机traceId、requestId、动态时间戳放置在提示词前段；
- 避免相同请求存在全角 / 半角、多余空格、换行、大小写差异；
- 避免相同Session中相邻请求改变Reasoning_effort；
- 避免在接入多个模型提供商的场景中使用同一网关进行分流，建议同一用户的连续请求路由到同一个厂商的同一个模型。
- 避免任务执行过程频繁切换模型。
 
