尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Chatlog 实战:解密微信本地库,用 MCP 与 HTTP API 接入 AI 检索

Chatlog 实战:解密微信本地库,用 MCP 与 HTTP API 接入 AI 检索 简介Chatlog 是 sjzar 基于 Go 语言开源的跨平台工具核心定位是把散落在本地数据库中的微信聊天记录转化为可搜索、可调用的结构化数据面向需要批量检索聊天内容、或将微信数据接入 AI 助手的开发者与进阶用户。它兼容微信 3.x 与 4.0 客户端无需 root 或越狱即可读取并解密消息文件支持 Windows 与 macOS。资源包共 139 个文件以 123 个 go 源码为主体辅以 proto 接口定义、yml/yaml 配置、Makefile 构建脚本及 md 说明文档整体约 213KB结构紧凑、便于二次开发。功能上覆盖本地数据自动发现与多账号切换、密钥提取与数据库解密、图片语音视频等加密附件实时解码并提供 REST API 与基于 MCP 的 SSE 增量推送双栈输出可无缝对接支持 MCP 的 AI 助手同时具备 Terminal UI 与命令行两种交互方式适配自动化脚本与 DevOps 场景。目前已有 939 人学习下载适合研究微信数据解析、构建个人知识库或开发 AI 对话上下文的读者参考。1. 从一堆加密数据库到可检索的聊天档案Chatlog 到底解决了什么微信 PC 端的聊天记录存在本地一个加密的 SQLite 数据库里路径通常在Documents\xwechat_files\wxid_xxx\db_storage下面文件名类似message_0.db、media_0.db。直接拿 SQLite 工具打开会报「file is not a database」因为前 4096 字节被异或加密了。想查一句「去年双十一到底谁跟我借了钱」靠微信自带的搜索框翻半天翻不到导出功能又只给文本、丢了图片和语音。Chatlog 这个 Go 语言写的工具就是冲着这个痛点来的它把本地加密库解密、合并、建索引对外暴露 HTTP API 和 MCP 协议接口让 AI 客户端能直接查你的聊天记录。适合两类人——想给自己做聊天归档和全文检索的普通用户以及想把微信数据接进 AI 工作流的开发者。源码是 Go 单二进制跨平台编译不依赖运行时。2. 解密与建库Chatlog 的数据链路拆解2.1 微信本地库的加密结构微信 PC 端每个账号一个目录db_storage下按消息类型分库message存文本和系统消息media存图片视频的元信息contact存联系人session存会话列表。每个库文件头部 4096 字节用账号相关的密钥做异或密钥本身又跟登录态绑定。Chatlog 的做法不是去逆向微信进程内存而是走「已知密钥 文件头解密」的路线它需要你提供密钥或者从运行中的微信进程里读。常见做法是用chatlog key子命令尝试自动获取失败时手动填。解密后的库是标准 SQLiteChatlog 会把它们复制到自己的工作目录避免直接操作原库导致微信写入冲突。这一步很关键——微信在运行时会锁库直接读可能拿到不一致的快照。2.2 建索引与数据模型解密只是第一步真正让查询快起来的是索引。Chatlog 在合并后的库上建了针对message表的索引字段包括talker会话 ID、create_time时间戳、type消息类型、content内容。消息类型用整数编码1 是文本3 是图片34 是语音43 是视频49 是引用或链接卡片。查询时按talker和时间范围过滤再对content做 LIKE 或全文检索。-- Chatlog 内部建索引的核心语句简化 CREATE INDEX IF NOT EXISTS idx_message_talker_time ON message(talker, create_time); CREATE INDEX IF NOT EXISTS idx_message_type ON message(type); -- 全文检索表用于 content 模糊匹配 CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts5(content, contentmessage, content_rowidlocal_id);逻辑说明idx_message_talker_time是复合索引先按会话再按时间覆盖「查某个人最近的消息」这个最高频场景。message_fts用 SQLite 的 FTS5 扩展做全文检索比LIKE %关键词%快一个数量级代价是建索引时多占磁盘。参数上content_rowid指向原表主键保证检索结果能回表拿完整记录。2.3 启动服务与 API 验证编译或下载二进制后第一步是初始化# 初始化工作目录默认在 ~/.chatlog ./chatlog init # 尝试自动获取密钥并解密 ./chatlog decrypt # 启动 HTTP 服务默认监听 127.0.0.1:5030 ./chatlog server --addr 127.0.0.1:5030init会在用户目录下建~/.chatlog里面放解密后的库和索引。decrypt是幂等的重复跑会跳过已解密的库。server启动后用 curl 验证# 查最近 10 条消息 curl http://127.0.0.1:5030/api/v1/messages?limit10 # 按联系人昵称搜索 curl http://127.0.0.1:5030/api/v1/search?keyword借钱talker张三返回是 JSON 数组每条含talker、create_time、type、content。如果返回空先确认decrypt是否成功——看~/.chatlog下有没有.db文件以及文件大小是否正常几 MB 到几百 MB 不等。3. 把聊天记录接进 AIMCP 协议与 HTTP 接口实战3.1 MCP 是什么Chatlog 怎么实现MCPModel Context Protocol是一套让 AI 客户端调用外部工具的协议本质是 JSON-RPC over stdio 或 HTTP。Chatlog 实现了 MCP server把「查消息」「搜关键词」「列联系人」包装成 toolAI 客户端连上后就能在对话里直接调。这比让 AI 读导出的文本文件强在两点一是实时二是能按结构化条件过滤不用把整个聊天记录塞进上下文。Chatlog 的 MCP 实现走 stdio 模式配置里指定命令和参数即可。常见客户端配置片段{ mcpServers: { chatlog: { command: /path/to/chatlog, args: [mcp, --addr, 127.0.0.1:5030] } } }逻辑说明command指向 chatlog 二进制args里mcp是子命令--addr告诉它去连已经跑起来的 HTTP 服务。这样 MCP 层只做协议转换数据查询还是走本地 HTTP职责分离。参数上如果 HTTP 服务没启动MCP 会报连接拒绝所以顺序是先server再配 MCP。3.2 用 HTTP API 做自定义集成不想用 MCP 的话直接调 HTTP API 更灵活。Chatlog 的 API 设计偏 RESTful几个核心端点端点方法参数用途/api/v1/messagesGETtalker,limit,offset按会话拉消息/api/v1/searchGETkeyword,talker,start,end关键词搜索/api/v1/contactsGET无列联系人/api/v1/sessionsGET无列会话用 Python 写个批量导出某人的聊天记录import requests BASE http://127.0.0.1:5030/api/v1 def export_chat(talker, start_ts, end_ts): 导出指定会话在时间范围内的消息 params { talker: talker, start: start_ts, # Unix 时间戳秒 end: end_ts, limit: 500 # 单次上限超过要翻页 } all_msgs [] offset 0 while True: params[offset] offset resp requests.get(f{BASE}/messages, paramsparams, timeout10) resp.raise_for_status() batch resp.json() if not batch: break all_msgs.extend(batch) offset len(batch) return all_msgs msgs export_chat(wxid_abc123, 1700000000, 1730000000) print(f共 {len(msgs)} 条)逻辑说明limit设 500 是经验值太大单次响应慢太小翻页次数多。offset翻页在数据量大时会有性能问题更好的做法是用start递增——每次拿最后一条的create_time作为下次的start。参数上talker可以是 wxid 也可以是备注名Chatlog 内部会做映射但备注名有重名风险生产环境建议用 wxid。3.3 消息类型处理与内容清洗拿到消息后type字段决定怎么解析content。文本直接可用图片和视频的content是 XML 片段含 CDN 地址和本地路径语音是 SILK 格式需要转码。常见做法是只处理文本和引用媒体单独走文件路径。import xml.etree.ElementTree as ET def parse_content(msg): 按消息类型解析 content t msg[type] raw msg[content] if t 1: return raw elif t 49: # 引用或链接卡片content 是 XML try: root ET.fromstring(raw) title root.findtext(.//title) or return f[卡片] {title} except ET.ParseError: return [卡片解析失败] elif t 3: return [图片] elif t 34: return [语音] else: return f[类型{t}]逻辑说明type 49的 XML 结构随微信版本变findtext用.//做模糊查找避免路径写死。ET.ParseError要捕获因为有些卡片内容不是合法 XML。参数上如果要做全文检索建议把解析后的纯文本另存一列而不是每次查询都解析。4. 避坑与排查密钥、锁库、编码这三道坎4.1 密钥获取失败decrypt 报「no key found」现象跑chatlog decrypt提示找不到密钥~/.chatlog下没有 db 文件。原因通常是微信没登录或者微信版本更新后密钥存储位置变了。Chatlog 自动获取依赖读微信进程内存或配置文件新版本微信可能改了偏移。解决先确认微信 PC 端已登录且保持运行如果还不行手动指定密钥——用chatlog key --manual按提示输入密钥可以从社区工具或自己逆向拿到。注意密钥跟账号绑定换账号要重新获取。4.2 解密后的库查询报「database is locked」现象API 返回 500日志里是database is locked。原因是 Chatlog 的工作库和微信原库在同一个磁盘微信写入时产生锁竞争。解决把~/.chatlog放到另一块盘或者用--work-dir指定独立目录。另外decrypt完成后尽量停掉微信再查减少锁冲突。如果必须边用微信边查把查询超时调大在配置里设busy_timeout5000。4.3 中文搜索搜不到英文正常现象/api/v1/search?keyword借钱返回空但搜hello有结果。原因是 FTS5 默认分词器对中文按字符切借钱被切成借和钱两个 token而查询时按整词匹配。解决建 FTS 表时指定tokenizeunicode61或装simple分词器更简单的做法是查询时把关键词拆成单字用 OR 连接。Chatlog 较新版本已经处理了这点如果用的是旧版手动改 FTS 配置。4.4 媒体文件路径失效现象消息里图片的content指向一个本地路径但文件不存在。原因是微信会定期清理缓存或者你换了设备。解决Chatlog 只能索引元信息文件本身要自己备份。常见做法是定期把msg\attach目录整个拷出来跟 Chatlog 的库放一起查询时用相对路径拼。别指望 Chatlog 帮你恢复已删文件它不做数据恢复。4.5 端口冲突导致 server 起不来现象chatlog server报bind: address already in use。原因是 5030 被占或者上次没退干净。解决lsof -i :5030找到进程 kill 掉或者换端口--addr 127.0.0.1:5031。换端口后记得同步改 MCP 配置里的--addr否则 MCP 连不上。5. 进阶用 Chatlog 做个人知识库的检索层把 Chatlog 当检索层上面接一个 RAG 流程是我觉得最有价值的用法。具体做法先用 API 把某个会话的全部文本消息拉下来按天切片每片做 embedding 存向量库查询时先向量召回再用 Chatlog 的精确搜索做二次过滤。这样既保留了语义检索的模糊匹配又能用talker和时间范围收窄。import requests, hashlib from datetime import datetime BASE http://127.0.0.1:5030/api/v1 def build_daily_chunks(talker): 按天聚合消息生成待 embedding 的文本块 msgs [] offset 0 while True: r requests.get(f{BASE}/messages, params{talker: talker, limit: 500, offset: offset}) batch r.json() if not batch: break msgs.extend(batch) offset len(batch) days {} for m in msgs: if m[type] ! 1: # 只处理文本 continue day datetime.fromtimestamp(m[create_time]).strftime(%Y-%m-%d) days.setdefault(day, []).append(m[content]) chunks [] for day, texts in days.items(): text \n.join(texts) chunks.append({ id: hashlib.md5(f{talker}{day}.encode()).hexdigest(), day: day, text: text[:2000] # 单块截断避免超 token }) return chunks逻辑说明按天聚合是因为聊天记录天然按时间组织一天一块语义相对完整。text[:2000]截断是防止单天消息过多导致 embedding 超长2000 字符大约 1000 token对多数模型安全。id用 md5 保证幂等重复跑不会产生重复块。参数上talker建议用 wxid避免备注名变更导致块 ID 变化。验证方法拿一个你知道答案的问题比如「上个月谁提过项目排期」先走向量检索拿到候选天再用 Chatlog 的/search在候选天范围内精确搜「排期」对比两次结果。如果向量召回漏了说明切片粒度太粗改成按半天或按会话轮次切。一个具体技巧Chatlog 的start和end参数接受 Unix 时间戳但微信消息的create_time是秒级别传毫秒否则范围全空。我踩过这个坑查了半天以为是索引没建好。从那以后我每次调时间范围接口都先用date %s确认单位再拼参数。希望帮到你。本文还有配套的精品资源点击获取
返回列表