# AI Agent 调用熔岩 MCP 的提示词与结果缓存清单：按更新节奏设 TTL 的 24 项检查


## 摘要

把熔岩（Lava）远程 MCP 接进 AI Agent 后，最常见的浪费不是调用失败，而是「同一个问题在一轮对话里被查了三次」：模型换个说法重新调用 `get_market_hot_stocks`，或者对同一只股票先后用简称和代码各查一次 `get_stock_news_heat`。熔岩的工具全部只读，成功调用按次计费，数据又各有更新节奏，因此非常适合在 Agent 侧做一层结果缓存，并在提示词里约束调用方式。本文以检查清单的形式给出 24 项要点，分为接入、缓存键、TTL、提示词、错误与计费、审计六组。TTL 数值均为示例，以你的业务节奏为准。

## 一、接入前提（4 项）

- [ ] **1. 端点与传输。** 远程 MCP 路径为 `/subscribe/v1/mcp`，传输为 Streamable HTTP。HTTP 版本的路径与 MCP 工具一一对应，缓存层可以同时服务两种调用。
- [ ] **2. 鉴权放在请求头。** 使用 `Authorization: Bearer <API Key>` 或 `X-API-Key`；也支持 OAuth 2.1（PKCE S256 + 动态注册）。Key 不放进网址，不写进提示词，也不进入缓存值。
- [ ] **3. 工具清单固定版本。** 启动时调用一次 `tools/list`，把工具名与 `inputSchema` 的哈希记入日志。参数演进时缓存键规则需要同步调整。
- [ ] **4. 明确只读边界。** 熔岩工具只返回结构化数字与短摘要，提示词里写明「不生成操作建议」，缓存层也不保存模型的二次加工结论，只保存工具原始返回。

## 二、缓存键怎么设计（4 项）

- [ ] **5. 键 = 工具名 + 规范化参数。** 参数按名称排序，默认值显式补齐。例如 `get_market_hot_stocks` 不传 `window` 与传 `window=24h` 应命中同一个键。
- [ ] **6. 标的统一成代码。** `stock_name` 与 `ts_code` 二选一的工具（`get_stock_news_heat`、`get_stock_heat_windows`、`get_stock_concept_heat` 等），缓存前先把简称映射成代码，避免一只股票两份缓存。映射表本身可以长期缓存。
- [ ] **7. 概念名保持原样。** `get_concept_stock_heat` 的 `concept` 精确匹配、不超过 30 字符，缓存键里不要做全半角或空格「清洗」，否则会把两个实际不同的请求合并成一个。
- [ ] **8. 市场范围进键。** `market=ashare` 与 `market=all` 合并键不同（前者按代码、后者按简称），必须作为独立缓存条目。

## 三、按数据板块设 TTL（4 项）

TTL 的上限由数据更新节奏决定：比更新更频繁地刷新只会得到重复内容。文档给出的节奏是：个股热榜约 1 分钟，概念热榜与新闻点名约 2 分钟，概念成分约 10 分钟。其余接口没有公开周期，下表按数据性质给出示例值。

| 熔岩数据板块 | MCP 工具 | 公开更新节奏 | 示例 TTL |
| --- | --- | --- | --- |
| 全市场热度：个股热榜 | `get_market_hot_stocks` | 约 1 分钟 | 盘中 2～5 分钟 |
| 全市场热度：加速榜、首提流 | `get_accelerating_hot_stocks`、`get_first_mention_stocks` | 未公开 | 盘中 5～10 分钟 |
| 概念板块：概念热榜 | `get_market_hot_concepts` | 约 2 分钟 | 5 分钟 |
| 概念板块：概念成分 | `get_concept_stock_heat` | 约 10 分钟 | 10～30 分钟 |
| 新闻点名 | `get_news_stock_hits` | 约 2 分钟 | 5 分钟 |
| 个股热度：消息面、多窗 | `get_stock_news_heat`、`get_stock_heat_windows` | 未公开 | 10 分钟 |
| 个股热度：日频面板 | `get_stock_heat_panel` | 自然日聚合 | 已结束的日期长期有效；含今天则 1 小时 |
| 机构/大V：PIT、日期列表 | `get_agency_pit`、`list_agency_pit_dates` | 按发布日 | 已发布截面长期有效 |
| 账户 | `get_account` | 实时，不计费 | 不缓存或 1 分钟 |

