资源管理
资源是智能体可以引用的外部知识。本模块提供资源的添加、导入/导出、临时文件上传等功能。
核心概念
资源类型
OpenViking 支持多种资源类型,按照功能分类如下:
文档类
| 类型 | 扩展名 | 说明 |
|---|---|---|
.pdf | 支持本地解析和 MinerU API 转换 | |
| Markdown | .md, .markdown, .mdown, .mkd | 原生支持,会提取结构并分段存储 |
| HTML | .html, .htm | 清理导航/广告后提取内容,转换为 Markdown |
| Word | .doc, .docx, .docm, .odt, .rtf | 基于 anydoc 提取文本、标题、表格和嵌入图片并转换为 Markdown |
| 纯文本 | .txt, .text | 直接导入处理 |
| EPUB | .epub | 基于 anydoc 将电子书内容和嵌入图片转换为 Markdown |
表格类
| 类型 | 扩展名 | 说明 |
|---|---|---|
| Excel | .xlsx, .xls, .xlsm, .xlsb, .ods, .csv | 基于 anydoc 按工作表转换为 Markdown 表格 |
| PowerPoint | .pptx, .ppt, .pptm, .pps, .ppsx, .ppsm, .pot, .odp | 基于 anydoc 按幻灯片提取内容和嵌入图片并转换为 Markdown |
代码类
| 类型 | 资源名 | 说明 |
|---|---|---|
| 代码文件 | *.py, *.js, ... | 支持常见编程语言(Python, JavaScript, Go, Rust, Java 等) |
| Git 协议代码仓库 | git://... | Git URL, 本地目录, .zip 包,遵循 .gitignore 并自动过滤 .git, node_modules 等目录 |
| Git 代码托管平台 | https://github.com/{org}/{repo} | GitHub, GitLab, Bitbucket 等代码托管平台的 URL |
| Git 代码托管平台上的 raw 文件 | https://github.com/{org}/{repo}/raw/{branch}/{path} | GitHub, GitLab, Bitbucket 等代码托管平台的 raw 文件下载 URL |
媒体类
| 类型 | 资源名 | 说明 |
|---|---|---|
| 图片 | *.jpg, *.jpeg, *.png, *.gif ... | 多种图片格式,通过 VLM 生成描述(实验特性) |
| 视频 | *.mp4, *.avi, *.mov ... | 提取关键帧后使用 VLM 分析(规划) |
| 音频 | *.mp3, *.wav, *.m4a ... | 进行语音转录处理(规划) |
云文档类
| 类型 | 说明 |
|---|---|
| 飞书/Lark | URL 方式,支持 doc/docx, wiki, sheets, bitable、Drive 文件和目录集。Wiki 默认仅导入入口文档,设置 args.feishu_recursive=true 可递归导入子节点。默认使用 FEISHU_APP_ID 和 FEISHU_APP_SECRET 应用凭证;用户 token 导入可传 args.feishu_access_token,用户 token watch 还需传 args.feishu_refresh_token,并可选传入 args.feishu_app_id / args.feishu_app_secret |
网页类(递归网页爬虫)
| 类型 | 资源名 | 说明 |
|---|---|---|
| 单页 / 递归抓取 | https://host/path | 默认仅抓入口页;设置 args.depth > 0 后,沿同域链接 BFS 递归展开,args.max_pages 只限制最多收集的页面数。每页用 trafilatura 抽成 Markdown。可选 args:depth、max_pages、include_paths、exclude_paths、allow_external_links、skip_download_links。页面中发现的下载链接默认跳过(skip_download_links=true),避免导入 llms.txt 等 sidecar 文件造成重复;设为 false 时会下载同域文件链接,并计入 max_pages。include_paths/exclude_paths 按路径前缀匹配(例如 /docs/ 仅匹配以 /docs/ 开头的路径,不会误命中 /blog/docs-tips)。 |
路由说明:
https://host/sitemap.xml、https://host/feed.xml、*.atom等 sitemap-looking URL 和显式args.site=true让出给下表的整站导入;https://github.com/{org}/{repo}等 Git 托管平台 URL 让出给上文的代码导入。
网站类(sitemap / RSS / Atom 整站导入)
| 类型 | 资源名 | 说明 |
|---|---|---|
| 站点地图 Sitemap | https://host/sitemap.xml、https://host/sitemap-index.xml | 解析 sitemap,将站点所有页面抓取为一棵资源树(每页一个子节点),支持嵌套 <sitemapindex> 递归。整站只生成一个资源,落在 viking://resources/<host>。 |
| RSS / Atom 订阅源 | https://host/rss.xml、https://host/atom.xml、https://host/feed | 解析 RSS 2.0 / Atom,逐条把文章正文抓成树节点(feed 内含全文则直接使用,省一次抓取)。 |
| 整站自动发现 | https://host + args.site=true | 对裸域名/普通页面强制整站导入:自动通过 robots.txt、HTML <link rel="alternate"> autodiscovery、常见路径发现 sitemap/RSS,再整站抓取。 |
抓取有界、非递归(不会超出所列页面继续爬),受 parsers.webfeed 配置约束(max_pages、max_concurrency、politeness_delay、same_host_only、respect_robots、max_depth),并遵守 robots.txt。对 sitemap/feed URL 设置 watch_interval 即可让整站周期刷新:每次运行自动纳入新增页面、移除已删除页面。添加单个首页(未带 args.site)时,返回信息可能附带一行"整站导入"提示——只提示,绝不自动爬全站。
资源处理流程
资源添加经过以下处理阶段:
源输入 → 解析 → 资源树构建 → 持久化 → 语义处理
↓ ↓ ↓ ↓ ↓
URL/文件 Parser TreeBuilder AGFS Summarizer/Vector阶段 1:源解析 (Parse)
- 使用
UnifiedResourceProcessor根据资源类型解析内容 - 支持多种格式:文档(PDF/Markdown/Word)、表格(Excel/PPT)、代码、媒体文件等
- 解析结果写入临时 VikingFS 目录
- 媒体文件通过 VLM(视觉语言模型)生成描述
阶段 2:资源树构建 (TreeBuilder)
TreeBuilder.finalize_from_temp()扫描临时目录结构- 构建资源树节点,处理 URI 冲突(自动重命名)
- 建立目录与资源的关联关系
阶段 3:持久化存储 (Persist)
- 检查目标 URI 是否已存在
- 新资源:移动临时文件到正式 AGFS 位置
- 已存在资源:保留临时树用于后续差异比较
- 获取生命周期锁防止并发修改
- 清理临时目录
阶段 4:语义处理 (Semantic Processing)
- 摘要生成:
Summarizer生成 L0(摘要)和 L1(概述) - 向量索引:将内容向量化用于语义搜索
- 通过
SemanticQueue异步处理,使用返回的task_id查询完成状态
非等待 Git 仓库导入
- 对 Git 仓库来源使用
wait=false时,OpenViking 会先校验仓库、解析目标 URI、预占最终root_uri,然后在 clone/parse/finalize 完成前返回。 - 立即响应包含
status、root_uri和task_id;抓取、解析、finalize 以及队列等待会在持久化后台任务中继续执行。 - 可通过
GET /api/v1/tasks/{task_id}查询任务状态。Git 资源导入任务的阶段包括queued、fetching、parsing、finalizing、processing_queue。 - 其他资源来源使用
wait=false时,会在响应前完成抓取/解析/finalize;返回的task_id只用于跟踪 semantic 和 embedding 队列完成情况。
资源的增量更新
资源增量更新通过监控任务 (Watch Task) 机制实现:
监控任务创建
- 调用
add_resource时,为 URL、sitemap、RSS 等可重新读取的来源设置watch_interval > 0(单位:分钟),即可创建监控任务 temp_file_id引用的上传内容只会作为一次性快照处理,不能创建监控任务;本地来源变化后请重新添加- 可指定
to参数确定目标 URI;未指定时,系统会使用本次导入返回的root_uri作为监控目标 - 把监控对象设为 sitemap/RSS/Atom URL,即可让整站保持同步:每次刷新重新读取 feed 并重建资源树,新发布的页面自动入库、已删除的页面自动移除
WatchManager负责任务持久化存储- 支持多租户权限控制(ROOT/ADMIN/USER 权限分级)
目标占用规则
- 同一账户内,原生 Watch 独占解析后的目标,暂停后仍然占用。新建原生 Watch 要求目标未被占用,Connector Watch 也不能与原生 Watch 共享目标。
- 多个 Connector Watch 可以共享目标。重复导入相同来源和目标会创建新的独立 Watch,不会更新或恢复已有任务;同一来源导入不同目标也会创建独立 Watch。
- Connector Watch 在首次导入前创建,调度器会等待首次导入记录结果后再执行定时任务。
任务调度执行
WatchScheduler每 60 秒检查到期任务- 默认并发控制,避免重复执行
- 到期任务自动重新调用
add_resource处理 - 更新任务的最后执行时间和下次执行时间
任务管理操作
- 创建:
watch_interval > 0时按上述目标占用规则创建新任务;不兼容的占用返回409 Conflict。 - 更新或恢复:通过
PATCH /api/v1/watches/{task_id}修改参数或设置is_active: true;重新导入不会更新或恢复已有任务。 - 暂停或删除:通过
PATCH /api/v1/watches/{task_id}设置is_active: false暂停任务,或通过DELETE /api/v1/watches/{task_id}删除任务以释放目标。原生导入显式指定to且watch_interval <= 0时,会暂停该目标上唯一可访问的 Watch;存在多个可访问的 Watch 时返回409 Conflict。一次性 Connector 导入不影响已有 Watch。 - 查询:通过任务 ID 查询;目标 URI 仅在对应唯一可访问的 Watch 时可用于定位。存在多个可访问的 Watch 时,按 URI 查询返回
409 Conflict,需通过task_id查询、更新或删除指定任务。
API 参考
add_resource
向知识库添加资源,支持本地文件/目录、URL 等多种来源。通过 temp_file_id 引用的上传内容是一次性快照,因此不能与 watch_interval > 0 组合使用。
1. API 实现介绍
此接口是资源管理的核心入口,支持多种来源的资源添加,默认返回 task_id 供调用方查询处理状态。SDK 可直接处理本地文件/目录、URL 等来源;直接 HTTP 调用只通过 path 接受远程 URL,或通过 temp_file_id 引用先上传的本地文件。
处理流程:
- 识别并校验资源来源(URL 或上传的临时文件)
- 解析目标 URI
- 调用对应格式 Parser;
args.parse_mode控制转换后的 Markdown 正文是否允许拆分 - 构建目录树并写入 AGFS
- 按
processing_mode执行入库后的处理:semantic_and_vectors生成语义产物和向量;vectors_only跳过语义理解,只提交文件向量化 - 默认返回
task_id;调用方通过任务 API 确认处理完成 - 如果
reason非空,将其追加到固定的资源 reason session 并 commit,复用常规记忆抽取链路,让合适的用户记忆引用该资源 URI - 如指定
--watch-interval,设置定时更新任务
代码入口:
sdk/python/openviking_sdk/client.py:AsyncHTTPClient.add_resource- Python SDK 入口openviking/server/routers/resources.py:add_resource- HTTP 路由openviking/service/resource_service.py- 核心服务实现crates/ov_cli/src/handlers.rs:handle_add_resource- CLI 处理
2. 接口和参数说明
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| path | string | 否 | - | 远程资源 URL(HTTP/HTTPS/Git)。与 temp_file_id 二选一 |
| temp_file_id | string | 否 | - | 临时上传文件 ID。与 path 二选一 |
| to | string | 否 | - | 本次导入的最终保存位置。目标已存在时会覆盖该目标;与 parent 互斥 |
| parent | string | 否 | - | 父级 Viking URI(资源放入此目录下)。与 to 互斥 |
| create_parent | bool | 否 | False | 如果父目录不存在,自动创建父目录(服务端标志) |
| reason | string | 否 | "" | 添加资源的原因;非空时会随资源 URI 进入常规 session 记忆抽取链路,并在生成的记忆中记录资源引用 |
| instruction | string | 否 | "" | 语义提取的处理指令(实验特性) |
| wait | bool | 否 | False | 是否等待语义处理和向量化完成才返回 |
| timeout | float | 否 | None | 超时时间(秒),仅 wait=true 时生效 |
| strict | bool | 否 | False | 是否使用严格模式 |
| ignore_dirs | string | 否 | None | 要忽略的目录名(逗号分隔) |
| include | string | 否 | None | 包含的文件模式(glob) |
| exclude | string | 否 | None | 排除的文件模式(glob) |
| directly_upload_media | bool | 否 | True | 是否直接上传媒体文件 |
| preserve_structure | bool | 否 | None | 是否保留目录结构 |
| args | object | 否 | {} | 传给特定 parser/accessor 的导入参数。原生 HTTPS Git 导入和 Watch 可通过 args.auth_config={"username":"oauth2","token":"..."} 在 TLS 上传递 HTTP Basic 凭据;username 默认为 oauth2。Git 的 branch 或 commit 仍放在 args 顶层。通过 HTTP(S) URL 导入私有 TOS 对象时,二选一传入非空字符串:args.tos_signature(映射为 X-Tos-Signature)或 args.tos_access(映射为 X-Tos-Access)。TOS 凭证只用于当前 HEAD/GET 抓取;资源会先保存为快照,凭证不会写入资源元数据或队列任务。args.parse_mode 支持 default(保持现有拆分行为)和 no_split(正常解析并将每个源文档正文保存为一个 Markdown 文件)。例如 args.site=true/false 强制/禁用整站(sitemap/RSS)导入,args.max_pages 等可覆盖 webfeed 配置;递归网页爬虫支持 args.depth、args.max_pages、args.include_paths、args.exclude_paths、args.allow_external_links、args.skip_download_links;飞书用户 token 导入传 args.feishu_access_token。path、to、watch_interval、include、exclude 等 add_resource 核心字段不能放入 args |
| watch_interval | float | 否 | 0 | 定时更新间隔(分钟)。>0 按目标占用规则为可重新读取的来源创建新 Watch;通过 temp_file_id 上传的一次性快照不能创建 Watch。≤0 不创建 Watch:原生导入显式指定 to 时暂停唯一可访问的任务(存在歧义时返回 409),Connector 导入不影响已有 Watch。显式 to 优先,否则绑定本次导入的 root_uri。 |
| is_active | bool | 否 | True | Watch 初始调度状态。设为 false 时要求 watch_interval > 0,并在 to、parent 中二选一。parent 仅支持原生飞书 URL 导入;Connector 仍要求精确的 to。首次导入仍执行一次,随后保持暂停 |
| processing_mode | string | 否 | semantic_and_vectors | 入库后的处理模式。semantic_and_vectors 是默认流程:生成语义产物(.abstract.md、.overview.md)并生成向量。vectors_only 跳过语义理解/VLM 总结,只对当前资源文件生成向量 |
| tags | string[] | 否 | None | 导入时写入向量检索记录的显式检索标签,格式必须是 k=v,例如 ["team=search", "env=test"]。搜索接口可用同名 tags 参数过滤召回 |
| tag_mode | string | 否 | "replace" | tags 的写入模式,可选 replace 或 append。导入新资源时会随本次生成的每条向量记录写入;不会在完成后额外调用 set_tags,响应也不返回 tags_result |
| telemetry | TelemetryRequest | 否 | False | 是否返回遥测数据 |
补充说明:
to和parent不能同时使用。to是最终保存位置:目标不存在就创建,目标已存在就覆盖该目标;如果目标是目录,目录里本次导入没有生成的旧文件或子目录会被删除。parent是保存目录,适合向已有目录追加新资源;父目录不存在时使用create_parent=true或 CLI 的--parent-auto-create。当导入后的root_uri与to相同时,语义与向量处理会复用未变化内容,只处理变化部分。- 创建新资源要求目标父目录可写;显式更新已有
to要求该目标可写。权限校验在任务入队前完成。自动命名按实际 URI 占用判断,即使同名资源不可读也会选择_1、_2等后缀,而不会尝试覆盖。 wait=false返回的status=accepted表示任务已通过预检查并入队,不表示资源处理已经完成;最终状态以对应task_id为准。- 如果同时省略
to和parent,服务端会先尝试使用当前用户的add_targets.resource_uri覆盖配置,再使用server.user_config_defaults.add_targets.resource_uri。两者都没有配置时,保持旧的目标解析行为。 - 资源目标可以使用公共
viking://resources/...、家目录别名viking://~/resources/...、显式用户viking://user/{user_id}/resources/...,或 peer 级viking://user/{user_id}/peers/{peer_id}/resources/...。家目录别名会按请求身份展开为 canonical 路径;无 uid 的写法viking://user/resources/...会被拒绝,并提示改用viking://~/resources/...。 user_id和peer_id路径片段必须是安全的单段标识,例如alice或web-visitor-alice。包含路径分隔符、.、..、:或+的值会被拒绝。path和temp_file_id不能同时指定,上传本地文件需要先通过 temp_upload 上传获取temp_file_id,在 SDK 和 CLI 中已经封装好。tags会在资源解析后、向量记录写入时同步写入底层向量库。add_resource(tags=...)不返回tags_result;需要验证时,可在/api/v1/search/find或/api/v1/search/search中传相同tags过滤召回。- 只有 Git 仓库来源在
wait=false时使用完整后台导入;OpenViking 会先完成仓库 preflight 和目标规划,再返回task_id。 - 原生 HTTPS Git 的
args.auth_config在watch_interval <= 0时只用于本次请求;当watch_interval > 0时,OpenViking 会把与仓库 URL 绑定的 username/token 保存到 Watch 私有鉴权状态,并只在后续 Git 拉取时恢复使用。凭据不会进入普通持久队列,也不会出现在 Watch API/MCP/CLI 返回中。Git PAT 没有通用刷新流程,过期或撤销后需要重建 Watch 来更换 token。为兼容已有用法,系统仍接受https://user:token@host/repo.git形式的 URL 内嵌凭据并原样传递;由于该 URL 同时也是资源来源标识,它可能被记录到进程参数、日志、队列、资源元数据和 Watch 状态中。新接入建议使用args.auth_config。args.auth_config的明文 HTTP 鉴权和带鉴权重定向仍会被拒绝。 - token 会放在 HTTPS 请求体中传输。生产环境应保持诊断请求体 dump 关闭;显式启用该功能可能记录秘密。
reason触发的记忆生成复用session.commit的抽取链路,只使用reason、资源 URI、可用的资源名称和目录摘要,不会读取或展开完整资源正文;系统会写入entities、events、preferences等已有记忆类型,不创建独立的资源记忆目录。- 删除资源时,系统会在删除前扫描本次上下文对应的 self 或 peer 记忆中的
resource_refs,清理对应资源 URI 和由该reason引入的内容,并重新刷新相关记忆的语义索引。 - 其他来源在
wait=false时会在响应前完成来源解析、目标解析和 AGFS 写入,仅 semantic 与 embedding 队列继续异步处理。 processing_mode=vectors_only不调用 VLM 语义理解阶段,也不会生成或刷新.abstract.md/.overview.md。对已存在目标,它会保留旧的语义产物和旧的语义向量;仍会更新资源树,在build_index=true时向量化当前非隐藏文件,并清理由本次刷新删除的文件 detail 向量。processing_mode只属于add_resource。管理员维护已有数据时,reindexAPI/CLI 仍使用mode(vectors_only、semantic_and_vectors、prune_orphans)。watch_interval > 0时,如果指定了to,监控任务绑定该目标;如果未指定to,监控任务绑定本次导入返回的root_uri。如果无法得到稳定root_uri,请求会报错并要求显式传to。- Connector 导入设置
is_active=false时会在提交前创建暂停状态的 Watch;原生飞书导入会通过资源队列透传is_active,解析出最终资源 URI 后再创建 Watch。两种情况下首次导入均执行一次,周期调度保持关闭。 - 飞书/Lark 应用 token 导入不传
args.feishu_access_token。OpenViking 保持原有应用凭证流程,由 SDK 使用app_id和app_secret自动获取 app/tenant token。该模式支持一次性导入和watch_interval > 0。 - 飞书/Lark 一次性用户 token 导入通过
args={"feishu_access_token": "u-..."}传入,且watch_interval <= 0。OpenViking 只在本次导入使用该用户 token,不保存。 - 飞书/Lark 用户 token watch 通过
args={"feishu_access_token": "u-...", "feishu_refresh_token": "r-..."}传入,且watch_interval > 0。还可同时传入feishu_app_id和feishu_app_secret;OpenViking 会将其保存在 watch task 私有状态中,并用于刷新该 watch 的用户 token。 - 请求未传应用凭证时,用户 token watch 回退使用
FEISHU_APP_ID和FEISHU_APP_SECRET,或ov.conf中的feishu.app_id和feishu.app_secret。飞书 refresh token 绑定签发它的应用,因此实际使用的应用凭证必须与传入的用户 token 匹配。 - Watch task 的 token 状态和请求传入的应用凭证保存在内部控制文件
viking://resources/.watch_tasks.json中,不会出现在 watch API/MCP/CLI 返回里。若启用了 VikingFS 文件加密,该控制文件会静态加密;否则服务端控制文件中会包含这些明文私有状态。 - 本地目录输入会遵循
.gitignore(根目录和子目录,标准 Git 语义);ignore_dirs、include、exclude会在此基础上进一步过滤。 - 目录导入仅在至少一个入选文件成功时采用 best-effort:失败文件写入
meta.failed_files,成功文件正常提交。嵌套 ZIP 的叶子失败使用bundle.zip/path/to/file形式的归档限定路径,并保留远端任务 ID。如果没有任何文件成功,或筛选后没有可处理文件,任务会失败且不会保留空资源目录。 args.parse_mode=no_split仍调用正常的格式 Parser。PDF、Word、PowerPoint、HTML 等受支持文档会转换为 Markdown,但跳过按标题、段落和长度拆分。目录导入会对每个受支持文档分别应用该规则,并继续遵循.gitignore、筛选参数和preserve_structure。该模式下,配置为走 Understanding 的目录文件会回退到对应的原生 Parser;没有原生解析能力的文件会写入meta.failed_files,但不会阻止其他入选文件成功导入。- 对单文件输入使用
no_split时,如果解析结果恰好只有一个可见文件且未指定to,该文件会直接放到解析出的父目录下(例如guide.md写入viking://resources/guide.md),不会创建同名上层目录,也不会生成目录级.abstract.md/.overview.md。如果解析结果还包含图片等其他可见文件,则保留上层目录。显式指定的to始终作为最终 URI 原样保留。 no_split只改变 Markdown 正文的存储布局,不改变语义处理、文件向量化和内部 embedding 分块。Markdown 相对链接会按同一个 no-split 输出布局解析,不会再指向仅拆分模式存在的路径。该模式下不会为目录文件调用 Understanding。- 如果要直接创建或更新纯文本内容,请使用 content/write,不要使用
add_resource。资源导入和内容写入后都会自动刷新语义与 embedding。
3. 使用示例
以下示例使用默认异步模式,不设置等待参数。提交后保存 task_id,通过 任务 API 查询状态;只有任务为 completed 时,才读取摘要或检索本次导入的内容。
HTTP API
POST /api/v1/resources
Content-Type: application/json# 从 URL 添加资源
curl -X POST http://localhost:1933/api/v1/resources \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"path": "https://example.com/guide.md",
"reason": "User guide documentation"
}'
# 导入并定时同步 HTTPS 私有 Git 仓库
curl -X POST http://localhost:1933/api/v1/resources \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"path": "https://git.example.com/team/private-repo.git",
"to": "viking://resources/private-repo",
"watch_interval": 60,
"args": {
"branch": "main",
"auth_config": {
"username": "oauth2",
"token": "replace-with-your-token"
}
}
}'
# 添加资源但只生成向量,不走 VLM 语义理解
curl -X POST http://localhost:1933/api/v1/resources \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"path": "https://example.com/guide.md",
"to": "viking://resources/guide",
"processing_mode": "vectors_only"
}'
# 递归抓取网页:从入口页沿同域链接展开,depth 控制层数,max_pages 限制页数
curl -X POST http://localhost:1933/api/v1/resources \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"path": "https://docs.openviking.ai/zh/getting-started/01-introduction",
"args": { "depth": 1, "max_pages": 10 }
}'
# 从本地文件添加(需先使用 temp_upload 上传)
TEMP_FILE_ID=$(
curl -s -X POST http://localhost:1933/api/v1/resources/temp_upload \
-H "X-API-Key: your-key" \
-F "file=@./documents/guide.md" \
| jq -r '.result.temp_file_id'
)
curl -X POST http://localhost:1933/api/v1/resources \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d "{
\"temp_file_id\": \"$TEMP_FILE_ID\",
\"to\": \"viking://resources/guide.md\",
\"reason\": \"User guide\"
}"
# 添加到当前用户私有资源根
curl -X POST http://localhost:1933/api/v1/resources \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d "{
\"temp_file_id\": \"$TEMP_FILE_ID\",
\"parent\": \"viking://~/resources/docs\",
\"create_parent\": true
}"
# 导入时设置检索标签;标签随本次生成的向量记录写入,可用于 search/find 过滤
curl -X POST http://localhost:1933/api/v1/resources \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d "{
\"temp_file_id\": \"$TEMP_FILE_ID\",
\"to\": \"viking://resources/tagged-guide.md\",
\"tags\": [\"team=search\", \"env=test\"],
\"tag_mode\": \"replace\"
}"
# 使用一次性用户 access token 添加飞书文档
curl -X POST http://localhost:1933/api/v1/resources \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"path": "https://example.feishu.cn/docx/doc_token",
"args": {
"feishu_access_token": "u-..."
}
}'
# 使用用户 token 自动刷新添加飞书文档
curl -X POST http://localhost:1933/api/v1/resources \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"path": "https://example.feishu.cn/docx/doc_token",
"to": "viking://resources/feishu/doc",
"watch_interval": 1440,
"args": {
"feishu_access_token": "u-...",
"feishu_refresh_token": "r-...",
"feishu_app_id": "cli_...",
"feishu_app_secret": "..."
}
}'Python SDK
from openviking_sdk import SyncHTTPClient
client = SyncHTTPClient(url="http://localhost:1933", api_key="your-key")
client.initialize()
## 添加本地文件
result = client.add_resource(
path="./documents/guide.md",
options={"reason": "User guide documentation"},
)
print(f"Task ID: {result['task_id']}")
## 正常解析并转换为 Markdown,但每个文档正文不拆分
result = client.add_resource(
path="./documents",
options={"args": {"parse_mode": "no_split"}},
)
## 从 URL 添加到指定位置
result = client.add_resource(
path="https://example.com/api-docs.md",
to="viking://resources/external/api-docs.md",
options={"reason": "External API docs"},
)
## 递归抓取网页(同域 BFS,depth 层数、max_pages 页数上限)
result = client.add_resource(
path="https://docs.openviking.ai/zh/getting-started/01-introduction",
options={
"args": {"depth": 1, "max_pages": 10},
},
)
## 递归抓取并按路径前缀过滤,同时下载页面中的文件链接
result = client.add_resource(
path="https://docs.openviking.ai/",
options={
"args": {
"depth": 2,
"max_pages": 50,
"include_paths": ["/zh/"],
"exclude_paths": ["/changelog"],
"skip_download_links": False,
},
},
)
## 添加到当前用户私有资源根
result = client.add_resource(
path="./documents/guide.md",
parent="viking://~/resources/docs",
options={
"create_parent": True,
},
)
## 查询最近一次导入任务;状态为 completed 后再使用处理结果
print(client.get_task(result["task_id"]))
## 开启定时更新
client.add_resource(
path="./documents/guide.md",
to="viking://resources/guide.md",
options={
"watch_interval": 60, # 每60分钟更新一次
},
)
# 使用一次性用户 access token 添加飞书文档
client.add_resource(
path="https://example.feishu.cn/docx/doc_token",
options={"args": {"feishu_access_token": "u-..."}},
)
# 使用用户 token 自动刷新添加飞书文档
client.add_resource(
path="https://example.feishu.cn/docx/doc_token",
to="viking://resources/feishu/doc",
options={
"watch_interval": 1440,
"args": {
"feishu_access_token": "u-...",
"feishu_refresh_token": "r-...",
"feishu_app_id": "cli_...",
"feishu_app_secret": "...",
},
},
)TypeScript SDK
const task = await client.addResource("https://example.com/docs", {
to: "viking://resources/docs/",
args: { parse_mode: "no_split" },
});
console.log(task.task_id);Go SDK
result, err := client.AddResource(ctx, "./documents/guide.md", &openviking.AddResourceOptions{
Reason: "User guide documentation",
Args: map[string]any{"parse_mode": "no_split"},
})
if err != nil {
return err
}
fmt.Println(result["task_id"])CLI
# 添加本地文件
ov add-resource ./documents/guide.md --reason "User guide"
# 正常解析,每个源文档只生成一个 Markdown 正文
ov add-resource ./documents --args parse_mode:no_split
# 从 URL 添加
ov add-resource https://example.com/guide.md --to viking://resources/guide.md
# 递归抓取网页:默认只抓入口页,depth>0 才沿同域链接展开
ov add-resource "https://docs.openviking.ai/zh/getting-started/01-introduction" \
--args="depth:1,max_pages:10"
# 递归抓取并按路径前缀过滤(只抓 /zh/,排除 changelog)
ov add-resource "https://docs.openviking.ai/" \
--args='{"depth":2,"max_pages":50,"include_paths":["/zh/"],"exclude_paths":["/changelog"]}'
# 默认跳过页面里的下载链接;如需一并下载 PDF/TXT/MD 等,显式关闭跳过
ov add-resource "https://example.com/docs" \
--args="depth:1,max_pages:20,skip_download_links:false"
# 使用提交时返回的 task_id 查询进度
ov task status TASK_ID
# 开启定时更新(每60分钟检测一次)
ov add-resource https://github.com/example/repo.git --to viking://resources/my_repo --watch-interval 60
# 开启定时更新并自动绑定本次导入生成的 URI
ov add-resource https://github.com/example/repo.git --watch-interval 60
# 通过 PATCH /api/v1/watches/{task_id} 提交 {"is_active": false} 暂停 Watch。
# 原生导入也可通过 --watch-interval 0 暂停目标上唯一可访问的 Watch。
# 一次性 Connector 导入不会影响已有 Watch。
# 使用一次性用户 access token 添加飞书文档
ov add-resource https://example.feishu.cn/docx/doc_token --args feishu_access_token:u-...
# 使用用户 token 自动刷新添加飞书文档
ov add-resource https://example.feishu.cn/docx/doc_token \
--to viking://resources/feishu/doc \
--watch-interval 1440 \
--args feishu_access_token:u-... \
--args feishu_refresh_token:r-... \
--args feishu_app_id:cli_... \
--args feishu_app_secret:...
# 添加到指定父目录(父目录必须存在)
ov add-resource ./documents/guide.md --parent viking://resources/docs
# 添加到当前用户私有资源根
ov add-resource ./documents/guide.md --parent viking://~/resources/docs
# 添加到指定 peer 的私有资源根
ov add-resource ./documents/guide.md \
--parent viking://user/alice/peers/web-visitor-alice/resources/docs
# 添加到指定父目录(父目录不存在时自动创建)
ov add-resource ./documents/guide.md -p viking://resources/docs/2026/05/07
# 或使用完整参数名
ov add-resource ./documents/guide.md --parent-auto-create viking://resources/docs/2026/05/07
# 使用路径变量配合自动创建父目录
ov add-resource ./documents/guide.md -p viking://resources/docs/{calendar:today}4. 响应示例
HTTP API 响应(默认异步模式)
{
"status": "ok",
"result": {
"status": "accepted",
"root_uri": "viking://resources/guide",
"task_id": "uuid-xxx"
}
}使用返回的 task_id 轮询 /api/v1/tasks/{task_id} 可查看队列完成情况。对于 wait=false 的 Git 仓库来源,同一个端点会跟踪完整后台导入,任务完成后的 result 会包含完整导入结果,包括 queue_status。
CLI 响应 (默认表格格式)
Note: Resource is being processed in the background.
Use 'ov task status <task_id>' to check progress, or 'ov task list' to see all tasks.
status accepted
root_uri viking://resources/01-overview
task_id uuid-xxxCLI 响应 (JSON 格式,使用 -o json)
{
"status": "accepted",
"root_uri": "viking://resources/01-overview",
"task_id": "uuid-xxx"
}字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
status | string | 处理状态:accepted 表示已入队,success 表示成功,error 表示失败 |
root_uri | string | 资源在 OpenViking 中的最终 URI |
task_id | string | (可选,仅当 wait=false 时)可轮询 /api/v1/tasks/{task_id} 的任务 ID。非 Git 导入用于队列跟踪;Git 仓库导入用于完整后台导入跟踪。 |
temp_uri | string | 导入过程中生成的临时 URI |
source_path | string | 原始源文件路径或 URL |
meta | object | 资源解析过程中的元数据(如文件类型、大小等) |
errors | array | 处理过程中的错误列表 |
warnings | array | (可选)处理过程中的警告列表(仅在 strict=False 时可能出现) |
queue_status | object | (可选,仅当 wait=true 时)队列处理状态,包含 pending、processing、completed 计数 |
memory_linking | object | (可选,仅当 reason 触发记忆生成时)本次资源 URI 与用户记忆的关联结果 |
完成后的资源添加任务结果
对于 wait=false 的 Git 仓库来源,后台任务的 task_type="add_resource",resource_id 等于返回的 root_uri。运行中的任务记录可能包含 stage。轮询 /api/v1/tasks/{task_id} 直到任务完成。完成后,任务内层的 result 会包含最终队列汇总和 context_count:
{
"status": "ok",
"result": {
"task_id": "uuid-xxx",
"task_type": "add_resource",
"status": "completed",
"resource_id": "viking://resources/guide",
"result": {
"status": "success",
"root_uri": "viking://resources/guide",
"queue_status": {
"Embedding": {
"processed": 11,
"requeue_count": 0,
"error_count": 0,
"errors": []
}
},
"context_count": 11
}
}
}context_count 是本次上传任务成功生成并完成索引的上下文数量。每条上下文对应的嵌入记录成功写入后,计数增加一次。该值不是 root_uri 下已有上下文的总数。如果服务器在任务持久化最终指标前重启,该字段会被省略,以避免返回不完整的计数。
temp_upload
上传临时文件,用于后续通过 add_resource 或 add_skill 导入本地文件。
1. API 实现介绍
此接口用于把本地文件上传到服务端托管的临时存储中,返回 temp_file_id 供后续 API 使用。这是一个辅助接口,通常不直接调用,而是通过 SDK 或 CLI 自动使用。
处理流程:
- 接收上传的文件
- 根据
upload_mode选择临时上传后端 - 保存文件并记录原始文件名
- 返回临时文件 ID
代码入口:
openviking/server/routers/resources.py:temp_upload- HTTP 路由openviking/service/resource_service.py- 服务实现
2. 接口和参数说明
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| file | UploadFile | 是 | - | 上传的文件(multipart/form-data) |
| telemetry | bool | 否 | False | 是否返回遥测数据 |
| upload_mode | string | 否 | "local" | 临时上传模式。local 保持现有单机行为;shared 将文件上传到共享临时存储,适用于分布式部署。 |
说明:
- 默认值是
local,所以现有客户端在不改动的情况下仍保持原有行为。 - 只有在你明确需要分布式共享临时上传时,才应显式使用
upload_mode=shared。 shared模式下返回的temp_file_id形如shared_<upload_id>;同一 account 在文件保留期间可以重复消费。- 新的 shared 上传会创建内部目录
viking://upload/<created_at_ms>-<uuid>/,目录内包含content和meta。目录名中的 13 位 Unix 毫秒时间戳即上传创建时间;meta最后写入,代表上传已完整完成。这些对象不属于普通文件系统浏览空间。 - shared 上传会保留
server.temp_upload.ttl_seconds指定的时长(默认 12 小时)。每次新的 shared 上传会对内部上传根目录执行一次列举,从每个一级上传目录名解析创建时间戳,并递归删除过期目录,不依赖文件系统修改时间。
3. 使用示例
HTTP API
POST /api/v1/resources/temp_upload
Content-Type: multipart/form-datacurl -X POST http://localhost:1933/api/v1/resources/temp_upload \
-H "X-API-Key: your-key" \
-F "file=@./documents/guide.md"分布式 / shared 上传:
curl -X POST http://localhost:1933/api/v1/resources/temp_upload \
-H "X-API-Key: your-key" \
-F "file=@./documents/guide.md" \
-F "upload_mode=shared"Python SDK
Python SDK 中的 add_resource、add_skill 等接口会自动处理本地文件上传,无需手动调用此接口。在 Python HTTP client 模式下,如果要启用分布式 shared 临时上传,可以在 ovcli.conf 中设置 upload.mode = "shared"。
Go SDK
client.AddResource、client.AddSkill、client.ImportOVPack 和 client.RestoreOVPack 会为本地文件自动调用 temp_upload。如需 shared 临时上传,设置 openviking.Config{UploadMode: "shared"}。
CLI
CLI 命令也会自动处理本地文件上传,无需手动调用此接口。
响应示例
{
"status": "ok",
"result": {
"temp_file_id": "upload_abc123def456.md"
},
"telemetry": {
"operation_id": "550e8400-e29b-41d4-a716-446655440000"
}
}shared 模式的响应示例:
{
"status": "ok",
"result": {
"temp_file_id": "shared_7f3c1b8d4f2e4b1bb0f6e8b2d9a4c123"
}
}相关文档
- 文件系统 - 文件和目录操作
- 技能 - 技能管理 API
- 检索 - 搜索和上下文获取
- ovpack 指南 - ovpack 导入导出详细说明
- OpenViking Assets - 声明式资源集合协议和运行指南
