news 2026/4/16 20:16:16

LangChain4j Metadata 说明与使用指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangChain4j Metadata 说明与使用指南

dev.langchain4j.data.document.Metadata是 LangChain4j 在 RAG / 文档处理链路里的元数据对象,用于给DocumentTextSegment挂载结构化附加信息,比如文件名、来源、更新时间、owner、业务标签等。官方 RAG 教程对它的定义很明确:Metadata是一个key-value map,key 是String,value 支持StringIntegerLongFloatDoubleUUID。(GitHub)


1. 这个类是什么

在 LangChain4j 官方 RAG 文档里,Document持有一个Metadata,它用来保存文档的补充信息,例如名称、来源、最后更新时间、owner,或者任何对检索和注入有帮助的上下文。官方还说明了它不只用于存储,还用于:

  • 把元数据一并注入 prompt,帮助模型理解上下文
  • 在检索阶段按 metadata 过滤内容
  • 在源文档更新时,依据 metadata 快速定位并同步 embedding store 中的对应记录 (GitHub)

所以一句话讲:

Metadata不是正文内容本身,而是文档/分块的“结构化上下文标签”。(GitHub)


2. 关键参数 / 数据结构

严格说,Metadata不是“带很多构造参数的配置类”,而是一个键值容器。它的关键点主要在“允许存什么”和“怎么组织 key”。

支持的 value 类型

官方文档明确列出的支持类型有:

  • String
  • Integer
  • Long
  • Float
  • Double
  • UUID(GitHub)

这意味着你不应该随便往里塞复杂 POJO、List、Map 之类的深层对象,至少从官方公开教程来看,这些不在标准支持集合里。做检索过滤时,优先用简单标量字段最稳。(GitHub)

常见 metadata key

官方没有强制统一 schema,但从教程和示例里,常见 key 包括:

  • file_name
  • index
  • animal
  • userId(GitHub)

其中,文档分块后每个TextSegment会继承原始Document的 metadata,并额外带一个index,表示它在文档中的分块位置,从 0 开始。(Javadoc)


3. 方法接口

官方 RAG 教程直接列出了Metadata的常用方法。你可以把它当成这个类最核心的 API 面。(GitHub)

静态工厂

  • Metadata.from(Map)

    从一个Map创建Metadata。(GitHub)

写入方法

  • Metadata.put(String key, String value)

  • put(String, int)

  • 以及其他对应类型的重载

  • Metadata.putAll(Map)

    一次性写入多个条目。(GitHub)

读取方法

  • Metadata.getString(String key)

  • getInteger(String key)

  • 以及其他类型读取方法

    官方说明这些方法会按所需类型返回对应 entry。(GitHub)

判断 / 删除 / 复制 / 转换

  • Metadata.containsKey(String key)
  • Metadata.remove(String key)
  • Metadata.copy()
  • Metadata.toMap()
  • Metadata.merge(Metadata)(GitHub)

兼容性提醒

官方旧版 deprecated 列表显示,早期一些基于Object的重载,例如Metadata.metadata(String, Object)from(String, Object)之类后来被弃用,官方建议改用更明确的 typed 版本。这个信号很重要:LangChain4j 在逐步收紧 Metadata 的类型边界。(Javadoc)


4. 核心方法的具体使用

下面我用接近官方风格的方式讲几个最常用方法。

4.1from(Map):批量创建

适合在 ingestion 时一次性挂上完整元数据。

importdev.langchain4j.data.document.Metadata;Metadatametadata=Metadata.from(Map.of("file_name","employee-handbook.pdf","department","HR","version",3));

依据官方教程,这种方式是标准入口之一。(GitHub)


4.2put(...)/putAll(...):逐步补充

适合文档经过多阶段处理时逐层补标签。

Metadatametadata=Metadata.from(Map.of("file_name","policy.txt"));metadata.put("owner","legal-team");metadata.put("year",2025);

如果一开始只知道一部分字段,后面再补sourcebiz_unitdoc_type,这种方式很自然。官方教程明确把putputAll列为常用方法。(GitHub)


4.3getString(...)/getInteger(...):按类型读取

适合 prompt 注入、自定义过滤、日志审计。

StringfileName=metadata.getString("file_name");Integerversion=metadata.getInteger("version");

