摘要
把熔岩(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 天无人提及 → 首提流;高分主体在讨论什么 → 加权热榜;单股时间结构 → 多窗」。规则越明确,模型越少「换个工具再试一次」。
示例系统提示片段:
你可以调用熔岩只读工具。规则:
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 新发布日之后,同一条记录的分数可能不同,因此消息面缓存也应在新发布日后失效,或在回答中注明「分数为取数时的当前分」。
一个最小缓存装饰器(示例)
# 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,就能同时降低费用、减少上下文噪声,并让每一条回答都可以追溯。
数据来自熔岩消息面热度监测,仅供研究参考,不构成投资建议。
MCP 接入示例与工具清单见 GitHub:https://github.com/dustinliang666-blip/lava-mcp