- [ ] **9. 区分「会变」与「已定格」。** 日频面板中已经结束的日期、`/agencies/pit` 的已发布截面，是可以长期缓存的历史；含今天的数据必须短 TTL。
- [ ] **10. `as_of_date` 的特殊处理。** `get_weighted_hot_stocks` 传入 `as_of_date` 时消息截至当天 15:00（Asia/Shanghai）、分数走 PIT。若 `as_of_date` 是今天且当前早于 15:00，截面尚未定格，只能短缓存；过了截稿时间才可以长期保存。
- [ ] **11. 盘中盘后两套 TTL。** 消息面盘后也在更新，但节奏较慢，盘后 TTL 可放大为盘中的 3～6 倍（示例）。
- [ ] **12. 交易日历由你提供。** 熔岩接口按自然日或滚动窗口统计，是否为交易时段要用自有日历判断，不要让模型自行推断。

## 四、提示词约束（4 项）

- [ ] **13. 先查缓存再调用。** 在工具描述或系统提示中写明：同一轮对话内，同一工具与同一参数只调用一次，结果复用。
- [ ] **14. 回答必须带时间与参数。** 每次引用数字都带 `window` 或 `days`、`market`、`min_score`，以及 `meta.server_time`。命中缓存时，回答中的时间应是缓存条目的 `server_time`，而不是当前时间。
- [ ] **15. 禁止模型补写字段。** 返回里没有的字段不得猜测；`summary` 不超过 120 字、`detail` 不超过 600 字，都是摘要而不是原文；新闻点名不含标题与正文；机构/大V 只用 `agency_id` 指代。
- [ ] **16. 工具选择写成规则表。** 例如「现在谁最热 → 个股热榜；谁变化最快 → 加速榜；此前 7 天无人提及 → 首提流；高分主体在讨论什么 → 加权热榜；单股时间结构 → 多窗」。规则越明确，模型越少「换个工具再试一次」。

示例系统提示片段：

```text
你可以调用熔岩只读工具。规则：
1) 调用前检查 cache[(tool, normalized_args)]，命中且未过期则直接使用。
2) stock_name 先映射为 ts_code 再作为缓存键。
3) 回答格式：结论(高/中/低) + 数字 + 参数 + server_time。
4) 机构/大V 只写 agency_id；不推测真名；不补写摘要外的内容。
5) 只描述统计结果，不给出任何操作建议。
```

## 五、错误、限流与计费（4 项）

- [ ] **17. 只缓存成功结果。** `ok=false` 的响应不进缓存，但要记录 `code`，避免模型在同一错误上无限重试。
- [ ] **18. 429 按 `Retry-After` 等待。** `RATE_LIMITED` 时按响应头等待后最多重试一次；`DAILY_QUOTA_EXCEEDED` 当日停止调用，切换为只读缓存并在回答中说明。
- [ ] **19. 402 余额不足如实转告。** `INSUFFICIENT_BALANCE` 时告诉用户到熔岩「AI数据服务」→「概览与充值」处理，不重试。
- [ ] **20. 用响应头核算费用。** 成功调用每次 0.005 元，非 2xx 与 `get_account` 不计费；响应头 `X-Billing-Charged-Yuan` 与 `X-Balance-Yuan` 可直接写入缓存条目的元数据，命中缓存即节省一次计费。

## 六、审计与复现（4 项）

- [ ] **21. 缓存条目带 `request_id`。** 保存 `meta.request_id` 与 `meta.server_time`，任何一条回答都能追溯到具体的那次调用。
- [ ] **22. 命中率与节省额入日志。** 每日统计「命中次数 × 单价」作为节省估算。示例：一天 600 次工具请求、命中 380 次，按 0.005 元计节省约 1.9 元（示例数字），更重要的是减少了重复数据进入上下文。
- [ ] **23. 上下文只放必要字段。** 榜单返回最多 200 行，放进模型上下文前按问题裁剪列和行，原始返回留在缓存里，需要时再取。
- [ ] **24. 定期回归。** 用固定问题集比较「缓存命中回答」与「实时调用回答」的参数和字段是否一致，发现工具参数演进时同步更新键规则。

