Replies: 1 comment
实施偏差备忘(v2 — 2026-05-18)PR #2110 已合 5 个 phase 全量落地,过程中相对 RFC 正文产生 4 处设计调整,记录在此供后来读者对照。RFC 正文未改,以本评论 + PR 代码为准。 1. §4.1 双键设计:放宽为「不一致才 400」
2. §4.5 trigger 语义:明确为 fire-and-forget
3. §4.6 ov CLI 命令树:嵌套到
|
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
RFC: Watch Management API(REST + ov CLI + MCP)
作者 / Author: zhengxiao.wu
状态 / Status: Draft
日期 / Date: 2026-05-18
范围 / Scope: 三个控制面(REST API / ov CLI / MCP)的 watch 管理接口设计
1. 摘要 / Executive Summary
add_resource已支持通过watch_interval参数开启周期性自动刷新(由WatchScheduler+WatchManager实现,全量重拉模式)。但watch 任务一旦创建,三个控制面(REST / ov CLI / MCP)都缺乏对外的管理接口——list:用户/agent 无法盘点自己开了哪些 watchpause / resume:只能通过watch_interval <= 0间接取消delete:同上trigger:手动刷新无入口update:改间隔/原因/指令只能重建后端
WatchManager实际上已具备所有必要能力。本 RFC 提议在 REST 层补齐/watches系列管理端点作为基础设施,ov CLI 做完整 parity 镜像作为 power user 工具,**MCP 仅暴露最小闭环(list + cancel)**以控制模型认知负担。三个面各自服务于不同用户群,零后端逻辑改动。2. 动机 / Motivation
2.1 现状对照表(截至 main 当前状态)
POST /resources带watch_intervalwatch_interval≤0)ov add-resource --watch-interval2.2 真实用户故事
2.3 安全/合规视角
用户在 OpenViking 上开了 watch 后,没有 API 可以盘点和停止——这从合规审计角度也站不住脚(数据自动同步任务必须可见、可控、可停)。
3. 目标与非目标 / Goals & Non-Goals
3.1 Goals
list / get / patch / delete / trigger五个端点ov watch *:与 REST 端点 1:1 镜像(power user 工具)list_watches+cancel_watch两件套(最小闭环)WatchManager已有的 public 方法,零业务逻辑新增WatchManager._check_permission现有规则(account_id / user_id / agent_id 三维)viking://resources/.watch_tasks.json机制3.2 Non-Goals(本 RFC 不涉及)
watch_scheduler引擎级改造)4. 设计 / Design
4.1 REST API 端点
所有端点遵循 OpenViking 现有 router 约定(
openviking/server/routers/resources.py风格)。GET/watches?active_only=true过滤WatchManager.get_all_tasksGET/watches/{task_id}或/watches?to_uri=...get_task/get_task_by_uriPATCH/watches/{task_id}或/watches?to_uri=...watch_interval/is_active/reason/instruction等update_taskDELETE/watches/{task_id}或/watches?to_uri=...delete_taskPOST/watches/{task_id}/trigger或/watches/trigger?to_uri=...WatchScheduler.schedule_task双键设计:
task_id与to_uri同时支持事实基础:
WatchManager已维护两个索引(watch_manager.py:205,402):_tasks: dict[task_id → WatchTask]主索引_uri_to_task: dict[to_uri → task_id]反向索引两者都是 O(1),双键支持无额外存储成本。
调用约定(每个单资源端点二选一):
/watches/{task_id}/watches?to_uri=<URI>冲突规则:如果同时传入
{task_id}path 和?to_uri=query,返回 400(避免歧义;强制调用方明确意图)。为什么不让
to_uri直接作为路径参数:viking://resources/foo/bar含://和多级/,需要双重 URL-encode 才能塞进路径,对人/工具/curl 都不友好。Query 参数能用浏览器原生 encoding,体验更顺。4.2 统一响应 schema
{ "task_id": "550e8400-e29b-41d4-a716-446655440000", "to_uri": "viking://resources/volcengine/OpenViking", "path": "https://github.com/volcengine/OpenViking", "parent_uri": null, "watch_interval": 1440.0, "is_active": true, "reason": "Track upstream changes", "instruction": "", "created_at": "2026-05-15T10:23:45Z", "last_execution_time": "2026-05-17T10:23:45Z", "next_execution_time": "2026-05-18T10:23:45Z" }字段全部来自
WatchTask数据类(openviking/resource/watch_manager.py:28-102),无需新增。列表端点返回
{"tasks": [...], "total": N}结构,留作未来加分页时不破坏 schema。4.3 权限模型
完全复用
WatchManager._check_permission(watch_manager.py:273-308):(account_id, user_id, agent_id)创建的 watch每个 endpoint 在调用
WatchManager方法时传入当前ctx,权限校验由后端自动处理,router 层无需重复实现。4.4 PATCH 语义细节
PATCH /watches/{task_id}请求体可包含任意子集:{ "watch_interval": 4320, // 改间隔到 3 天 "is_active": false, // 同时暂停 "reason": "Project archived", "instruction": "" }字段缺失 = 不变。
watch_interval与is_active是正交的:watch_interval控制频率is_active控制是否参与调度这样可以"暂停但保留间隔配置",恢复时
{"is_active": true}一行搞定。4.5 触发限流(Open Question 之一)
POST /watches/{task_id}/trigger会强制开始一次全量重拉。是否要在 router 层限流(例如每个 task 每 5 分钟最多触发一次)?详见 §6。4.6 ov CLI 子命令
设计原则:CLI 是 power user 工具,与 REST 端点全 parity。模型认知负担不是约束,功能完整性优先。
ov watch ls [--active-only] [--to-uri=PAT] [--format=table|json]GET /watches--format=json给脚本管道ov watch show <task-id|to-uri>GET /watches/{id}ov watch rm <task-id|to-uri> [--yes]DELETE /watches/{id}--yes跳过确认ov watch pause <task-id|to-uri>PATCH is_active=falseov watch resume <task-id|to-uri>PATCH is_active=trueov watch set-interval <task-id|to-uri> <minutes>PATCH watch_intervalov watch trigger <task-id|to-uri>POST .../trigger双键参数设计:positional 参数自动识别——见到
viking://前缀按 URI 走?to_uri=查找,否则当task_id。CLI 用户记 URI 比记 UUID 容易。输出格式:默认人类可读 table;
--format=json输出与 REST schema 一致的 JSON 数组,方便jq处理与脚本编排。4.7 MCP 工具
设计原则:MCP 工具描述会进入模型 system prompt,每个工具都消耗上下文且增加决策成本。所以严格砍到"闭环最小集合"。
4.7.1 给 MCP
add_resource净新增 watch 能力main 当前 MCP
add_resource仅有path+description两个参数,没有任何 watch 入口。本 RFC 新增两个参数:watch_interval: float = 0:与 REST/CLI 命名完全一致,0 = 不开 watch,>0 触发周期性全量重拉to: str = "":当watch_interval > 0时必填(服务端硬约束,需要稳定 URI 作为刷新目标)为什么用
watch_interval而不是watch: bool:>=1440分钟,特殊场景由 agent 自决4.7.2 新增工具(仅两个)
list_watches()cancel_watch(to_uri: str)cancel_watch用to_uri作主键(不暴露task_id):agent 记不住 UUID,URI 是它已经在用的标识符。4.7.3 主动砍掉的 MCP 工具
pause_watch / resume_watchadd_resource(watch_interval=N, to=...)refresh_watch_nowadd_resource(path=..., to=同 URI)自然触发全量重拉set_intervaladd_resource(watch_interval=N, to=...)show_watchlist_watches已含全部字段闭环:
add_resource(watch_interval>0) ↔ cancel_watch ↔ list_watches,三个动词构成"加—查—停"的对偶,认知模型最小。4.8 三个面的职责分工
agent 想做"高级控制"(暂停、改间隔、立即触发)时,应在错误/帮助消息中明确引导用户走 ov CLI,避免诱导模型自行组合多步操作。
4.9 兼容性
POST /resources的watch_interval参数保留,沿用现有语义(>0 创建/更新,≤0 取消)/watches端点不破坏现有 Rust CLI 调用方add_resource工具仅有path+description,本 RFC 净新增watch_interval+to参数为 opt-in(默认 0,行为完全不变),现有 agent 代码无需改动/watches系列做管理;watch_interval作为"一步开 watch"的便利写法POST /resources.watch_interval,留作 §6 Open Question5. 实现拆分 / Implementation Phases
按依赖顺序拆分。每阶段独立 PR,CLI/MCP 依赖 REST 端点先就位。
GET /watches、GET /watches/{id}、DELETE /watches/{id}+ 单测PATCH /watches/{id}+POST /watches/{id}/trigger+ 单测add_resource新增watch_interval+to参数(净新增,main 当前 MCP 无 watch 能力)list_watches+cancel_watch两个工具ov watch ls/show/rm/pause/resume/set-interval/trigger子命令组6. 未决问题 / Open Questions
{task_id}path 和?to_uri=query,二选一;同传报 400。是否更宽松(query 覆盖 path 静默生效)会更友好?还是更严格(query 仅作 list 过滤,不在单资源端点出现)?watch_interval=0的语义:算暂停(is_active=false但保留任务)还是删除?建议:暂停。删除明确走DELETEPOST /resources.watch_interval长期归宿:保留为便利接口 / 软弃用 / 硬弃用?trigger配额:全量重拉成本高,是否需要每 task / 每 user 限流?get_all_tasks当前一次性返回所有任务,未来任务量大时是否需要?limit&offset或 cursor?POST /watches:batch_delete、POST /watches:batch_pause?还是逐个调用即可?7. 备选方案 / Alternatives Considered
方案 A:在
POST /resources上堆更多 watch 控制参数即新增
watch_pause: bool、watch_trigger_now: bool等。否决:语义混乱,违背 REST 资源化原则,单个端点承载多重职责。
方案 B:完全废弃
watch_interval,只允许独立/watches要求所有"开 watch"都走"先 add → 再 POST /watches"两步。
否决:破坏现有 CLI / 插件 / 文档的向后兼容;两步流程对终端用户不友好。
方案 C(本 RFC 采纳):并行存在
POST /resources保留watch_interval作为"一步式"便利接口/watches作为正式的管理面/watches,但watch_interval不弃用8. 风险与缓解 / Risks & Mitigations
DELETE /watches/{id}误删delete_task已有权限校验;考虑加?confirm=true显式确认参数POST .../trigger被滥用导致算力浪费GET /watches返回过大update_task当前是 last-write-wins;如有需要再加 ETag / version 字段9. 验证 / Verification
实现完成后,至少覆盖:
viking://resources/.watch_tasks.json恢复,新端点能正确读取10. 参考资料 / References
openviking/resource/watch_manager.py—— public API 及权限模型openviking/resource/watch_scheduler.py:113-140——schedule_task立即触发openviking/resource/watch_storage.py—— VikingFS 持久化openviking/server/routers/resources.py:84,180——AddResourceRequest.watch_intervalopenviking/service/resource_service.py:106-289——add_resource中的 watch 处理bot/docs/rfc-openviking-cli-ov-chat.md—— 项目内 RFC 格式模板欢迎评论。 重点欢迎对 §6 各 Open Question 的意见,以及 §7 中是否还有更优的备选方案被遗漏。
All reactions