内容
内容 API 负责读取 L0/L1/L2 内容、写入文本,以及维护内容对应的语义和向量索引。
API 参考
abstract()
读取 L0 摘要(约 100 token 的概要),不包括 okf 文件头。
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| uri | str | 是 | - | Viking URI(必须是目录) |
Python SDK
abstract = client.abstract(uri="viking://resources/docs/")
print(f"Abstract: {abstract}")
# Output: "Documentation for the project API, covering authentication, endpoints..."TypeScript SDK
const abstract = await client.abstract("viking://resources/docs/");
console.log(abstract);Go SDK
abstract, err := client.Abstract(ctx, "viking://resources/docs/")
if err != nil {
return err
}
fmt.Println(abstract)HTTP API
GET /api/v1/content/abstract?uri={uri}curl -X GET "http://localhost:1933/api/v1/content/abstract?uri=viking://resources/docs/" \
-H "X-API-Key: your-key"CLI
openviking abstract viking://resources/docs/响应
{
"status": "ok",
"result": "Documentation for the project API, covering authentication, endpoints...",
"time": 0.1
}overview()
读取 L1 概览,适用于目录,不包括 okf 文件头。
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| uri | str | 是 | - | Viking URI(必须是目录) |
Python SDK
overview = client.overview(uri="viking://resources/docs/")
print(f"Overview:\n{overview}")TypeScript SDK
const overview = await client.overview("viking://resources/docs/");
console.log(overview);Go SDK
overview, err := client.Overview(ctx, "viking://resources/docs/")
if err != nil {
return err
}
fmt.Println(overview)HTTP API
GET /api/v1/content/overview?uri={uri}curl -X GET "http://localhost:1933/api/v1/content/overview?uri=viking://resources/docs/" \
-H "X-API-Key: your-key"CLI
openviking overview viking://resources/docs/响应
{
"status": "ok",
"result": "## docs/\n\nContains API documentation and guides...",
"time": 0.1
}read()
读取 L0/L1/L2 文件完整文本内容。
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| uri | str | 是 | - | Viking URI(如 viking://resources/docs/api.md)或 32 字符十六进制向量记录 id(由 stat() 返回) |
| offset | int | 否 | 0 | 起始行号(0 开始) |
| limit | int | 否 | -1 | 读取的行数,-1 表示读到结尾 |
| raw | bool | 否 | false | 返回未过滤 MEMORY_FIELDS 的原始存储内容(仅 HTTP API,Python SDK 暂未暴露)。 |
说明
read()只接受文件 URI。传入已存在的目录 URI 时返回INVALID_ARGUMENT(400),而不是NOT_FOUND。该错误会携带结构化的details字段——details.expected为"file",details.actual为"directory",details.resource为出错的 URI(HTTP 路径上会带上)——客户端据此即可以编程方式判断"文件 vs 目录"不匹配(例如回退到list),而无需对错误消息做字符串匹配。- 除 Viking URI 外,还可以传入
stat()返回的 32 字符十六进制文件id。服务端通过向量索引查找对应 URI 并执行相同的权限校验。由于索引是异步生成的,新返回的 ID 可能暂时无法解析;对应向量记录被删除后,按 ID 查询也会失败。这两种情况下,服务端都会返回NOT_FOUND,并提示数据可能尚未索引或已经删除。 - 公开 URI 参数接受
resources和user作用域。访问 session 文件时,使用viking://user/{user_id}/sessions/{session_id},也可以使用向后兼容的viking://session/{session_id}别名。temp、queue等内部作用域会返回INVALID_URI。
Python SDK
content = client.read(uri="viking://resources/docs/api.md")
print(f"Content:\n{content}")TypeScript SDK
const content = await client.read("viking://resources/docs/api.md", 0, -1);
console.log(content);Go SDK
content, err := client.Read(ctx, "viking://resources/docs/api.md", 0, -1)
if err != nil {
return err
}
fmt.Println(content)HTTP API
GET /api/v1/content/read?uri={uri}curl -X GET "http://localhost:1933/api/v1/content/read?uri=viking://resources/docs/api.md" \
-H "X-API-Key: your-key"CLI
openviking read viking://resources/docs/api.md响应
{
"status": "ok",
"result": "# API Documentation\n\nFull content of the file...",
"time": 0.1
}write()
写入文件,并自动刷新相关语义与向量。
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| uri | str | 是 | - | 要写入的文件 URI |
| content | str | 是 | - | 要写入的新内容 |
| mode | str | 否 | replace | replace 覆盖已有文件、缺失时创建;append 追加已有文件、缺失时创建;create 仅创建缺失文件,目标已存在时返回 409 Conflict |
| wait | bool | 否 | false | 是否等待后台语义/向量刷新完成 |
| timeout | float | 否 | null | 当 wait=true 时的超时时间(秒) |
| tags | string[] | 否 | 未设置 | 写入文件的显式检索标签,例如 ["team=search", "env=prod"] |
| tag_mode | string | 否 | replace | 提供 tags 时的更新方式:replace 覆盖标签,append 按 key 合并标签 |
说明
replace和append在目标文件缺失时都会创建文件;其中append会以传入内容作为新文件的初始内容。create仅用于创建缺失文件,目标路径已存在时返回409 Conflict。目录始终会被拒绝。- 显式
create只允许以下文本类扩展名:.md、.txt、.json、.yaml、.yml、.toml、.py、.js、.ts。所有写入模式都会自动创建父目录。 - 已存在的
.abstract.md/.overview.md可以修改正文,但不能通过公共 API 创建;只提交正文时会保留现有 OKF metadata,提交完整 OKF 时 metadata 必须与存量值一致。未知 metadata 字段会静默丢弃。sidecar 正文写入只重建该目录实际存在的 L0/L1 向量,不触发语义重新生成。 - 文件内容会在 API 返回前完成更新;
wait只控制是否等待语义/向量刷新完成。 - 公共 API 已不再接受
regenerate_semantics或revectorize;写入后一定会自动刷新相关语义与向量。 - 提供
tags时,标签会在该文件首次向量 upsert 时写入,而非在处理完成后再单独更新。省略tags不改变已有标签;显式tags: []配合tag_mode: "replace"可清空标签。
Python SDK
result = client.write(
uri="viking://resources/docs/api.md",
content="# Updated API\n\nFresh content.",
mode="replace",
wait=True,
options={"tags": ["team=search", "env=prod"], "tag_mode": "replace"},
)
print(result["root_uri"])TypeScript SDK
await client.write("viking://resources/docs/new.md", "# New document\n", {
wait: true,
tags: ["team=search", "env=prod"],
tagMode: "replace",
});Go SDK
result, err := client.Write(
ctx,
"viking://resources/docs/api.md",
"# Updated API\n\nFresh content.",
&openviking.WriteOptions{
Mode: "replace",
Wait: true,
Tags: []string{"team=search", "env=prod"},
TagMode: "replace",
},
)
if err != nil {
return err
}
fmt.Println(result["root_uri"])HTTP API
POST /api/v1/content/writecurl -X POST "http://localhost:1933/api/v1/content/write" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"uri": "viking://resources/docs/api.md",
"content": "# Updated API\n\nFresh content.",
"mode": "replace",
"wait": true,
"tags": ["team=search", "env=prod"],
"tag_mode": "replace"
}'CLI
openviking write viking://resources/docs/api.md \
--content "# Updated API\n\nFresh content." \
--tags team=search,env=prod \
--tag-mode replace \
--wait响应
{
"status": "ok",
"result": {
"uri": "viking://resources/docs/api.md",
"root_uri": "viking://resources/docs",
"context_type": "resource",
"mode": "replace",
"written_bytes": 29,
"content_updated": true,
"semantic_status": "complete",
"vector_status": "complete",
"queue_status": {
"Semantic": {
"processed": 1,
"error_count": 0,
"errors": []
},
"Embedding": {
"processed": 2,
"error_count": 0,
"errors": []
}
}
}
}batch_write()
在一个 Resource 或 Memory 目录下写入多个文件;全部写完后,再统一刷新一次受影响的语义与向量索引。
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
root_uri | string | 是 | - | 包含所有写入目标的已存在 Resource 或 Memory 目录 |
operations | array | 是 | - | 要校验并执行的文件写入 |
wait | boolean | 否 | true | 是否等待语义/向量刷新 |
timeout | number | 否 | null | wait=true 时的刷新超时时间(秒) |
telemetry | boolean/object | 否 | false | 是否返回操作遥测信息 |
每个 operation 包含:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
uri | string | 是 | 位于 root_uri 下的目标文件 URI |
content | string | 条件必填 | UTF-8 文本;与 content_base64 必须且只能提供一个 |
content_base64 | string | 条件必填 | Base64 编码的字节;Memory 目标不支持 |
mode | string | 否 | replace(默认)、append、create 或 upsert |
说明
- 单次请求最多包含 256 个 operation,单文件不超过 8 MiB,总内容不超过 16 MiB。
- 所有目标必须是
root_uri下的文件、属于同一 context type,且 canonical URI 不能重复。 - Resource 目标允许任意安全文件扩展名;Memory 目标仍使用文本扩展名白名单,且不接受二进制内容。
replace、append、create与write()语义一致;upsert会覆盖已有文件或创建缺失文件。- 写入期间整批共用一个目标 tree lock。所有文件写完并释放锁后才启动语义处理,因此
.overview.md/.abstract.md每批只统一刷新一次。 - 底层 I/O 中途失败时,本批次较早完成的写入仍可能已经可见。
- 已存在的
.abstract.md/.overview.md可以 replace 或 append;系统会保留并校验受保护的 OKF metadata,并只重建对应目录实际存在的 L0/L1 向量。 - 响应体中,通过
semantic_status(queued、complete或deferred)表达目录聚合状态,通过vector_status表达变化文件的向量维护状态。
Python SDK
result = client.batch_write(
root_uri="viking://resources/wiki",
operations=[
{
"uri": "viking://resources/wiki/new.md",
"content": "# 新页面\n",
"mode": "upsert",
},
{
"uri": "viking://resources/wiki/existing.md",
"content": "# 更新后的页面\n",
"mode": "upsert",
},
],
wait=True,
)HTTP API
POST /api/v1/content/batch-writecurl -X POST http://localhost:1933/api/v1/content/batch-write \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"root_uri": "viking://resources/wiki",
"operations": [
{
"uri": "viking://resources/wiki/new.md",
"content": "# 新页面\n",
"mode": "upsert"
}
],
"wait": true
}'响应
{
"status": "ok",
"result": {
"root_uri": "viking://resources/wiki",
"created": ["viking://resources/wiki/new.md"],
"updated": [],
"unchanged": [],
"semantic_status": "complete",
"vector_status": "complete",
"queue_status": {
"Semantic": {
"processed": 1,
"error_count": 0,
"errors": []
}
}
}
}TypeScript、Go SDK 和 CLI 当前不直接暴露 batch write。
download()
以原始字节流下载文件,适用于图片、PDF 和其他非文本内容。响应使用 application/octet-stream,并通过 Content-Disposition 返回文件名。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
uri | string | 是 | 要下载的文件 URI |
HTTP API
GET /api/v1/content/download?uri={uri}curl --get http://localhost:1933/api/v1/content/download \
-H "X-API-Key: your-key" \
--data-urlencode "uri=viking://resources/images/logo.png" \
--output logo.pngCLI
ov get viking://resources/images/logo.png ./logo.png响应
成功时返回 HTTP 200 和文件原始字节,不使用标准 JSON 响应包:
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Disposition: attachment; filename*=UTF-8''logo.png
<binary body>ov get <uri> <local-path> 通过上述 HTTP API 下载文件并写入本地路径。Python、TypeScript 和 Go SDK 当前没有独立的原始字节下载方法。
set_tags()
设置用于检索过滤的显式 k=v 标签。replace 替换已有标签,append 追加标签;对目录设置 recursive=true 时会更新目录下的文件。
Python SDK
result = client.set_tags(
uri="viking://resources/project/",
tags=["team=search", "env=prod"],
mode="replace",
recursive=True,
)TypeScript SDK
const result = await client.setTags(
"viking://resources/project/",
["team=search", "env=prod"],
{ mode: "replace", recursive: true },
);Go SDK
result, err := client.SetTags(
ctx,
"viking://resources/project/",
[]string{"team=search", "env=prod"},
&openviking.SetTagsOptions{Mode: "replace", Recursive: true},
)HTTP API
POST /api/v1/content/set_tags
Content-Type: application/jsoncurl -X POST http://localhost:1933/api/v1/content/set_tags \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"uri":"viking://resources/project/",
"tags":["team=search","env=prod"],
"mode":"replace",
"recursive":true
}'POST /api/v1/fs/attrs/set_tags 是等价兼容路径,当前 Python、TypeScript、Go SDK 和 CLI 使用该路径。
CLI
ov set-tags viking://resources/project/ \
--tags team=search,env=prod \
--mode replace \
--recursive响应
{
"status": "ok",
"result": {
"uri": "viking://resources/project/",
"updated_uris": [
"viking://resources/project/guide.md"
],
"root_uri": "viking://resources/project/",
"context_type": "resource",
"tags": [
"team=search",
"env=prod"
],
"mode": "replace",
"success_count": 1,
"skipped_count": 0,
"failed_count": 0,
"tags_updated": true
}
}updated_uris 是实际更新的语义记录 URI;目录递归更新时,success_count、skipped_count 和 failed_count 汇总各目标的处理结果。
reindex()
对已经存储在 OpenViking 中的现有内容,重新构建语义产物和/或向量索引。这是一个运维维护接口,适用于 embedding 模型更换、VLM 更换、向量库重刷、版本升级后修复历史索引等场景。
这个接口面向已有的 viking://... 内容,不负责导入新文件。常规导入请使用 Resources。
认证
api_key模式下,共享区viking://resources/...需要 admin key;普通 user key 只能重建自己的viking://user/<user_id>/...,也可以使用等价的viking://~/...家目录别名。root key 不能访问租户级数据 API。- Python HTTP client / CLI:使用当前认证身份发起请求
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| uri | str | 是 | - | 要重新索引的 Viking URI |
| mode | str | 否 | vectors_only | 重建模式:vectors_only、semantic_and_vectors 或 prune_orphans |
| wait | bool | 否 | true | 是否等待任务完成 |
| dry_run | bool | 否 | false | 仅适用于 mode="prune_orphans";只报告 orphan 向量记录,不实际删除 |
| recursive | bool | 否 | true | 是否递归处理下级内容;false 仅对 resource、memory 或 skill 目录的 semantic_and_vectors 生效 |
| tags | list[str] | 否 | null | 写入本次成功重建的全部向量记录。省略时保留已有 tags;空数组配合 replace 可清空 |
| tag_mode | str | 否 | replace | tags 的写入模式:replace 或 append |
HTTP 请求体不接受未知字段。uri 可以使用其他 content API 支持的 OpenViking 路径变量,服务端会先解析再校验。
支持的 URI 范围
viking://viking://userviking://user/<user_id>viking://resourcesviking://resources/...viking://user/<user_id>/memories/...viking://user/<user_id>/skillsviking://user/<user_id>/skills/<skill_name>
reindex() 不支持会话命名空间。请求 viking://session/... 或 viking://user/<user_id>/sessions/... 会被拒绝;重建更大的 user 命名空间时, session 子树会被跳过。
模式说明
vectors_only:基于当前仍可恢复的源数据重建向量库记录,不会重写.abstract.md和.overview.mdsemantic_and_vectors:先重新生成语义产物,再基于新的语义结果重建向量prune_orphans:删除请求 URI 范围内源文件已不存在的向量库记录。设置dry_run=true时,只报告会删除多少记录,不实际删除。
对于 resource 和 skill,semantic_and_vectors 会刷新目录/文件语义产物,包括 .abstract.md 和 .overview.md。对于 memory,它会重建当前已持久化 memory 子树的语义和向量,但不会回放历史记忆抽取顺序。
对于 semantic_and_vectors,语义刷新和向量重建由 reindex executor 串行编排。语义刷新阶段不会再额外向后台 embedding queue 投递自己的向量化任务;向量由 reindex 阶段统一重建,因此 wait=true 表示等待 reindex 操作本身完成。
对 resource 或 memory 目录设置 recursive=false 时,只重新生成目标目录的 .abstract.md、.overview.md,并重建该目录的 L0/L1 向量;下级目录不会重新生成语义产物,下级目录和文件也不会重新向量化。目标目录仍会读取本轮确定性采样命中的既有下级摘要;若采样命中直接文件,仍会为当前目录聚合准备这些文件的摘要。对 skill 目标设置 recursive=false 时,会根据 SKILL.md 重新生成 skill 目录的 L0/L1 语义产物及向量,但不会重建 SKILL.md 的 L2 向量。该参数不改变 vectors_only、prune_orphans 或 namespace 目标的既有行为。
对于 prune_orphans,源文件是否存在以当前文件系统为准。如果整个目录已经不存在,该目录下的正文文件向量和语义 sidecar 向量(例如 .abstract.md、.overview.md)会一起清理。dry_run 用在其他模式时会被拒绝。
传入 tags 时,标签会随 reindex 生成的向量记录在同一次 upsert 中写入,不会在完成后额外调用 set_tags。目录或 namespace reindex 会把标签应用到本次成功重建的目录 L0/L1 和叶子 L2 记录。replace 覆盖已有标签,append 按 key 合并;省略 tags 时不校验 tag_mode 且不修改已有标签。prune_orphans 不生成向量,因此会忽略 tags 和 tag_mode。
子树 reindex 不是事务性操作。如果部分记录因缺少语义来源或 embedding 失败而未重建,只有成功写入的记录会更新标签。
Python SDK
result = client.reindex(
uri="viking://resources",
mode="vectors_only",
wait=True,
options={
"tags": ["team=search", "env=prod"],
"tag_mode": "replace",
},
)
print(result)result = client.reindex(
uri="viking://user/default/skills",
mode="semantic_and_vectors",
wait=False,
)
print(result["status"])result = client.reindex(
uri="viking://resources",
mode="prune_orphans",
dry_run=True,
)
print(result["would_delete_records"])TypeScript SDK
console.log(await client.reindex("viking://resources/docs/", {
tags: ["team=search"],
tagMode: "append",
}));Go SDK
传入非 nil 的 ReindexOptions 时,需要显式设置 Wait。Go 的布尔零值为 false;只有 opts=nil 时 SDK 才会应用 wait=true 的默认值。
result, err := client.Reindex(ctx, "viking://resources", &openviking.ReindexOptions{
Mode: "vectors_only",
Wait: true,
Tags: []string{"team=search"},
TagMode: "replace",
})
if err != nil {
return err
}
fmt.Println(result["status"])result, err := client.Reindex(ctx, "viking://resources", &openviking.ReindexOptions{
Mode: "prune_orphans",
Wait: true,
DryRun: true,
})
if err != nil {
return err
}
fmt.Println(result["would_delete_records"])HTTP API
POST /api/v1/content/reindex不存在 /api/v1/maintenance/reindex 端点。请使用 /api/v1/content/reindex。
curl -X POST http://localhost:1933/api/v1/content/reindex \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-H "X-OpenViking-Account: default" \
-d '{
"uri": "viking://resources",
"mode": "vectors_only",
"wait": true,
"tags": ["team=search", "env=prod"],
"tag_mode": "replace"
}'CLI
openviking reindex viking://resources --mode vectors_only \
--tags team=search,env=prod --tag-mode replaceCLI 仅在 --tags 非空时发送标签字段;如需用 tags: [] 清空标签,请使用 HTTP 或 SDK。
openviking reindex viking://user/default/skills --mode semantic_and_vectors --wait falseopenviking reindex viking://resources --mode prune_orphans --dry-run同步响应(wait=true)
{
"status": "ok",
"result": {
"uri": "viking://resources",
"mode": "vectors_only",
"status": "completed",
"object_type": "resource",
"scanned_records": 120,
"rebuilt_records": 118,
"deleted_records": 0,
"would_delete_records": 0,
"unsupported_records": 2,
"failed_records": 0,
"duration_ms": 1284,
"warnings": []
},
"time": 0.1
}异步响应(wait=false)
{
"status": "ok",
"result": {
"uri": "viking://resources",
"mode": "vectors_only",
"object_type": "resource",
"status": "accepted",
"task_id": "task_xxx"
},
"time": 0.1
}使用返回的 task 查询后台任务:
curl -X GET http://localhost:1933/api/v1/tasks/task_xxx \
-H "X-API-Key: your-key" \
-H "X-OpenViking-Account: default"Reindex 后台任务的 task_type 为 admin_reindex,resource_id 等于请求中的 uri,也可以这样列出:
GET /api/v1/tasks?task_type=admin_reindex&resource_id=viking://resources任务记录持久化在 /local/{account_id}/_system/tasks/{user_id}/{task_id}.json,服务重启后仍可查询。
结果字段
| 字段 | 说明 |
|---|---|
| status | 同步完成时为 completed,后台执行时为 accepted |
| uri | 解析路径变量后的请求 URI |
| object_type | 推断出的目标类型,例如 resource、skill、memory、user_namespace、skill_namespace 或 global_namespace |
| mode | 实际执行的 reindex 模式 |
| scanned_records | 被检查的记录或语义源数量 |
| rebuilt_records | 成功重建的向量记录数量 |
| deleted_records | prune_orphans 实际删除的向量记录数量;dry_run=true 时为 0 |
| would_delete_records | prune_orphans dry-run 模式下将会删除的向量记录数量 |
| unsupported_records | 因没有可用向量来源而跳过的记录数量 |
| failed_records | 重建失败的记录数量 |
| duration_ms | 同步执行耗时,单位毫秒 |
| warnings | 可恢复的单条记录级 warning |
| task_id | 后台任务 ID,仅 wait=false 时返回 |
行为说明
vectors_only和semantic_and_vectors是非破坏式的,采用重建/覆盖写入,不需要先 drop 向量集合。prune_orphans除非设置dry_run=true,否则会删除源文件已经不存在的向量记录。- 对
viking://发起 reindex 时,会向下分发到支持的顶层命名空间,并显式排除session。 - 命名空间级 reindex,例如
viking://user,会继续传播到其支持的子内容类型。 - 如果只是 embedding 模型或向量索引需要刷新,应使用
vectors_only。 - 如果语义产物本身也需要重建,再做重向量化,应使用
semantic_and_vectors。 - 如果文件系统曾绕过正常 API 发生删除,向量库可能还残留已删除路径的记录,应使用
prune_orphans。 - 同一个 URI 和 owner 同时只能运行一个 reindex 任务。对同一目标的并发请求会返回 conflict。
- 对 resource 文件,文本文件在没有 summary 时可以使用文件正文;非文本文件需要已生成的 summary 或已有向量记录 fallback,否则会计为 unsupported。
当前限制
- Reindex 会使用当前系统中“尽可能可恢复”的输入进行重建,不保证所有场景都能逐字节回放历史当时的 embedding 输入。
- Memory 的 semantic reindex 基于当前已持久化的 memory 树,不会重建最初按时间顺序执行的记忆抽取流水线。
