拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于Docker与MCP构建LLM Agent记忆系统:hindsight架构实战

基于Docker与MCP构建LLM Agent记忆系统:hindsight架构实战

1. 从“hindsight”说起:为什么我们需要给Agent装上“后视镜”

第一次看到“hindsight”这个词,我脑子里蹦出来的不是词典释义,而是自己踩过的一个坑。去年做一套基于LLM的客服工单自动分类系统,模型在单轮对话里表现堪称完美,准确率能到92%以上。可一旦把对话拉长到十几轮,用户前面提过的订单号、产品型号、投诉诉求,它就开始“选择性失忆”——要么把A用户的问题安到B用户头上,要么反复追问已经回答过的信息。那段时间我天天盯着日志怀疑人生,直到把整个会话历史重新灌进上下文窗口,准确率才勉强拉回来,但token成本直接翻了四倍。

这就是hindsight要解决的核心问题。它不是某个具体的开源库名字,而是一类设计思路的统称:让LLM Agent具备对历史交互的“后见之明”,能够从过往的对话、操作、工具调用记录中提取、存储、检索并复用关键信息,而不是每次都把全部历史硬塞进上下文。你可以把它理解成给Agent装了一面“后视镜”——开车时不用一直扭头看后窗,但需要变道、倒车时,后视镜里的信息必须准确、及时、不遗漏。

围绕这个思路,社区里衍生出了一堆相关概念:agent memory(智能体记忆)、working memory(工作记忆)、LLM wiki知识库、MCP协议(Model Context Protocol)等等。这些词看着散,其实都在回答同一个问题:Agent的记忆到底该怎么存、怎么取、怎么用。而Docker在这套体系里扮演的是“地基”角色——没有容器化,你很难把记忆存储、向量检索、MCP服务这些组件稳定地跑在一起。

这篇文章适合谁看?如果你正在做LLM应用开发,被多轮对话的上下文管理折磨过;或者你听说过MCP但还没动手搭过;又或者你只是想搞清楚“agent memory”到底是不是又一个炒概念的东西——那接下来的内容应该能帮你省下不少查文档和踩坑的时间。我会从设计思路讲到实操落地,把hindsight这套逻辑拆开揉碎,配上能直接抄的Docker配置和MCP接入步骤。

2. 核心思路拆解:Agent记忆到底该怎么分层

2.1 为什么“全量塞上下文”是条死路

先算一笔账。假设你的Agent平均每轮对话产生200个token的文本,用户和Agent各占一半,一轮就是400 token。如果一次会话持续30轮,那就是12000 token。这还只是纯文本,没算工具调用的JSON结构、系统提示词、few-shot示例。按GPT-4级别的模型算,12000 token的输入成本大约是0.36美元(按$30/1M token估算),一天1000次会话就是360美元。一个月下来光上下文成本就过万美元。

更致命的是注意力稀释。Transformer的注意力机制虽然理论上能处理长上下文,但实际表现是:当上下文超过一定长度后,模型对中间位置信息的召回率会明显下降。这不是我瞎说,社区里已经有大量实测数据表明,在128K上下文里,位于中段的关键信息被正确引用的概率比开头和结尾低30%以上。你把所有历史都塞进去,模型反而更容易“看漏”。

所以hindsight的第一条设计原则就是:不是所有历史都值得记住,更不是所有记忆都需要实时加载。

2.2 三层记忆架构:工作记忆、情景记忆、语义记忆

参考认知科学的分层模型,Agent记忆可以拆成三层:

工作记忆(Working Memory)是当前会话的“桌面”。它只保留最近N轮对话和当前任务相关的上下文,容量有限但访问速度极快。通常直接放在prompt里,或者通过一个轻量级的滑动窗口管理。N的取值取决于任务复杂度——简单问答3到5轮足够,复杂任务规划可能需要10到15轮。

情景记忆(Episodic Memory)是“日记本”。它记录的是具体发生过的事件:某年某月某日,用户A问了什么问题,Agent调用了什么工具,返回了什么结果。这些记忆按时间线存储,检索时通常按时间范围或事件类型过滤。向量数据库在这里很常用,因为你可以把每个事件embedding后存进去,用语义相似度做召回。

