Skip to content

资源管理

资源是智能体可以引用的外部知识。本模块提供资源的添加、导入/导出、临时文件上传等功能。

核心概念

资源类型

OpenViking 支持多种资源类型,按照功能分类如下:

文档类

类型扩展名说明
PDF.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 ...进行语音转录处理(规划)

云文档类

类型说明
飞书/LarkURL 方式,支持 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。可选 argsdepthmax_pagesinclude_pathsexclude_pathsallow_external_linksskip_download_links。页面中发现的下载链接默认跳过(skip_download_links=true),避免导入 llms.txt 等 sidecar 文件造成重复;设为 false 时会下载同域文件链接,并计入 max_pagesinclude_paths/exclude_paths路径前缀匹配(例如 /docs/ 仅匹配以 /docs/ 开头的路径,不会误命中 /blog/docs-tips)。

路由说明:https://host/sitemap.xmlhttps://host/feed.xml*.atom 等 sitemap-looking URL 和显式 args.site=true 让出给下表的整站导入;https://github.com/{org}/{repo} 等 Git 托管平台 URL 让出给上文的代码导入。

网站类(sitemap / RSS / Atom 整站导入)

类型资源名说明
站点地图 Sitemaphttps://host/sitemap.xmlhttps://host/sitemap-index.xml解析 sitemap,将站点所有页面抓取为一棵资源树(每页一个子节点),支持嵌套 <sitemapindex> 递归。整站只生成一个资源,落在 viking://resources/<host>
RSS / Atom 订阅源https://host/rss.xmlhttps://host/atom.xmlhttps://host/feed解析 RSS 2.0 / Atom,逐条把文章正文抓成树节点(feed 内含全文则直接使用,省一次抓取)。
整站自动发现https://host + args.site=true对裸域名/普通页面强制整站导入:自动通过 robots.txt、HTML <link rel="alternate"> autodiscovery、常见路径发现 sitemap/RSS,再整站抓取。

抓取有界、非递归(不会超出所列页面继续爬),受 parsers.webfeed 配置约束(max_pagesmax_concurrencypoliteness_delaysame_host_onlyrespect_robotsmax_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 完成前返回。
  • 立即响应包含 statusroot_uritask_id;抓取、解析、finalize 以及队列等待会在持久化后台任务中继续执行。
  • 可通过 GET /api/v1/tasks/{task_id} 查询任务状态。Git 资源导入任务的阶段包括 queuedfetchingparsingfinalizingprocessing_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} 删除任务以释放目标。原生导入显式指定 towatch_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 引用先上传的本地文件。

处理流程

  1. 识别并校验资源来源(URL 或上传的临时文件)
  2. 解析目标 URI
  3. 调用对应格式 Parser;args.parse_mode 控制转换后的 Markdown 正文是否允许拆分
  4. 构建目录树并写入 AGFS
  5. processing_mode 执行入库后的处理:semantic_and_vectors 生成语义产物和向量;vectors_only 跳过语义理解,只提交文件向量化
  6. 默认返回 task_id;调用方通过任务 API 确认处理完成
  7. 如果 reason 非空,将其追加到固定的资源 reason session 并 commit,复用常规记忆抽取链路,让合适的用户记忆引用该资源 URI
  8. 如指定 --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. 接口和参数说明

参数

