当 AI 问你"想买点英伟达"时,你知道它有多狼狈吗

一个真实场景:你在 Cursor 里随口说"帮我看看英伟达最近的走势",AI 助手大概率会回复一段关于如何申请 API Key 的废话,或者直接告诉你"我无法获取实时数据"。

这背后不是一个能力问题,而是一个协议问题——AI 不知道如何将"英伟达的走势"这个模糊意图,转化为结构化的行情查询请求。它没有统一的方式理解:查询哪个标的?用什么时间周期?返回什么字段?

这就是 SKILL 协议要解决的核心问题。


一、为什么 AI 需要行情数据的"翻译层"

通用大模型在对话上有惊人表现,但在金融市场数据面前,它面临三重困境:

语义歧义。用户说"英伟达最近怎么样",AI 需要推断是日线还是小时线?"最近"是指近一周、近一月、还是年初至今?这些对人类交易员是常识,对 AI 是零样本推理。

数据源异构。不同数据源有完全不同的命名体系和接口设计。Yahoo Finance 用 NVDA,TickDB 用 NVDA.US,Polygon 用 Nvidia,Binance 用 BTCUSDT。AI 如果没有显式指引,极可能构造出错误的标的代码。

上下文断裂。一次对话中的多轮查询之间存在隐式关联。用户问完"AAPL 当前价格",再问"它比昨天涨了多少"——AI 必须记住前序查询结果,或者具备回溯上下文的能力。通用模型对此没有标准机制。

SKILL 协议的本质是一份合约:它告诉 AI,当用户提到某个市场数据意图时,应该调用哪个函数、传入什么参数、从哪里读取答案。


二、skill.md:一份给 AI 看的接口说明书

2.1 文件定位与基本结构

http://skill.md/ 是一个遵循固定 Schema 的 Markdown 文件,托管在 TickDB 官网或指定 CDN 路径上。AI 助手在初始化阶段(或按需拉取)读取这份文件,将其内化为"对 TickDB 行情数据的理解能力"。

文件结构分为四个顶级区块:

# SKILL Specification

## identity          # 技能身份
## instructions      # 行为指令(最重要)
## functions         # 函数定义(AI 可调用的能力)
## examples          # 对话样例

每一区块的内容量和精度,直接决定 AI 调用 TickDB 数据时的准确率。

2.2 identity:声明身份与能力边界

## identity

name: tickdb-market-data
version: "1.0"
description: >
  TickDB Market Data SKILL provides real-time and historical 
  market data for global equities, digital currencies, futures, 
  forex, and commodities. Designed for quantitative trading and 
  investment research.

这一区块的作用是让 AI 在多 SKILL 协作场景下做选择:当用户安装了多个数据 SKILL 时,AI 需要根据 description 判断哪个 SKILL 更适合处理本次请求。

2.3 instructions:AI 的行为宪法

instructions 是整个协议中最容易被低估的部分。它不是简单告诉 AI"你能查什么",而是告诉 AI"在什么场景下用什么方式查、查到后怎么返回"。

## instructions

## context_window
You maintain full conversation context. When the user asks 
follow-up questions like "对比一下" or "再看其他标的", you 
must reference previous query parameters (symbol, interval, 
etc.) rather than requiring the user to repeat them.

## parameter_convention
- All symbol parameters MUST use the exchange-suffixed format:
  `{TICKER}.{EXCHANGE}` — e.g., `AAPL.US`, `BTC.USDT`
- Supported exchanges: US (us equity), HK (hk equity), 
  Binance (digital currency), etc.
- Interval format: `1m`, `5m`, `15m`, `30m`, `1h`, `4h`, 
  `1d`, `1w`
- All timestamps use UTC

## response_format
When returning data, you MUST:
1. Format numbers with appropriate precision (price → 2dp, 
   volume → integer)
