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

2026-09-25 01:20:18 1 次浏览
GEOlearningResourceTypeSchema.orgAI搜索JSON-LD知识付费

你需要已经会在课程页里塞 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-CN ja 被日语内容类查询错误引用
英文原版课 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 搜索的索引链路大致是这样的:

flowchart LR
    A[爬取课程页] --> B[解析 JSON-LD 结构化数据]
    B --> C{类型标签是否自洽?}
    C -- 一致 --> D[进入知识图谱/实体对齐]
    C -- 冲突 --> E[降权或丢弃结构化数据]
    D --> F[按意图聚类: 语言x形态x用途]
    F --> G[生成答案时按意图召回引用]
    E --> G

关键在实体对齐那一步。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 门课手改到明年也改不完。我们的流程分四步,画成时序更清楚:

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

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

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

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

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

import json, re
from bs4 import BeautifulSoup

# 只认站内规范里的形态词,营销词(爆款/好课)在这里直接拦下
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})?$")

def check_course_jsonld(html: str) -> list[str]:
    # 输入是课程页完整 HTML,输出问题列表,非空即阻断发布
    # 每页可能有多段 JSON-LD,任何一段非法都算整页不过
    soup = BeautifulSoup(html, "html.parser")
    problems = []

    # 一个页面可能有多段 JSON-LD,逐段校验
    for tag in soup.find_all("script", type="application/ld+json"):
        try:
            data = json.loads(tag.string)
        except json.JSONDecodeError:
            problems.append("JSON-LD 解析失败,检查是否混入了模板变量")
            # 模板变量没渲染就发布,是最常见的低级错误
            continue

        # learningResourceType 统一转小写再比对,忽略大小写抖动
        lrt = str(data.get("learningResourceType", "")).lower()
        if lrt and lrt not in ALLOWED_TYPE:
            problems.append(f"learningResourceType 非法: {lrt}")
            # 运营贴营销词会命中这里,需要人工改成合法词汇

        # inLanguage 可能是字符串也可能是数组,统一按数组处理
        lang = data.get("inLanguage")
        langs = lang if isinstance(lang, list) else [lang]
        for l in langs:
            if not l or not ALLOWED_LANG.match(str(l)):
                # 缺失时 l 是 None,同样报错,等于强制补齐
                problems.append(f"inLanguage 非法或缺失: {l}")
    # 问题列表非空时,CI 直接判失败并输出到 PR 评论
    return problems

在线校验(发布后):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 区看到过相关讨论,官方还没有定论。这块如果哪位读者有更好的实践,评论区聊。

参考与延伸

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

🤖
本内容由 AI 辅助生成,经人工校对审核;部分素材、资料来源于公开网络,仅作个人观点分享与交流使用,无任何商业侵权意图。若内容、图片、文字涉及您的合法著作权、版权权益,请联系本人,核实后将第一时间删除、修改相关内容。