Skip to content

Latest commit

 

History

History
450 lines (312 loc) · 18.3 KB

File metadata and controls

450 lines (312 loc) · 18.3 KB

运维

语言: English · Tiếng Việt · 中文

业务规则、部署流程、测试矩阵、故障排查手册。这是你在把 Viking rollout 给团队之前要读的东西。


业务规则决策

B1 —— 部署模型:按团队自托管

决策:OpenViking 自托管运行,每个团队一个实例(或单人开发者一人一个)。不是 SaaS 模式。

原因

  • OpenViking 的默认端口 127.0.0.1:1933 和单服务器架构暗示 "local-first"
  • OpenSpace 的隐私优先哲学(self-evolution、本地 skill DB)与此契合
  • 团队局域网部署很便宜(单个 docker 容器)
  • 无数据出境意味着监管合规简单直接

部署形态

  • 单人开发者:在笔记本上 docker run -p 1933:1933 volcengine/openviking:latest
  • 小团队:团队局域网或内部云上的单个共享 Viking 实例
  • 企业:多个 Viking 实例按团队或数据分类划分

B2 —— 隐私边界:非对称隔离

决策

  • Agent 记忆toolspatternsskillscases)→ 团队共享
  • 用户记忆preferencesprofile)→ 按用户隔离

原因:工具知识是集体智能的全部意义 —— 每个用户复制一份 "chromedriver 版本兼容性" 就失去了意义。用户偏好则是个人的:"Alice 偏好柱状图" 绝不能出现在 Bob 的会话中。

强制执行:通过 namespace + user_id config 进行 URI 前缀隔离。Client 的 agent_memory_uri() 返回团队作用域路径;user_memory_uri() 返回用户作用域路径。Viking 的 target_uri 目录过滤器在服务器端强制边界。

威胁模型覆盖

  • 共享 Viking 上的诚实用户:隔离防止意外偏好泄漏
  • 处于错误 namespace 的诚实团队成员:namespace 配置错误会落到错误的记忆桶,但不会外泄数据
  • 恶意用户:超出范围 —— 需要 Viking 级别的 RBAC(API key 作用域)

B3 —— 保留策略

决策

  • 原始 session 数据 → 30 天 TTL(在 Viking 侧配置)
  • 抽取的记忆 → 永久(在 session 删除后仍然存在)

原因

  • Session 是审计跟踪 —— 大,在抽取后与记忆重复
  • 记忆经过去重和整理 —— 是永久存储
  • 在 100 用户 × 10 task/天 × 3 进化 skill = 3,000 session/天 ≈ 90k/月 × ~5KB = ~450MB/月原始 session 数据
  • 30 天给调试提供足够的取证窗口,又不会让存储失控

实现:Viking 侧配置(sessions.retention_days)。OpenSpace 不管理保留;它只创建 session,让 Viking 自行老化。

B4 —— Skill 资源所有权

决策:团队拥有推送的 skill 资源;作者在元数据中被致谢。

原因:集体智能意味着整个团队从任何成员的进化 skill 中受益。个人所有权与跨 Agent 共享的全部意义相冲突。归因保留 "谁贡献了什么" 的叙事,而不 gate 访问。

实现push_evolved_skills() 附加元数据:

{
  "source": "openspace",
  "origin": "derived" | "captured" | "fixed",
  "description": "<skill 描述>",
  "parent_skill_ids": ["parent1", "parent2"],
  "change_summary": "相对 parent 改变了什么",
  "generation": <int>
}

团队管理员通过 Viking 自身的资源管理进行删除(超出 OpenSpace 范围)。

B5 —— API Key 生命周期

决策:静态环境变量,重启时轮转。没有热重载。

原因

  • OpenSpace 进程通常是短生命周期的(CLI 单次调用、MCP 服务器)
  • 重启轮转简单可靠
  • 热重载 secret 引入 race window,运维收益极小
  • Viking 支持多个同时有效的 key,可以实现无停机的滚动轮转