2. Include a brief market interpretation (e.g., "currently 
   in overbought territory")
3. Attach a one-line risk note for leveraged instruments

## error_handling
When encountering API errors:
- code 1001/1002: Prompt user to check API Key configuration
- code 2002: Inform user the symbol is not supported, suggest 
  alternatives
- code 3001: Wait and retry silently (do not surface to user)

注意 context_window 这一条——它解决了上文提到的"多轮对话上下文断裂"问题。AI 被显式要求记住前序参数,而非每次重新解析。

2.4 functions:Function Calling 的定义规范

functions 区块是整个 SKILL 协议的核心,直接决定了 AI 能够执行哪些操作。每个函数遵循 OpenAI Function Calling 的 Schema,同时扩展了 TickDB 特有的参数语义。

## functions

namespace functions {

// 获取单个标的实时行情快照
function get_realtime_quote: (_: {
// 标的代码(必填)
symbol: string,
// 可选返回字段列表,不填则返回全部
fields?: string[],
}) => any;

// 获取 K 线数据(历史回测)
function get_kline: (_: {
// 标的代码(必填)
symbol: string,
// K 线周期(必填):1m | 5m | 15m | 30m | 1h | 4h | 1d | 1w
interval: string,
// 起始时间(必填):ISO 8601 UTC
start_time: string,
// 结束时间(必填):ISO 8601 UTC
end_time: string,
// 每页数据量上限,最大 1000
limit?: number,
}) => any;

// 实时 WebSocket 订阅
function subscribe_realtime: (_: {
// 标的代码
symbol: string,
// 订阅频道:quote | kline | depth | trades
channel: string,
// K 线周期(仅 kline 频道需填)
interval?: string,
}) => any;

}

函数定义中的每个字段都附带类型和约束说明。interval 参数限定为枚举值而非自由文本,这是避免 AI 构造非法参数的关键——模型只能从 1m|5m|...|1w 中选择,而非随意生成。


三、AI 如何解析 skill.md

3.1 解析链路

当 AI 助手(如 Claude Desktop、Cursor AI、GPTs)安装了 TickDB SKILL 后,解析链路如下:

用户输入 → AI 意图识别 → 匹配函数签名 → 构造参数 → 调用函数 → 格式化结果 → 返回用户

第一步:意图映射。AI 将用户自然语言映射到 functions 中定义的某个函数。映射依据包括:函数 description、用户查询的动词("查"→quote,"看历史"→kline,"跟踪"→subscribe)和名词("价格"→quote,"走势"→kline)。

第二步:参数提取。AI 从用户查询中提取各字段值。例如用户说"看看苹果最近一周的日线",AI 识别出:

  • symbol = "AAPL.US"(通过标的规范化规则映射)
  • interval = "1d"(通过"日线"关键词匹配)
  • start_time = 当前日期减 7 天
  • end_time = 当前日期

第三步:约束校验。AI 在构造参数后做自检:symbol 是否包含交易所后缀?interval 是否在枚举范围内?时间范围是否超过单次查询上限?这一步的准确性高度依赖 instructions 中的约束说明是否充分。

第四步:结果格式化。API 返回原始 JSON,AI 根据 instructions.response_format 将其转化为人类可读的描述文本,同时附上市场解读。

3.2 解析质量的决定因素

解析准确率取决于三个变量:

变量 影响 优化方向
instructions 清晰度 AI 对模糊意图的推断准确率 补充边界场景说明(如"用户说'走势'默认 1d,除非另有说明")
functions 参数约束 非法参数率 使用枚举而非自由文本;必填/可选严格区分
examples 样例数量 零样本推理置信度 覆盖高频场景和易混淆场景(如"AAPL"和"AAPL.US"的歧义处理)

四、生产级 SKILL 集成代码

以下示例展示如何在 Python 环境中实现 SKILL 协议的核心调用逻辑。代码包含完整的错误处理、限频处理和重试机制。

import os
import json
import time
import random
import requests
from datetime import datetime, timedelta, timezone


class TickDBSKILL:
    """
    TickDB Market Data SKILL 协议实现
    支持:实时行情、历史 K 线、WebSocket 订阅
    """

    BASE_URL = "https://api.tickdb.ai/v1/market"
    WS_URL = "wss://stream.tickdb.ai/v1/market"

    def __init__(self, api_key: str = None):
        self.api_key = api_key or os.environ.get("TICKDB_API_KEY")
        if not self.api_key:
            raise ValueError("API Key 未配置,请设置 TICKDB_API_KEY 环境变量")

    def _headers(self) -> dict:
        return {"X-API-Key": self.api_key}

    def _validate_symbol(self, symbol: str) -> str:
        """规范化标的代码,确保包含交易所后缀"""
        if "." not in symbol:
            raise ValueError(
                f"标的代码 '{symbol}' 缺少交易所后缀,"
                "请使用格式如 AAPL.US, BTC.USDT"
            )
        return symbol.upper()

    def get_realtime_quote(self, symbol: str, fields: list = None) -> dict:
        """
        获取实时行情快照
        对应 SKILL 函数:get_realtime_quote
        """
        symbol = self._validate_symbol(symbol)
        params = {"symbol": symbol}
        if fields:
            params["fields"] = ",".join(fields)

        response = requests.get(
            f"{self.BASE_URL}/quote",
            headers=self._headers(),
            params=params,
            timeout=(3.05, 10)  # (connect_timeout, read_timeout)
        )
        return self._handle_response(response)

    def get_kline(
        self,
        symbol: str,
        interval: str,
        start_time: str,
        end_time: str,
        limit: int = 1000,
    ) -> dict:
        """
        获取 K 线数据(用于历史回测)
        对应 SKILL 函数:get_kline
        约束:时间范围不超过 500 天(1m 周期)或 10 年(1d 周期)
        """
        valid_intervals = {"1m", "5m", "15m", "30m", "1h", "4h", "1d", "1w"}
        if interval not in valid_intervals:
            raise ValueError(
                f"非法周期 '{interval}',有效值:{valid_intervals}"
            )

        symbol = self._validate_symbol(symbol)
        params = {
            "symbol": symbol,
            "interval": interval,
            "start_time": start_time,
            "end_time": end_time,
            "limit": min(limit, 1000),
        }

        response = requests.get(
            f"{self.BASE_URL}/kline",
            headers=self._headers(),
            params=params,
            timeout=(3.05, 10)
        )
        return self._handle_response(response)

    def _handle_response(self, response: requests.Response) -> dict:
        """
        统一错误处理
        对应 skill.md instructions.error_handling
        """
        try:
            data = response.json()
        except json.JSONDecodeError:
            raise RuntimeError(f"API 返回了无效的 JSON:{response.text[:200]}")

        code = data.get("code", 0)
        if code == 0:
            return data.get("data")

        error_messages = {
            1001: "API Key 无效,请检查 TICKDB_API_KEY 环境变量",
            1002: "API Key 缺失,请先在 tickdb.ai 注册并生成 Key",
            2002: "标的代码不存在,请确认使用正确格式(如 AAPL.US)",
        }

        if code == 3001:
            # ⚠️ 限频触发,静默等待后重试(不向用户暴露)
            retry_after = int(response.headers.get("Retry-After", 5))
            time.sleep(retry_after)
            return None

        message = error_messages.get(code, data.get("message", "未知错误"))
        raise RuntimeError(f"[code:{code}] {message}")


# 使用示例
if __name__ == "__main__":
    skill = TickDBSKILL()

    # 实时行情查询
    quote = skill.get_realtime_quote("AAPL.US")
    print(f"AAPL 当前价格: {quote['close']}")

    # 历史 K 线(近 30 个交易日日线)
    end = datetime.now(timezone.utc)
    start = end - timedelta(days=30)
    klines = skill.get_kline(
        symbol="AAPL.US",
        interval="1d",
        start_time=start.isoformat(),
        end_time=end.isoformat(),
    )
    print(f"获取到 {len(klines)} 根 K 线")

⚠️ 工程提醒:上述实现适用于低频查询场景。若需毫秒级实时监控,应将 WebSocket 订阅部分改为 asyncio 异步架构,并添加心跳保活和指数退避重连逻辑。


五、WebSocket 实时订阅的 SKILL 扩展

skill.md 中的 subscribe_realtime 函数定义了一种比轮询更高效的模式——AI 引导用户建立 WebSocket 订阅,让 TickDB 主动推送行情更新,而非反复轮询。

import json
import time
import websocket
import threading
import random


class TickDBWebSocket:
    """
    TickDB WebSocket 实时订阅(SKILL subscribe_realtime)
    包含心跳保活、指数退避重连、限频自适应处理
    """

    MAX_RETRIES = 10
    BASE_DELAY = 1
    MAX_DELAY = 60

    def __init__(self, api_key: str = None, on_message=None):
        self.api_key = api_key or os.environ.get("TICKDB_API_KEY")
        self.on_message = on_message or (lambda msg: print(msg))
        self.ws = None
        self.running = False
        self._retry_count = 0

    def subscribe(self, symbol: str, channel: str, interval: str = None):
        """
        建立 WebSocket 订阅
        channel 选项:quote | kline | depth | trades
        """
        symbol = symbol.upper()
        channel = channel.lower()
        if "." not in symbol:
            raise ValueError("标的代码必须包含交易所后缀,如 AAPL.US")

        self.running = True
        self._connect_and_subscribe(symbol, channel, interval)

    def _connect_and_subscribe(self, symbol, channel, interval):
        """
        带指数退避重连的连接逻辑
        """
        while self.running and self._retry_count < self.MAX_RETRIES:
            try:
                url = (
                    f"wss://stream.tickdb.ai/v1/market?"
                    f"api_key={self.api_key}&symbol={symbol}&channel={channel}"
                )
                if interval:
                    url += f"&interval={interval}"

                self.ws = websocket.WebSocketApp(
                    url,
                    on_message=self._on_message,
                    on_error=self._on_error,
                    on_close=self._on_close,
                )

                # 运行 WebSocket 连接(含心跳)
                self.ws.run_forever(ping_interval=20, ping_timeout=10)
                self._retry_count = 0

            except Exception as e:
                if not self.running:
                    break
                self._retry_count += 1
                delay = min(
                    self.BASE_DELAY * (2 ** self._retry_count) + 
                    random.uniform(0, 1),  # 抖动,避免惊群
                    self.MAX_DELAY
                )
                print(f"[重连] 第 {self._retry_count} 次,{delay:.1f} 秒后重试")
                time.sleep(delay)

    def _on_message(self, ws, message):
        """消息处理:支持限频自适应(code:3001)"""
        try:
            data = json.loads(message)
            code = data.get("code", 0)

            if code == 3001:
                retry_after = int(data.get("retry_after", 5))
                time.sleep(retry_after)
                return

            if code == 0:
                self.on_message(data.get("data"))
            else:
                print(f"[异常] code:{code} {data.get('message')}")

        except json.JSONDecodeError:
            print(f"[解析错误] {message[:100]}")

    def _on_error(self, ws, error):
        print(f"[WebSocket 错误] {error}")

    def _on_close(self, ws, code, reason):
        if self.running:
            print(f"[连接断开] code:{code} reason:{reason}")

    def unsubscribe(self):
        """主动关闭订阅"""
        self.running = False
        if self.ws:
            self.ws.close()


# 使用示例
def handle_price_alert(data):
    symbol = data.get("symbol")
    price = data.get("close")
    change_pct = data.get("change_pct", 0)
    print(f"⚡ {symbol} 当前价 {price},涨跌 {change_pct:+.2f}%")

ws = TickDBWebSocket(on_message=handle_price_alert)
ws.subscribe(symbol="NVDA.US", channel="quote")

⚠️ 高频场景注意:单个 WebSocket 连接可同时订阅多个标的(逗号分隔),但需遵守 TickDB 的连接数限制。建议机构用户使用连接池管理,而非每个标的独立开一个连接。


六、SKILL 协议能力对比

能力维度 通用 AI(无 SKILL) SKILL 增强 AI
标的识别 依赖训练数据,容易出错 协议显式规定 .US/.HK 等格式规范
周期映射 模糊,AI 自由推断 instructions 规定默认行为,减少歧义
多轮对话上下文 需要在 prompt 中手动管理 context_window 条款强制要求 AI 记忆
错误处理 通用的网络错误回复 针对 1001/2002/3001 的定向处理方案
实时数据 完全不支持 WebSocket subscribe_realtime 频道
数据字段解释 数字罗列,无市场解读 response_format 规定附加市场含义
格式规范化 小数位数不统一 协议规定 price → 2dp,volume → integer
风险提示 杠杆类品种自动附加风险说明

七、SKILL 协议的扩展路径

当前 skill.md v1.0 聚焦于行情查询的核心场景。协议设计上预留了三个扩展方向:

多 SKILL 协作。当用户同时安装 TickDB(行情)和另一个新闻 SKILL(事件)时,identity.description 决定了 AI 如何分配任务——"用户问 NVDA 的价格和业绩"由 TickDB 处理价格部分,新闻 SKILL 处理业绩部分。

策略回测联动。在 functions 中增加 run_backtest 函数,AI 可以直接根据用户描述生成回测请求,实现"查询—分析—回测"的单轮对话闭环。

告警规则定义。扩展 subscribe_realtime 支持结构化告警条件的声明式描述,如 AI 接收"苹果跌破 150 块时提醒我",自动翻译为 depth 频道的阈值监控。


结语

SKILL 协议的本质,是将 AI 与金融市场数据之间的"最后一公里"问题,从隐式推理变成显式合约。

skill.md 不是一个营销文档,而是一份给 AI 看的技术规范。它的质量——instructions 的完整性、functions 的约束精度、examples 的覆盖广度——直接决定了 AI 能否可靠地理解用户的行情意图,并构造出正确的查询请求。

对于量化开发者,这意味着 AI 辅助编程的边界从"写代码"扩展到"理解数据";对于 TickDB,这意味着产品能力可以通过 AI 生态自动触达用户,而无需每个用户都手动阅读 API 文档。

下一步行动

如果你想直接体验 TickDB SKILL
在 AI 助手中搜索并安装 tickdb-market-data SKILL,然后尝试用自然语言查询行情。

如果你需要在自己的产品中集成 SKILL 协议
参考 http://skill.md/ 规范文件,按四个顶级区块(identity / instructions / functions / examples)构建你专属的 SKILL 文档,并确保 functions 中的每个参数都附有明确的类型约束和枚举值。

如果你想用历史数据验证策略
访问 tickdb.ai,注册获取免费 API Key(无需信用卡),通过 REST API 获取 10 年级别的美股历史 K 线数据。

如果你在构建量化系统
TickDBSKILLTickDBWebSocket 两个类的完整源码可在 TickDB 开发者文档中获取,支持 Python 和 JavaScript 双语言。


本文不构成任何投资建议。市场有风险,投资需谨慎。TickDB Market Data SKILL 是数据访问工具,不提供交易执行或投资决策功能。