这里要注意:读取时最好和写入类型保持一致。你如果把某个 key 作为Integer写入,后续就不要把它当String读,这样最稳。官方虽然没在教程里展开异常语义,但 typed getter 的设计本身就说明应当遵守类型一致性。(GitHub)


4.4containsKey(...)/remove(...)

适合做健壮性处理和 metadata 清洗。

if(metadata.containsKey("temp_flag")){metadata.remove("temp_flag");}

这类方法在做 ingestion pipeline 时很常见,比如先打一个临时标签,最终写库前清掉。(GitHub)


4.5copy()/merge(...)

适合“基础元数据 + 阶段元数据”的场景。

Metadatabase=Metadata.from(Map.of("file_name","faq.md","lang","zh-CN"));Metadataextra=Metadata.from(Map.of("channel","wechat","biz","support"));Metadatamerged=base.merge(extra);

官方把copy()merge(Metadata)都列为常用方法,说明这是设计时就考虑到的用法。(GitHub)


5. 典型调用链

Metadata单独看很简单,但它真正的价值在调用链里。

5.1 文档加载 → metadata 生成

官方 RAG 教程指出,Document包含Metadata;文档加载器会创建Document,其中 metadata 往往会携带来源信息。(GitHub)

典型形态:

Documentdocument=Document.from(text,metadata);

官方教程明确列出了Document.from(String, Metadata)。(GitHub)


5.2 文档切分 → segment 继承 metadata

官方DocumentBySentenceSplitter的 Javadoc 说明:每个TextSegment会继承原始Document的全部 metadata,并新增indexkey。(Javadoc)

这一步很关键,因为后续检索、过滤、prompt 注入,实际往往作用在TextSegment级别,而不是整篇Document。(Javadoc)


5.3 入库 → 向量库携带 metadata

官方 advanced RAG 示例里,TextSegment会被加到EmbeddingStore;示例同时展示了 metadata 被用于过滤和 prompt 注入。(GitHub)


5.4 检索 → 按 metadata 过滤

官方示例中直接用:

FilteronlyDogs=metadataKey("animal").isEqualTo("dog");

然后把这个 filter 传给EmbeddingStoreContentRetriever.builder().filter(...),从而把检索限制在某类 segment 上。(GitHub)


5.5 注入 prompt → 指定 metadataKeysToInclude

官方_04_Advanced_RAG_with_Metadata_Example里,用DefaultContentInjector.builder().metadataKeysToInclude(asList("file_name", "index")),把指定 metadata 字段一起注入 prompt,让模型知道内容来自哪个文件、属于第几个片段。(GitHub)


一条完整典型链路

可以概括成:

原始文档 → Document(text + Metadata) → Splitter 切分 → TextSegment 继承 Metadata + index → EmbeddingStore 入库 → ContentRetriever 按 Metadata 检索/过滤 → ContentInjector 把 Metadata 注入 prompt → LLM 回答

这条链路是官方 RAG 教程和两个 advanced metadata 示例共同展示出来的。(GitHub)


6. 适用边界

6.1 适合的场景

文档来源追踪

比如记录:

  • 文件名
  • URL
  • owner
  • 部门
  • 版本
  • 业务线

这正是官方列举 Metadata 用途的核心场景。(GitHub)

检索过滤

比如只检索:

  • 某个用户的数据
  • 某种文档类型
  • 某个主题
  • 某个租户的数据

官方示例已给出animal=doguserId=1这类过滤思路。(GitHub)

Prompt 增强

例如把file_nameindexsource一起注入 prompt,帮助模型回答“这条规则出自哪个文件”。官方示例就是这么做的。(GitHub)


6.2 不适合的场景

不适合塞复杂嵌套业务对象

从官方教程公开的支持类型看,Metadata 设计目标是简单标量键值,不是通用 JSON 文档容器。(GitHub)

不适合承载正文

正文仍然应放在Document.text()/TextSegment.text(),不要把一大段文本塞进 metadata。否则既影响过滤价值,也会让注入 prompt 时噪声很大。这个结论是基于官方设计职责作出的工程推断。(GitHub)

不适合 schema 混乱

同一个 key 一会儿写"2025",一会儿写2025,会让 typed getter、过滤条件和下游存储都变得不稳定。虽然官方没有专门写“禁止”,但从 typed API 和过滤示例看,保持 schema 稳定是必要的。(GitHub)


7. 中文场景优化

官方没有单列“中文优化”章节,但结合它给出的 Metadata 用法,可以提炼出几条对中文业务非常实用的做法。

