1. 为什么要在 Claude Code 和 GLM 之间共享上下文
如果你同时用 Claude Code 写代码、用 GLM 做中文推理或文档整理,大概率遇到过这个尴尬:在 Claude Code 里聊了半小时的架构方案,切到 GLM 想让它接着分析,结果它完全不知道前面发生了什么,你得把上下文重新贴一遍。模型之间没有共享记忆,每次切换都像换了个失忆的助手。
这个问题的本质是:每个模型的会话上下文都锁在自己的客户端里,没有一个中间层来存、来查、来复用。OpenViking 就是干这个的——它是一个面向 AI Agent 的上下文数据库,支持语义检索、自动分块向量化、多格式存储。配合 MCP(Model Context Protocol),Claude Code 可以把对话内容写进去、搜出来,GLM 那边通过 CLI 或 HTTP API 也能读到同一份上下文。
我试过把这套流程跑通之后,最直观的变化是:Claude Code 里讨论的接口设计,切到 GLM 让它写单元测试时,它能直接引用之前的字段定义和边界条件,不用我再复述。跨模型上下文共享不是炫技,它解决的是真实的多模型协作断点问题。
这篇文章面向的是已经在用 Claude Code、同时手头有 GLM API Key 的开发者。你需要会基本的 Python 环境操作、能改 JSON 配置文件、看得懂 curl 请求。不需要你懂向量数据库原理,OpenViking 把这些都封装好了。整个流程分四步:装 OpenViking、配 MCP Server、接 Claude Code、验证两端消息一致。每一步我都会给可复制的配置片段和验证命令。
先说清楚架构,这样后面排错时你知道该查哪一层:
Claude Code (VSCode) │ MCP Protocol (stdio) ▼ openviking_mcp.py (MCP Server) │ HTTP API ▼ OpenViking HTTP Server (localhost:8765) │ ▼ Vector DB + Local Storage ▲ │ CLI / HTTP API GLM (命令行检索或脚本调用)Claude Code 通过 MCP 协议调用你写的 MCP Server,MCP Server 再通过 HTTP 请求 OpenViking 的本地服务。GLM 那边不直接支持 MCP,所以走 CLI 或 HTTP API 读同一份数据。两条路径最终落到同一个向量库,这就是上下文共享的实现方式。
2. 前置准备:装好 OpenViking 并拿到可用的 API Key
这一节的目标是让 OpenViking 服务在本地跑起来,并且 embedding 和 VLM 都能正常工作。OpenViking 本身不提供 embedding 模型,它需要你接一个外部的 embedding API。这里用智谱的 embedding-3 模型,中文语义检索效果比较稳。
2.1 环境检查和安装
先确认 Python 版本,OpenViking 要求 3.8 以上:
python --version然后安装 OpenViking 和 MCP 依赖:
pip install openviking pip install mcp装完验证一下:
pip show openviking预期输出里能看到Version: 0.1.17或更高版本。如果 pip 装的时候报编译错误,大概率是缺少 C 编译工具链,Windows 上装个 Visual Studio Build Tools 就行。
2.2 创建配置目录和主配置文件
OpenViking 的配置放在用户目录下的.openviking文件夹里:
# Windows mkdir %USERPROFILE%\.openviking # macOS/Linux mkdir ~/.openviking然后创建主配置文件ov.conf。Windows 路径是C:\Users\YourUsername\.openviking\ov.conf,macOS/Linux 是~/.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需要你去智谱 AI 开放平台注册账号,在 API Keys 页面创建一个密钥。embedding 模型选embedding-3,维度 2048,中文支持好。VLM 部分配的是glm-4v,用于处理图像类资源,如果你暂时不处理图片,这部分可以先留着不影响。
注意:
api_base的地址要写完整,embedding 的路径是/api/paas/v4/embeddings,VLM 的路径是/api/paas/v4,两者不一样,别写混了。
2.3 创建 CLI 配置文件
CLI 工具需要知道 OpenViking 服务的地址,创建ovcli.conf:
# Windows echo {"url": "http://localhost:8765"} > %USERPROFILE%\.openviking\ovcli.conf # macOS/Linux echo '{"url": "http://localhost:8765"}' > ~/.openviking/ovcli.conf2.4 启动 OpenViking 服务
临时启动(测试用,关掉终端就停):
python -m openviking serve --port 8765后台启动(推荐,Windows PowerShell):
Start-Process -WindowStyle Hidden python -ArgumentList "-m openviking serve --port 8765"macOS/Linux 后台启动:
nohup python -m openviking serve --port 8765 &启动后验证服务是否正常:
curl http://localhost:8765/health预期输出{"status":"ok"}。如果这一步就失败了,先别往下走,检查端口 8765 是否被占用:
# Windows netstat -ano | findstr :8765 # macOS/Linux lsof -i :8765有占用就换端口,或者把占用进程杀掉。服务跑起来之后,OpenViking 这一层就准备好了。
3. 可复制配置:写 MCP Server 并接入 Claude Code
OpenViking 本身不带 MCP Server 模块,需要你自己写一个桥接脚本。这个脚本的作用是:接收 Claude Code 通过 MCP 协议发来的工具调用请求,转成 HTTP 请求发给 OpenViking 服务,再把结果返回给 Claude Code。
3.1 创建 MCP Server 脚本
文件路径放在C:\Users\YourUsername\.openviking\openviking_mcp.py(macOS/Linux 对应~/.openviking/openviking_mcp.py)。完整代码如下:
#!/usr/bin/env python3 """OpenViking MCP Server for Claude Code integration.""" import asyncio import json import logging import tempfile import subprocess from pathlib import Path from typing import Any from datetime import datetime from urllib.request import Request, urlopen from urllib.error import URLError from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) OPENVIKING_URL = "http://localhost:8765" server = Server("openviking-server") def http_post(path: str, data: dict) -> dict: url = f"{OPENVIKING_URL}{path}" body = json.dumps(data).encode('utf-8') req = Request(url, data=body, headers={'Content-Type': 'application/json'}) with urlopen(req, timeout=30) as response: return json.loads(response.read().decode('utf-8')) def http_get(path: str) -> dict: url = f"{OPENVIKING_URL}{path}" req = Request(url) with urlopen(req, timeout=30) as response: return json.loads(response.read().decode('utf-8')) @server.list_tools() async def list_tools() -> list[Tool]: return [ Tool( name="ov_save_context", description="Save conversation context to OpenViking", inputSchema={ "type": "object", "properties": { "content": {"type": "string", "description": "Content to save"}, "title": {"type": "string", "description": "Title (optional)"} }, "required": ["content"] } ), Tool( name="ov_search", description="Search OpenViking using semantic search", inputSchema={ "type": "object", "properties": { "query": {"type": "string", "description": "Search query"}, "limit": {"type": "integer", "default": 5} }, "required": ["query"] } ), Tool( name="ov_health", description="Check OpenViking server health", inputSchema={"type": "object", "properties": {}} ), Tool( name="ov_list", description="List resources in OpenViking", inputSchema={ "type": "object", "properties": { "uri": {"type": "string", "default": "viking://resources"} } } ) ] @server.call_tool() async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]: try: if name == "ov_health": result = http_get("/health") return [TextContent(type="text", text=json.dumps({ "status": "ok", "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')}" 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: 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", "title": title, "location": f"viking://resources/claude_conversations/{title}" }, indent=2))] else: return [TextContent(type="text", text=json.dumps({ "status": "error", "error": result.stderr }, 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) result = subprocess.run( ['python', '-m', 'openviking', 'find', query, '--uri', 'viking://resources', '--limit', str(limit)], capture_output=True, text=True, timeout=30 ) return [TextContent(type="text", text=json.dumps({ "status": "success" if result.returncode == 0 else "error", "query": query, "results": result.stdout or result.stderr }, indent=2, ensure_ascii=False))] elif name == "ov_list": uri = arguments.get("uri", "viking://resources") 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(): logger.info("Starting OpenViking MCP Server...") try: health = http_get("/health") logger.info(f"Connected to OpenViking: {health}") except Exception as e: logger.error(f"Cannot connect to OpenViking: {e}") 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())这个脚本定义了四个 MCP 工具:ov_save_context保存上下文、ov_search语义搜索、ov_health健康检查、ov_list列出资源。Claude Code 会根据你的自然语言指令自动选择调用哪个工具。
3.2 配置 Claude Code 的 MCP 设置
找到 Claude Code 的配置文件位置:
- 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
如果文件不存在就创建它,写入以下内容:
{ "mcpServers": { "openviking": { "command": "python", "args": [ "C:/Users/YourUsername/.openviking/openviking_mcp.py" ], "env": { "OPENVIKING_CONFIG_FILE": "C:/Users/YourUsername/.openviking/ov.conf" } } } }三个关键点必须对齐:command是 Python 可执行文件路径(如果在 PATH 里直接写python就行)、args里是 MCP 脚本的绝对路径、env里指向主配置文件。路径用正斜杠/或双反斜杠\\,别用单反斜杠,JSON 会转义出错。
注意:如果你用的是 Claude Code CLI 而不是桌面版,MCP 配置在
~/.claude/settings.json或项目根目录的.mcp.json里,格式类似但字段名可能不同,以你本地版本为准。
3.3 重启并确认 MCP 加载
完全关闭 VSCode 再重新打开,Claude Code 启动时会自动加载 MCP Server。你可以在 Claude Code 的对话里输入「检查 OpenViking 状态」,如果配置正确,它会调用ov_health工具并返回服务状态。
如果 Claude Code 没有识别到 MCP 工具,先手动跑一下脚本看有没有报错:
python C:/Users/YourUsername/.openviking/openviking_mcp.py正常的话会输出日志然后挂起等待 stdio 输入。如果报ModuleNotFoundError: No module named 'mcp',说明 MCP 依赖没装到当前 Python 环境里,重新pip install mcp。
4. 验证请求:确认两端消息一致
配置写完不算完,得实际验证 Claude Code 写进去的上下文,GLM 那边能搜到、内容一致。这一节给完整的验证步骤。
4.1 验证 OpenViking 服务层
先确认服务活着:
curl http://localhost:8765/health返回{"status":"ok"}就对了。
4.2 从 Claude Code 写入上下文
在 Claude Code 对话框里输入:
把这段对话保存到 OpenViking:我们在设计一个用户认证模块,采用 JWT + refresh token 方案,access token 有效期 15 分钟,refresh token 有效期 7 天,存储用 Redis。Claude Code 会调用ov_save_context工具,把内容写到viking://resources/claude_conversations/下面。返回结果里会有status: success和保存路径。
4.3 用 CLI 验证写入结果
在终端里列出资源,确认内容进去了:
python -m openviking ls viking://resources/claude_conversations应该能看到刚才保存的那条记录。然后做语义搜索:
python -m openviking find "JWT token 有效期" --uri "viking://resources" --limit 3预期能搜到刚才保存的认证模块设计内容。这一步验证的是 OpenViking 的向量化和检索链路是通的。
4.4 从 GLM 侧读取同一份上下文
GLM 不直接支持 MCP,所以走 CLI 或 HTTP API。最简单的方式是写个搜索脚本:
# Windows echo @echo off > ov_search.bat echo python -m openviking find %1 --uri "viking://resources" --limit 5 >> ov_search.bat # macOS/Linux echo 'python -m openviking find "$1" --uri "viking://resources" --limit 5' > ov_search.sh chmod +x ov_search.sh用的时候:
ov_search.bat "认证模块的 token 方案"把搜出来的内容贴到 GLM 对话里,GLM 就能基于这份上下文继续工作。你也可以用 Python 直接调 HTTP API:
import requests response = requests.post('http://localhost:8765/api/v1/search/find', json={ "query": "JWT refresh token 有效期", "target_uri": "viking://resources", "limit": 5 }) print(response.json())4.5 一致性核对
验证两端消息一致的关键是:Claude Code 写入的原文,和 GLM 侧搜出来的内容,语义上要对得上。具体操作:
在 Claude Code 里保存一段带明确标识的文本,比如「项目代号 Falcon,数据库用 PostgreSQL 15,缓存用 Redis 7」。然后在 GLM 侧搜「Falcon 项目用什么数据库」,看返回结果里有没有「PostgreSQL 15」。如果有,说明跨模型上下文共享链路是通的。
我实测下来,embedding-3 对中文技术术语的检索准确率不错,但如果你搜的是很短的英文缩写(比如只搜「JWT」),可能会召回多条不相关记录。这时候把查询写具体一点,比如「JWT access token 过期时间」,命中率会明显提升。
5. 常见报错排查:401、local proxy failed、reading choices
这一节列几个实际跑的时候最容易撞上的错误,以及对应的排查路径。
5.1 401 Unauthorized
现象:add-resource或find返回 401,日志里提示 authentication failed。
原因:ov.conf里的api_key无效,或者api_base写错了。
排查:
# 直接测 embedding API 是否可用 curl -X POST https://open.bigmodel.cn/api/paas/v4/embeddings \ -H "Authorization: Bearer 你的_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"embedding-3","input":"test"}'如果这个 curl 也返回 401,说明 Key 本身有问题,去智谱平台重新生成一个。如果 curl 通了但 OpenViking 还报 401,检查ov.conf里api_key字段有没有多余空格或引号嵌套错误。
5.2 local proxy failed / connection refused
现象:MCP Server 启动时报Cannot connect to OpenViking server,或者 Claude Code 调用工具时返回local proxy failed。
原因:OpenViking HTTP 服务没跑起来,或者端口不对。
排查:
curl http://localhost:8765/health如果连不上,先确认服务进程在不在:
# Windows tasklist | findstr python # macOS/Linux ps aux | grep openviking服务没跑就重新启动。如果服务在跑但 curl 不通,检查ovcli.conf里的 URL 和实际启动端口是否一致。MCP 脚本里的OPENVIKING_URL也要和这个端口对齐。
5.3 reading choices / JSON 解析错误
现象:Claude Code 调用 MCP 工具后返回error reading choices或 JSON 解析失败。
原因:MCP Server 返回的内容不是合法 JSON,或者 subprocess 调用的 CLI 输出里混了非 JSON 内容。
排查:手动跑一次 CLI 命令,看输出格式:
python -m openviking find "test" --uri "viking://resources" --limit 3如果 CLI 输出里带了进度条、日志行或者 ANSI 颜色码,MCP 脚本直接把它塞进 JSON 就会解析失败。解决办法是在 subprocess 调用时加--quiet参数(如果 CLI 支持),或者在脚本里对输出做清洗,只取 JSON 部分。
5.4 OAuth / token 过期
现象:之前能用,过了一段时间突然报 OAuth 相关错误。
原因:智谱 API Key 有有效期,或者账户余额不足。
排查:登录智谱开放平台,检查 API Key 状态和账户余额。如果 Key 过期了,重新生成一个,更新ov.conf里的api_key,然后重启 OpenViking 服务。
5.5 Claude Code 看不到 MCP 工具
现象:重启 VSCode 后,Claude Code 里输入「检查 OpenViking 状态」,它没有调用ov_health,而是直接用自然语言回复。
原因:MCP 配置没被加载,或者脚本路径不对。
排查:检查claude_desktop_config.json的 JSON 格式是否合法(可以用在线 JSON 校验工具过一遍)。确认args里的脚本路径是绝对路径且文件存在。然后看 Claude Code 的 MCP 日志(通常在输出面板里选 Claude Code 频道),里面会打印 MCP Server 的启动日志和报错信息。
6. 把上下文共享接进日常编码流程
跑通验证之后,接下来是怎么把它用顺。这里给几个实际场景的操作方式。
6.1 在 Claude Code 里自动保存关键对话
你可以在 Claude Code 的对话里直接说「把这段保存到 OpenViking」,它会调用ov_save_context。更省事的做法是在项目根目录放一个CLAUDE.md,里面写一条规则:
每当讨论完一个架构决策或接口设计,自动调用 ov_save_context 保存摘要,标题用「决策-模块名-日期」格式。这样 Claude Code 会在合适的时候自动保存,不用你每次手动提醒。
6.2 在 GLM 侧做检索增强
GLM 那边没有 MCP,但你可以把搜索脚本包一层,做成一个简单的检索增强流程。比如写个 Python 脚本,先搜 OpenViking 拿到相关上下文,拼进 prompt 再发给 GLM:
import requests def ask_glm_with_context(question, api_key): # 先从 OpenViking 搜上下文 search_resp = requests.post('http://localhost:8765/api/v1/search/find', json={ "query": question, "target_uri": "viking://resources", "limit": 3 }) context = search_resp.json().get("results", "") # 拼进 prompt 发给 GLM prompt = f"参考以下上下文:\n{context}\n\n问题:{question}" glm_resp = requests.post( 'https://open.bigmodel.cn/api/paas/v4/chat/completions', headers={"Authorization": f"Bearer {api_key}"}, json={ "model": "glm-4", "messages": [{"role": "user", "content": prompt}] } ) return glm_resp.json()这个模式的好处是:GLM 不需要原生支持 MCP,你通过脚本把上下文注入进去就行。Claude Code 写进去的内容,GLM 这边通过检索拿到,跨模型上下文共享就闭环了。
6.3 批量导入文档构建知识库
如果你有一堆本地文档想让两个模型都能查,批量导入:
for file in docs/*.pdf; do python -m openviking add-resource "$file" \ --to "viking://resources/docs" \ --wait done导入后,Claude Code 里搜「文档里关于 XX 的内容」,GLM 侧用同样的查询也能搜到同一批结果。
6.4 多模型协作的实际工作流
一个我常用的流程:在 Claude Code 里做代码实现和重构,每完成一个模块就保存上下文摘要;切到 GLM 做代码审查或写文档时,先搜相关模块的上下文,让 GLM 基于 Claude Code 的实现细节来审查。这样两个模型的输出是连贯的,不会出现「Claude Code 改了接口但 GLM 还在按旧接口写测试」的情况。
如果你需要更稳定的长期编码和 Agent 协作能力,可以考虑用 Coding Plan 来管理多模型调用配额和路由。接入文档里有完整的 MCP 配置示例和 API 说明,API Key 在控制台的 API Keys 页面生成。模型对话入口可以用来快速验证 GLM 侧的检索增强效果,不用每次都写脚本。
整套流程跑下来,核心就三件事:OpenViking 服务活着、MCP Server 配置对、两端用同一个向量库。把这三件事盯住,跨模型上下文共享就能稳定工作。