k-skill korean-stock-search 实战指南:无需 KRX_API_KEY 的 KRX 韩国股票查询与代理实现剖析
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
本文以 k-skill 仓库中的korean-stock-search技能说明文档为核心,完整讲解如何通过 k-skill-proxy 的三个 HTTP 端点完成 KRX 上市股票搜索、个股基本资讯与每日行情查询,并结合代理服务器源码剖析参数校验、结果打分、缓存策略与上游故障处理机制。读完后你可以直接用 curl 调通全部端点,并掌握在 Agent 场景下如何正确解释结果与处理degraded、not_found等失败状态。
技能定位与应用边界
korean-stock-search是 k-skill("한국인을 위한 스킬 모음집")中面向金融数据查询的技能。它的默认行为是向https://k-skill-proxy.nomadamas.org/v1/korean-stock/...发起请求,完成三类只读查询:KRX 上市股票搜索(search)、个股基本资讯(base-info)、个股每日行情(trade-info)。
技能说明书(korean-stock-search/instruction.md)明确了适用与不适用场景:
适合使用的典型请求:
- "삼성전자 종목코드랑 시장구분 찾아줘"(找一下三星电子的证券代码和市场区分)
- "005930 기본정보 보여줘"(查看 005930 的基本资讯)
- "SK하이닉스 20260408 종가/거래량 알려줘"(查询 SK 海力士 20260408 的收盘价与成交量)
- "KOSDAQ 에서 알테오젠 시세 확인해줘"(在 KOSDAQ 市场确认 Alteogen 的行情)
明确不属于本技能范围的场景:
- 美国/日本/虚拟资产等非韩国股票查询
- 实时成交(체결)、买卖盘口(호가)、分钟 K 线查询
- 财务报表与公告原文分析
- 投资建议或买入推荐
skill.json中的元数据也印证了这一定位:profiles 为proxy与lookup,分类finance,地区ko-KR(见 korean-stock-search/skill.json)。
核心设计:密钥集中在代理层,用户零凭证
该技能最关键的设计是用户不需要申请KRX_API_KEY,也不需要本地安装 MCP 服务器。upstream 方案设计参考了开源项目jjlabsio/korea-stock-mcp,但用户侧被完全屏蔽了密钥管理:KRX_API_KEY只在 k-skill-proxy 服务器上配置与注入。
从代理源码看,这一点在三个层面得到确认:
- 代理从进程环境变量读取密钥:
config.krxApiKey = trimOrNull(env.KRX_API_KEY)(packages/k-skill-proxy/src/server.js#L264); - 每个端点在命中缓存后都会检查
config.krxApiKey,若缺失直接返回 503upstream_not_configured(packages/k-skill-proxy/src/server.js#L5337-L5350); - 底层 KRX 请求函数在发请求前再次兜底校验密钥,同样抛出 503(packages/k-skill-proxy/src/krx-stock.js#L126-L132)。
代理基地址的选取规则:如果设置了KSKILL_PROXY_BASE_URL环境变量,则使用其值;否则回退到默认地址https://k-skill-proxy.nomadamas.org。此外不需要任何客户端 API 层——直接向代理发 HTTP GET 请求即可。
输入参数详解
技能定义的全部输入如下,参数校验逻辑与默认值均可在代理源码中逐一对应:
| 参数 | 说明 | 校验规则与默认值(源自源码) |
|---|---|---|
q | 股票名或证券代码搜索词,仅search端点使用 | 必填;缺失时抛错返回 400 |
market | 市场区分:KOSPI|KOSDAQ|KONEX | base-info/trade-info必填;search可选,缺省时并行查询全部三个市场 |
code | 证券代码,通常为 6 位短代码(如005930) | base-info/trade-info必填;支持逗号分隔的多代码入参,取第一个 |
bas_dd | 基准日,格式YYYYMMDD | 可选;缺省时取 KST(Asia/Seoul)当天日期;若传入值不是 8 位数字则 400 |
limit | 搜索结果条数 | 默认 10,范围 1~20,越界返回 400 |
参数归一化函数位于 packages/k-skill-proxy/src/server.js#L1447-L1498:
normalizeKoreanStockDate:bas_dd为空时用getCurrentKstDate()(基于Intl.DateTimeFormat+Asia/Seoul时区,见 packages/k-skill-proxy/src/krx-stock.js#L50-L59)计算 KST 当天,然后强制匹配^\d{8}$;normalizeKoreanStockSearchQuery:接受q或query别名,bas_dd/basDd/date均可,limit默认 10 且上限 20;normalizeKoreanStockLookupQuery:code支持codes/codeList/stockCode/stock_code别名,逗号分隔并去重后取第一项,用于base-info与trade-info。
前置条件(Prerequisites):无。使用者无需准备KRX_API_KEY,upstream 密钥仅在代理服务器侧注入。
三个受支持的端点与 curl 示例
1. 股票搜索
GET /v1/korean-stock/search?q={검색어}&bas_dd={YYYYMMDD}curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/korean-stock/search' \ --data-urlencode 'q=삼성전자' \ --data-urlencode 'bas_dd=20260408'2. 个股基本资讯
GET /v1/korean-stock/base-info?market={KOSPI|KOSDAQ|KONEX}&code={종목코드}&bas_dd={YYYYMMDD}curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/korean-stock/base-info' \ --data-urlencode 'market=KOSPI' \ --data-urlencode 'code=005930' \ --data-urlencode 'bas_dd=20260408'3. 个股每日行情
GET /v1/korean-stock/trade-info?market={KOSPI|KOSDAQ|KONEX}&code={종목코드}&bas_dd={YYYYMMDD}curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/korean-stock/trade-info' \ --data-urlencode 'market=KOSPI' \ --data-urlencode 'code=005930' \ --data-urlencode 'bas_dd=20260408'注意:中文/韩文等非 ASCII 搜索词务必使用--data-urlencode做 URL 编码,这在代理测试中也有覆盖——测试用例会用%EC%82%BC%EC%84%B1%EC%A0%84%EC%9E%90("삼성전자" 的编码形式)注入请求(packages/k-skill-proxy/test/server.test.js#L2332-L2341)。
响应结构逐字段解读
三个端点共享统一响应骨架:业务数据(items/item)+ 回显的query+ 代理元信息proxy。proxy.cache.hit标识本次是缓存命中还是实时查询,ttl_ms为缓存 TTL(默认 300000 毫秒)。
搜索响应
{ "items": [ { "market": "KOSPI", "code": "005930", "standard_code": "KR7005930003", "name": "삼성전자", "short_name": "삼성전자", "english_name": "Samsung Electronics", "listed_at": "1975-06-11" } ], "query": { "q": "삼성전자", "bas_dd": "20260408", "limit": 10 }, "proxy": { "name": "k-skill-proxy", "cache": { "hit": false, "ttl_ms": 300000 } } }基本资讯响应
{ "item": { "market": "KOSPI", "code": "005930", "standard_code": "KR7005930003", "name": "삼성전자", "short_name": "삼성전자", "english_name": "Samsung Electronics", "security_group": "주권", "section_type": "대형주", "stock_certificate_type": "보통주", "par_value": 100, "listed_shares": 5969782550 }, "query": { "market": "KOSPI", "code": "005930", "bas_dd": "20260408" }, "proxy": { "name": "k-skill-proxy", "cache": { "hit": false, "ttl_ms": 300000 } } }每日行情响应
{ "item": { "market": "KOSPI", "code": "005930", "standard_code": "KR7005930003", "base_date": "20260408", "name": "삼성전자", "close_price": 84000, "change_price": 1000, "fluctuation_rate": 1.2, "open_price": 83000, "high_price": 84500, "low_price": 82800, "trading_volume": 12345678, "trading_value": 1030000000000, "market_cap": 500000000000000 }, "query": { "market": "KOSPI", "code": "005930", "bas_dd": "20260408" }, "proxy": { "name": "k-skill-proxy", "cache": { "hit": false, "ttl_ms": 300000 } } }行情字段由normalizeTradeItem从 KRX 原始字段映射而来:TDD_CLSPRC→close_price、CMPPREVDD_PRC→change_price、FLUC_RT→fluctuation_rate、TDD_OPNPRC/TDD_HGPRC/TDD_LWPRC→开高低、ACC_TRDVOL/ACC_TRDVAL→量额、MKTCAP→market_cap(packages/k-skill-proxy/src/krx-stock.js#L87-L116)。映射时对数字做了去千分位逗号的解析(parseNumber),保证返回的是纯数字而非字符串。
Upstream 实现:KRX API 如何被真正调用
代理到 KRX 的完整数据流在 packages/k-skill-proxy/src/krx-stock.js 中,要点如下:
按市场分表的上游 URL。KRX 的 dbg 数据接口为三个市场提供了不同路径,源码中维护了静态映射(packages/k-skill-proxy/src/krx-stock.js#L5-L15):
| 市场 | 基本资讯 upstream | 每日行情 upstream |
|---|---|---|
| KOSPI | stk_isu_base_info | stk_bydd_trd |
| KOSDAQ | ksq_isu_base_info | ksq_bydd_trd |
| KONEX | knx_isu_base_info | knx_bydd_trd |
三者均挂在data-dbg.krx.co.kr/svc/apis/sto/下,请求仅带basDd查询参数,并在请求头中以AUTH_KEY携带 API 密钥。
搜索是"全市场快照 + 本地打分",而非服务端模糊查询。searchStocks对选定市场(未指定market时为 KOSPI/KOSDAQ/KONEX 全部三个)用Promise.allSettled并发拉取该市场当日的基本资讯快照,然后在本地执行匹配打分(packages/k-skill-proxy/src/krx-stock.js#L264-L325):
- 搜索词与
code/standard_code/name/short_name/english_name任一字段完全相等(忽略大小写)得 100 分; - 按空白切分后每个 token 都出现在某个字段中得 50 分;
- 未命中得 -1 分并过滤,最后按分数降序、同分按韩文
localeCompare排序后截取前limit条。
这种设计解释了为什么搜索是确定性的、可缓存的,也解释了文档中"종목명이 모호하면 먼저search로 시장/종목코드를 좁힌 뒤"(股票名模糊时先收窄)这一响应策略的必要性。
degraded 的部分故障语义。只要有一个市场拉取成功,search就返回 200,并附带upstream: { degraded: true, requested_markets, successful_markets, failed_markets };只有全部市场都失败时才向上抛错、由路由层转成 502。failed_markets中每一项包含code/status_code/message序列化信息(serializeKrxError)。代理测试对这一行为有专门断言:当另一个市场失败时,search 响应会带出 degraded 元数据(packages/k-skill-proxy/test/server.test.js#L2477)。
trade-info 的短代码回退匹配。fetchTradeInfo先用 6 位短代码(ISU_SRT_CD)直接匹配当日行情快照;若无直接命中,会再拉一次基本资讯快照,用 12 位标准代码(ISU_CD,如KR7005930003)做二次匹配,并把基本资讯中的名称、上市股数等补进行情结果(packages/k-skill-proxy/src/krx-stock.js#L168-L191)。这层回退让"只有短代码的用户"也能查到行情。
网络层细节。krxRequest通过fetchWithRetry发起请求,超时设为 20 秒(AbortSignal.timeout(20000));上游返回非 2xx 时抛upstream_error(502),响应体缺少OutBlock_1数组时抛krx_api_error(502)——KRX 的数据就装在OutBlock_1里。
缓存策略与失败模式
缓存键与命中回显。每个路由用makeCacheKey({ route, ...参数 })生成缓存键(search 会把q转小写后再拼入键,保证大小写不敏感的命中)。命中缓存的响应会带proxy.cache.hit=true。测试中验证了带空格/别名的两种请求(q=vsquery=+date=)会归一化为同一查询(packages/k-skill-proxy/test/server.test.js#L2332-L2341)。
degraded 响应不进缓存。缓存层显式拒绝写入error或upstream.degraded=true的载荷(cache.set返回 false,见 packages/k-skill-proxy/test/server.test.js#L247-L251);search 路由也只有在!result.upstream?.degraded时才写缓存(packages/k-skill-proxy/src/server.js#L5389-L5395)。此外测试还覆盖了按客户端 IP(解析cf-connecting-ip与x-forwarded-for链)的限流行为,超限返回rate_limited。
完整失败模式对照表(与文档 Failure modes 一节一致,且全部可在路由源码中对应到具体分支):
| 条件 | HTTP 状态 | error 标识 | 源码位置 |
|---|---|---|---|
q/market/code/bas_dd格式错误,或limit越界 | 400 | bad_request | server.js#L5306-L5314 |
代理未配置KRX_API_KEY | 503 | upstream_not_configured | server.js#L5337-L5350 |
| 部分市场 upstream 失败 | 200 | upstream.degraded=true+failed_markets | krx-stock.js#L315-L322 |
| 全部市场 upstream 失败 / KRX 返回错误 | 502 | upstream_error/krx_api_error | krx-stock.js#L143-L156 |
| 该基准日/市场下找不到该股票 | 404 | not_found | server.js#L6324-L6330 |
特别注意 404 分支的两个细节:base-info的提示语是"기준일 X 에 Y 시장 종목 Z 을(를) 찾지 못했습니다",而trade-info的提示语额外说明了"휴장일이거나 데이터가 아직 없을 수 있습니다"(可能是休市日或数据尚未生成)(server.js#L6417-L6423)。
响应策略与输出规范(Agent 必读)
技能文档对"拿到数据之后怎么讲"给出了明确的行为规范,这部分是技能质量的核心:
- 查询顺序:股票名模糊时先走
search收窄市场/证券代码,再进入base-info或trade-info; - 部分故障要如实说明:看到
upstream.degraded=true与failed_markets时,要在答案中一并说明哪些市场查询失败; - 不要过度承诺时效:
trade-info是日别 snapshot,不能表述为实时盘口/成交; - 休市日处理:
bas_dd为休市日或盘前时该日期可能无数据,应回退到最近营业日重试;此时trade-info可能以 404not_found而非 502 结束; - 数字呈现:用 원/주/억/조 等易读单位简短解读,同时保留原始数字;
- 免责声明:答案末尾简短附上"KRX 공식 데이터 기준 / 투자 조언 아님"(以 KRX 官方数据为准 / 非投资建议)。
紧凑答案要求(Keep the answer compact):答案只保留股票名/市场/证券代码、基准日、收盘价/涨跌幅/成交量/市值;仅在需要时补充上市日/上市股数/面值;出现多个候选时只展示前 3~5 个,交由用户选择。
完成判据(Done when):模糊搜索词已先经search收窄;已按需用base-info与trade-info整理核心数值;全程保持了"用户无需KRX_API_KEY也能查询"的体验;已简短标注数据出处。
合规与法律边界
SKILL.md 声明本技能只读查询专用(read-only 조회 전용),且与 KRX、交易所、上市公司或任何数据提供方无官方关系。korean-stock-search/references/DISCLAIMER.md 进一步约束了使用边界:
- 公开市场信息的自动收集仅限个人、非组织性的信息查询用途;
- 禁止组织性/大规模爬取,禁止构建或再分发行情数据库;
- 不得绕过访问控制、封锁或 quota;
- 查询结果仅为投资参考信息,不构成投资劝诱、咨询或收益保证;
- 该免责声明引用了韩国大法院判例(2005도1637、2021도1533)与商标法、信息通信网法、著作权法相关条款作为背景,但明确声明其本身不是法律保证。
小结
korean-stock-search展示了 k-skill 代理型技能的典型形态:把有密钥门槛的上游 API(KRX Open API,参考设计来自jjlabsio/korea-stock-mcp)收敛到一个统一代理后面,用户侧只剩三个 GET 请求和一组被严格归一化、有默认值、有明确失败码的查询参数。源码层面值得复用的点在于:全市场快照加本地确定性打分的搜索实现、短代码到标准代码的两级回退匹配、"degraded 不缓存"的缓存策略,以及对 400/404/502/503 各失败分支的清晰划分——这些设计让该技能既能在 Agent 对话中给出紧凑可靠的答复,又能把上游的部分故障透明地暴露给调用方。
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考