☰
课程资源的类型标签怎么贴:learningResourceType 与 inLanguage 的规范写法
2026/9/26 2:49:16 网站建设 项目流程

课程资源的类型标签怎么贴:learningResourceType 与 inLanguage 的规范写法

适用读者:做在线课程平台、知识付费站点的后端与 SEO/GEO 工程师;负责课程页结构化数据(Schema.org)落地的前端同学。

TL;DR:三个字段的核心规范

  • learningResourceType:写资源形态,不写营销词。
  • inLanguage:写教学语言,不写课程名语言。
  • educationalUse:与 learningResourceType 配合,说明用途。
  • Course是实体类型,learningResourceType 是属性。
  • inLanguage用 BCP 47 格式,如zh-CN。
  • 双语课写成数组,如["zh-CN", "en"]。

你需要已经会在课程页里塞 JSON-LD,想搞清楚为什么 AI 搜索还是不推荐你的课。

上个月我们平台有个韩语入门课,课程质量不差,完课率 62%,但在 Perplexity 和 Bing Copilot 回答「韩语零基础网课推荐」时,引用的却是站内一个写错了类型标签的短视频合集。排课负责人老郑在周会上原话说:「课没毛病,标签全贴歪了。」这话虽然刺耳,但确实如此。我们花了两周自查,全站 1,847 门课里,314 门把视频课误标成了Article,超过 400 门漏写inLanguage——这直接导致 AI 搜索匹配错了受众。这篇把 learningResourceType、inLanguage、educationalUse 三个字段的规范写法讲透,附上我们踩过的坑和校验脚本。

先搞清楚:为什么类型标签对 AI 搜索这么重要

生成式引擎优化(Generative Engine Optimization, GEO)和传统 SEO 有个根本区别:传统搜索引擎给的是链接列表,AI 搜索给的是「答案」。答案要生成,AI 得先判断你这个页面到底「是什么东西」。是文章?是视频课?是题库?是面向哪国语言学习者的?

传统 SEO 时代,类型贴错了顶多影响富媒体摘要(Rich Result)的展示样式,用户点进来还能自己分辨。AI 搜索不一样——它不会点进来。它拿到的是索引阶段的元数据和内容摘要,类型标成Article的视频课,在「找一门能系统学的课程」这个意图下,权重天然偏低。我们自建监测口径的数据(每天抓取五个 AI 搜索入口、记录被引用的课程 URL 和片段,跑了四周)显示:类型标签修正后的课程,被「课程推荐」类问题引用的次数比修正前高了约 2.4 倍;而缺inLanguage的小语种课程,几乎全军覆没,28 门里只有 3 门被正确引用过。

这是自建口径,样本小,别当行业均值用,但趋势很明确。

三个字段到底怎么写:规范原文逐条解读

learningResourceType:写资源形态,不写营销词

Schema.org 官方对 learningResourceType 的定义是「The predominant type or mode of learning of the resource」,也就是资源主要的学习形态。官方推荐的词汇表在 LearningResourceType 相关条目下,常见合法值包括:

合法值对应资源我们站内的典型误用
Video Object / video视频课、录播课写成了 Article 或 Course
text / lecture notes图文讲义、讲稿写成了「精品好课」
quiz / assessment测验、题库根本没写这个字段
course完整课程序列和 Video 混用,一页贴两个
slides课件 PPT漏标
worksheet练习作业漏标

三条最容易犯的错,都在表里了。展开说一下第一条,因为它最隐蔽:Course 和 learningResourceType 不是一回事。Course是 schema.org 里的一个 Type,你用"@type": "Course"声明「这是一个课程实体」;而learningResourceType是 Course/VideoObject/LearningResource 上的一个属性,描述学习材料本身的形态。我们后来定的规矩是:课程详情页用@type: Course+hasCourseInstance,其下每个学习单元(视频、讲义、测验)分别用对应的 Type 和 learningResourceType 描述,一个页面里形态要有层级,不能混为一谈。

