OpenViking 实战:在 Claude Code 和 GLM 之间实现跨模型上下文共享

摘要:本文详细介绍如何使用 OpenViking 作为上下文管理中心,实现 Claude Code(VSCode 插件)和智谱 GLM 之间的对话内容共享和语义检索。通过 MCP(Model Context Protocol)集成,让不同 AI 模型能够访问共同的上下文记忆库。


目录

  1. 什么是 OpenViking
  2. 应用场景
  3. 系统架构
  4. 环境准备
  5. 安装 OpenViking
  6. 配置 OpenViking
  7. 启动 OpenViking 服务器
  8. 创建 MCP 服务器
  9. 配置 Claude Code
  10. 测试验证
  11. 使用指南
  12. 常见问题
  13. 进阶技巧

什么是 OpenViking

OpenViking 是由字节跳动开发的 Agent-native Context Database(面向 AI Agent 的上下文数据库),主要功能包括:

  • 语义检索:基于向量嵌入的智能搜索
  • 上下文存储:自动分块、向量化存储对话和文档
  • 多模态支持:支持文本、PDF、网页等多种格式
  • API 接口:提供 HTTP API 和 CLI 工具
  • VLM 集成:支持视觉语言模型处理图像

官方仓库:OpenViking


应用场景

场景 1:跨模型上下文共享

Claude Code 对话 → OpenViking ← GLM 对话
                         ↓
                    统一上下文库

场景 2:长期记忆管理

  • 保存重要的对话内容
  • 检索历史讨论
  • 跨会话知识积累

场景 3:文档知识库

  • 自动索引本地文档
  • 语义搜索相关内容
  • AI 辅助阅读和写作

系统架构

┌─────────────────┐
│   Claude Code   │
│   (VSCode)      │
└────────┬────────┘
         │ MCP Protocol
         ▼
┌─────────────────────────────────┐
│     OpenViking MCP Server       │
│  (openviking_mcp.py)            │
└────────┬────────────────────────┘
         │ HTTP API
         ▼
┌─────────────────────────────────┐
│   OpenViking HTTP Server        │
│   (localhost:8765)              │
└────────┬────────────────────────┘
         │
         ▼
┌─────────────────────────────────┐
│   Vector Database + Storage     │
│   (Chroma + Local Files)        │
└─────────────────────────────────┘

环境准备

系统要求

  • 操作系统:Windows / macOS / Linux
  • Python:3.8 或更高版本
  • 内存:建议 4GB 以上
  • 网络:需要访问 embedding API(本教程使用智谱 AI)

检查 Python 环境

python --version

安装 OpenViking

步骤 1:使用 pip 安装

pip install openviking

步骤 2:验证安装

pip show openviking

预期输出:

Name: openviking
Version: 0.1.17
Summary: An Agent-native context database

步骤 3:安装 MCP 依赖

pip install mcp

配置 OpenViking

步骤 1:创建配置目录

# Windows
mkdir %USERPROFILE%\.openviking

# macOS/Linux
mkdir ~/.openviking

步骤 2:创建主配置文件 ov.conf

Windows 路径C:\Users\YourUsername\.openviking\ov.conf

{
  "embedding": {
    "dense": {
      "provider": "openai",
      "api_base": "https://open.bigmodel.cn/api/paas/v4/embeddings",
      "api_key": "你的_API_KEY",
      "model": "embedding-3",
      "dimension": 2048
    }
  },
  "vlm": {
    "provider": "openai",
    "api_base": "https://open.bigmodel.cn/api/paas/v4",
    "api_key": "你的_API_KEY",
    "model": "glm-4v"
  }
}

获取 API Key

  1. 访问 智谱 AI 开放平台
  2. 注册/登录账号
  3. 在 API Keys 页面创建新的密钥

步骤 3:创建 CLI 配置文件 ovcli.conf

# Windows
echo {"url": "http://localhost:8765"} > %USERPROFILE%\.openviking\ovcli.conf

# macOS/Linux
echo '{"url": "http://localhost:8765"}' > ~/.openviking/ovcli.conf

启动 OpenViking 服务器

方式 1:临时启动(测试用)