运维流程

  1. 通过管理 API 向 Viking 添加新 key
  2. 用新 key 更新团队 .env
  3. 滚动重启 OpenSpace 实例(或就等下一次 CLI 调用)
  4. 传播后从 Viking 移除旧 key

部署检查清单

部署前

  • OpenViking 服务器运行中,curl <URL>/health 返回 200
  • OPENVIKING_URL 设置正确(检查端口、如适用检查 TLS)
  • 如果 Viking 启用了认证,配置 OPENVIKING_API_KEY
  • OPENVIKING_NAMESPACE 设置为团队标识符(或有意为单用户留空)
  • OPENVIKING_USER_ID 策略已决定(自动回退 vs 显式按用户)
  • OPENVIKING_PUSH_SKILLS 已根据隐私政策评估

冒烟测试

# 验证 client 可以到达 Viking
python3 -c "
import asyncio
from openspace.viking import OpenVikingClient

async def check():
    c = OpenVikingClient()
    print('available:', await c.is_available())
    print('namespace:', c.namespace)
    print('user_id:', c.user_id)
    print('agent tools uri:', c.agent_memory_uri('tools'))
    print('user prefs uri:', c.user_memory_uri('preferences'))
    await c.close()

asyncio.run(check())
"

期望输出:

available: True
namespace: <你的 namespace>
user_id: <你的 user id>
agent tools uri: viking://tenants/<ns>/agent/memories/tools/
user prefs uri: viking://tenants/<ns>/user/<uid>/memories/preferences/

第一个真实 task

openspace --query "simple task to prime the memory base"

检查日志:

