k-skill 韩国大众交通路径查询技能实战指南:基于 ODsay LIVE API 与 Kakao Local geocoding 的门到门路线规划
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
本指南围绕 k-skill 仓库中的korean-transit-route技能展开,讲解如何实现「韩国境内门到门(door-to-door)大众交通路线查询」:从任意地址/场所名/坐标出发,整合 Kakao Local geocoding 完成地址到坐标的转换,再调用 ODsay LIVE API 获取地铁、公交与步行组合的最优路径,并按「推荐 / 最少时间 / 最少换乘」等策略输出结果。读完本文,你将掌握该技能的环境变量配置、完整调用流程、请求参数语义、响应结构解析、输出规范与失败排查方法,并了解其底层k-skill-proxy的 geocoding 路由与缓存实现原理。
技能定位与能力边界
korean-transit-route是 k-skill 仓库中的一个「查询型(lookup)」技能,定位为「Korean door-to-door public transit routing(subway + bus + walking)」,其元数据声明于 korean-transit-route/skill.json:profile为proxy+lookup,locale为ko-KR,许可协议为 MIT。技能的完整指令内容见 korean-transit-route/instruction.md。
该技能解决的核心问题是:当用户以自然语言提出「강남에서 잠실 지하철로 어떻게 가?」「서울역 → 인천공항 대중교통 경로」「환승 가장 적은 경로」等需求时,如何返回结构化的、包含步行接驳的大众交通路线。它不做驾车导航,也不做步行/骑行导航,而是专注于地铁、公交与步行换乘的组合规划。
能力范围可归纳为四件事:
- 出发地 → 目的地门到门大众交通路径查询(地铁 + 公交 + 步行);
- 基于 ODsay LIVE API 的换乘信息、总耗时、票价查询;
- 通过 Kakao Local geocoding 将「地址 / 场所名」转换为 WGS84 坐标;
- 支持「推荐顺序(추천순)/ 最少时间(최소시간)/ 最少换乘(최소환승)」三种路径策略。
前置准备:密钥、白名单与代理说明
在调用该技能之前,需要完成三项准备工作:
- ODsay Server API Key 的申请与 IP 白名单注册。ODsay 的 Server 型密钥强制要求将「发起调用的出口 IP」登记进白名单,未登记 IP 的请求将直接返回
error响应。密钥申请在 ODsay 官方开发者平台(lab 站点)完成。 - 确认
k-skill-proxy可达或已配置。Kakao Local geocoding 默认经由 k-skill 托管的k-skill-proxy转发,因此终端用户侧无需申请 Kakao REST API Key;只有自行部署(self-host)proxy 的运维人员才需要在服务器端配置KAKAO_REST_API_KEY。 - 完成仓库通用配置与安全规范。具体请参阅 docs/setup.md(通用环境配置)与 docs/security-and-secrets.md(密钥与秘密管理策略)。
其中,ODsay 调用的前提是拿到ODSAY_API_KEY;Kakao 侧的密钥则由 proxy 服务器持有,调用方凭据在 geocoding 环节不参与鉴权(详见下文「源码佐证」)。
环境变量与凭据加载
技能运行所需的环境变量只有一个必填项:
| 变量名 | 说明 | 必填 |
|---|---|---|
ODSAY_API_KEY | ODsay LIVE API 的 Server 密钥 | 是 |
KSKILL_PROXY_BASE_URL | 自建 proxy 的基础地址(仅当不使用默认 hosted proxy 时设置) | 否 |
ODSAY_API_KEY的注入方式有两种:
- 写入
~/.config/k-skill/secrets.env,在调用前通过set -a; . ~/.config/k-skill/secrets.env; set +a加载到当前 shell 环境; - 直接作为进程环境变量注入。
KSKILL_PROXY_BASE_URL是可选项。代码中给出的默认值为 hosted 代理地址,Python 示例通过os.environ.get('KSKILL_PROXY_BASE_URL', 'https://k-skill-proxy.nomadamas.org').rstrip('/')读取——即:配置了自建 proxy 时用自建地址,否则回落至默认 hosted 服务。Kakao Local geocoding 走 proxy 转发时,用户侧不需要KAKAO_REST_API_KEY;只有 self-host proxy 的运维者才需要在服务器端设置KAKAO_REST_API_KEY,该键由 proxy 在服务端注入,绝不暴露给调用方。
输入参数与调用约束
技能接受两类输入:
- 必填:出发地、到达地,二者均支持三种形态——地址(주소)、场所名(장소명)或直接坐标;
- 可选:路径策略
OPT与交通工具范围SearchPathType。
| 参数 | 取值 | 含义 |
|---|---|---|
OPT | 0(默认) | 推荐顺序(추천순) |
OPT | 4 | 最少时间(최소시간) |
OPT | 5 | 最少换乘(최소환승) |
SearchPathType | 0(默认) | 地铁 + 公交 |
SearchPathType | 1 | 仅地铁 |
SearchPathType | 2 | 仅公交 |
一个重要的调用约束是:ODsay 只接受坐标,不接受地址或场所名。因此,当输入不是坐标时,必须先做 geocoding,这是不可跳过的前置步骤(instruction.md 中明确标注为「필수 선행 단계」)。
基本流程:从自然语言到门到门路线
整个查询流程分为四个阶段,这也是技能的核心工作流:
- 坐标化:将出发地/到达地文本通过
k-skill-proxy的/v1/kakao-local/geocode接口转为坐标。Proxy 内部按「Kakao Localaddress.json→ 无结果时回落keyword.json」的顺序尝试匹配(该 fallback 逻辑的源码实现在 packages/k-skill-proxy/src/server.js 中)。 - 调 ODsay:向 ODsay
searchPubTransPathT接口传入出发坐标(SX/SY)、到达坐标(EX/EY)与OPT/SearchPathType选项。 - 结果裁剪:将响应的
result.path[]整理为不超过 3 条候选路径。 - 分段呈现:将每条路径的
subPath[]按trafficType分类展示,首段与末段的步行段必须保留——它们代表了从实际出发地到车站、以及从车站到实际目的地的真实步行接驳。
实战示例一:坐标直接输入(curl)
当出发/到达坐标已知(如126.9706, 37.5559→127.0276, 37.4979)时,可直接调用 ODsay,无需 geocoding:
set -a; . ~/.config/k-skill/secrets.env; set +a KEY=$(python3 -c "import os,urllib.parse;print(urllib.parse.quote(os.environ['ODSAY_API_KEY'],safe=''))") curl -s "https://api.odsay.com/v1/api/searchPubTransPathT?apiKey=${KEY}&SX=126.9706&SY=37.5559&EX=127.0276&EY=37.4979&OPT=0&SearchPathType=0"这里有两个值得注意的工程细节:
ODSAY_API_KEY在嵌入 URL 前通过 Python 的urllib.parse.quote(..., safe='')做了百分号编码,避免密钥中可能存在的特殊字符破坏 URL 结构;- 所有坐标均为WGS84经纬度(
SX,SY为出发经/纬,EX,EY为到达经/纬)。
实战示例二:地址 → 坐标 → 路径(Python)
地址/场所名场景下的完整链路是先 geocoding 再路由。geocoding 封装如下(取自 korean-transit-route/instruction.md):
import os, urllib.parse, urllib.request, json PROXY = os.environ.get('KSKILL_PROXY_BASE_URL', 'https://k-skill-proxy.nomadamas.org').rstrip('/') def geocode(q): url = PROXY + '/v1/kakao-local/geocode?q=' + urllib.parse.quote(q) with urllib.request.urlopen(url, timeout=10) as resp: d = json.loads(resp.read()) if d.get('documents'): doc = d['documents'][0] return float(doc['x']), float(doc['y']), doc.get('place_name') or doc.get('address_name') return None sx, sy, s_name = geocode('서울역') ex, ey, e_name = geocode('강남역') # 之后调用 ODsay searchPubTransPathT代码要点:
- 响应取
documents[0],其中x为经度、y为纬度(与 ODsay 的SX/EX经度、SY/EY纬度口径一致); - 名称字段回退策略:优先
place_name(场所名),缺失时用address_name(地址名); - 请求设置
timeout=10,避免上游超时阻塞整体链路; - 如果
documents为空则返回None,调用方需据此进入「geocoding 无结果」的失败分支。
拿到坐标后,调用 ODsaysearchPubTransPathT(与示例一的 curl 参数完全相同),即可获得路径结果。
补充说明:如果只掌握精确的地铁站名(而非实际门牌地址),也可使用 ODsay 的searchStation接口换取车站坐标;但注意这会让首/末步行段无法被正确计算——只有使用实际出发/到达点的坐标,ODsay 才会返回包含两端步行的门到门结果(详见下文 Helpers 一节)。
响应结构解析
ODsaysearchPubTransPathT的响应核心是result.path[]数组,每条path包含:
pathType:路径类型,1=地铁、2=公交、3=地铁+公交;info.totalTime:总耗时(分钟);info.payment:总票价(韩元);info.subwayTransitCount/info.busTransitCount:地铁 / 公交换乘次数;info.totalWalk:总步行距离(米);info.firstStartStation/info.lastEndStation:首班乘车站与末班下车/到达站名称;subPath[]:逐段明细数组。
subPath[]中的每个元素按trafficType区分段类型:
trafficType=1:地铁段,可进一步读取lane[0].name(线路名)、startName(上车站)、endName(下车站)、passStopList.stations[](途经站列表,用于呈现「N 个站」的信息);trafficType=2:公交段,结构类似地铁段;trafficType=3:步行段,通常出现在路径的首段与末段。
技能建议对path[]最多整理 3 条候选路径,避免信息过载;当用户明确偏好「最少时间」或「最少换乘」时,改用OPT=4/OPT=5重新发起查询。
门到门输出规范
这是该技能最有辨识度的部分:输出必须呈现「起点门牌 → 终点建筑」的完整门到门摘要,步行段一个都不能少。instruction.md 给出的输出模板如下:
🚇 범안로95번길 32 → SKT타워 경로 1: 54분 · 1,950원 · 환승 2회 · 도보 688m 🚶 도보 1분 🚌 19번 부천범박힐스테이트 → 역곡역 (9분) 🚶 도보 2분 🚇 1호선 역곡 → 종각 (15정거장, 35분) 🚶 도보 7분这条输出的语义拆解:
- 第一行是「实际出发地址 → 实际到达建筑」,而非站名,以强调门到门性质;
- 汇总行包含总耗时、票价、换乘次数、总步行距离四项核心指标;
- 每个
subPath段用🚶/🚌/🚇图标区分步行/公交/地铁,地铁与公交段标注线路名、上车站 → 下车站、耗时(以及地铁的途经站数); - 首段「도보 1분」与末段「도보 7분」就是门到门的关键——它们代表从出发点到车站、从车站到终点的实际步行。
指令中定义了「Done when」完成标准,可作为 Agent 自检清单:出发/到达已完成 geocoding(或坐标/站名已明确确认);ODsay 响应中至少整理出 1 条路径;每条路径都包含总耗时、票价、换乘次数、总步行距离;展示了含首/末步行段的门到门摘要;且上游 API 密钥未出现在响应中。
Helpers:仅知站名时的searchStation
当只知道站名(如「강남」)而不知道坐标时,可使用 ODsaysearchStation接口获取车站坐标:
curl -s "https://api.odsay.com/v1/api/searchStation?apiKey=${KEY}&stationName=강남&CID=1000"参数CID=1000表示首都圈(수도권)。响应中的result.station[].x, y即为该站的 WGS84 坐标。注意:该接口的调用量与searchPubTransPathT合并计入每日配额,因此应在一次问答中尽量减少调用次数(详见下文「限制」)。
失败模式与排查
技能定义了一组明确的失败分支及对应处置策略:
| 失败现象 | 处置方式 |
|---|---|
ODsay 返回error响应 | 将响应中的msg字段原样展示给用户,并提示可能原因是「ApiKey 未注册」或「调用 IP 未加入白名单」 |
Kakao geocoding 无结果(documents为空) | 与用户确认地址/场所名的拼写,或请求更具体的表达方式 |
| 坐标正常但 ODsay 无路径结果 | 可能属于大众交通未开通区域、步行可达距离、或涉及海上/机场等特殊区段,需向用户核实 |
| 配额超限(quota exceeded) | 停止后续 API 调用,并告知用户已达每日限额 |
限制与注意事项
技能文档明确列出了以下约束,实际使用时必须遵守:
- IP 白名单为硬性要求:ODsay Server 密钥未登记调用 IP 时,请求会返回
error;申请与登记均须在 ODsay 开发者平台完成; - 配额有限:以 ODsay 官方 Basic 商品为准,免费体验为「每日 1,000 次、持续 6 个月」,且
searchPubTransPathT与searchStation的调用合并计算——这意味着一次问答应尽量收敛到 1 次路径查询; - 仅支持韩国境内坐标:韩国之外的坐标不在支持范围内;
- 禁止替代方案:Kakao Map / Naver Map 的 directions API 均不公开大众交通路由(只提供驾车与步行),因此不得用它们来做公共交通导航;
- 密钥安全:绝对不要把上游 API Key 暴露在响应中。
源码佐证:k-skill-proxy的 geocoding 路由实现
上文流程中「Proxy 内部先 address 后 keyword 的 fallback」并非文档自述,而是有明确的源码支撑。在 packages/k-skill-proxy/src/server.js 中,GET /v1/kakao-local/geocode路由的实现逻辑为:
- 通过
normalizeKakaoLocalGeocodeQuery校验参数——q必填,size(每页条数)默认5、上限15,page默认1、上限45(见 server.js); - 以
route: "kakao-local-geocode"+ 规范化参数生成缓存键,命中缓存则直接返回,第二个相同请求不再触达上游; - 未命中时先请求 Kakao Local
address端点,若响应成功但documents为空,再回落请求keyword端点; - 成功响应写入缓存(TTL 由 proxy 的
cacheTtlMs配置控制)。
底层的 Kakao Local HTTP 封装位于 packages/k-skill-proxy/src/kakao-map.js:它暴露address、keyword等端点映射,请求头携带authorization: KakaoAK <server-key>与固定的user-agent: k-skill-proxy/kakao-map,并设置 20 秒超时;若服务器未配置KAKAO_REST_API_KEY则返回503 upstream_not_configured。
代理安全性在测试中得到验证:在 packages/k-skill-proxy/test/server.test.js 中,请求 URL 携带了apiKey=client-key,但断言上游收到的参数中apiKey为null——调用方传入的 client 级 key 被服务端忽略,真正的 Kakao 密钥只在服务器侧注入。同一测试还验证了「第二次请求由代理缓存直接服务」(断言只发生 2 次上游调用)。这正是技能文档中「用户侧无需 Kakao 密钥」的设计由来:密钥隔离、缓存复用、白名单式代理,既降低了终端用户的使用门槛,也避免密钥在调用链路中泄露。
小结
korean-transit-route是一个「Kakao 出坐标、ODsay 出路线」的双层查询技能:Kakao Local geocoding 负责把自然语言地址/场所名翻译成 WGS84 坐标,ODsay LIVE API 负责在坐标之间规划地铁/公交/步行的门到门路径。其工程要点集中在三处——前置坐标化(ODsay 不接受地址)、首末步行段必现(保证门到门语义)、密钥隔离与缓存(借由k-skill-proxy实现)。配合本文的参数表、curl/Python 示例、响应字段说明与失败排查清单,读者可以在自己的 Agent 或脚本中原样落地这一「韩国大众交通问路」能力。
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考