参数类型必填默认值说明
pathstring-远程资源 URL(HTTP/HTTPS/Git)。与 temp_file_id 二选一
temp_file_idstring-临时上传文件 ID。与 path 二选一
tostring-本次导入的最终保存位置。目标已存在时会覆盖该目标;与 parent 互斥
parentstring-父级 Viking URI(资源放入此目录下)。与 to 互斥
create_parentboolFalse如果父目录不存在,自动创建父目录(服务端标志)
reasonstring""添加资源的原因;非空时会随资源 URI 进入常规 session 记忆抽取链路,并在生成的记忆中记录资源引用
instructionstring""语义提取的处理指令(实验特性)
waitboolFalse是否等待语义处理和向量化完成才返回
timeoutfloatNone超时时间(秒),仅 wait=true 时生效
strictboolFalse是否使用严格模式
ignore_dirsstringNone要忽略的目录名(逗号分隔)
includestringNone包含的文件模式(glob)
excludestringNone排除的文件模式(glob)
directly_upload_mediaboolTrue是否直接上传媒体文件
preserve_structureboolNone是否保留目录结构
argsobject{}传给特定 parser/accessor 的导入参数。原生 HTTPS Git 导入和 Watch 可通过 args.auth_config={"username":"oauth2","token":"..."} 在 TLS 上传递 HTTP Basic 凭据;username 默认为 oauth2。Git 的 branchcommit 仍放在 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.depthargs.max_pagesargs.include_pathsargs.exclude_pathsargs.allow_external_linksargs.skip_download_links;飞书用户 token 导入传 args.feishu_access_tokenpathtowatch_intervalincludeexcludeadd_resource 核心字段不能放入 args
watch_intervalfloat0定时更新间隔(分钟)。>0 按目标占用规则为可重新读取的来源创建新 Watch;通过 temp_file_id 上传的一次性快照不能创建 Watch。≤0 不创建 Watch:原生导入显式指定 to 时暂停唯一可访问的任务(存在歧义时返回 409),Connector 导入不影响已有 Watch。显式 to 优先,否则绑定本次导入的 root_uri
is_activeboolTrueWatch 初始调度状态。设为 false 时要求 watch_interval > 0,并在 toparent 中二选一。parent 仅支持原生飞书 URL 导入;Connector 仍要求精确的 to。首次导入仍执行一次,随后保持暂停
processing_modestringsemantic_and_vectors入库后的处理模式。semantic_and_vectors 是默认流程:生成语义产物(.abstract.md.overview.md)并生成向量。vectors_only 跳过语义理解/VLM 总结,只对当前资源文件生成向量
tagsstring[]None导入时写入向量检索记录的显式检索标签,格式必须是 k=v,例如 ["team=search", "env=test"]。搜索接口可用同名 tags 参数过滤召回
tag_modestring"replace"tags 的写入模式,可选 replaceappend。导入新资源时会随本次生成的每条向量记录写入;不会在完成后额外调用 set_tags,响应也不返回 tags_result
telemetryTelemetryRequestFalse是否返回遥测数据

补充说明

  • toparent 不能同时使用。to 是最终保存位置:目标不存在就创建,目标已存在就覆盖该目标;如果目标是目录,目录里本次导入没有生成的旧文件或子目录会被删除。parent 是保存目录,适合向已有目录追加新资源;父目录不存在时使用 create_parent=true 或 CLI 的 --parent-auto-create。当导入后的 root_urito 相同时,语义与向量处理会复用未变化内容,只处理变化部分。
  • 创建新资源要求目标父目录可写;显式更新已有 to 要求该目标可写。权限校验在任务入队前完成。自动命名按实际 URI 占用判断,即使同名资源不可读也会选择 _1_2 等后缀,而不会尝试覆盖。
  • wait=false 返回的 status=accepted 表示任务已通过预检查并入队,不表示资源处理已经完成;最终状态以对应 task_id 为准。
  • 如果同时省略 toparent,服务端会先尝试使用当前用户的 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_idpeer_id 路径片段必须是安全的单段标识,例如 aliceweb-visitor-alice。包含路径分隔符、...:+ 的值会被拒绝。
  • pathtemp_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_configwatch_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_configargs.auth_config 的明文 HTTP 鉴权和带鉴权重定向仍会被拒绝。
  • token 会放在 HTTPS 请求体中传输。生产环境应保持诊断请求体 dump 关闭;显式启用该功能可能记录秘密。
  • reason 触发的记忆生成复用 session.commit 的抽取链路,只使用 reason、资源 URI、可用的资源名称和目录摘要,不会读取或展开完整资源正文;系统会写入 entitieseventspreferences 等已有记忆类型,不创建独立的资源记忆目录。
  • 删除资源时,系统会在删除前扫描本次上下文对应的 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。管理员维护已有数据时,reindex API/CLI 仍使用 modevectors_onlysemantic_and_vectorsprune_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_idapp_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_idfeishu_app_secret;OpenViking 会将其保存在 watch task 私有状态中,并用于刷新该 watch 的用户 token。
  • 请求未传应用凭证时,用户 token watch 回退使用 FEISHU_APP_IDFEISHU_APP_SECRET,或 ov.conf 中的 feishu.app_idfeishu.app_secret。飞书 refresh token 绑定签发它的应用,因此实际使用的应用凭证必须与传入的用户 token 匹配。
  • Watch task 的 token 状态和请求传入的应用凭证保存在内部控制文件 viking://resources/.watch_tasks.json 中,不会出现在 watch API/MCP/CLI 返回里。若启用了 VikingFS 文件加密,该控制文件会静态加密;否则服务端控制文件中会包含这些明文私有状态。
  • 本地目录输入会遵循 .gitignore(根目录和子目录,标准 Git 语义);ignore_dirsincludeexclude 会在此基础上进一步过滤。
  • 目录导入仅在至少一个入选文件成功时采用 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
bash
# 从 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

python
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

typescript
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

go
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

