关于 Lambë
Lambë 是用于检查和转换结构化数据的查询语言与命令行工具,无需预先了解数据形状。它会分析各管道阶段的数据形状和输出格式兼容性,并提供模式检查与可操作的错误提示。
主要功能
- 01查询 JSON、YAML、TOML、HCL、CSV、TSV 和 Markdown
- 02查看每个管道阶段的数据形状
- 03检查输出格式兼容性并建议转换方式
- 04使用 JSON Schema 检查数据形状
- 05交互式 REPL 与 Dart 库
适用场景
- 01探索不熟悉的 API 响应
- 02审查 Helm chart 或 CI 配置
- 03根据数据模式检查查询
- 04让 AI Agent 根据形状信息提取字段
MCP 相关
Lambë 附带 MCP 服务,可将查询能力作为运行时工具提供。它也以 Agent Skill 形式发布。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Lambë
一种用于结构化数据的查询语言,让你了解正在处理的数据。
lam 可查询 JSON、YAML、TOML、HCL、CSV、TSV 和 Markdown。与其他查询工具不同,它会在运行查询前告诉你查询会做什么——每个管道阶段的数据形状、哪些输出格式可以序列化结果,以及可能出错的地方。
当你还不了解数据时,可以使用它:检查陌生的 API 响应、审计 Helm chart、验证 CI 管道的假设,或让 AI Agent 提取信息而不猜测数据结构。
$ lam --to toml '.dependencies | keys' pubspec.yaml
Error: TOML output requires a map at the root, got list<string>.
Try appending one of:
| as(toml) # Wraps the list under a single-entry map (equivalent to `{items: .}`).
$ lam --to toml '.dependencies | keys | as(toml)' pubspec.yaml
items = ["rumil", "rumil_parsers", "rumil_expressions"]
查询的范围有限,且总会终止。没有递归、没有 lambda、没有 def。这是必要的取舍:Lambë 不试图成为一种编程语言,因此它的形状推断、--explain、--schema 和错误修复建议都能正常工作。
Lambë(发音为“lam-beh”)在昆雅语(托尔金创作的精灵语)中意为“语言”。为兼容 ASCII,软件包名称为 lambe。
安装
单行安装脚本(适用于 Linux 和 macOS,无需 sudo,并会验证 SHA256 校验和):
curl -fsSL https://raw.githubusercontent.com/hakimjonas/lambe/main/install.sh | sh
此脚本会从最新的 GitHub 发布版本下载 lam 和 lam-mcp,并将它们安装到 ~/.local/bin/。支持环境变量 LAMBE_VERSION(固定版本)和 LAMBE_PREFIX(更改安装目录);详情请参阅脚本。
其他选项:
# From pub.dev (Dart users)
dart pub global activate lambe
# Dart library
dart pub add lambe
# Build from source
git clone https://github.com/hakimjonas/lambe.git && cd lambe
dart compile exe bin/lam.dart -o lam
有关所有安装选项,请参阅入门指南。
感知形状的输出
Lambë 会检查查询结果是否符合目标格式可序列化的数据形状。符合时,就会生成输出;不符合时,错误会指出所需的形状,并列出可用于转换的查询片段。在交互式终端中,Lambë 会询问是否应用所选片段,并在原处重试。
$ lam --to toml '.name' pubspec.yaml
TOML output requires a map at the root, got string.
Try appending one of:
| as(toml) # Wraps the scalar under a single-entry map (equivalent to `{value: .}`).
Apply a bridge?
[1] | as(toml) # Wraps the scalar under a single-entry map (equivalent to `{value: .}`).
[q] cancel
> 1
value = "rumil"
同样的流程也适用于 CSV 和 TSV(根节点必须是记录列表)以及 HCL(必须是映射)。
建议会展示意图层面的 as(<format>) 形式。说明中会指出桥接操作组合使用的原始片段({value: .}、to_entries 等),因此任何需要这些功能的人仍可使用 --explain 和手动组合。
CSV/TSV 中的非标量单元格
默认情况下,CSV/TSV 单元格中的嵌套列表或映射会被拒绝——分隔符格式无法忠实地呈现它们。如果你需要快速导出且可以接受有损转换,可以使用 --flatten-cells json(CLI)或 :flatten-cells json(REPL),将它们作为 JSON 字符串内嵌编码。将生成的文件重新读入 Lambë 并不能恢复原始结构;如果需要保留数据完整性,请优先在查询中重塑数据。
as(fmt) — 在查询语言中进行桥接
如果事先已知目标格式的数据形状,as(fmt) 可以在查询中完成桥接。当输入已符合目标要求时,此组合器不执行任何操作;如果存在一个经过筛选的桥接方案,则应用该方案;如果有多个方案适用,则列出候选项。
$ lam --to toml '.dependencies | as(toml)' pubspec.yaml
rumil = "^0.6.0"
rumil_parsers = "^0.6.0"
rumil_expressions = "^0.6.0"
$ lam --to csv '.dependencies | as(csv)' pubspec.yaml
key,value
rumil,^0.6.0
rumil_parsers,^0.6.0
rumil_expressions,^0.6.0
as 接受 json、yaml、toml、csv、tsv 和 hcl。
--explain — 查看每个管道阶段的数据形状
--explain 会遍历查询的管道主干,报告每个阶段的数据形状,然后列出最终形状可序列化为哪些输出格式。它只执行静态分析,不会计算查询;传入数据文件可用实际形状信息进行分析,也可省略文件,以未知输入为基础进行追踪。
$ lam --explain '.dependencies | keys' pubspec.yaml
.dependencies : map<rumil: string, rumil_parsers: string, rumil_expressions: string>
| keys : list<string>
Writable as: json, yaml, csv, tsv
Not writable as: toml, hcl
默认情况下,Explain 会标记可证明结果为空的筛选器(针对已知形状使用 filter(.missing)),以及会在运行时因不匹配而被拒绝的查询(对非列表输入使用 filter)。传入 --explain-trivial,还会标记其参数引用缺失字段的 sort_by/group_by/map/unique_by(通常是拼写错误,有时是有意为之)。对于 Agent 工具和构建管道,--explain-json 会以结构化 JSON 文档的形式输出相同信息。
--schema — 声明形状,让 Lambë 检查你的工作
如果你有数据的 JSON Schema(来自 API 契约、OpenAPI 规范或手写文档),可以将 --schema 指向它:
$ lam --schema api.schema.json --explain '.users | map(.email)' response.json
.users : list<map<id: string, name: string, email: optional<string>>>
| map(.email) : list<optional<string>>
Writable as: json, yaml, csv, tsv
Not writable as: toml, hcl
Schema 可以补充单靠数据无法表达的信息:可选字段(来自 JSON Schema 的 required)、空列表中元素的形状,以及 shapeOf 无法通过采样推断的类型。--explain 会显示这些信息;求值器会信任它们。
存在数据时,Lambë 也会进行验证:如果 Schema 声明 age: number,而数据中的 age: "30",那么加载时会以 1 退出,并给出带 JSON 路径注释的诊断信息。不会静默偏离,也不会针对与契约不匹配的数据运行查询。
系统会自动检测同级的 <datafile>.schema.json,因此将 Schema 放在数据文件旁边的项目约定无需显式标志即可生效。
反向操作也同样对称:lam --print-shape data.json 会将推断出的形状输出为 JSON Schema 文档。往返示例:
lam --print-shape data.json > data.schema.json # bootstrap a schema from data
lam --schema data.schema.json '.users' data.json # use it back
接受的 JSON Schema 关键字:type、properties、items、required。值级约束(minimum、pattern、enum 等)、结构组合器(allOf、oneOf)、$ref 和条件 Schema 都会因各自的关键字而被拒绝。Lambë 是形状系统,而非验证引擎;如需更丰富的验证,请使用 ajv 或 check-jsonschema。
查询语法
查询以 .(当前数据)开头,并使用 | 链接操作:
. the whole document
.name access a field
.users[0] index into a list
.users[0].address.city chain access
.users | filter(.age > 30) pipe into an operation
.users | map(.name) transform each element
管道从左向右读取。每个 | 都会将结果传递给下一个操作:
.users | filter(.active) | sort_by(.name) | map(.name)
此查询会获取 .users,保留活跃用户,按姓名排序,并提取姓名。
表达式
.price * .qty arithmetic (+, -, *, /, %)
.age > 30 comparison (<, >, <=, >=, ==, !=)
.active && .verified logic (&&, ||, !)
if .age > 65 then "senior" else "active" conditional
{name, total: .price * .qty} construct a new object
"\(.name) is \(.age)" string interpolation
.[1:3] slice a list or string
操作
操作跟在 | 后面,用于转换通过管道传递的值:
. | filter(.age > 30) keep matching elements
. | map(.name) transform each element
. | sort_by(.age) sort by a key
. | group_by(.dept) group into [{key, values}]
. | length count elements
. | first first element
. | sum sum numbers
. | keys map keys or list indices
. | has("field") check if a field exists
. | unique remove duplicates
. | flatten flatten one level of nesting
. | to_entries map to [{key, value}] pairs
. | filter_values(. > 5) filter a map's values
. | as(toml) bridge to an output format
请参阅下文管道操作中的完整列表。
CLI
# Extract values
lam '.database.host' config.toml
lam '.spec.containers[0].image' deployment.yaml
# Filter and transform
lam '.users | filter(.age > 30) | map(.name)' data.json
# Aggregate
lam '.items | map(.price) | sum' data.json
# Sort and pick
lam '.items | sort_by(.price) | first' data.json
# Object construction
lam '.users | map({name, senior: .age > 65})' data.json
# String interpolation
lam '.users | map("\(.name) is \(.age)")' data.json
# Shape trace
lam --explain '.users | map(.name)' data.json
# Shape inspection (JSON Schema output)
lam --print-shape data.json
# Schema-checked queries: validate data against a schema as it runs
lam --schema api.schema.json '.users | map(.email)' response.json
# CI validation
lam --assert '.version != "0.0.0"' package.json
lam --assert '.replicas >= 2' deployment.yaml
# Format conversion
lam --to yaml '.config' data.json
lam --to csv '.users | map({name, age})' data.json
lam --to toml '.config | as(toml)' data.json
lam --to csv --flatten-cells json '.users' data.json # encode nested cells as JSON
# Line-delimited JSON (logs, event streams)
lam --ndjson '.user.id' events.ndjson
tail -f app.log | lam --ndjson '.level'
# Query any format (auto-detected from extension)
lam '. | filter(.status != "closed")' issues.csv
lam '.resource | map(._labels)' main.tf
lam '.children | filter(.type == "heading") | map(.children[0].text)' README.md
# Pipe from stdin
curl -s https://api.example.com/users | lam '.results | filter(.active)'
交互式 REPL
lam -i data.json
lambe v0.14.0 - type :help for commands, :q to quit
Data loaded: {3 fields, 42 users}
lambe> .users | filter(.age > 30) | map(.name)
["Bob", "Carol"]
lambe> .users[0]
{name: "Alice", age: 25, active: true}
lambe> :schema
{users: [{name: "string", age: "number", active: "boolean"}]}
lambe> :to yaml
Output format: yaml
当查询产生的结果无法通过当前输出格式序列化时,REPL 会在行内列出可用的桥接方式;按下建议对应的数字即可应用该桥接方式并打印转换后的输出。字段名(.us<TAB>)和管道操作(| fil<TAB>)均支持 Tab 补全。REPL 还支持语法高亮、持久化历史记录(~/.lambe_history)、Ctrl+R 反向搜索,以及使用 \ 续行的多行输入。
库
import 'package:lambe/lambe.dart';
// Query pre-parsed data
final name = query('.users[0].name', data);
// Query a JSON string
final version = queryJson('.version', '{"version": "1.0.0"}');
// Query any format
final host = queryString('.database.host', tomlString, format: Format.toml);
// Parse once, evaluate many times
final ast = parseAst('.users | filter(.active) | map(.name)');
final result1 = evaluateAst(ast, dataset1);
final result2 = evaluateAst(ast, dataset2);
// Format conversion
final yaml = formatOutput(data, OutputFormat.yaml);
final csv = formatOutput(users, OutputFormat.csv);
// Shape inference and JSON Schema output
final shape = shapeOf(data); // Shape ADT
final schemaJson = renderJsonSchema(shape); // JSON Schema text
// Or parse a schema file and merge with observed data
final schema = parseJsonSchema(schemaSource);
final merged = mergeSchemaWithData(schema, shape); // throws on disagreement
形状和桥接 API
// Infer the structural shape of a value
final shape = shapeOf(data);
// e.g. SMap({'users': SList(SMap({'name': SString(), 'age': SNum()}))})
// Check whether a value can be written in a given format
final report = canWriteAs(result, OutputFormat.toml);
switch (report) {
case Writable():
stdout.writeln(formatOutput(result, OutputFormat.toml));
case NotWritable(:final suggestions):
for (final r in suggestions) {
print('${r.label}: | ${r.display} — ${r.explanation}');
}
}
// Compose a user query with a bridge fragment
final bridges = synthesize(shape, OutputFormat.csv);
if (bridges.isNotEmpty) {
final composed = applyBridge(userAst, bridges.first);
final bridged = evaluateAst(composed, data);
}
// Static shape trace
final trace = explain(parseAst('.users | map(.name)'), shapeOf(data));
for (final stage in trace.stages) {
print('${stage.source}: ${renderShape(stage.shape)}');
}
支持的格式
| 格式 | 输入 | 输出 | 符合规范 |
|---|---|---|---|
| JSON | 是 | 是 | RFC 8259(318/318) |
| YAML | 是 | 是 | YAML 1.2.2(333/333) |
| TOML | 是 | 是 | TOML 1.1(681/681) |
| HCL/Terraform | 是 | 是 | HashiCorp 规范(2760/2760) |
| CSV | 是 | 是 | RFC 4180 + 自动方言检测 |
| TSV | 是 | 是 | CSV 的制表符分隔变体 |
| Markdown | 是 | — | CommonMark 0.31.2(652/652) |
解析器来自 rumil_parsers,并通过官方规范测试套件进行测试。
此版本中 Markdown 仅支持输入。Markdown AST 是一种呈现树,而非数据结构,因此无法将任意查询结果通用地映射回 Markdown 文本。Markdown 文档的投影(标题列表、计数、筛选后的章节)可通过常规 --to 标志输出为 JSON、YAML、CSV 或 TSV。
流水线操作
| 操作 | 示例 | 说明 |
|---|---|---|
filter | .users | filter(.active) | 保留符合谓词条件的元素 |
map | .users | map(.name) | 转换每个元素 |
sort | . | sort | 按自然顺序排序 |
sort_by | .users | sort_by(.age) | 按键排序 |
group_by | .users | group_by(.dept) | 分组为 {key, values} |
unique | . | unique | 移除重复项 |
unique_by | .users | unique_by(.id) | 按键移除重复项 |
flatten | . | flatten | 展平一层 |
reverse | . | reverse | 反转顺序 |
keys | . | keys | 映射的键或列表索引 |
values | . | values | 映射的值 |
length | . | length | 列表、映射或字符串的长度 |
first | . | first | 第一个元素 |
last | . | last | 最后一个元素 |
sum | . | sum | 数字总和 |
avg | . | avg | 平均值 |
min | . | min | 最小值 |
max | . | max | 最大值 |
has | . | has("name") | 检查字段是否存在 |
to_entries | . | to_entries | 将映射转换为 [{key, value}] |
from_entries | . | from_entries | 将 [{key, value}] 转换为映射 |
to_number | .price | to_number | 将字符串解析为数字 |
type | . | type | 以字符串形式返回运行时类型 |
filter_values | . | filter_values(. > 5) | 筛选映射的值 |
map_values | . | map_values(. * 2) | 转换映射的值 |
filter_keys | . | filter_keys(. != "secret") | 筛选映射的键 |
as | . | as(toml) | 转换为输出格式的数据形状 |
AI 集成
Lambë 同时以 Agent Skill(作为专业知识加载到 Agent 的会话中)和 MCP 服务(可调用的运行时工具)的形式提供。
Agent Skill
该技能文件夹位于此仓库的 .agents/skills/lambe/,遵循跨厂商的 agent-skills 规范,Claude Code、OpenAI Codex、GitHub Copilot、Cursor 和 Microsoft Agent Framework 均可读取此规范。
若要让其他项目中的 Agent 使用 Lambë,请将该文件夹复制到 Agent 约定的位置:
# Personal (available across all your projects)
git clone https://github.com/hakimjonas/lambe /tmp/lambe-skill
mkdir -p ~/.agents/skills
cp -r /tmp/lambe-skill/.agents/skills/lambe ~/.agents/skills/
# Project-local
cp -r /tmp/lambe-skill/.agents/skills/lambe <your-project>/.agents/skills/
遵循此规范的 Agent 会在会话开始时自动发现该技能。
MCP 服务
安装后,将 .mcp.json 添加到项目中:
{
"mcpServers": {
"lambe": {
"command": "lam-mcp",
"args": []
}
}
}
这样,AI 助手便可使用五种工具,覆盖整个反馈循环:
lambe_query— 提取、筛选和转换数据;可选的schema参数会在查询运行前对数据进行结构验证。lambe_print_shape— 检查不熟悉的数据;返回 JSON Schema 子集文档。lambe_check— 根据 JSON Schema 验证数据。返回{"ok": true}或{"ok": false, "error": "..."},并指出不匹配的路径。lambe_explain— 静态跟踪查询(可带数据,也可不带);返回结构化 JSON 报告,其中包含各阶段的数据形状、警告和可写性。lambe_assert— 对查询结果进行布尔断言。
当 lambe_query 遇到请求的输出格式与数据形状不匹配时,错误响应会包含结构化的 suggestions 数组:每个条目都包含 template_text、apply_as(将模板附加到原始表达式后形成的完整查询)以及一行说明。Agent 可以原样使用 apply_as 再次调用该工具。
面向 AI 编码 Agent
将 AGENTS.md 和 .mcp.json 添加到项目根目录。打开项目的 AI 助手会发现并使用 Lambë 执行数据查询。
在 CI 中
# Validate config in GitHub Actions
- run: |
dart pub global activate lambe
lam --assert '.version != "0.0.0"' pubspec.yaml
lam --assert '.jobs | keys | length > 0' .github/workflows/ci.yml
测试匹配器
lambe_test 软件包为 Dart 提供测试匹配器:
import 'package:lambe_test/lambe_test.dart';
expect(response, lamWhere('.errors | length == 0'));
expect(config, lamEquals('.database.port', 5432));
expect(data, lamMatches('.name', startsWith('A')));
expect(data, lamHas('.users[0].address.city'));
文档
- 入门 - 安装和首次查询
- 语法参考 - 完整的查询语言
- REPL 指南 - 交互模式、命令和键盘快捷键
- Schema 指南 - JSON Schema 子集、合并语义和往返转换
- 示例 - Kubernetes、Terraform、CI、CSV 的实际模式
- Man 手册页 - Unix 手册页(
man -l doc/lam.1)
lambë 不是什么
Lambë 是一个作用于 JSON 形状数据的有界树转换器。它有意省略了图灵完备性、用户定义函数、递归下降(..)、try/catch、正则表达式、流式处理和原地修改。保持有界,才能实现形状推断、--explain 和 as(fmt) 桥接。
完整列表以及替代各项省略功能的 lambë 惯用法,请参阅 doc/non-goals.md。
设计
架构和设计决策请参阅 DESIGN.md。
Arda 生态系统的一部分
基于支持左递归语法的 Rumil 解析器组合器构建。
- Rumil - 支持左递归的解析器组合器
- Rumil Parsers - 用于 JSON、YAML、TOML、XML、CSV、HCL、Proto3 和 Markdown 的格式解析器
- Rumil Expressions - 共享的求值辅助工具
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。