语义记忆(Semantic Memory)是“知识库”。它不记录具体事件,而是从大量交互中抽象出的规律、偏好、事实。比如“用户A偏好用邮件沟通”“产品B的退货率在夏季会上升”。这部分通常需要额外的抽取和归纳步骤,可以用LLM做定期总结,也可以人工维护。

hindsight的核心操作就是在这三层之间做写入、检索、更新、遗忘。写入策略决定了什么信息值得存;检索策略决定了怎么快速找到相关记忆;更新策略决定了记忆如何随时间演化;遗忘策略决定了哪些旧信息该被清理。这四个环节任何一个出问题,整个记忆系统就会变成“垃圾进垃圾出”。

2.3 MCP在记忆体系里的角色

MCP(Model Context Protocol)是Anthropic在2024年底开源的一套协议,目的是标准化LLM与外部工具、数据源的交互方式。你可以把它理解成“AI世界的USB-C接口”——不管后端是数据库、文件系统还是API,只要实现了MCP Server,任何支持MCP的客户端都能即插即用。

在hindsight体系里,MCP的价值在于把记忆存储和检索抽象成标准工具。比如你可以写一个MCP Server,暴露三个工具:store_memory、retrieve_memory、forget_memory。Agent在需要记住某件事时调用store_memory,需要回忆时调用retrieve_memory。这样记忆逻辑就和Agent的主逻辑解耦了,换模型、换框架都不用重写记忆模块。

而且MCP天然支持多客户端共享。你可以在Docker里跑一个记忆MCP Server,同时给本地的Claude Desktop、Cursor、自己的Agent程序提供服务。记忆数据集中管理,不会出现“这个客户端记得、那个客户端不记得”的碎片化问题。

2.4 Docker为什么是必选项

有人可能会问:我就跑一个Python脚本存向量,为什么非要上Docker?答案很简单:依赖隔离和可复现性。

记忆系统通常涉及多个组件:向量数据库(如Qdrant、Chroma)、嵌入模型服务(如Ollama、TEI)、MCP Server、Agent主程序。这些组件对Python版本、CUDA版本、系统库的要求各不相同。我试过在一台机器上直接pip install所有依赖,结果Chroma要求numpy<1.26,而另一个库要求numpy>=1.26,直接冲突到无法运行。用Docker Compose把每个组件拆成独立容器,网络互通但环境隔离,问题迎刃而解。

另外,Docker的卷挂载让记忆数据持久化变得极其简单。向量数据库的数据目录挂到宿主机,容器删了重建,记忆还在。这对于需要长期运行的Agent来说太重要了。

3. 实操环境搭建:从Docker到MCP Server

3.1 Docker Desktop安装与常见坑

Windows用户直接去官网下载Docker Desktop安装包,双击一路下一步就行。但有两个坑几乎每个人都会踩:

第一个坑是WSL2没装或没更新。Docker Desktop在Windows上依赖WSL2作为后端。如果你启动时报“Virtualization support not detected”或者“Docker Desktop failed to start because virtualization support is not enabled”,先去BIOS里确认Intel VT-x或AMD-V是开启状态,然后在PowerShell里跑:

wsl --install wsl --update

装完重启,再启动Docker Desktop基本就能过。

第二个坑是Hyper-V冲突。如果你之前装过VMware或VirtualBox,它们可能占用了Hyper-V。解决办法是在“启用或关闭Windows功能”里把Hyper-V和“虚拟机平台”都勾上,然后重启。如果还是不行,检查VMware的“虚拟化引擎”设置,把“虚拟化Intel VT-x/EPT”关掉。

Linux用户直接用官方脚本:

curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker $USER

最后一行是把当前用户加入docker组,避免每次都要sudo。执行完记得重新登录终端。

macOS用户下载Docker Desktop for Mac,注意区分Intel芯片和Apple Silicon芯片的版本。M系列芯片选Apple Silicon版,性能会好很多。

3.2 用Docker Compose编排记忆服务栈

下面是我在实际项目中用的docker-compose.yml,包含Qdrant向量库、Ollama嵌入服务和自建的MCP记忆Server:

