k-skill 实战:韩国高速公路实时路况与 CCTV 查询技能 highway-traffic-status 深度解析
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
本篇技术指南以 k-skill 仓库中的highway-traffic-status技能为对象,完整讲解它如何基于韩国道路公社(data.ex.co.kr)实时交通量 API 与国家交通信息中心 ITS(openapi.its.go.kr)CCTV 信息 API,实现高速公路区间(콘존)级速度、交通量、拥堵等级(通畅/缓行/拥堵)以及坐标范围内的 CCTV 流元数据查询。读完本文,你将掌握该技能的完整命令行用法、JSON 输出字段、密钥与代理策略、失败模式,并通过仓库源码与测试用例理解其底层实现原理,能够直接在自己的 Agent 工作流中调用或复刻这一"零密钥、纯标准库"的查询方案。
技能定位:查询专用,不越界
highway-traffic-status是一个典型的lookup(查询)导向技能,其核心职责清晰且边界严格:
- 通过韩国道路公社公共数据门户(
data.ex.co.kr)的实时交通量 API,查询全国高速公路区间(콘존)级的速度、交通量、通行时间与拥堵等级(원활 通畅 / 서행 缓行 / 정체 拥堵); - 通过国家交通信息中心 ITS(
openapi.its.go.kr)的 CCTV 信息 API,查询指定坐标范围内的高速公路 CCTV 名称、经纬度与 HLS 流地址。
技能元数据在 skill.json 中明确声明了三条画像:proxy、browser、lookup,类别为transit、地区为ko-KR、版本阶段为v1,并在描述中限定了触发条件:"用户询问高速公路拥堵、交通状况、线路/区间通行、高速公路 CCTV 时使用;不用于公共交通路线或汽车路径导航"。
技能本身是查询专用的:路径规划、导航、通行费计算均不在其范围内。完整指令文档见 highway-traffic-status/instruction.md,CLI 按运行时(generic / dolshoi)装配后的指令快照见 packages/k-skill-cli/test/snapshots/highway-traffic-status.generic.md 与 packages/k-skill-cli/test/snapshots/highway-traffic-status.dolshoi.md。
适用与不适用的场景
**适用(When to use)**的典型用户表达:
- "现在京釜高速堵吗?"
- "告诉我首尔收费站那边的拥堵情况"
- "西海岸线上行通行如何?"
- "给我看板桥附近的高速公路 CCTV"
**不适用(When not to use)**时,指令明确要求转派给其他技能:
- 公共交通路线 →
korean-transit-route - 汽车路径/导航 →
kakao-map - 市区道路、一般国道的详细通行(v1 以高速公路为中心)
运行环境与前置条件
技能的运行门槛极低,这是它的一大特点:
- Python 3.9+,且仅使用标准库。实现脚本 highway_traffic.py 顶部导入的模块只有
argparse、contextlib、json、os、pathlib、sys、urllib系与xml.etree.ElementTree,没有任何第三方依赖,无需pip install。 - 用户无需 API 密钥。两个上游接口均可用公开演示密钥
test直接访问(脚本头部注释记录该结论于 2026-07-21 实测确认)。因为属于公开端点,该技能不经过 k-skill-proxy 转发,直接调用上游,这符合仓库"免费 API 直连、付费/受限 API 走代理"的代理策略。
可选的密钥环境变量(应对演示密钥回收/配额)
为了在演示密钥test被回收或遭遇配额限制时平滑切换,指令提供了两个可选环境变量:
| 环境变量 | 对应上游 | 获取途径 |
|---|---|---|
KSKILL_EXDATA_API_KEY | data.ex.co.kr 实时交通量 | 在韩国道路公社公共数据门户注册后签发个人认证密钥 |
KSKILL_ITS_API_KEY | openapi.its.go.kr CCTV 信息 | 在国家交通信息中心开放数据门户获取个人认证密钥 |
此外,脚本还会读取~/.config/k-skill/secrets.env中同名键。这一机制的实现位于resolve_api_key()(highway_traffic.py):优先取环境变量,若为空则解析secrets.env(支持#注释行、KEY=VALUE键值对、去除引号包裹),解析函数load_secrets()用朴素行解析实现,无需额外配置库。
注意:指令同时强调硬性安全规则——绝不在聊天、文件或 shell 参数中以明文形式索取、打印或存储凭据。个人密钥应通过环境变量或 secrets 文件注入,而不是写死在命令行里。
两条上游 API 的技术要点
指令文档的 "Data sources & fallback order" 一节给出了明确的调用顺序与技术细节,脚本 docstring 与测试进一步印证:
1. 实时交通量快照(韩国道路公社)
GET https://data.ex.co.kr/openapi/odtraffic/trafficAmountByRealtime?key=<key>&type=json[&numOfRows=N&pageNo=N]- 返回全国 VDS 实时快照(JSON 格式,
list数组),并非按区间维度分页下发;线路/区间过滤由 helper 在客户端本地完成。 - 无效密钥时接口仍返回 HTTP 200,但响应体为
{"code": "ERROR", "message": "인증키가 유효하지 않습니다."},需按类型化错误处理。 - URL 构造逻辑见
build_traffic_url():固定携带key与type=json两个查询参数,密钥为空时自动回退到演示密钥test。
2. CCTV 信息(国家交通信息中心 ITS)
GET https://openapi.its.go.kr:9443/cctvInfo?apiKey=<key>&type=ex&cctvType=1&minX=..&maxX=..&minY=..&maxY=..&getType=json- 这是一个值得注意的坑:尽管请求中带了
getType=json,成功响应实际是 XML。因此脚本必须"先按 XML 解析,失败再回退到 JSON 错误包"(见normalize_cctv()的实现顺序)。 - 无效密钥返回 HTTP 401,JSON 体为
{"header": {"resultCode": 4005, ...}}。 - 参数
type由--road-type控制:ex(高速公路,默认)/its(国道)/all;cctvType=1固定为路况 CCTV。
两个上游的演示密钥test均有效;当环境变量/密钥文件中存在个人密钥时,优先使用个人密钥。
实时交通量/路况查询实战(traffic 子命令)
基本用法
npx -y @nomadamas/k-skill@0 exec highway-traffic-status scripts/highway_traffic.py -- traffic --route 경부 --text这条命令表示:通过 CLI 的exec子命令执行技能捆绑的 helper 脚本(这是指令强制要求的调用方式——捆绑助手只能通过npx -y @nomadamas/k-skill@0 exec highway-traffic-status scripts/<file> -- <args>执行,不得假定仓库相对路径或已安装技能的相对路径;如需文件系统路径,用npx -y @nomadamas/k-skill@0 path highway-traffic-status <relative-path>解析)。
参数详解
| 参数 | 说明 |
|---|---|
--route | 线路名片段(如경부京釜、서해안西海岸)或线路编号(如0010) |
--keyword | 区间(콘존)名称关键字(如서울TG、양재) |
--limit N | 输出行数上限,默认 30 |
--text | 输出人类可读的摘要;不传该标志则输出结构化 JSON |
--route的匹配逻辑在filter_traffic()中实现:对每条记录,若路由名中包含该关键字或路由编号与之完全相等即命中;--keyword则对conzone_name(区间名)做子串匹配。测试用例test_filter_traffic_by_route_matches_name_or_number验证了按名称경부命中 2 条、按编号0150精确命中"서해안선"各 1 条;test_filter_traffic_by_keyword_matches_conzone验证关键字서울TG命中"서울TG-양재IC"区间。
JSON 输出字段
不传--text时,脚本输出一个结构化的 JSON 对象,外层包含:
{ "result": "ok", "total_matched": 2, "rows": [ ... ], "source": "data.ex.co.kr trafficAmountByRealtime" }result:ok或empty(无匹配时);total_matched:过滤后的总匹配数(不受--limit影响);rows:实际输出的记录数组;source:数据来源端点标识。
rows中每条记录的字段(由normalize_traffic()生成,已做类型与标签归一化):
| 字段 | 含义 |
|---|---|
route_name | 线路名称(如경부선) |
route_no | 线路编号(如0010) |
conzone_name | 区间(콘존)名称 |
conzone_id | 区间编号 |
direction | 方向(상행上行 /하행下行) |
speed_kmh | 速度(km/h),无法解析时为null |
traffic_volume | 交通量(车辆数) |
travel_time_sec | 平均通行时间(秒) |
congestion | 拥堵等级(원활通畅 /서행缓行 /정체拥堵) |
observed_at | 观测时间(由stdDate日期与stdHour时刻拼接) |
上游原始字段的映射关系:speed→speed_kmh、trafficAmout→traffic_volume、timeAvg→travel_time_sec、updownTypeCode→direction、grade→congestion、stdDate+stdHour→observed_at。等级与方向码的映射常量定义在脚本顶部:
GRADE_LABELS = {"1": "원활", "2": "서행", "3": "정체"} DIRECTION_LABELS = {"S": "상행", "E": "하행", "N": "상행", "W": "하행"}测试test_normalize_traffic_converts_numbers_and_grade_label验证了该映射:grade1→원활、grade3→정체、方向码E→하행、S→상행。
文本摘要格式
--text模式调用render_traffic_text()输出一行一条的记录,格式为:
[경부선 하행] 구서IC-영락IC: 원활 · 89km/h · 교통량 20 (기준 20260721 1530)即:[线路名 方向] 区间名: 拥堵等级 · 速度 · 交通量 (观测时间);速度为None时显示"속도 미상"(速度未知)。
CCTV 元数据查询实战(cctv 子命令)
基本用法
坐标范围(经度--min-x/--max-x、纬度--min-y/--max-y)是必填的。用户只说出地名时,Agent 应将其转换为大致的 bounding box 再调用:
npx -y @nomadamas/k-skill@0 exec highway-traffic-status scripts/highway_traffic.py -- cctv \ --min-x 126.9 --max-x 127.2 --min-y 37.3 --max-y 37.6 --text上述示例覆盖首尔市区周边(约东经 126.9–127.2、北纬 37.3–37.6)范围。
参数与坐标校验
--road-type:ex(高速公路,默认)/its(国道)/all;- 响应的
url字段是HLS 流地址;流媒体的实际播放由用户环境(浏览器/播放器)负责,技能只提供元数据; - 坐标校验在
_validate_bbox()中强制执行(构造 URL 前调用):四个坐标必须齐全、min < max,且必须落在韩国范围(经度 124.0–132.0、纬度 33.0–39.5)内,否则抛出带明确提示的HelperError:
KOREA_LON_RANGE = (124.0, 132.0) KOREA_LAT_RANGE = (33.0, 39.5)测试test_cctv_bbox_bounds_are_validated与test_cctv_bbox_must_stay_in_korea_range分别覆盖了"min 不小于 max"与"超出韩国坐标范围"两类非法输入。
JSON 输出与 XML 解析
不传--text时输出:
{ "result": "ok", "cameras": [ { "name": "[수도권제1순환선] 성남", "url": "http://cctvsec.example/stream1", "format": "HLS", "lat": 37.42889, "lon": 127.12361 } ], "source": "openapi.its.go.kr cctvInfo" }normalize_cctv()的解析策略(与 ITS 接口"声称 JSON 实则 XML"的行为对齐):
- 若响应体以
{开头,尝试按 JSON 解析——能解析出header.resultMsg说明是错误包,抛"认证密钥需确认"的HelperError; - 否则按 XML 解析,遍历所有
<data>节点,提取cctvname、cctvurl、cctvformat、coordx、coordy;经纬度无法转为 float 的记录会被跳过; - 根节点不是
response且没有任何 CCTV 时,抛"响应中无数据"错误。
测试test_normalize_cctv_parses_xml_metadata用一段含 2 个<data>节点的 XML 验证了解析结果;test_normalize_cctv_raises_on_key_error_json验证 4005 错误 JSON 的正确报错;test_normalize_cctv_raises_on_unexpected_body验证<html>类非预期响应会报"无法解析"错误。
结果摘要规则(Agent 行为约束)
指令对查询结果的呈现方式提出了三条明确约束,确保 Agent 不擅自加工数据:
- 照搬上游等级,不加自身判断:拥堵状态直接采用上游
grade字段,映射为通畅/缓行/拥堵展示,不自行推断或修改; - 必须附带观测时间:同时告知
observed_at基准时刻,并明确这是"实时快照"; - 驾驶安全:若判断用户处于驾驶中,建议其使用语音或由同行乘客确认,不鼓励驾驶中操作。
从运行时装配指令(generic/dolshoi 快照)看,该技能被定性为 lookup 导向:完成的定义是"数据已获取,并连同来源(表/端点、周期、单位)一起总结,且将任何请求的后续动作连接到支持它的官方界面",而不是输出结果后就宣告结束。
失败模式与容错处理
指令文档用表格形式完整列出了各类失败场景及 Agent 应采取的应对动作,是运行期排障的核心依据:
| 场景 | 应对动作 |
|---|---|
空结果(result: "empty") | 提示无符合条件的区间/CCTV,建议放宽线路名与坐标范围后重试 |
| 认证密钥错误("인증키가 유효하지 않습니다" / ITS 401 resultCode 4005) | 提示演示密钥可能被回收,指导用户申请个人密钥并配置环境变量 |
| 上游 HTTP 错误/超时 | 提示稍后重试,并说明可能是上游维护中 |
| JSON/XML 解析失败 | 提示可能被拦截或接口格式变更,建议报告 issue |
| 坐标范围错误 | 输出韩国范围(经度 124–132、纬度 33–39.5)及 min<max 的校验提示 |
这些错误在代码层全部收敛为统一的HelperError异常:http_get_text()把 HTTP 401 或响应体含"인증키"的情况、其他 HTTP 错误、连接失败分别转成带中文提示的HelperError;http_get_json()对非 JSON 响应报"可能被拦截或维护中"。run()顶层捕获HelperError后写入 stderr 并返回退出码 1,测试test_run_reports_helper_error_to_stderr对此做了断言。
源码实现细节:请求、归一化与主流程
在 highway_traffic.py 中,整个工具遵循清晰的分层设计:
- 请求层:
build_traffic_url()/build_cctv_url()负责参数拼装与校验;http_get_text()用urllib.request发起请求(携带自定义User-Agent: k-skill-highway-traffic/0.1),默认超时 30 秒(可用--timeout覆盖); - 归一化层:
normalize_traffic()把上游非规范字段(数值字符串、等级码、方向码、日期+时刻)整理为上文所述的稳定 JSON 结构;normalize_cctv()负责 XML/JSON 双格式识别; - 过滤与渲染层:
filter_traffic()做线路名/编号、区间关键字的客户端本地过滤;render_traffic_text()输出人读摘要; - 入口层:
parse_args()用argparse子命令结构(traffic/cctv两个子解析器,公共参数--secrets-path、--timeout、--text);run()按子命令分发,统一以 JSON(含result、total_matched/cameras、source)或文本方式输出,退出码 0 表示成功(含空结果),1 表示HelperError。
其中"演示密钥直连、无需代理"的决策在脚本 docstring 中有明确记录:两个上游都可用演示密钥免注册工作,故按免费 API 代理策略直接调用上游(不注册 k-skill-proxy 路由);用户可用环境变量覆盖密钥以换取更高配额。
测试验证:行为即规格
test_highway_traffic.py 通过importlib从仓库路径动态加载脚本模块(不依赖 pip 安装),共覆盖四大类共 14 个用例:
- URL 构造(
BuildUrlTests):验证交通量 URL 指向data.ex.co.kr的/trafficAmountByRealtime且默认密钥为演示密钥test、用户密钥优先、CCTV URL 指向openapi.its.go.kr且参数齐全、坐标校验生效; - 解析与过滤(
ParseTests):验证数值转换、等级/方向标签映射、按线路名与编号过滤、按区间关键字过滤、XML 元数据解析、密钥错误 JSON 报错、非预期响应报错; - 端到端运行(
RunTests):通过mock.patch注入假响应,验证traffic子命令的 JSON 输出结构(rows、total_matched、source)、空结果显式标记empty、cctv子命令的摄像头输出、错误写入 stderr 且退出码为 1、--text人类摘要渲染。
测试夹具中给出了真实的样例数据形态(如경부선区间서울TG-양재IC速度 35km/h、grade 3、timeAvg140 秒),可以直接用作本地联调或 mock 数据的参考。
使用边界与注意事项
- 更新前置:使用 k-skill CLI 工具前,应先执行
npx -y @nomadamas/k-skill@0 update,确保 CLI 与所有编码 Agent 技能安装(包括~/.agents/skills)都是最新版本;技能完整指令通过npx -y @nomadamas/k-skill@0 instruct highway-traffic-status获取,捆绑文件清单用npx -y @nomadamas/k-skill@0 files highway-traffic-status查看(见 SKILL.md)。 - 代理策略:普通查询默认走托管的
k-skill-proxy(https://k-skill-proxy.nomadamas.org),无需用户 API 密钥;仅在自托管或使用替代代理时才设置KSKILL_PROXY_BASE_URL。而本技能因上游免费直连,是"跳过代理"的特例。 - CCTV 流地址时效性:ITS 返回的流 URL 是签名 URL,会随时间过期,需重新查询以刷新。
- 演示密钥风险:若上游调整演示密钥策略,本技能将退化为仅支持个人密钥(BYOK)模式;届时需重新评估是否将本技能纳入代理路由。
总体而言,highway-traffic-status是一个"小而完整"的技能范本:外部接口差异(XML 伪装成 JSON)、密钥分级(演示密钥兜底 + BYOK 覆盖)、错误分类(HTTP/认证/解析/坐标)、输出契约(结构化 JSON + 人读摘要)都在一个纯标准库 Python 文件中得到清晰实现,并配有完整的单元测试,非常适合作为 Agent 技能开发的参考蓝本。
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考