python -m openviking serve --port 8765

方式 2:后台启动(推荐)

Windows (PowerShell)

Start-Process -WindowStyle Hidden python -ArgumentList "-m openviking serve --port 8765"

Windows (后台服务)

python -m openviking serve --port 8765 &

验证服务器运行

curl http://localhost:8765/health

预期输出:

{"status":"ok"}

创建 MCP 服务器

为什么需要 MCP 服务器?

OpenViking 本身不包含 MCP 服务器模块,需要创建一个自定义的 MCP 服务器来桥接 Claude Code 和 OpenViking。

步骤 1:创建 MCP 服务器脚本

文件路径C:\Users\YourUsername\.openviking\openviking_mcp.py

#!/usr/bin/env python3
"""OpenViking MCP Server for Claude Code integration.

Uses HTTP API to communicate with OpenViking server.
"""

import asyncio
import json
import logging
import tempfile
from pathlib import Path
from typing import Any
from datetime import datetime
from urllib.request import Request, urlopen
from urllib.error import URLError

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# MCP imports
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent

# Configuration
OPENVIKING_URL = "http://localhost:8765"

# Create server
server = Server("openviking-server")


def http_post(path: str, data: dict) -> dict:
    """Make HTTP POST request to OpenViking server."""
    url = f"{OPENVIKING_URL}{path}"
    body = json.dumps(data).encode('utf-8')
    req = Request(url, data=body, headers={'Content-Type': 'application/json'})
    try:
        with urlopen(req, timeout=30) as response:
            return json.loads(response.read().decode('utf-8'))
    except URLError as e:
        raise Exception(f"HTTP request failed: {e}")


def http_get(path: str) -> dict:
    """Make HTTP GET request to OpenViking server."""
    url = f"{OPENVIKING_URL}{path}"
    req = Request(url)
    try:
        with urlopen(req, timeout=30) as response:
            return json.loads(response.read().decode('utf-8'))
    except URLError as e:
        raise Exception(f"HTTP request failed: {e}")


@server.list_tools()
async def list_tools() -> list[Tool]:
    """List available MCP tools."""
    return [
        Tool(
            name="ov_save_context",
            description="Save conversation context to OpenViking",
            inputSchema={
                "type": "object",
                "properties": {
                    "content": {
                        "type": "string",
                        "description": "Content to save (will be stored as markdown)"
                    },
                    "title": {
                        "type": "string",
                        "description": "Title for the context (optional, auto-generated if not provided)"
                    }
                },
                "required": ["content"]
            }
        ),
        Tool(
            name="ov_search",
            description="Search OpenViking for relevant context using semantic search",
            inputSchema={
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "Search query for semantic retrieval"
                    },
                    "limit": {
                        "type": "integer",
                        "description": "Maximum number of results (default: 5)",
                        "default": 5
                    }
                },
                "required": ["query"]
            }
        ),
        Tool(
            name="ov_health",
            description="Check OpenViking server health status",
            inputSchema={
                "type": "object",
                "properties": {}
            }
        ),
        Tool(
            name="ov_list",
            description="List resources in OpenViking",
            inputSchema={
                "type": "object",
                "properties": {
                    "uri": {
                        "type": "string",
                        "description": "URI to list (default: viking://resources)",
                        "default": "viking://resources"
                    }
                }
            }
        )
    ]