version: '3.8' services: qdrant: image: qdrant/qdrant:latest container_name: hindsight-qdrant ports: - "6333:6333" - "6334:6334" volumes: - ./data/qdrant:/qdrant/storage restart: unless-stopped ollama: image: ollama/ollama:latest container_name: hindsight-ollama ports: - "11434:11434" volumes: - ./data/ollama:/root/.ollama restart: unless-stopped deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu] memory-mcp: build: ./memory-mcp container_name: hindsight-mcp ports: - "8080:8080" environment: - QDRANT_URL=http://qdrant:6333 - OLLAMA_URL=http://ollama:11434 - EMBED_MODEL=nomic-embed-text depends_on: - qdrant - ollama restart: unless-stopped

几个关键点解释一下。Qdrant的6333端口是HTTP API,6334是gRPC,两个都暴露方便调试。Ollama的GPU配置那段,如果你没有NVIDIA显卡,把deploy整段删掉,它会自动用CPU跑,只是嵌入速度会慢一些。memory-mcp是我自己写的MCP Server,下面会给代码。

启动命令:

docker compose up -d

第一次跑会拉镜像,Qdrant大概200MB,Ollama大概1.5GB,耐心等几分钟。启动后用docker compose ps确认三个容器都是running状态。

3.3 拉取嵌入模型并验证

Ollama启动后需要手动拉模型:

docker exec -it hindsight-ollama ollama pull nomic-embed-text

这个模型大概274MB,专门做文本嵌入,768维输出,在MTEB榜单上表现不错。拉完后验证一下:

curl http://localhost:11434/api/embeddings -d '{ "model": "nomic-embed-text", "prompt": "测试嵌入服务" }'

如果返回一个768维的浮点数数组,说明嵌入服务正常。如果报错“model not found”,检查模型名是否拼写正确。

3.4 编写MCP记忆Server

MCP Server可以用Python或TypeScript写。我用Python的mcp库,代码结构如下:

from mcp.server import Server, NotificationOptions from mcp.server.models import InitializationOptions import mcp.server.stdio import mcp.types as types from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams, PointStruct import httpx import uuid import os app = Server("hindsight-memory") qdrant = QdrantClient(url=os.getenv("QDRANT_URL", "http://localhost:6333")) OLLAMA_URL = os.getenv("OLLAMA_URL", "http://localhost:11434") EMBED_MODEL = os.getenv("EMBED_MODEL", "nomic-embed-text") COLLECTION = "agent_memory" def ensure_collection(): collections = [c.name for c in qdrant.get_collections().collections] if COLLECTION not in collections: qdrant.create_collection( collection_name=COLLECTION, vectors_config=VectorParams(size=768, distance=Distance.COSINE) ) async def get_embedding(text: str): async with httpx.AsyncClient() as client: resp = await client.post( f"{OLLAMA_URL}/api/embeddings", json={"model": EMBED_MODEL, "prompt": text}, timeout=30.0 ) return resp.json()["embedding"] @app.list_tools() async def list_tools(): return [ types.Tool( name="store_memory", description="存储一条Agent记忆", inputSchema={ "type": "object", "properties": { "content": {"type": "string", "description": "记忆内容"}, "memory_type": {"type": "string", "enum": ["episodic", "semantic"]}, "metadata": {"type": "object"} }, "required": ["content", "memory_type"] } ), types.Tool( name="retrieve_memory", description="检索相关记忆", inputSchema={ "type": "object", "properties": { "query": {"type": "string"}, "limit": {"type": "integer", "default": 5}, "memory_type": {"type": "string"} }, "required": ["query"] } ) ] @app.call_tool() async def call_tool(name: str, arguments: dict): ensure_collection() if name == "store_memory": embedding = await get_embedding(arguments["content"]) point_id = str(uuid.uuid4()) qdrant.upsert( collection_name=COLLECTION, points=[PointStruct( id=point_id, vector=embedding, payload={ "content": arguments["content"], "memory_type": arguments["memory_type"], "metadata": arguments.get("metadata", {}) } )] ) return [types.TextContent(type="text", text=f"已存储记忆: {point_id}")] elif name == "retrieve_memory": embedding = await get_embedding(arguments["query"]) results = qdrant.search( collection_name=COLLECTION, query_vector=embedding, limit=arguments.get("limit", 5) ) memories = [ {"content": r.payload["content"], "score": r.score, "type": r.payload["memory_type"]} for r in results ] return [types.TextContent(type="text", text=str(memories))] async def main(): async with mcp.server.stdio.stdio_server() as (read, write): await app.run(read, write, InitializationOptions( server_name="hindsight-memory", server_version="0.1.0" )) if __name__ == "__main__": import asyncio asyncio.run(main())