tail -n 50 logs/openspace/*.log | grep "Viking"

期望看到:

✓ OpenViking integration active [namespace=..., user_id=...]
Viking telemetry: available=True hits=0 enrich_chars=0 feedback=committed pushed=1

在记忆库填充起来之前 hit 会是 0。这对第一个 task 是预期的。

持续验证

监控这些指标:

  • 命中率hits > 0):应随记忆库增长呈上升趋势
  • 反馈状态:应大多为 committed —— failed 表示 Viking 问题
  • 推送 skill 数:启用推送时应与进化 skill 数匹配
  • 增强字符数:典型范围 500–2000;>3000 暗示记忆过于冗长

测试矩阵(全部通过)

63 个测试 覆盖每个新代码路径。运行方式:

python3 -m pytest tests/test_viking_client.py -v --asyncio-mode=auto

覆盖分解

领域 测试数
Client graceful failure 6 —— 不可达服务器、find 方法、session 方法、skill push
Namespace URI 构建 3 —— 有/无 namespace/user_id 组合
查询组合 4 —— 仅 task、历史合并、multimodal 内容、截断
健康缓存 1 —— 成功 vs 失败 TTL 区别
Skill push 3 —— graceful fail、跳过不完整 record、truthy 响应处理
增强流水线 4 —— 空检索、分析 context、历史感知、丰富反馈
身份解析 5 —— 覆盖、环境变量、OS 用户回退、清理、空状态
Push 豁免环境变量 3 —— truthy/falsy/回退到 config
VikingExecutionStats 2 —— 默认形状、变异隔离
Score threshold + resolvers(Round 6) 7 —— min score env 覆盖、范围限制、回退、scrub pii 开关、find 传播
PII 清理器(Round 6) 12 —— Anthropic/GitHub/AWS keys、email、basic-auth URL、JWT、私钥块、CC Luhn、非 CC 数字、幂等、None 处理、嵌套 records
Negative feedback + anti-patterns(Round 6) 5 —— graceful fail、antipatterns URI、stale memory report、antipattern key 存在、env fingerprint 形状
MCP 工具(Round 6) 8 —— unavailable、missing query、no-client status、polarity 校验、forget 需要 URI、category 规范化、register count、tool_memory_status
迭代中 RetrieveMemoryTool(Round 6) 3 —— unavailable、empty query、tool shape

关键测试不变式

每个测试都针对端口 127.0.0.1:19999(故意不可达)。这意味着测试套件:

  • 无论 OpenViking 是否运行都能通过
  • 执行真实部署最常遇到的失败路径
  • 总运行时间不到一秒(无网络等待)
  • 永远不会污染真实的 Viking 实例

针对真实 Viking 实例的集成测试不在本套件中 —— 它们应该放在单独的 tests/integration/ 目录中,由 @pytest.mark.viking_live 这样的 marker 守护,仅在 CI 中用专用 Viking 容器运行。


故障排查手册

症状:"OpenViking init skipped" 出现在日志中

原因:Client 初始化失败。通常是:

  • httpx 未安装(ModuleNotFoundError
  • OPENVIKING_URL 不正确(无法解析)
  • openspace.viking.config 中的 import 错误(不太可能,但检查 sanitization 函数)

修复:检查 .log 文件中的完整堆栈。logger.debug 包含异常。

症状:"OpenViking not available" 但服务器正在运行

原因is_available() 把 False 结果缓存了 12 秒。

修复

  • 等 12 秒再重试
  • 直接从 OpenSpace 主机 curl <OPENVIKING_URL>/health
  • 检查 OpenSpace 和 Viking 之间的防火墙 / 网络
  • 检查 Viking 在预期端口监听(默认 1933)

如果 curl 有效但 OpenSpace 的 client 无效,检查:

  • 代理环境变量(HTTP_PROXYHTTPS_PROXY)干扰
  • 不同的主机名解析(例如 localhost vs 127.0.0.1

症状:每个 task 都 hits=0

原因:记忆库为空或查询不匹配。

诊断

# 检查 Viking 是否有任何记忆
curl -X POST http://localhost:1933/api/v1/search/find \
  -H "Content-Type: application/json" \
  -d '{"query": "anything", "limit": 5}'

如果为空,Viking 还没有抽取的记忆。抽取是异步的 —— 等几次 task commit 之后。

如果非空但 OpenSpace 仍然 0 hit:

  • 检查 namespace 匹配:OpenSpace 查询 URI vs 记忆存储 URI
  • 用明确的 target_uri curl,匹配 OpenSpace 发送的内容
  • 记录 resolve_viking_identity() 输出以验证使用了哪个 URI 前缀

症状:遥测中 feedback=failed

原因:在反馈 session 创建或 commit 期间出现异常。

诊断:检查 .log 中的 OpenViking feedback skipped: <exception>。常见原因:

  • Viking 拒绝 session 格式(当前实现不应出现)
  • Viking 慢,某条消息超时(每个请求 5s)
  • Viking 返回 4xx/5xx(检查 Viking 自己的日志)

修复:通常是临时的。如果持续:

  • 增加 client.py 中的 _REQUEST_TIMEOUT(不是用户可配置;需要 fork)
  • 检查 Viking 磁盘 / 队列容量

症状:每个 task 都有延迟峰值

原因is_available() 没有被缓存,每个 task 都命中 /health

诊断:如果 Viking 在 available→unavailable 之间反复切换,缓存会 churn。通常表明 Viking 本身不健康或网络不稳定。

修复:稳定 Viking。缓存 TTL 不能再降低,否则对温缓存命中的回归不可接受。

症状:"我设置了 OPENVIKING_PUSH_SKILLS=false 但 skill 仍然被推送"

原因:优先级顺序。OpenSpaceConfig.openviking_auto_push_skills=True + 环境变量未正确解析。

诊断

from openspace.viking.config import resolve_viking_push_enabled
print(resolve_viking_push_enabled(config_default=True))

如果环境变量设置正确应该打印 False。接受的值:0 false no off

修复:确保环境变量值恰好是识别字符串之一(大小写不敏感)。OPENVIKING_PUSH_SKILLS=no 有效;OPENVIKING_PUSH_SKILLS=NO 有效;OPENVIKING_PUSH_SKILLS=nope 回退到 config 默认。

症状:"我本地删除了 skill 但 Viking 记忆仍然存在"

预期行为。OpenViking 独立于 OpenSpace 的本地 skill DB 存储记忆(从 session 抽取的 abstract)。删除本地 skill 不会删除它的 Viking 记忆。

清理:如需使用 Viking 自己的管理 API 删除记忆,或通过 host agent MCP 工具(Round 6)调用 openviking_forget_memory(uri)。这是故意的 —— 跨 Agent 传播要求记忆在任何单个 Agent 的本地状态之外持久存在。

症状(Round 6):"设置 OPENVIKING_MIN_SCORE 后命中率降为 0"

原因:阈值高于任何记忆的实际分数。Viking 的嵌入相似度通常为好匹配提供 0.3–0.8;设置 OPENVIKING_MIN_SCORE=0.9 会 drop 几乎所有东西。

诊断

# 暂时禁用阈值并检查原始分数
OPENVIKING_MIN_SCORE=0.0 openspace --query "typical task"
# 检查 result["viking"]["hit_counts"] —— 如果现在 >0,阈值就太高

修复:从 OPENVIKING_MIN_SCORE=0.0(默认)开始,观察几天真实工作负载的分数分布,然后提高到只丢弃底部 10–20% 命中的水平。

症状(Round 6):"Viking sessions 包含 redacted 占位符而不是我的测试数据"

预期行为。PII 清理器(OPENVIKING_SCRUB_PII=true,默认)在写入 Viking 之前将 secrets、凭证和 PII 替换为 [REDACTED_*] 占位符。这保护生产 secrets 不泄漏到共享记忆中。

仅用于测试:如果你需要原始数据通过 Viking 往返,在你的测试环境中设置 OPENVIKING_SCRUB_PII=false。永远不要在生产环境中禁用。

症状(Round 6):"openviking_retrieve_memory MCP 工具未出现在 host agent 中"

原因:Host agent 的 MCP 客户端在启动时缓存了工具列表,还没有在 OpenSpace 的 Viking 工具注册后重新获取。

诊断

  1. 检查 OpenSpace 日志中的 Registered 5 OpenViking MCP tools for host agent access
  2. 从 host agent 明确列出 MCP 工具
  3. 验证直接调用 openviking_memory_status() 返回 {"status": "ok"}

修复:重启 host agent 以便它重新列出 MCP 工具。大多数 host(Claude Code、Codex、OpenClaw)在 MCP 服务器连接时获取列表。

症状(Round 6):"provide_feedback 返回 status=skipped"

原因:要么 Viking 未配置(_viking_clientNone),要么 is_available() 返回 False

诊断:响应字典包含 reason 字段:

result = await openspace.provide_feedback(task_id, "positive")
# {"status": "skipped", "reason": "OpenViking not configured"}  或
# {"status": "skipped", "reason": "OpenViking unavailable"}

修复:以上面"为什么 Viking 什么都没做?"相同的方式验证 Viking 配置。

症状(Round 6):"grounding agent 从不调用 retrieve_memory 工具"

短任务中的预期。工具只在 openviking_mid_iter_tool=True(默认)且 Viking 客户端可用时才注册。LLM 决定何时调用它 —— 它不是每个任务都调用,只在初始 enrichment 不够用时才调用。

诊断:检查日志中的 Added retrieve_memory tool (OpenViking mid-iteration)。如果不存在:

  • 验证 config.openviking_mid_iter_toolTrue
  • 验证 Viking 在 OpenSpace 初始化期间可用
  • 检查 openspace._grounding_agent._viking_client 不为 None

如果工具已注册但从未被调用:LLM 判断初始 enrichment 已足够。这是正确的行为。工具作为安全阀存在,用于 iter-1 提示误导或不完整的情况。


回滚流程

如果出了问题,需要立即禁用 Viking:

# 选项 1:最快 —— 环境变量禁用
export OPENVIKING_ENABLED=false
# 重启 OpenSpace 进程

# 选项 2:源头禁用 config
# 在 OpenSpaceConfig 中:openviking_enabled=False

# 选项 3:核选项 —— 卸载 httpx
# Client._get_client() 在 httpx 缺失时返回 None → 完全 graceful fail

OpenSpace 的行为将和集成之前完全一致。无数据丢失,无需迁移,无 skill DB 影响。

稍后要重新启用,就翻转环境变量。记忆在 Viking 侧持久存在,重新启用后的第一次查询就会找到。


性能验证

在生产环境宣告 Viking "工作" 之前,测量以下之一:

快速检查:日志 grep

# 总 task 数
grep "Viking telemetry" logs/*.log | wc -l

# 至少有一个命中的 task
grep "Viking telemetry" logs/*.log | grep -v "hits=0" | wc -l

# 计算命中率
echo "scale=2; $(grep 'Viking telemetry' logs/*.log | grep -v 'hits=0' | wc -l) / $(grep 'Viking telemetry' logs/*.log | wc -l)" | bc

目标:使用第一周后命中率 >30%。

更深入:执行时间对比

用 Viking 启用运行相同 task 10 次,用 OPENVIKING_ENABLED=false 运行 10 次。对比:

  • 结果中的平均 execution_time
  • 平均 iteration 数
  • Token 数(从 recording 元数据)

期望:温缓存运行在两个维度都减少 20–40%。

严格:完整 benchmark

运行 GDPVal 或你内部的 task 套件两次:

  • 一次禁用 Viking
  • 一次启用 Viking(在 prime 记忆库之后)

OpenSpace 公布数字:46% 更少 token。与 Viking 叠加,期望:比 baseline(非 OpenSpace)Agent 减少 55–65% token


监控 / 告警建议

如果你在团队规模下运行 Viking,考虑对以下情况告警:

告警 阈值 行动
feedback_status=failed 1h 内 >5% 检查 Viking 健康、磁盘、队列
available=False 1h 内 >10% 检查 Viking 正常运行时间 / 网络
hits=0 1 周后 >70% 调查记忆抽取流水线
pushed_skills 单调下降 7 天内下降趋势 检查 push_skills 环境变量是否被翻转关闭
平均 enrichment_chars >5000 记忆膨胀 —— 考虑 Viking 侧清理

这些从每次执行的 viking stats dict 导出。通过 metrics exporter 管道到你的可观测性栈(Prometheus、Datadog、CloudWatch 等)—— 不内置于 OpenSpace。


第 1 天 vs 第 30 天要测量什么

第 1 天(引导期):

  • 大多数 task 上 available=True → 集成在工作
  • 大多数 task 上 hits=0 → 记忆库为空,这是正确的
  • 带 evolution 的 task 上 feedback=committed → Viking 接收到反馈

第 7 天

  • 一些 task 上 hits>0 → 记忆抽取赶上了
  • 重复 task 模式开始出现 iteration 减少的迹象
  • 开始看到 selector 选出与无 hints 时不同的 skill

第 30 天

  • 稳定的命中率 30–60%,取决于 task 多样性
  • 相对 baseline 可测量的 iteration/token 减少
  • 用户偏好开始在无需明确提示的情况下塑造 Agent 行为

何时不使用 Viking

  • 需要原始 Agent 性能的 benchmark 运行
  • 有严格确定性要求的 task
  • 隐私敏感的一次性 task,连抽取的 abstract 都不能离开执行
  • 记忆填充成本超过节省的单 task 流水线
  • 学习状态会混淆信号的调试会话

其他所有情况,保持开启。失败模式是"集成不可见",不是"集成破坏 OpenSpace"。


返回索引 · 架构 · Token 经济学 · 配置