@server.call_tool()
async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
    """Handle tool calls."""
    try:
        if name == "ov_health":
            result = http_get("/health")
            return [TextContent(
                type="text",
                text=json.dumps({
                    "status": "ok",
                    "message": "OpenViking server is running",
                    "server": result
                }, indent=2)
            )]

        elif name == "ov_save_context":
            content = arguments.get("content", "")
            title = arguments.get("title", "")

            if not title:
                title = f"conversation_{datetime.now().strftime('%Y%m%d_%H%M%S')}"

            # Create temp file with content
            with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False, encoding='utf-8') as f:
                f.write(f"# {title}\n\n{content}")
                temp_path = f.name

            try:
                # Use CLI to add resource
                import subprocess
                result = subprocess.run(
                    ['python', '-m', 'openviking', 'add-resource',
                     temp_path,
                     '--to', f'viking://resources/claude_conversations/{title}',
                     '--reason', 'Auto-saved from Claude Code MCP',
                     '--wait'],
                    capture_output=True,
                    text=True,
                    timeout=60
                )

                Path(temp_path).unlink(missing_ok=True)

                if result.returncode == 0:
                    return [TextContent(
                        type="text",
                        text=json.dumps({
                            "status": "success",
                            "message": f"Context saved to OpenViking as '{title}'",
                            "title": title,
                            "location": f"viking://resources/claude_conversations/{title}"
                        }, indent=2)
                    )]
                else:
                    return [TextContent(
                        type="text",
                        text=json.dumps({
                            "status": "error",
                            "error": result.stderr or "Failed to save context"
                        }, indent=2)
                    )]

            except Exception as e:
                Path(temp_path).unlink(missing_ok=True)
                raise

        elif name == "ov_search":
            query = arguments.get("query", "")
            limit = arguments.get("limit", 5)

            # Use CLI for search
            import subprocess
            result = subprocess.run(
                ['python', '-m', 'openviking', 'find',
                 query,
                 '--uri', 'viking://resources',
                 '--limit', str(limit)],
                capture_output=True,
                text=True,
                timeout=30
            )

            if result.returncode == 0:
                return [TextContent(
                    type="text",
                    text=json.dumps({
                        "status": "success",
                        "query": query,
                        "results": result.stdout
                    }, indent=2, ensure_ascii=False)
                )]
            else:
                return [TextContent(
                    type="text",
                    text=json.dumps({
                        "status": "error",
                        "error": result.stderr
                    }, indent=2)
                )]

        elif name == "ov_list":
            uri = arguments.get("uri", "viking://resources")

            import subprocess
            result = subprocess.run(
                ['python', '-m', 'openviking', 'ls', uri],
                capture_output=True,
                text=True,
                timeout=30
            )

            return [TextContent(
                type="text",
                text=json.dumps({
                    "status": "success" if result.returncode == 0 else "error",
                    "uri": uri,
                    "output": result.stdout or result.stderr
                }, indent=2, ensure_ascii=False)
            )]

        else:
            return [TextContent(
                type="text",
                text=f"Unknown tool: {name}"
            )]

    except Exception as e:
        logger.error(f"Error in {name}: {e}", exc_info=True)
        return [TextContent(
            type="text",
            text=json.dumps({
                "status": "error",
                "tool": name,
                "error": str(e)
            }, indent=2)
        )]


async def main():
    """Main entry point."""
    logger.info("Starting OpenViking MCP Server...")
    logger.info(f"OpenViking URL: {OPENVIKING_URL}")

    # Check server health
    try:
        health = http_get("/health")
        logger.info(f"Connected to OpenViking: {health}")
    except Exception as e:
        logger.error(f"Cannot connect to OpenViking server: {e}")
        logger.error("Make sure OpenViking server is running: python -m openviking serve")

    async with stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            server.create_initialization_options()
        )


if __name__ == "__main__":
    asyncio.run(main())

步骤 2:测试 MCP 脚本

python -c "from mcp.server import Server; print('MCP imports OK')"

配置 Claude Code

步骤 1:找到配置文件位置

Windows

C:\Users\YourUsername\AppData\Roaming\Claude\claude_desktop_config.json

macOS

~/Library/Application Support/Claude/claude_desktop_config.json

Linux

~/.config/Claude/claude_desktop_config.json

步骤 2:编辑配置文件

如果文件不存在,创建它。添加以下内容:

{
  "mcpServers": {
    "openviking": {
      "command": "python",
      "args": [
        "C:/Users/YourUsername/.openviking/openviking_mcp.py"
      ],
      "env": {
        "OPENVIKING_CONFIG_FILE": "C:/Users/YourUsername/.openviking/ov.conf"
      }
    }
  }
}

注意:将路径中的 YourUsername 替换为你的实际用户名,并使用正斜杠 / 或双反斜杠 \\