这个Server暴露了两个工具:store_memory和retrieve_memory。存储时把文本embedding后写入Qdrant,检索时用查询文本的embedding做相似度搜索。memory_type字段用来区分情景记忆和语义记忆,检索时可以按类型过滤。

3.5 在客户端接入MCP

以Claude Desktop为例,编辑配置文件(macOS在~/Library/Application Support/Claude/claude_desktop_config.json,Windows在%APPDATA%\Claude\claude_desktop_config.json):

{ "mcpServers": { "hindsight-memory": { "command": "docker", "args": ["exec", "-i", "hindsight-mcp", "python", "-m", "memory_mcp.server"] } } }

重启Claude Desktop后,在对话框里输入“请记住:我的项目代号是Falcon,使用Python 3.11”,Agent就会调用store_memory工具把这条信息存进去。下次新开会话问“我的项目代号是什么”,它会调用retrieve_memory从Qdrant里捞出来。

如果你用的是其他支持MCP的客户端(比如Cursor、Continue等),配置方式类似,核心就是告诉客户端怎么启动或连接MCP Server。Docker方式的好处是Server跑在容器里,客户端只需要有Docker命令权限就行,不用在宿主机装Python依赖。

4. 记忆写入与检索的实战细节

4.1 什么该记、什么不该记

这是hindsight落地时最容易翻车的地方。我见过太多项目把每一轮对话原封不动塞进向量库,结果检索时返回一堆“你好”“谢谢”“再见”之类的噪音。记忆写入必须做过滤和提炼。

我的经验是分三类处理:

直接存储:用户明确表达的偏好、事实、指令。比如“我习惯用中文回复”“这个项目的截止日期是3月15日”“不要用markdown格式”。这类信息原样存储,metadata里标记type: preference或type: fact。

摘要存储:一段对话结束后,用LLM生成一句话摘要再存。比如用户和Agent讨论了半小时的架构方案,最终决定用微服务,那摘要就是“用户决定采用微服务架构,原因是团队熟悉Spring Cloud”。原始对话可以丢弃或归档,摘要进向量库。

不存储:寒暄、重复确认、工具调用的中间结果(除非结果本身有长期价值)。这些信息对后续决策没有帮助,存了只会增加检索噪音。

注意:过滤规则不要写死在代码里,最好做成可配置的。不同业务场景对“什么值得记”的定义差别很大。客服场景可能连用户情绪都要记,代码助手场景只关心技术决策。

4.2 检索策略:相似度不是唯一指标

纯向量相似度检索有个致命问题:它只关心语义相似,不关心时间新鲜度和重要性。用户上周说“我喜欢蓝色”,这周说“我改主意了,喜欢红色”,两条记忆的embedding可能非常接近,检索时可能把旧的排在新的前面。

解决办法是混合排序。Qdrant支持在payload里存额外字段,检索后做二次排序。我通常用这个公式:

final_score = 0.6 * cosine_similarity + 0.3 * recency_score + 0.1 * importance_score

recency_score按时间衰减,比如exp(-days_ago / 30),30天前的记忆权重降到0.37。importance_score可以人工标注,也可以让LLM在存储时打分(1到5)。这个权重分配不是固定的,如果你的场景对时效性要求极高,可以把recency提到0.5。

Qdrant本身支持在搜索时加filter,比如只检索最近7天的记忆:

from qdrant_client.models import Filter, FieldCondition, Range results = qdrant.search( collection_name=COLLECTION, query_vector=embedding, query_filter=Filter( must=[ FieldCondition( key="timestamp", range=Range(gte=seven_days_ago_timestamp) ) ] ), limit=10 )

4.3 记忆更新与冲突处理

当新记忆和旧记忆冲突时怎么办?比如用户之前说“预算50万”,后来改成“预算80万”。如果两条都留在库里,检索时可能同时返回,Agent就懵了。

我的做法是版本化+软删除。每条记忆有个version字段和is_latest标记。新记忆写入时,先检索是否有语义相似的旧记忆(相似度>0.9),如果有,把旧记忆的is_latest设为false,新记忆version加1。检索时默认只返回is_latest=true的记录。