7.1 key 用英文稳定命名,value 保留中文业务语义

推荐:

  • doc_type = "合同"
  • department = "法务"
  • lang = "zh-CN"
  • region = "华东"

不太建议 key 直接写成长中文句子。

原因是 key 往往还要参与过滤、prompt 模板、甚至不同存储系统的字段映射,英文 snake_case 更稳。这个建议是基于官方 key-value 设计与过滤链路作出的工程实践总结。(GitHub)

7.2 中文业务里优先补这几类字段

对中文企业文档尤其建议补:

  • file_name
  • source
  • doc_type
  • biz_unit
  • lang
  • effective_date
  • version
  • owner
  • tenant_id

因为这些字段最容易在“跨部门、多版本、同主题不同来源”的中文知识库里发挥作用。官方已经证明file_nameindexuserId、主题标签这类字段能直接影响检索和回答质量。(GitHub)

7.3 日期和编号要标准化

中文文档经常会出现:

  • 2025年10月
  • 2025/10
  • 十月 2025
  • 2025-10

如果 metadata 要参与过滤,建议统一成一种格式,比如:

  • 日期:yyyy-MM-dd
  • 月份:yyyy-MM
  • 版本:纯整数或统一字符串
  • 文号:规范字符串

这属于工程优化建议,但它直接服务于官方支持的 metadata filter 用法。(GitHub)

7.4 中文检索场景把lang显式打上

如果你的知识库里中英混合,建议显式加:

metadata.put("lang","zh-CN");

然后在检索器里按语言过滤,避免把英文 FAQ 和中文制度混在一起召回。这个是对官方 filter 机制的直接应用。(GitHub)


8. 一个更贴近中文项目的写法

importdev.langchain4j.data.document.Document;importdev.langchain4j.data.document.Metadata;Metadatametadata=Metadata.from(Map.of("file_name","员工手册-2025版.pdf","doc_type","制度","biz_unit","人力资源","lang","zh-CN","version",2025,"source","hr-policy-repo"));Documentdocument=Document.from(text,metadata);

后续切分后,每个 segment 会继承这些字段,并自动带上index。(GitHub)

如果要做过滤:

FilteronlyZhPolicy=metadataKey("lang").isEqualTo("zh-CN");

这种用法与官方 metadata filtering 示例是一致的。(GitHub)

如果要把来源一起注入模型:

ContentInjectorinjector=DefaultContentInjector.builder().metadataKeysToInclude(asList("file_name","doc_type","index")).build();

这与官方 advanced metadata injection 示例完全同类。(GitHub)


9. 实战建议

如果你把Metadata用在正式项目里,我建议把它当成一份小型 schema 来治理,而不是临时 Map。比较稳的做法是:

  • 先定义一组固定 key
  • 明确每个 key 的类型
  • 约束日期/版本/租户字段格式
  • 只把真正有检索价值、注入价值的字段放进去
  • 避免“同 key 多种类型”

这套做法和官方 API 设计方向是一致的:Metadata 适合做轻量、稳定、可过滤、可注入的上下文标签。(GitHub)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 20:11:14

Python实现斐波那契数列乱序加密与解密(附达芬奇密码案例)

Python实现斐波那契数列乱序加密与解密:从数学之美到密码实践 斐波那契数列这个数学界的瑰宝,不仅在自然界中随处可见黄金分割的身影,在密码学领域也展现出独特的魅力。当斐波那契数列遇上乱序加密,会碰撞出怎样的火花&#xff1f…

作者头像 李华
网站建设 2026/4/16 20:10:18

低成本全能科研AI工具!学生党闭眼冲

作为一个已经在博士阶段熬了三年的老科研狗,我太懂科研人刚进入一个新领域时的迷茫了,看着几十篇上百篇的英文文献头大,不知道从哪里切入;遇到跨领域问题想查资料,翻半天维基和综述还是摸不清脉络;实验卡壳…

作者头像 李华
网站建设 2026/4/16 20:09:45

从TMDS编码到FPGA实现:HDMI接口的硬件设计全解析

1. HDMI接口与TMDS技术基础 HDMI(高清多媒体接口)已经成为现代数字设备的标准配置,从4K电视到游戏主机再到专业显示器,几乎无处不在。但你是否想过,这个小小的接口是如何在物理层实现高速数据传输的?答案就…

作者头像 李华