## 缓存失效的触发条件

TTL 到期只是最基本的失效方式。下面几种情况也应主动清除相关条目，否则缓存会把旧口径带进新回答：

| 触发条件 | 影响范围 | 处理（示例） |
| --- | --- | --- |
| `tools/list` 哈希变化 | 参数或字段可能演进 | 清空对应工具的全部条目，重新核对默认值 |
| 自然日切换（Asia/Shanghai 0 点） | 含「今天」的面板、`days` 类回看 | 清除包含前一日为终点的条目 |
| 当天 15:00 截稿 | `as_of_date` 为今天的加权热榜 | 15:00 前的短缓存作废，截稿后重新取一次再长期保存 |
| 机构/大V 新发布日 | `get_agency_ranking` 当前排行 | 用 `list_agency_pit_dates` 发现新日期后刷新排行缓存 |
| 用户显式要求刷新 | 单个问题 | 跳过缓存调用一次，并用新结果覆盖 |

一个容易忽略的点：`get_stock_news_heat` 返回的意见记录里，`agency_score` 是主体的当前分。机构/大V 新发布日之后，同一条记录的分数可能不同，因此消息面缓存也应在新发布日后失效，或在回答中注明「分数为取数时的当前分」。

## 一个最小缓存装饰器（示例）

```python
# lava_cache.py —— 示例，地址与令牌为占位
import json, time, hashlib

TTL = {"get_market_hot_stocks": 180, "get_market_hot_concepts": 300,
       "get_concept_stock_heat": 900, "get_news_stock_hits": 300,
       "get_stock_news_heat": 600, "get_stock_heat_windows": 600}
DEFAULTS = {"get_market_hot_stocks": {"window": "24h", "market": "ashare", "limit": 50}}
_store = {}

def key(tool, args):
    a = {**DEFAULTS.get(tool, {}), **args}
    raw = tool + json.dumps(a, sort_keys=True, ensure_ascii=False)
    return hashlib.sha256(raw.encode()).hexdigest()

def cached_call(tool, args, call_fn):
    k, now = key(tool, args), time.time()
    hit = _store.get(k)
    if hit and now - hit["t"] < TTL.get(tool, 300):
        return hit["resp"], True
    resp = call_fn(tool, args)          # call_fn 负责实际 MCP/HTTP 调用
    if resp.get("ok"):
        _store[k] = {"t": now, "resp": resp}
    return resp, False
```

这段代码只演示键规范化与按工具区分 TTL，生产环境还需要持久化存储、并发锁与盘中盘后切换。

## FAQ

**Q：缓存会不会让 Agent 给出过时的热度？**  
A：只要 TTL 不长于业务可接受的延迟，并且回答里展示的是缓存条目的 `server_time`，读者就能判断数据时点。需要最新数字时，允许用户显式要求「刷新」，跳过缓存。

**Q：MCP 和 HTTP 两条通道能共用一份缓存吗？**  
A：可以。两者对应同一套数据与参数，缓存键用工具名或路径统一映射即可；鉴权信息不进入键和值。

**Q：日频面板可以缓存多久？**  
A：已经结束的自然日可以长期保存；包含今天的那一行是未完结日，应短 TTL 或剔除。注意 `weighted_heat` 是当前机构分口径，若机构分后续更新，重新拉取的历史 `weighted_heat` 可能变化，缓存时要标注取数日期。

## 结论

熔岩 MCP 的只读、按次计费与分板块更新节奏，决定了 Agent 侧缓存是性价比最高的工程优化。按「工具名 + 规范化参数」设计键，按数据板块的更新节奏设 TTL，区分已定格的历史与仍在变化的今天，再用提示词约束模型复用结果、带上参数与 `server_time`，就能同时降低费用、减少上下文噪声，并让每一条回答都可以追溯。

数据来自熔岩消息面热度监测，仅供研究参考，不构成投资建议。

只读监测与字段说明见：https://lava.ai-dolphin.com/stock-heat-monitoring