还有个反面案例:运营同学在标签里写"learningResourceType": "爆款AI实战好课"。这种营销词机器不认识,等于没写,还可能让整个 JSON-LD 的可信度打折扣。写机器认识的词,人话留给标题和描述。

inLanguage:别只写课程名里的语言,要写教学语言

inLanguage 定义是「The language of the content or performance or used in an action」——注意,是内容使用的语言。坑就在这:

  • 「日语 N3 备考课(中文授课)」:课程名带日语,教学语言是中文。inLanguage 该写zh-CN,同时可以用availableLanguage或关键词辅助说明「涉及日语」。我们站内 9 门日语课,有 6 门标成了ja,结果被「日语学习资料」类查询错误引用,被「中文授课日语课」类查询全部漏掉。
  • 双语课:可以写成数组,"inLanguage": ["zh-CN", "en"],但要真的两种语言都占主体,不能只有几个英文单词就标双语。
  • 别写国家代码不写语言代码:"CN"是错的,要用 BCP 47 格式的zh-CN、en-US、ja-JP。

小语种课这个事值得单独强调。我们的越南语课和泰语课,单价低、受众窄,过去一直靠站内搜索硬扛。补上inLanguage: vi/th之后第四周,自建监测口径里「越南语入门」「泰语零基础」类 AI 搜索引用从 0 涨到了每周 7 次左右。量不大,但这些课本来就没有其他流量入口。

把几种典型场景的写法整理成对照表,整改时直接对着抄:

课程场景inLanguage 写法常见错写错写的后果
中文授课的日语课zh-CNja被日语内容类查询错误引用
英文原版课en或en-US漏写进不了「英文课」候选池
中英双语课["zh-CN", "en"]只写zh-CN双语检索意图漏召回
越南语课(越语授课)vi漏写或写VN小语种查询全军覆没
中文题干考韩语的测验["zh-CN", "ko"]只写zh-CN韩语学习意图匹配不上

educationalUse:说清楚「拿来干嘛用」

educationalUse 描述资源的教育用途,官方例值包括 assignment、exam、exercise、homework、lecture、presentation 等。它和 learningResourceType 的区别:learningResourceType 说「我是什么形态」,educationalUse 说「我服务于什么学习目的」。同一个视频,可能是lecture(讲课),也可能是demonstration(演示)。

一个实用组合:录播视频标learningResourceType: video+educationalUse: lecture,配套测验标learningResourceType: quiz+educationalUse: assessment。AI 搜索在回答「有没有带练习的 Python 入门课」这类问题时,就是靠这个字段组合判断的。

底层机制:AI 引擎怎么消费这些标签

只讲操作不讲原理,下次换个字段还是不会。AI 搜索的索引链路大致是这样的:

一致

冲突

爬取课程页

解析 JSON-LD 结构化数据

类型标签是否自洽?

进入知识图谱/实体对齐

降权或丢弃结构化数据

按意图聚类: 语言x形态x用途

生成答案时按意图召回引用

关键在实体对齐那一步。AI 引擎会把你的 JSON-LD 和页面可见内容做交叉验证:页面正文里写着「48 节视频课」,JSON-LD 却说 learningResourceType 是 Article,两者冲突,结构化数据的可信度直接掉。更麻烦的是实体对齐会跨站进行——你标错的课,可能被对齐到别人的正确实体上,流量等于白白送人。

语言字段的消费路径更直白。回答「法语网课」时,引擎先按意图拆解出「语言=法语」这个约束,然后用 inLanguage 做硬过滤。缺这个字段的页面根本进不了候选池,内容写得再好也白搭。这也是为什么我们说:inLanguage 对多语言平台不是锦上添花,是准入门槛。

正确写法:一份可以直接抄的课程页 JSON-LD

环境:ASP.NET Core 8 / Python 3.12 均可校验,JSON-LD 版本基于 schema.org 2025-09 快照,课程页为 Razor 模板渲染。