步骤 3:重启 Claude Code

  1. 完全关闭 VSCode
  2. 重新打开 VSCode
  3. Claude Code 会自动加载 MCP 服务器

测试验证

测试 1:验证 OpenViking 服务器

curl http://localhost:8765/health

预期输出:

{"status":"ok"}

测试 2:添加测试资源

python -m openviking add-resource "path/to/your/file.pdf" --to "viking://resources/test" --wait

测试 3:语义搜索

python -m openviking find "your search query" --uri "viking://resources" --limit 5

测试 4:在 Claude Code 中测试

重启后,在 Claude Code 对话框中输入:

检查 OpenViking 状态

Claude 会自动调用 ov_health 工具并返回结果。


使用指南

在 Claude Code 中使用

保存对话内容

自然语言指令

"把这段对话保存到 OpenViking"
"保存上下文:我们今天讨论了 AI 架构设计"
"记录这次关于 Python 性能优化的讨论"
搜索历史内容

自然语言指令

"在 OpenViking 中搜索关于 LaTeX 的内容"
"查找之前关于控制理论的讨论"
"搜索所有关于 backstepping 的内容"
检查系统状态

自然语言指令

"OpenViking 运行正常吗?"
"检查 OpenViking 服务器状态"

在 GLM 中使用

由于 GLM 目前不直接支持 MCP,你可以通过以下方式使用 OpenViking:

方式 1:命令行检索
# 搜索相关上下文
python -m openviking find "你的问题" --uri "viking://resources" --limit 3

# 将结果复制到 GLM 对话中
方式 2:创建快捷脚本
# 创建搜索脚本
echo '@echo off
python -m openviking find %1 --uri "viking://resources" --limit 5' > ov_search.bat

# 使用
ov_search.bat "your query"

常见问题

Q1: OpenViking 服务器无法启动

症状:端口被占用

解决

# 查找占用端口的进程
netstat -ano | findstr :8765

# 终止进程
taskkill /PID <进程ID> /F

Q2: MCP 服务器连接失败

症状:Claude Code 无法连接到 OpenViking

检查清单

  1. OpenViking 服务器是否运行:curl http://localhost:8765/health
  2. MCP 脚本路径是否正确
  3. 配置文件 JSON 格式是否正确
  4. Python 路径是否在系统 PATH 中

Q3: 向量化失败

症状add-resource 返回错误

解决

  1. 检查 API Key 是否有效
  2. 检查网络连接
  3. 查看配置文件中的 api_base 地址

Q4: 中文搜索结果不准确

解决

  • 使用智谱的 embedding 模型(对中文支持更好)
  • 调整搜索查询,使用更具体的关键词

进阶技巧

技巧 1:自动保存重要对话

创建一个系统提示词,定期保存对话:

每当讨论一个重要话题时,自动调用 ov_save_context 保存内容

技巧 2:构建知识库

# 批量添加文档
for file in docs/*.pdf; do
    python -m openviking add-resource "$file" --to "viking://resources/docs" --wait
done

技巧 3:多会话上下文共享

在不同 AI 模型间共享上下文:

Claude Code → OpenViking ← GLM/ChatGPT/Claude Web

技巧 4:API 直接调用

使用 Python 脚本直接调用 OpenViking API:

import requests

# 搜索
response = requests.post('http://localhost:8765/api/v1/search/find', json={
    "query": "your search query",
    "target_uri": "viking://resources",
    "limit": 5
})
print(response.json())

项目结构总结

.openviking/
├── ov.conf              # 主配置文件
├── ovcli.conf           # CLI 配置文件
├── openviking_mcp.py    # MCP 服务器脚本
└── data/                # 数据存储目录
    ├── vectordb/        # 向量数据库
    └── context/         # 上下文存储

总结

通过本教程,你已经实现了:

✅ OpenViking 服务器部署
✅ MCP 服务器创建和配置
✅ Claude Code 集成
✅ 跨模型上下文共享
✅ 语义检索功能

现在,你可以在不同的 AI 模型之间无缝共享上下文,构建属于你自己的 AI 知识库!


参考资料


如果这篇文章对你有帮助,请给个 赞和收藏 支持一下!有问题欢迎在评论区讨论。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