bash
# 添加本地文件
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 响应(默认异步模式)

json
{
  "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-xxx

CLI 响应 (JSON 格式,使用 -o json)

json
{
  "status": "accepted",
  "root_uri": "viking://resources/01-overview",
  "task_id": "uuid-xxx"
}

字段说明

字段类型说明
statusstring处理状态:accepted 表示已入队,success 表示成功,error 表示失败
root_uristring资源在 OpenViking 中的最终 URI
task_idstring(可选,仅当 wait=false 时)可轮询 /api/v1/tasks/{task_id} 的任务 ID。非 Git 导入用于队列跟踪;Git 仓库导入用于完整后台导入跟踪。
temp_uristring导入过程中生成的临时 URI
source_pathstring原始源文件路径或 URL
metaobject资源解析过程中的元数据(如文件类型、大小等)
errorsarray处理过程中的错误列表
warningsarray(可选)处理过程中的警告列表(仅在 strict=False 时可能出现)
queue_statusobject(可选,仅当 wait=true 时)队列处理状态,包含 pendingprocessingcompleted 计数
memory_linkingobject(可选,仅当 reason 触发记忆生成时)本次资源 URI 与用户记忆的关联结果

完成后的资源添加任务结果

对于 wait=false 的 Git 仓库来源,后台任务的 task_type="add_resource"resource_id 等于返回的 root_uri。运行中的任务记录可能包含 stage。轮询 /api/v1/tasks/{task_id} 直到任务完成。完成后,任务内层的 result 会包含最终队列汇总和 context_count

json
{
  "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_resourceadd_skill 导入本地文件。

1. API 实现介绍

此接口用于把本地文件上传到服务端托管的临时存储中,返回 temp_file_id 供后续 API 使用。这是一个辅助接口,通常不直接调用,而是通过 SDK 或 CLI 自动使用。

处理流程

  1. 接收上传的文件
  2. 根据 upload_mode 选择临时上传后端
  3. 保存文件并记录原始文件名
  4. 返回临时文件 ID

代码入口

  • openviking/server/routers/resources.py:temp_upload - HTTP 路由
  • openviking/service/resource_service.py - 服务实现

2. 接口和参数说明

参数

参数类型必填默认值说明
fileUploadFile-上传的文件(multipart/form-data)
telemetryboolFalse是否返回遥测数据
upload_modestring"local"临时上传模式。local 保持现有单机行为;shared 将文件上传到共享临时存储,适用于分布式部署。

说明:

  • 默认值是 local,所以现有客户端在不改动的情况下仍保持原有行为。
  • 只有在你明确需要分布式共享临时上传时,才应显式使用 upload_mode=shared
  • shared 模式下返回的 temp_file_id 形如 shared_<upload_id>;同一 account 在文件保留期间可以重复消费。
  • 新的 shared 上传会创建内部目录 viking://upload/<created_at_ms>-<uuid>/,目录内包含 contentmeta。目录名中的 13 位 Unix 毫秒时间戳即上传创建时间;meta 最后写入,代表上传已完整完成。这些对象不属于普通文件系统浏览空间。
  • shared 上传会保留 server.temp_upload.ttl_seconds 指定的时长(默认 12 小时)。每次新的 shared 上传会对内部上传根目录执行一次列举,从每个一级上传目录名解析创建时间戳,并递归删除过期目录,不依赖文件系统修改时间。

3. 使用示例

HTTP API

POST /api/v1/resources/temp_upload
Content-Type: multipart/form-data
bash
curl -X POST http://localhost:1933/api/v1/resources/temp_upload \
  -H "X-API-Key: your-key" \
  -F "file=@./documents/guide.md"

分布式 / shared 上传:

bash
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_resourceadd_skill 等接口会自动处理本地文件上传,无需手动调用此接口。在 Python HTTP client 模式下,如果要启用分布式 shared 临时上传,可以在 ovcli.conf 中设置 upload.mode = "shared"

Go SDK

client.AddResourceclient.AddSkillclient.ImportOVPackclient.RestoreOVPack 会为本地文件自动调用 temp_upload。如需 shared 临时上传,设置 openviking.Config{UploadMode: "shared"}

CLI

CLI 命令也会自动处理本地文件上传,无需手动调用此接口。

响应示例

json
{
  "status": "ok",
  "result": {
    "temp_file_id": "upload_abc123def456.md"
  },
  "telemetry": {
    "operation_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}

shared 模式的响应示例:

json
{
  "status": "ok",
  "result": {
    "temp_file_id": "shared_7f3c1b8d4f2e4b1bb0f6e8b2d9a4c123"
  }
}

相关文档

Released under the Apache-2.0 License.