ARTICLE DETAIL

资讯详情

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

GraphRAG+Ollama本地部署实战:从知识图谱到企业问答系统

GraphRAG+Ollama本地部署实战:从知识图谱到企业问答系统 1. 为什么我决定把 GraphRAG 和 Ollama 组在一起先说背景。最近团队要做一套内部知识库问答系统文档量大、内容专业而且明文规定不能把数据送到外部 API。我最初的想法很简单本地部署一个大模型然后走传统 RAG 就够了。但真正调研完才发现传统 RAG 在跨段落推理和关系类问题上会暴露出明显短板光靠向量相似度检索根本接不住复杂业务问题。折腾了一轮之后我决定用 GraphRAG 做图谱层用 Ollama 跑本地模型把整套东西完全落在内网。这篇文章就把整个过程的选型逻辑、部署步骤、配置细节和踩坑点全部摊开写。1.1 传统 RAG 在复杂问答上的软肋普通的 RAG 流程其实是这样的文档切块 - 向量化 - 存入向量库 - 检索 Top-K - 拼 Prompt 送给 LLM 生成回答。这个链路在答案就在某一段原文里的场景下挺好用比如差旅报销标准是多少这类问题只要向量库里能找到对应片段回答质量就不错。可一旦问题变成财务部一共几个人参与报销审批他们的角色分别是什么传统 RAG 就开始露怯了。因为答案分散在多个文档的不同段落里每一段单独和提问向量比对相似度都不高Top-K 根本召不回来。更麻烦的是实体之间的关系张三 - 属于 - 财务部、财务部 - 负责 - 报销审批在图谱里是一跳就能查出来的但在向量空间里这种逻辑关联几乎不可见。这就是我转向 GraphRAG 的核心理由它先把文档中的实体、关系、属性显式地抽取出来构建成知识图谱再在图谱上做社区发现和摘要最后查询时把图结构和摘要一起送给 LLM。简单说传统 RAG 靠词面相似GraphRAG 靠结构推理。1.2 GraphRAG 和 Ollama 在整个系统里的分工这套系统里Ollama 负责把 LLM 跑起来。它就是一个本地模型管理工具支持一条命令拉模型、启动 OpenAI 兼容接口进程一开GraphRAG 就能通过 HTTP 调用本地模型不用装 CUDA 工具链也不用写复杂的推理代码。GraphRAG 则是微软开源的那个图谱增强 RAG 框架负责两件大事索引阶段Indexing用 LLM 把文档拆解成实体、关系、描述三元组构建图谱并做社区摘要。查询阶段Querying基于图谱做局部检索Local Search或全局检索Global Search把最相关的图谱片段和原文片段交给 LLM 组织答案。这个组合对企业场景特别有价值一方面数据完全不出内网另一方面又能覆盖跨文档、多跳推理的问题。如果你手头的需求也属于文档多、关系复杂、不能上云、还要求回答有依据那这篇文章应该能帮你少走不少弯路。2. 部署第一关Ollama 安装与模型管理的那些坑Ollama 本身是轻量工具但装好、下载快、能稳定跑这三个目标实际落地时坑比想象中多。我把我在 Windows 和 Linux 两种环境下的实测结果、下载慢的解决办法、显存限制下如何选模型、以及局域网访问配置一次性说清楚。2.1 从下载到启动安装环节最容易卡住的地方Ollama 官方安装包直接去官网下载就行但很多人在下载这一步就卡住了。我先说现象官网安装包在国内网络环境下经常只有几十 KB 每秒一个几百 MB 的安装包能下半小时。我自己试过几个思路最有效的不是找各种网盘转载而是配置国内镜像源。在 Linux 上下载时可以通过环境变量换源export OLLAMA_BASE_URLhttps://ollama.com curl -fsSL https://ollama.com/install.sh | sh如果你的网络访问官方源不稳可以改用镜像站地址安装。只要把安装脚本里的默认域名替换成可达的镜像域名即可。Windows 用户则可以直接下载安装包后手动安装再用环境变量指向镜像源。安装完成之后先跑一下版本号确认ollama --version ollama serveollama serve会启动本地服务默认监听127.0.0.1:11434。如果这一步正常浏览器访问http://127.0.0.1:11434能看到Ollama is running的提示。这一步没踩通后面 GraphRAG 对接一定会失败所以我建议先单独验证。2.2 换源之后模型下载依旧慢的原因与对策很多人以为安装包下完就万事大吉真正痛苦的是后面的ollama pull。我拉取 qwen2.5:7b 的时候默认源速度只有几十 KB一个 4.7GB 的模型要下到天荒地老。这里涉及两个问题一是默认仓库在国外二是模型文件大。解法有两个层面层面一配置国内镜像源。在 Windows 上新增环境变量setx OLLAMA_BASE_URL 你的镜像地址重启终端后再执行ollama pull qwen2.5:7b镜像源如果速度快4.7GB 的模型几分钟就能拉完。层面二善用模型文件的断点续传特性。ollama pull其实是支持断点续传的。网络中断后重复执行同一条拉取命令它不会从头再来而是从断点继续。所以网络特别差的情况下多试几次总能拉完不要中途手动删除临时文件。提示设置好环境变量后务必重启终端或重启 ollama 服务否则新配置不会生效。这个不起眼的问题浪费了我不少时间。2.3 模型存放路径与安装到 D 盘的姿势Ollama 默认把模型放在 C 盘用户目录下Windows 上路径是C:\Users\用户名\.ollama\models。模型随便一拉就是几个 GBC 盘小的同学必须改路径。方法是新增环境变量setx OLLAMA_MODELS D:\ollama\models改完后需要把旧的.ollama目录下的模型文件迁移过去或者重新拉取。我建议直接在 D 盘建一个干净目录然后重新ollama pull省得文件移动后 checksum 对不上。Linux 用户同理改OLLAMA_MODELS或者直接把挂载点指到数据盘export OLLAMA_MODELS/data/ollama/models2.4 6GB 显存到底能跑什么模型显存约束是本地部署绕不开的话题。我在一台 6GB 显存的笔记本上做了多组实测给你一组可以直接抄的结论模型参数量量化级别显存占用能否流畅运行实际体验qwen2.5:7b7BQ4_K_M约 4.8GB可以中文理解好问答质量较高llama3.1:8b8BQ4_K_M约 5.2GB勉强可以英文强中文一般gemma2:9b9BQ4_0约 5.8GB偏卡生成速度明显下降phi3:mini3.8BQ4_K_M约 2.5GB流畅适合快速验证流程nomic-embed-text0.55BF16约 1.2GB流畅用于 embedding我最终选择的是 qwen2.5:7b 作为主模型nomic-embed-text 作为 embedding 模型。理由很简单在 6GB 显存下qwen2.5:7b 的中文生成质量明显比等效参数的 Llama 系列稳定而且它对工具调用和 JSON 输出的支持不错GraphRAG 索引阶段需要模型输出结构化 JSON 来抽取实体关系。如果你的显存只有 4GB我的建议是优先考虑 qwen2.5:3b 或 phi3:mini先跑通整个流程再决定是否升级硬件。流程本身的价值比模型大小重要得多。2.5 局域网访问配置如果你像我一样机器上跑 Ollama但要用另一台机器比如装了 Neo4j 的主机来访问必须让 Ollama 监听所有网卡接口。新增环境变量setx OLLAMA_HOST 0.0.0.0:11434重启 ollama 服务后局域网内其他机器就能通过http://主机IP:11434访问模型接口。注意生产环境如果不需要不建议直接暴露到公网。最好在防火墙层面限制 11434 端口的来源 IP只允许内网特定网段访问。3. GraphRAG 初始化与索引构建从文档到知识图谱Ollama 跑通之后下一步就是让 GraphRAG 认识它。这里涉及环境准备、配置文件修改、索引执行三个阶段。我一步一步说。3.1 环境准备Python 版本和依赖安装GraphRAG 对 Python 版本有要求建议用 Python 3.10 到 3.12。我用了 conda 创建独立环境避免依赖冲突conda create -n graphrag python3.11 -y conda activate graphrag pip install graphrag如果你是 ARM 架构的 Mac 或者 Linux 机器依赖里有部分 C 扩展需要确保编译工具链完整。Windows 用户建议安装 Visual Studio Build Tools否则某些轮子源码编译会失败。安装完成后建个工作目录比如D:\graphrag-demo把待处理的文档放进去。GraphRAG 目前支持的文档格式包括 txt、md、pdf、docx、csv 等。官方推荐把文档放在一个input子目录里。3.2 初始化项目与 settings.yaml 配置的关键参数执行初始化命令graphrag init --root D:\graphrag-demo这一步会生成两个文件.env和settings.yaml。.env里默认要求填 OpenAI API Key因为我们用的是 Ollama所以 Key 可以随便填一个非空字符串真正起作用的是api_base指向本地。我实际使用的settings.yaml核心配置如下llm: api_key: ${GRAPHRAG_API_KEY} type: openai_chat model: qwen2.5:7b model_supports_json: true api_base: http://localhost:11434/v1 max_tokens: 4000 temperature: 0 embeddings: llm: api_key: ${GRAPHRAG_API_KEY} type: openai_embedding model: nomic-embed-text api_base: http://localhost:11434/v1 max_tokens: 2000.env里设置GRAPHRAG_API_KEYollama这里面有三个重点第一api_base必须是http://localhost:11434/v1。Ollama 的 OpenAI 兼容接口挂在/v1路径下如果漏掉/v1GraphRAG 会认为接口不存在。第二model_supports_json: true要显式配置。GraphRAG 索引阶段会让 LLM 输出 JSON 格式的实体关系抽取结果。如果模型本身不支持 JSON 模式或者配置没开后面解析结果时会出现大量报错。第三max_tokens不能给太小。实体抽取时模型要同时输出多个实体和关系输出长度动不动就上千 token。我第一次设置max_tokens: 1000结果频繁截断图谱质量很差。调到 4000 之后明显改善。但也要注意 Ollama 服务的上下文窗口设置如果模型上下文是 8Kmax_tokens调到 4000留下 4000 给输入这样基本可以跑通大多数场景。3.3 索引构建执行过程与输出文件解读配置文件准备好后运行graphrag index --root D:\graphrag-demo这一步是整个系统中最耗时、最烧模型的阶段。它实际上做了一连串事情把文档切成 chunk然后让 LLM 逐个 chunk 抽取实体和关系再把关系合并成全局图谱之后用 Leiden 算法做社区发现最后对每个社区生成摘要。我跑了一批 50 篇 PDF 的文档总字数约 20 万字用的是 qwen2.5:7b。在不开启增量索引的情况下耗时大约两个小时。这个时间跟你文档量、模型推理速度成正比6GB 显存下 7B 模型生成速度大概在 20~30 token/s所以耐心是必须的。第一次跑完后在输出目录里会看到 prompts、artifacts 等子目录以及一个index-stats.json文件。判断索引是否成功最直接的办法是看index-stats.json里的documents和entities数量{ documents: 50, entities: 1240, communities: 87, text_units: 312 }如果entities是 0 或非常少说明抽取阶段就出了问题。最常见的原因是 prompt 中要求输出 JSON但模型的输出格式不符合解析器预期。解决办法是优先确保model_supports_json配置为 true并且把settings.yaml中对应 prompt 的temperature调低到 0减少格式漂移。这里有一个非常重要的实操心得不要贪图省事把全部文档一次性塞进去跑索引。我建议先放 3~5 篇典型文档试跑确认抽取出来的实体关系质量没问题再全量跑。全量跑一次成本很高如果中途发现 prompt 或模型选型有问题改配置再跑等于浪费几个小时。4. 查询与问答跑通第一轮对话索引构建完成知识图谱已经有了。接下来的查询阶段是验证系统价值的临门一脚。4.1 Global Search 与 Local Search 怎么选GraphRAG 提供两种查询方法很多人不知道区别导致结果不理想。Local Search局部搜索适用于问题有明确实体指向比如张三在财务部负责什么工作。它先在图谱里定位到张三这个实体再沿关系边获取关联实体、相关原文片段和社区摘要然后把这些信息打包送给 LLM 生成答案。速度和精度都比较好适合高频业务问答。Global Search全局搜索适用于跨社区、全图性的问题比如整个公司有哪些审批流程它需要把所有社区摘要汇总出来用 Map-Reduce 的方式让 LLM 分块归纳再合并得到最终答案。全局搜索每次查询成本很高因为要遍历所有社区摘要。我个人经验是日常问答优先用 Local Search只有当你明确知道这个问题需要全图范围才能回答时才用 Global Search。比如哪个部门的人数最多这类聚合类问题Local Search 很容易漏Global Search 更适合。4.2 命令行查询实测先用命令行跑一个 Local Searchgraphrag query --root D:\graphrag-demo --method local --query 张三在财务部负责什么工作GraphRAG 会输出答案并在结果后面附上引用来源。我看一下实际效果张三在财务部主要负责报销单审核同时参与预算编制的数据汇总。根据知识库文档他在财务部担任审核岗对报销单的金额、票据和审批流程进行核对。这个答案没法直接从单篇文档里拼出来它综合了张三-任职于-财务部、财务部-负责-报销审核、张三-负责-预算数据汇总等多条知识图谱路径。这正是 GraphRAG 相对普通 RAG 的价值体现。再跑一个全局搜索graphrag query --root D:\graphrag-demo --method global --query 公司目前一共有哪些审批流程全局搜索的输出会比较长因为它是多社区摘要聚合的结果。实测下来答案能够覆盖文档中提到的主要审批流程分类但耗时比局部搜索长了不少每次调用能跑 30~60 秒。4.3 在 Python 脚本里调用查询接口命令行适合验证但真要接到业务系统里还是得用 Python API。GraphRAG 提供的查询接口可以直接嵌进项目from graphrag.query.indexer_adapters import read_indexer_entities from graphrag.query.input.loaders.dfs import store_entity_semantic_mentions from graphrag.query.context_builder.entity_extraction import EntityVectorStoreFactory from graphrag.query.question_gen.local_gen import LocalSearch from graphrag.query.llm.oai.chat_openai import ChatOpenAI from graphrag.query.llm.oai.typing import OpenAITextCompletion from graphrag.query.stores import LocalSearch from graphrag.query.stores.llm import LLMSemanticFilter from graphrag.query.stores.typing import EntityStore # 这里需要加载索引产物并构建检索器 # 完整示例较长核心是构建 LocalSearch 的 search_engine 后调用 answer search_engine.search(张三在财务部负责什么工作) print(answer.response)不过说实话GraphRAG 的 Python API 封装得并不算友好官方一些类名在不同版本里有变动。如果你只是要把问答能力接进生产系统我更推荐直接把命令行输出解析成 JSON或者等社区把查询模块包装成 Service 再对接这样升级版本时不会太痛苦。5. 进阶接入 Neo4j 让图谱可视化与深度探索GraphRAG 默认把图谱数据存在本地 parquet 文件里能做问答但你想直观看到实体关系或者用 Cypher 自己写复杂查询就得把图谱导入 Neo4j。5.1 为什么非要用 Neo4j 再导一遍有人问GraphRAG 不是已经有图谱了吗为什么还要 Neo4j我的回答是GraphRAG 内置图谱主要用于社区摘要和问答检索它不是一个让你自由探索的图数据库。实际业务中你经常想自己查所有和某某项目相关的实体路径或者想统计哪个部门关联的实体最多这类探索性查询用 Cypher 写非常高效但用内置 parquet 文件自己解析非常痛苦。Neo4j 还能直接和图谱交互式可视化。你把 GraphRAG 生成的实体、关系导入 Neo4j 后可以用 Browser 界面看节点和边也可以做路径分析、中心度计算这些都是纯问答接口给不了的。5.2 从 parquet 产物到 Neo4j 导入的完整链路GraphRAG 索引输出里的output/时间戳/artifacts目录会有entities.parquet和relationships.parquet文件。我写了一个 Python 脚本把它们读出来转成 CSV再用neo4j-admin import导入。实体表主要结构是id, name, type, description, human_readable_id关系表主要结构是source_id, target_id, relationship_type, description, weight转换脚本核心逻辑import pandas as pd entities pd.read_parquet(entities.parquet) relationships pd.read_parquet(relationships.parquet) entities.to_csv(entities.csv, indexFalse) relationships.to_csv(relationships.csv, indexFalse)然后停掉 Neo4j用neo4j-admin import导入neo4j-admin database import full --nodesentities.csv --relationshipsrelationships.csv --databasegraph.db导入成功后重启 Neo4j在 Browser 里执行MATCH p(n)-[r]-(m) RETURN p LIMIT 25就能看到实体关系网了。如果你想自己写复杂查询比如查某实体两跳以内的关联路径MATCH path (a:Entity {name: 张三})-[*1..2]-(b) RETURN path这套链路我实际跑通后最大的感受是图谱可视化不仅是为了好看更是排查索引质量的好工具。你一眼就能看出实体是否正确合并、关系方向是否有问题、有没有大量孤立节点。这些在纯文本问答阶段很难发现。注意neo4j-admin import是脱机导入方式必须停掉数据库再执行。如果不想停机可以改用apoc.import.csv或者逐条 Cypher 写入但数据量大时速度会慢很多。6. 生产级问答系统的优化与避坑清单跑通 Demo 只是第一步。把这套系统放到真实业务环境里还有一堆工程化问题要处理。我把这轮折腾过程中最有价值的优化手段和踩坑经验整理成清单按重要性排序。6.1 索引质量优化的关键旋钮GraphRAG 的索引质量受几个参数控制默认值不一定适合所有场景。我试出来的有效调节项包括参数位置我的建议原因chunk_sizesettings.yaml800~1200太小割裂实体关系太大超出上下文窗口chunk_overlapsettings.yaml100~200避免实体跨块丢失entity_extract_max_gleaningsettings.yaml1~2数值越大越慢但能减少漏抽取temperaturesettings.yaml0索引阶段必须低温度稳定优先max_tokenssettings.yaml4000防止实体抽取输出被截断我踩过最深的坑是 chunk_overlap 设置太小导致跨段落的实体关系被拆断。比如一个实体出现在文档第 3 段另一个实体出现在第 4 段两个段落的 overlap 只有 50 字关联信息就没了。后来把 overlap 调到 150实体关系数量有明显提升。6.2 常见问题的完整排查链路问题一索引阶段频繁报 Max Retries Exceeded。我一开始以为是网络问题后来发现是对应模型服务不稳定或者 API Key 配置不合法虽然是本地 Ollama但 Key 为空字符串也会被某些 SDK 拦截。排查链路是先确认.env里 Key 非空再确认ollama serve进程活着最后用 curl 测试接口curl http://localhost:11434/v1/models问题二实体数量极少。先检查 chunk 数量是否正常再检查日志中是否有 JSON 解析失败记录。大概率是模型输出格式问题优先调model_supports_json和温度。问题三查询结果总是笼统套话。这种问题多半是社区摘要太粗或者查询时 Top-K 太小。我一般先调大查询中的上下文数量再看具体是哪段信息缺失。Local Search 里有一个控制上下文包大小的参数默认值保守适当放大后答案细节会丰富很多。问题四Ollama 并发请求时接口响应越来越慢。问过的都知道Ollama 对并发的支持不算好。索引阶段 GraphRAG 是并行调 LLM 的如果并发数太高Ollama 本身会成为瓶颈。解决办法是在settings.yaml里调低并行请求数或者把 Ollama 服务部署到显存更大的机器上。6.3 生产落地还需要补的几块板如果你真正要把这套系统上线我认为还有几件事必须提前想清楚网络接口的封装。GraphRAG 官方查询接口不适合直接暴露给前端建议包一层 FastAPI 服务做权限控制和查询日志记录。增量索引策略。文档库会不断增长每次都全量重建索引不现实。GraphRAG 支持增量索引但增量索引对实体合并质量的要求更高建议定期做一次全量重建来校正。模型选型的持续迭代。本地模型更新很快同一个框架下换模型只需要改 settings.yaml 和重新拉模型。我建议每隔一段时间跑一批标准测试题对比新旧模型在抽取质量和回答准确率上的表现。数据备份。GraphRAG 索引产物就是你的知识图谱资产这个目录必须纳入备份体系。丢了它等于丢了几天甚至几周的索引计算成本。我个人在实际操作中的体会是GraphRAG 加 Ollama 这套方案真正的门槛不在安装配置而在于理解图谱生成的质量瓶颈。很多参数看起来只是数字但每一个都影响知识图谱的完整性和准确性。参数调优没有银弹唯一可靠的办法是多试、多对比、多观察图谱产物。建议你动手时先用少量高质量文档做测试集反复跑几轮索引和查询把参数手感建立起来再上全量数据。这样踩的坑会少很多也会对系统行为更有底气。
返回列表