1. 为什么我要把 GraphRAG 的 API 地址换掉
GraphRAG 是微软开源的一套基于知识图谱的检索增强生成框架,它跟普通 RAG 最大的区别在于:索引阶段会调用大模型把文档拆成实体、关系、社区摘要,查询阶段再做全局或局部检索。也正因为索引阶段几乎每一步都要打 LLM,一次完整构建动辄几百次调用、几十万 Token,用量统计和接口稳定性就成了绕不开的问题。
我这次跑的是西游记白话文前九回,约 19485 字,用 qwen-turbo 做抽取、text-embedding-v1 做向量,最终统计是 284 次调用、575086 tokens。原文里.env和main.py都指向本地http://127.0.0.1:3000/v1,也就是先起一个本地转发服务再转发到云端。这套方案能跑通,但多一层本地服务就多一个故障点:端口占用、进程挂了、Key 写死在两个地方容易不一致。
所以这篇走的是「验证用量」视角:把.env和main.py里的GRAPHRAG_API_BASE统一改成 TaoToken 的地址,Key 换成 TaoToken 控制台创建的 Key,模型仍然是 qwen-turbo 和 text-embedding-v1,重新跑一遍graphrag.index和apiTest.py,最后拿 TaoToken 控制台的 Token 用量跟本地统计对一遍。适合已经在跑 GraphRAG、想简化接入层并核对真实消耗的人。
2. 前置准备:TaoToken Key 与地址规则
TaoToken 是一个兼容 OpenAI 接口协议的模型调用平台,GraphRAG 底层用的是openai_chat和openai_embedding两种 type,只要 base 地址和 Key 对得上就能直接接。你需要先打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 注册并创建一个 Key,创建入口在控制台的 API Keys 页面。
这里有个最容易踩的坑:base 地址不要带/v1。GraphRAG 的settings.yaml里api_base会被 SDK 拼上/chat/completions或/embeddings,如果你写成https://taotoken.net/api/v1,最终请求会变成https://taotoken.net/api/v1/chat/completions,路径就重复了。正确写法是:
GRAPHRAG_API_BASE=https://taotoken.net/api而main.py里用的是ChatOpenAI和OpenAIEmbedding这两个 GraphRAG 自带的封装,它们内部同样按 OpenAI 协议拼路径,所以main.py里的api_base也写https://taotoken.net/api,保持一致。Key 建议只放一份在.env,main.py里通过os.getenv读,避免两处不一致。
| 配置项 | 原方案 | 新方案 |
|---|---|---|
| GRAPHRAG_API_BASE | http://127.0.0.1:3000/v1 | https://taotoken.net/api |
| GRAPHRAG_CHAT_API_KEY | one-api 生成的 sk-xxx | TaoToken 控制台 Key |
| GRAPHRAG_CHAT_MODEL | qwen-turbo | qwen-turbo(不变) |
| GRAPHRAG_EMBEDDING_MODEL | text-embedding-v1 | text-embedding-v1(不变) |
注意:Key 不要提交到 Git,
.env记得加进.gitignore。控制台里可以随时吊销重建,比写死在代码里安全。
3. 可复制配置:改 .env 与 main.py
3.1 修改 .env
打开ragtest/.env,把原来指向本地转发服务的几行替换掉。下面是我改完后的完整片段,注释里保留了模型说明,方便你对照:
# 通过 TaoToken 接入 qwen 系列 GRAPHRAG_API_BASE=https://taotoken.net/api GRAPHRAG_CHAT_API_KEY=sk-你的TaoTokenKey GRAPHRAG_CHAT_MODEL=qwen-turbo GRAPHRAG_EMBEDDING_API_KEY=sk-你的TaoTokenKey GRAPHRAG_EMBEDDING_MODEL=text-embedding-v1 GRAPHRAG_ENTITY_EXTRACTION_PROMPT_FILE=prompts/entity_extraction.txt GRAPHRAG_SUMMARIZE_DESCRIPTIONS_PROMPT_FILE=prompts/summarize_descriptions.txt GRAPHRAG_CLAIM_EXTRACTION_PROMPT_FILE=prompts/claim_extraction.txt GRAPHRAG_COMMUNITY_REPORT_PROMPT_FILE=prompts/community_report.txt GRAPHRAG_INPUT_DIR=input GRAPHRAG_CACHE_DIR=cache GRAPHRAG_STORAGE_DIR=inputs/artifacts GRAPHRAG_REPORTING_DIR=inputs/reportssettings.yaml本身不用动,因为它引用的是${GRAPHRAG_API_BASE}这类变量,.env改了它自动生效。确认一下llm和embeddings两段的api_base都是${GRAPHRAG_API_BASE}即可。
3.2 修改 main.py 的 api_base 与 api_key
main.py里有两处硬编码,一处在setup_llm_and_embedder的ChatOpenAI,一处在OpenAIEmbedding。把它们改成从环境变量读,这样以后换 Key 只改.env:
import os from dotenv import load_dotenv load_dotenv() API_BASE = os.getenv("GRAPHRAG_API_BASE", "https://taotoken.net/api") API_KEY = os.getenv("GRAPHRAG_CHAT_API_KEY") CHAT_MODEL = os.getenv("GRAPHRAG_CHAT_MODEL", "qwen-turbo") EMBED_MODEL = os.getenv("GRAPHRAG_EMBEDDING_MODEL", "text-embedding-v1") llm = ChatOpenAI( api_base=API_BASE, api_key=API_KEY, model=CHAT_MODEL, api_type=OpenaiApiType.OpenAI, ) text_embedder = OpenAIEmbedding( api_base=API_BASE, api_key=API_KEY, model=EMBED_MODEL, deployment_name=EMBED_MODEL, api_type=OpenaiApiType.OpenAI, max_retries=20, )另外INPUT_DIR记得改成你自己的 artifacts 路径,比如r"D:\pythonProject\...\ragtest\inputs\artifacts"。改完先别急着跑索引,用一段最小脚本确认接口通不通。
3.3 最小连通性测试
在项目根目录建一个check_api.py,直接打一次 chat 和一次 embedding:
import os from dotenv import load_dotenv from openai import OpenAI load_dotenv() client = OpenAI( base_url=os.getenv("GRAPHRAG_API_BASE"), api_key=os.getenv("GRAPHRAG_CHAT_API_KEY"), ) r = client.chat.completions.create( model="qwen-turbo", messages=[{"role": "user", "content": "只回复两个字:通了"}], ) print("chat:", r.choices[0].message.content) e = client.embeddings.create(model="text-embedding-v1", input="测试向量") print("embedding dim:", len(e.data[0].embedding))跑python check_api.py,如果 chat 返回「通了」、embedding 打印出维度(text-embedding-v1 一般是 1536),说明 base 和 Key 都没问题。这一步能省掉后面索引跑到一半才报 401 的时间。
4. 重新构建索引并验证问答
4.1 跑 graphrag.index
连通性没问题后,进入ragtest目录重新构建索引。因为改了 base 地址,建议先清掉cache目录,避免旧缓存里存着本地服务的响应:
cd ragtest rm -rf cache inputs/artifacts inputs/reports python -m graphrag.index --root ./构建过程会依次跑文本分块、实体关系抽取、社区检测、社区摘要,全程都是 LLM 调用。我这次实测下来,19485 字的输入对应 284 次调用、575086 tokens,耗时 5 到 10 分钟,具体取决于并发和网络。跑完后inputs/artifacts下会生成一堆 parquet,重点看这几个:
| 文件 | 作用 |
|---|---|
| create_final_entities.parquet | 最终实体集合 |
| create_final_relationships.parquet | 实体间关系边 |
| create_final_communities.parquet | 社区划分结果 |
| create_final_community_reports.parquet | 社区摘要,全局检索用 |
| create_final_text_units.parquet | 文本单元,局部检索用 |
如果create_final_community_reports.parquet行数明显偏少,通常是社区摘要阶段有请求失败被跳过,去inputs/reports里翻日志确认。
4.2 启动服务并跑 apiTest.py
索引就绪后启动 FastAPI 服务:
python main.py看到在端口 8012 上启动服务器和初始化完成就说明加载成功。然后另开一个终端跑apiTest.py,它默认测的是局部检索:
import requests, json url = "http://localhost:8012/v1/chat/completions" headers = {"Content-Type": "application/json"} local_data = { "model": "graphrag-local-search:latest", "messages": [{"role": "user", "content": "唐僧是谁,他的主要关系是什么?请用中文回答。"}], "temperature": 0.7, } response = requests.post(url, headers=headers, data=json.dumps(local_data)) print(response.json()['choices'][0]['message']['content'])想测全局检索就把model换成graphrag-global-search:latest,问题换成「这个故事的首要主题是什么」。两个都返回了带实体和关系描述的中文答案,就说明索引构建和问答链路都通了。
4.3 对照 TaoToken 控制台用量
问答跑通后,打开 TaoToken 控制台的用量页面,按时间范围筛出刚才构建索引的那段。你会看到 qwen-turbo 的调用次数和 Token 总量,跟本地统计的 284 次、575086 tokens 应该在同一量级。差异主要来自两点:一是重试请求也会计入,二是 embedding 调用单独计费、不在这 284 次里。如果控制台显示的次数远小于本地,检查是不是有请求走了缓存没真正发出。
5. 本篇常见报错排查
5.1 401 Unauthorized 或 invalid api key
最常见的原因是 Key 里带了空格或换行,.env复制时容易多一个尾随空格。用print(repr(os.getenv("GRAPHRAG_CHAT_API_KEY")))打出来看首尾字符。另一个原因是main.py里还留着旧的硬编码 Key,覆盖了环境变量。
5.2 404 Not Found,路径变成 /api/v1/chat/completions
这就是前面强调的/v1问题。把GRAPHRAG_API_BASE改成https://taotoken.net/api,不要带/v1。main.py里的api_base同样处理。
5.3 索引跑到一半报 model not found
确认GRAPHRAG_CHAT_MODEL和GRAPHRAG_EMBEDDING_MODEL拼写正确,qwen-turbo 和 text-embedding-v1 都是小写加连字符。如果控制台里该模型没开通,也会报这个错,去控制台确认模型可用性。
5.4 社区摘要为空或行数异常
多半是max_tokens设太小导致响应被截断。settings.yaml里llm.max_tokens建议保持 2000 以上,community_reports.max_input_length和max_length也别压得太低。改完清 cache 重跑。
5.5 本地统计与控制台用量对不上
先确认统计口径:本地 284 次是 chat 调用,embedding 是另一条链路。再看是否有重试,max_retries=20在网络抖动时会放大调用次数。最后确认控制台筛选的时间窗口覆盖了完整构建过程。
6. 后续怎么继续用这套配置
索引和问答都验证通过后,这套配置就可以固化成模板:.env只维护一份 Key,main.py全部走环境变量,换模型只改.env里的 model 名。如果你要长期跑编码类或 Agent 类任务,可以到 Coding Plan 页面看套餐;日常调试模型效果,直接用模型对话页面就能对比不同模型的输出;需要新建或轮换 Key,去 API Keys 页面操作;接入细节和参数说明在接入文档里都有。
把 base 地址统一成https://taotoken.net/api之后,本地那层转发服务就可以彻底不起了,少一个进程、少一个端口冲突,用量也能在控制台一眼看到。下次换文档重跑索引时,你只需要替换input目录里的 txt,其余配置原样复用即可。