1. GraphRAG 本地复现为什么总卡在依赖与 embedding 配置
GraphRAG 是微软开源的一套基于知识图谱的检索增强生成方案,它能把你的一堆本地文档抽成实体关系图,再配合社区摘要做全局问答。适合谁?适合想在自己机器上跑通「文档进、图谱出、问答准」这条链路的人,尤其是做知识库、做行业问答、做研究复现的开发者。但真正动手时,十个人里有八个会先卡在依赖安装和 embedding 配置上,报错五花八门,从uv sync装了个寂寞,到litellm抛BadRequestError,再到向量维度对不上。
我自己复现时踩过的坑是:明明在仓库根目录跑了uv sync,终端也没报错,结果一执行uv run poe index就提示找不到graphrag模块。后来才搞明白,GraphRAG 仓库早就从「单包」改成了 monorepo 结构,根目录的pyproject.toml只是工具链配置,真正的 Python 包藏在packages/graphrag/下面。你如果只在根目录同步,装的是 lint、test、docs 这些开发工具,业务模块根本没进虚拟环境。
这篇文章就按「环境初始化 → 依赖安装 → embedding 配置 → 索引验证 → 报错排查」这条完整路径走一遍。每一步都给可复制的命令和配置片段,你照着敲就能复现。核心检索词先摆出来:GraphRAG 复现、pyproject.toml 依赖、uv sync 安装、litellm embedding 配置。搞懂这几个,后面基本就顺了。
先说清楚整体链路。GraphRAG 的索引流程大致是:读取settings.yaml→ 调用 completion model 抽实体和关系 → 调用 embedding model 把文本转向量 → 写入 LanceDB 向量库 → 生成社区报告。这里面 completion 和 embedding 是两条独立的模型配置,任何一条配错,索引都会中途挂掉。而依赖安装决定了这些模块能不能被 import 到。所以排查顺序建议是:先确认包装对了,再确认模型连得通,最后确认向量维度一致。
下面从最容易被忽略的 monorepo 结构讲起,把pyproject.toml和uv sync的关系彻底理清。
2. pyproject.toml 与 uv sync 的 monorepo 依赖安装排查
GraphRAG 仓库根目录有一个pyproject.toml,packages/graphrag/下面还有一个pyproject.toml。很多人看到根目录有就直接uv sync,然后以为装好了。实际上根目录那份是给整个仓库做统一工具链用的,里面可能包含[tool.uv]、[dependency-groups]或者通过 poe 间接引用子包。uv只要看到pyproject.toml就会尝试解析依赖,所以uv sync能跑通,但它不会把graphrag模块本身装进环境。
你可以先用一条命令确认自己装的是哪个包:
uv pip list | grep -i graphrag如果输出为空,说明业务包没装上。这时候要进入真正的 Python 包目录再装:
cd packages/graphrag uv pip install -e .-e是 editable 模式,改源码能直接生效,调试阶段强烈建议这么装。装完再验证一次:
uv run python -c "import graphrag; print(graphrag.__file__)"能打印出路径就说明import graphrag注册成功了。如果还是报ModuleNotFoundError,检查packages/graphrag/下有没有__init__.py,以及当前虚拟环境是不是uv管理的那个。
如果你想让uv sync一次性把所有子包都装上,可以用:
uv sync --all-packages uv sync --all-extras--all-packages会把 monorepo 里所有 workspace 成员都纳入同步,--all-extras会把可选依赖也装上。两个一起用,基本能覆盖 GraphRAG 的完整依赖。但要注意,有些版本对 workspace 的支持有差异,如果--all-packages报错,就老老实实进子目录uv pip install -e .。
下面给一份可复制的pyproject.toml依赖片段参考,放在packages/graphrag/pyproject.toml里,重点是dependencies和[project.optional-dependencies]:
[project] name = "graphrag" version = "0.3.0" requires-python = ">=3.10,<3.13" dependencies = [ "litellm>=1.40.0", "lancedb>=0.6.0", "pyarrow>=15.0.0", "pydantic>=2.5.0", "numpy>=1.24.0", "pyyaml>=6.0", "tiktoken>=0.6.0", "graspologic-native>=1.2.0", ] [project.optional-dependencies] dev = [ "pytest>=7.4.0", "ruff>=0.4.0", "poethepoet>=0.24.0", ] [tool.uv] dev-dependencies = [ "pytest>=7.4.0", "ruff>=0.4.0", ]这里litellm是关键,它负责统一调用各家模型接口,completion 和 embedding 都走它。lancedb和pyarrow负责向量存储,pydantic负责配置校验。版本号不要卡太死,但pydantic必须 2.x,因为 GraphRAG 的配置模型用的是Literal和BaseModel新特性。
装完之后,用uv run poe --help看看 poe 任务有没有注册进来。正常应该能看到index、query、test这些任务。如果poe命令找不到,说明poethepoet没装,补一句uv pip install poethepoet即可。
依赖这关过了,接下来才是真正的硬骨头:embedding 配置。
3. settings.yaml 里 litellm embedding 的可复制配置
GraphRAG 的模型配置全在settings.yaml里,completion 和 embedding 分开写。很多人第一次配的时候,把 completion 配通了,embedding 随便填了个 ollama,结果索引跑到向量化那一步就崩。下面给一份能直接用的配置,先讲本地 ollama 方案,再讲云端兼容方案。
本地 ollama 方案,前提是你已经ollama serve起来,并且ollama pull nomic-embed-text拉过模型:
embedding_models: default_embedding_model: model_provider: ollama model: nomic-embed-text api_base: http://localhost:11434 auth_method: api_key api_key: "ollama" extra_params: encoding_format: "float"注意这里用的是api_base而不是base_url。GraphRAG 新版本对 litellm 的透传参数做了调整,base_url在某些版本里会被忽略,导致 litellm 找不到 provider,报LLM Provider NOT provided。改成api_base后,litellm 能正确识别 ollama 的 OpenAI 兼容接口。
云端兼容方案,以阿里云 dashscope 的 OpenAI 兼容模式为例:
embedding_models: default_embedding_model: model_provider: openai model: text-embedding-v3 api_base: https://dashscope.aliyuncs.com/compatible-mode/v1 auth_method: api_key api_key: "sk-你的key" extra_params: encoding_format: "float"这里model_provider写openai,因为 dashscope 提供的是 OpenAI 兼容接口,litellm 走 openai 分支就能通。extra_params里的encoding_format: "float"是必须的,否则某些接口会报'encoding_format' only support with [float, base64]。
如果你用 TaoToken 这类聚合接入服务,配置思路一样,把api_base换成对应的兼容端点,api_key换成你的 key,model填你要用的 embedding 模型 ID。TaoToken 的 API 地址是https://taotoken.net/api,模型对话入口在https://taotoken.net/models,接入文档在https://taotoken.net/doc。配置时记得三件套齐全:Base URL、Key、Model ID,缺一个都会报 provider 找不到。
completion 模型也顺手给一份,方便你对照:
completion_models: default_completion_model: model_provider: openai model: deepseek-chat api_base: https://api.deepseek.com/v1 auth_method: api_key api_key: "sk-你的key" timeout: 300 max_retries: 3 retry: type: exponential_backoff base_delay: 5 max_delay: 60这里有个坑:auth_method只能是api_key或azure_managed_identity,写none会直接触发ValidationError。因为 GraphRAG 的LLMConfig里用了Literal["api_key", "azure_managed_identity"],pydantic 会严格校验。
配置写完后,把settings.yaml放到你的工作根目录下,比如./inputs/settings.yaml。GraphRAG 会把--root指定的目录当作 workspace root,所有相对路径都基于它查找。输入文本放./inputs/input/,prompt 模板放./inputs/prompts/。prompts 目录需要从仓库里复制过去,否则索引时会提示找不到模板。
4. 索引构建验证与向量维度一致性检查
配置就绪后,执行索引命令:
uv run poe index --root ./inputs这条命令会依次跑实体抽取、关系抽取、社区检测、embedding 向量化、写入 LanceDB。成功的话,终端最后会打印Pipeline complete,并且./inputs/output/下会生成一堆 parquet 文件和 lancedb 目录。
验证索引是否真的成功,别只看终端。做三个检查动作:
第一,看输出目录结构:
ls -R ./inputs/output | head -50正常应该能看到create_final_entities.parquet、create_final_relationships.parquet、create_final_communities.parquet这些文件。如果只有stats.json没有 parquet,说明流程中途挂了。
第二,用 python 读一下实体数量:
uv run python -c " import pandas as pd df = pd.read_parquet('./inputs/output/create_final_entities.parquet') print('实体数量:', len(df)) print(df[['title', 'type']].head()) "能打印出实体和类型,说明抽取成功。
第三,检查向量维度。这是最容易出问题的地方。打开./inputs/output/lancedb/下的表,或者直接在代码里读:
uv run python -c " import lancedb db = lancedb.connect('./inputs/output/lancedb') tbl = db.open_table('default-entity-description') print(tbl.schema) "重点看 vector 字段的维度。如果你换了 embedding 模型,维度必须和settings.yaml里配置的一致。比如nomic-embed-text是 768 维,text-embedding-v3是 1024 维。GraphRAG 内部有个DEFAULT_VECTOR_SIZE,默认可能是 1024 或 1536,如果你用的模型维度对不上,就会报Column 1 named vector expected length 400 but got length 100这类错误。
这个报错的本质是:LanceDB 建表时按某个维度创建了 FixedSizeListArray,但实际写入的向量长度不一样。解决办法有两个:一是统一 embedding 模型,别混用;二是改DEFAULT_VECTOR_SIZE匹配你的模型。改完之后,一定要删掉output/和cache/目录,因为里面存了旧维度的向量,不删会继续报错。
rm -rf ./inputs/output ./inputs/cache删完重新跑索引,让 LanceDB 按新维度重建表。这一步很多人忘,改完配置直接重跑,结果还是老报错,就是因为缓存没清。
5. litellm 报错排查:401、BadRequestError 与维度不匹配
复现过程中最常见的报错集中在 litellm 这一层,下面按真实报错逐条对照。
报错一:litellm.BadRequestError: LLM Provider NOT provided
这个通常是因为settings.yaml里model_provider没写对,或者api_base写成了base_url导致 litellm 识别不出 provider。检查三件套:model_provider、api_base、model是否齐全。如果是自定义兼容端点,model_provider写openai,api_base写完整路径。
报错二:401 Unauthorized或AuthenticationError
key 错了或者没传。检查api_key字段有没有引号包住,yaml 里sk-xxx不加引号可能被解析成特殊类型。另外确认auth_method: api_key写了,不然 GraphRAG 不会把 key 透传给 litellm。
报错三:litellm.BadRequestError: DeepseekException - This response_format type is unavailable now
这是 completion 模型返回格式的问题。DeepSeek 某些版本不支持response_format: json_object,但 GraphRAG 默认会传。解决办法是在packages/graphrag-llm/graphrag_llm/completion/lite_llm_completion.py里做兼容处理:检测到 provider 是 deepseek 时,不直接传response_format,而是在 messages 末尾追加一句「Please respond in JSON only」,再把response_format设成{"type": "json_object"}。同时把返回的response.choices[0].message.content用json.loads解析后塞进response.formatted_response。
报错四:Column 1 named vector expected length 400 but got length 100
向量维度不匹配。前面讲过,统一模型 + 改DEFAULT_VECTOR_SIZE+ 删 output/cache。如果还不行,去packages/graphrag-vectors/graphrag_vectors/lancedb.py的load_documents里加调试输出,打印每个向量的 shape 和self.vector_size,看看到底哪个环节维度变了。
报错五:litellm.APIConnectionError: OllamaException - Client error '400 Bad Request'
ollama 日志里会显示the input length exceeds the context length。这是 chunk 太大,超过了 embedding 模型的上下文窗口。调小chunk_size,或者换上下文更长的 embedding 模型。改完同样要清 output/cache。
报错六:'encoding_format' only support with [float, base64]
在settings.yaml的 embedding 配置里加extra_params: encoding_format: "float"。如果还报,去.venv/Lib/site-packages/litellm/llms/openai/openai.py的_base_embedding和_base_embedding_async里,在new_args构造后强制加new_args["encoding_format"] = "float"。
报错七:OAuth相关或local proxy failed
这类多半是网络层或认证方式不对。确认没有走任何本地代理,api_base直连。如果是企业内网,检查出口是否放行。GraphRAG 本身不涉及代理配置,报这个通常是环境变量里残留了HTTP_PROXY,清掉再试。
排查时建议开两个终端,一个跑索引,一个tail -follama 或服务端日志,报错能第一时间定位到是哪一步。
6. 从环境到模型接入的完整复现路径与 CTA
把上面的步骤串起来,完整路径是这样的:先cd packages/graphrag && uv pip install -e .确保包装上,再配好settings.yaml的 completion 和 embedding 两段,把 prompts 复制到./inputs/prompts/,输入文本放./inputs/input/,然后uv run poe index --root ./inputs。跑通后检查 output 目录和向量维度,出报错就按第 5 节对照排查。
如果你不想在本地折腾 ollama 的模型拉取和显存占用,可以用聚合接入的方式,把 completion 和 embedding 都指向统一的兼容端点。配置时记住三件套:Base URL 填https://taotoken.net/api,Key 在控制台生成,Model ID 按你要用的模型填。生成 Key 的入口在https://taotoken.net/api-keys,接入文档在https://taotoken.net/doc,模型列表可以在https://taotoken.net/models里查。想先验证模型通不通,直接去https://taotoken.net/models发一条测试消息最快。
长期做编码和 Agent 复现的话,可以考虑 Coding Plan,入口在https://taotoken.net/coding-plan。Claude Code 相关的接入配置在https://taotoken.net/claude-code-anthropic,控制台在https://taotoken.net/console。
最后留一个实用技巧:每次改完settings.yaml里的模型或维度,先rm -rf output cache再重跑,能省掉一半的「改了没用」的困惑。索引跑通后,用uv run poe query --root ./inputs提个问题,能返回带社区摘要的答案,就说明整条链路真正打通了。