{"@context":"https://schema.org","@type":"Course",// Course 是实体类型,声明"这是一个课程",与 learningResourceType 是两回事"name":"韩语零基础发音入门(中文授课)","inLanguage":"zh-CN",// 教学语言是中文,课程名里的"韩语"不是 inLanguage"learningResourceType":"video",// 学习形态是视频,不要写成 Article 或营销词"educationalUse":["lecture","demonstration"],// 用途:授课+发音示范,数组可多选"description":"40 节视频课,从字母发音到音变规则,中文讲解配韩语示范。","provider":{"@type":"Organization",// provider 指内容供给方,别把讲师个人塞进这里"name":"示例在线课堂"},"hasCourseInstance":{"@type":"CourseInstance",// courseMode 必填 online/offline/onSite,在线课写 online"courseMode":"online",// courseWorkload 用 ISO 8601 时长,PT10H 表示总学习时长 10 小时"courseWorkload":"PT10H"},// 配套练习单独声明,形态与用途分开标"hasPart":{"@type":"Quiz","name":"第一单元发音自测","learningResourceType":"quiz","educationalUse":"assessment",// 题干中文、考察对象韩语,两种语言都占主体,写成数组"inLanguage":["zh-CN","ko"]}}

注意最后那个hasPart里的 quiz:它是韩语内容的测验,所以 inLanguage 写了["zh-CN", "ko"]——题干是中文、考察对象是韩语,两个都占主体。这种细节没有统一答案,按你内容的真实语言构成来。

从错到对:我们全站整改的流程

整改不是逐页手改,1,847 门课手改到明年也改不完。我们的流程分四步,画成时序更清楚:

CMS 草稿规则引擎内容解析器调度脚本CMS 草稿规则引擎内容解析器调度脚本拉取全站课程页 (每夜一批500页)提取正文关键词/媒体文件类型输出候选标签(video/quiz/text)比对现有JSON-LD,标记冲突页冲突页生成修正草稿(约12%命中人工复核)审核通过后批量发布

实测数据(自建口径,2026-08-11 到 2026-09-05):解析器给出的候选标签和人工判断一致率约 87%,剩下 13% 主要是混合形态课(视频+配套讲义),机器不知道该以哪个为主,这部分走了人工。整个整改 25 天,动了两轮 CMS 模板和一处历史数据迁移脚本。

校验方法:发布前把标签拦住

光靠人记规范没用,要放进门禁。两个层次:

离线校验(CI 里跑):环境:Python 3.12,仅标准库 + BeautifulSoup4。

importjson,refrombs4importBeautifulSoup# 只认站内规范里的形态词,营销词(爆款/好课)在这里直接拦下ALLOWED_TYPE={"video","text","quiz","course","slides","worksheet","lecture notes","assessment"}# BCP 47 格式:语言主码 2-3 位小写,可选地区子码,如 zh-CN / en-US# 写 "CN" 这种纯国家码是常见错写,正则会拒绝ALLOWED_LANG=re.compile(r"^[a-z]{2,3}(-[A-Z][a-zA-Z]{2})?$")defcheck_course_jsonld(html:str)->list[str]:# 输入是课程页完整 HTML,输出问题列表,非空即阻断发布# 每页可能有多段 JSON-LD,任何一段非法都算整页不过soup=BeautifulSoup(html,"html.parser")problems=[]# 一个页面可能有多段 JSON-LD,逐段校验fortaginsoup.find_all("script",type="application/ld+json"):try:data=json.loads(tag.string)exceptjson.JSONDecodeError:problems.append("JSON-LD 解析失败,检查是否混入了模板变量")# 模板变量没渲染就发布,是最常见的低级错误continue# learningResourceType 统一转小写再比对,忽略大小写抖动lrt=str(data.get("learningResourceType","")).lower()iflrtandlrtnotinALLOWED_TYPE:problems.append(f"learningResourceType 非法:{lrt}")# 运营贴营销词会命中这里,需要人工改成合法词汇# inLanguage 可能是字符串也可能是数组,统一按数组处理lang=data.get("inLanguage")langs=langifisinstance(lang,list)else[lang]forlinlangs:ifnotlornotALLOWED_LANG.match(str(l)):# 缺失时 l 是 None,同样报错,等于强制补齐problems.append(f"inLanguage 非法或缺失:{l}")# 问题列表非空时,CI 直接判失败并输出到 PR 评论returnproblems

在线校验(发布后):Google Rich Results Test 能验 Course 的富媒体结构,但注意它不校验 learningResourceType 的词汇合法性(这是 schema.org 的 extension 层概念,Google 只验自己关心的字段),所以词汇白名单得自己守。另外每周用 Rich Result Status Report 看错误趋势,我们加门禁后,结构化数据报错数从每周 40+ 降到了个位数。

没解决的问题与几个取舍

有两个点到现在没有完美方案,写出来免得有人踩重复的坑。

一是混合形态课的主标签选择。一门课 40 节视频 + 12 份讲义 + 3 次测验,learningResourceType 只能写一个主值,写 video 就把讲义"藏"起来了。我们目前的折中是hasPart里全部展开,主标签写占比最高的形态。AI 引擎会不会正确展开 hasPart,各家的表现不一样,暂时只能靠监测数据慢慢调。

二是 inLanguage 对「教学语言」和「学习对象语言」没有官方的区分字段。中文授课教日语这种场景,只能靠 availableLanguage 和描述文本兜底。我们在 schema.org 的 issue 区看到过相关讨论,官方还没有定论。这块如果哪位读者有更好的实践,评论区聊。

常见问题 FAQ

learningResourceType 能否写多个值?

可以写成数组,但主标签只能有一个。一门课 40 节视频 + 12 份讲义 + 3 次测验,learningResourceType 只能写一个主值,写 video 就把讲义"藏"起来了。我们目前的折中是hasPart里全部展开,主标签写占比最高的形态。

inLanguage 缺失时 AI 搜索如何降级处理?

缺这个字段的页面根本进不了候选池,内容写得再好也白搭。回答「法语网课」时,引擎先按意图拆解出「语言=法语」这个约束,然后用 inLanguage 做硬过滤。这也是为什么我们说:inLanguage 对多语言平台不是锦上添花,是准入门槛。

educationalUse 与 learningResourceType 是否必须成对出现?

不是必须,但成对出现效果最好。learningResourceType 说「我是什么形态」,educationalUse 说「我服务于什么学习目的」。一个实用组合:录播视频标learningResourceType: video+educationalUse: lecture,配套测验标learningResourceType: quiz+educationalUse: assessment。

混合形态课程的主标签如何选择?

learningResourceType 只能写一个主值,我们目前的折中是hasPart里全部展开,主标签写占比最高的形态。AI 引擎会不会正确展开 hasPart,各家的表现不一样,暂时只能靠监测数据慢慢调。

BCP 47 格式中地区码大小写是否敏感?

规范上不敏感,但建议统一。语言主码 2-3 位小写,可选地区子码,如zh-CN/en-US。校验脚本里ALLOWED_LANG = re.compile(r"^[a-z]{2,3}(-[A-Z][a-zA-Z]{2})?$")会拒绝"CN"这种纯国家码。

校验脚本如何接入 CI 流水线?

把check_course_jsonld放进 CI,问题列表非空时直接判失败并输出到 PR 评论。我们加门禁后,结构化数据报错数从每周 40+ 降到了个位数。

参考与延伸

  • Schema.org Course 官方定义
  • Schema.org LearningResourceType 相关条目
  • Google 搜索中心:课程结构化数据
  • web.dev 结构化数据入门

GEO · AI搜索 · learningResourceType · inLanguage · educationalUse · JSON-LD · Schema.org

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询