这样既保留了历史变更记录(方便审计和回溯),又不会让Agent看到过时信息。Qdrant的payload支持嵌套结构,实现起来不复杂。

4.4 遗忘机制:不是所有记忆都值得永久保留

GDPR之类的法规要求用户有权要求删除个人数据,但即使没有法规压力,从工程角度也需要遗忘机制。向量库无限增长会导致检索变慢、存储成本上升、噪音比例增加。

我通常设三条清理规则:

  • 时间阈值:超过90天的情景记忆自动归档到冷存储(比如S3或本地文件),向量库里只保留摘要。
  • 容量阈值:集合内点数超过10万时,触发一次压缩,把相似度高于0.95的重复记忆合并。
  • 显式删除:提供forget_memory工具,用户说“忘掉我刚才说的”时调用。

实操心得:清理任务不要放在主流程里同步执行,用定时任务(比如每天凌晨3点)跑。我试过在写入时同步做去重,结果每次存储延迟从50ms涨到300ms,用户体验明显变差。

5. 常见问题与排查技巧实录

5.1 Docker网络不通导致MCP Server连不上Qdrant

这是最高频的问题。现象是MCP Server日志报Connection refused或Timeout,但Qdrant容器本身是running状态。

排查步骤:

  1. 进入MCP容器:docker exec -it hindsight-mcp bash
  2. 测试连通性:curl http://qdrant:6333/collections
  3. 如果报Could not resolve host,说明两个容器不在同一网络。检查docker-compose.yml里是否在同一个networks下,或者是否用了默认网络。
  4. 如果解析成功但连接被拒,检查Qdrant是否真的在监听6333端口:docker exec hindsight-qdrant netstat -tlnp。

根本原因通常是docker-compose.yml里写了network_mode: host,这会让容器失去DNS解析能力。删掉这行,用默认的bridge网络就行。

5.2 嵌入模型返回维度不匹配

Qdrant创建集合时指定了768维,但嵌入模型实际输出可能是384维或1024维。报错信息通常是Wrong vector dimension: expected 768, got 384。

解决办法:先确认模型输出维度。nomic-embed-text是768维,all-MiniLM-L6-v2是384维,text-embedding-3-small是1536维。创建集合前用一条测试文本调一次嵌入接口,看返回数组长度。如果集合已经建错了,删掉重建:

curl -X DELETE http://localhost:6333/collections/agent_memory

然后修改代码里的VectorParams(size=实际维度)。

5.3 检索结果全是无关内容

如果retrieve_memory返回的记忆和查询完全不相关,按这个顺序排查:

可能原因检查方法解决方案
嵌入模型没拉取成功docker exec hindsight-ollama ollama list重新pull模型
存储时embedding为空查Qdrant collection的points检查存储逻辑
相似度阈值设太低打印检索score加score>0.7过滤
查询文本太短看query长度补充上下文再检索
集合里数据太少curl localhost:6333/collections/agent_memory先存够测试数据

我遇到过一次是因为Ollama容器内存不足,嵌入请求返回了空数组,但代码没做校验直接存了零向量。后来在get_embedding里加了长度检查,问题再没出现过。

5.4 MCP客户端连接超时

Claude Desktop配置MCP Server后,如果一直显示“connecting”然后超时,大概率是Server启动失败。MCP的stdio模式要求Server在标准输入输出上通信,任何print语句都会干扰协议。

检查清单:

  • Server代码里有没有print()?全部改成写日志文件。
  • Docker exec命令是否正确?docker exec -i的-i不能少,否则stdin不保持打开。
  • Server启动时间是否过长?Claude Desktop默认等待时间大概10秒,如果Server要加载大模型,可能来不及。解决办法是把模型加载放到首次工具调用时懒加载。

5.5 记忆写入重复

同一件事被存了多次,检索时返回一堆重复结果。原因通常是Agent在每轮对话都调用了store_memory,但内容其实没变。

去重方案:存储前先做一次相似度检索,如果已有相似度>0.95的记忆,跳过存储或更新旧记忆的时间戳。这个逻辑放在MCP Server里,对Agent透明。

existing = qdrant.search( collection_name=COLLECTION, query_vector=embedding, limit=1 ) if existing and existing[0].score > 0.95: return [types.TextContent(type="text", text="记忆已存在,跳过存储")]

注意:0.95这个阈值不是绝对的。对于短文本(少于20字),相似度普遍偏高,阈值可以提到0.98;对于长文本,0.9就够了。最好用实际数据跑一批测试来确定。

6. 性能调优与扩展思路

6.1 嵌入批处理降低延迟

逐条调用嵌入接口在数据量大时很慢。Ollama支持批量嵌入,一次传多个prompt:

async def get_embeddings_batch(texts: list[str]): async with httpx.AsyncClient() as client: resp = await client.post( f"{OLLAMA_URL}/api/embed", json={"model": EMBED_MODEL, "input": texts}, timeout=60.0 ) return resp.json()["embeddings"]

注意端点是/api/embed而不是/api/embeddings,参数是input而不是prompt。批量大小建议控制在32到64之间,太大容易超时,太小提升不明显。

6.2 Qdrant索引优化

默认情况下Qdrant用HNSW索引,对于10万级数据量够用。但如果你的记忆库超过百万级,需要调整HNSW参数:

qdrant.create_collection( collection_name=COLLECTION, vectors_config=VectorParams(size=768, distance=Distance.COSINE), hnsw_config={ "m": 32, "ef_construct": 256 } )

m控制每个节点的连接数,越大检索越准但内存占用越高。ef_construct控制构建时的候选集大小,越大索引质量越好但构建越慢。对于记忆检索这种对召回率要求高的场景,m=32、ef_construct=256是个不错的起点。

6.3 多Agent共享记忆的隔离

如果你有多个Agent(比如客服Agent、代码Agent、写作Agent),它们应该共享一个记忆库还是各自独立?我的建议是共享存储、逻辑隔离。

在Qdrant里用agent_id字段区分,检索时加filter:

query_filter=Filter( must=[ FieldCondition(key="agent_id", match=MatchValue(value="customer-service")) ] )

但有些记忆是跨Agent通用的,比如“用户偏好中文”。这类记忆的agent_id设为global,所有Agent都能检索到。这样既避免了信息孤岛,又防止了不同业务线的记忆互相污染。

6.4 监控与告警

记忆系统跑起来后,至少监控三个指标:

  • 写入延迟:从调用store_memory到返回的耗时。超过500ms就要查嵌入服务或Qdrant是否过载。
  • 检索命中率:检索返回结果中score>0.7的比例。如果持续低于30%,说明存储内容质量有问题。
  • 集合大小:Qdrant collection的points数量。增长过快说明过滤规则太松,需要收紧。

这些指标可以用Prometheus抓取,配Grafana面板。如果不想搞这么重,写个定时脚本每天输出到日志文件也行。

7. 关于hindsight落地的一些个人体会

我在三个项目里用过这套hindsight架构,最大的感受是:记忆系统的价值不在于技术多先进,而在于过滤规则和检索策略是否贴合业务。同样的Qdrant+MCP组合,在客服场景里把准确率从67%拉到89%,在代码助手场景里却只提升了5个百分点。原因很简单——代码场景的上下文本身就很结构化,函数签名、文件路径这些信息直接塞prompt里比向量检索更可靠。

所以如果你正准备给自己的Agent加记忆,我的建议是先别急着搭全套基础设施。拿个Excel表格手动记录一周的“什么信息值得记”,然后分析这些信息的检索频率和时效性。如果80%的检索都集中在最近3轮对话,那你可能只需要一个滑动窗口,不需要向量库。如果大量检索涉及跨会话的偏好和事实,再上hindsight也不迟。

另外,MCP协议虽然好用,但它的生态还在快速变化。我写这篇文章时的API和三个月前已经有了一些差异。建议你在动手前先看一眼官方仓库的最新示例,别完全照搬网上的老教程。Docker Compose的配置倒是相对稳定,那部分可以直接抄。

最后分享一个排查MCP连接问题的小技巧:在Claude Desktop的日志目录里(macOS是~/Library/Logs/Claude/),有个mcp.log文件,里面会记录每次MCP Server的启动命令、stdout/stderr输出和错误堆栈。连接不上时先看这个日志,比盲目改配置快得多。

返回列表