ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

记录一下ollamaone-api+GraphRAG的辛酸经历:从neo4j到TaoToken的踩坑复盘

记录一下ollamaone-api+GraphRAG的辛酸经历:从neo4j到TaoToken的踩坑复盘 1. 从 neo4j 写入失败说起本地 ollama 串联 GraphRAG 的典型报错场景如果你正在本地用 ollama 跑模型、用 one-api 做统一网关、再拿 GraphRAG 建知识图谱大概率会在某个深夜被 neo4j 的写入报错拦住。我这次复现的链路是ollama 提供 chat 与 embedding 模型one-api 把多个渠道聚合成一个 OpenAI 兼容入口GraphRAG 负责抽取实体关系最后把 parquet 结果灌进 neo4j 做可视化。听起来很顺但真正跑起来坑集中在三个地方容器之间互相访问不到、neo4j 的 APOC 过程没加载、GraphRAG 的 settings.yaml 里 api_base 写错导致 embedding 维度对不上。先说最典型的报错。执行导入脚本时控制台直接抛neo4j.exceptions.ServiceUnavailable: Could not connect to Neo4j at neo4j://IP:7687。很多人第一反应是 neo4j 没启动其实docker ps里容器活得好好的。问题出在 neo4j 默认只监听容器内部的 7687而你的导入脚本跑在宿主机或另一个容器里网络命名空间不同。另一个高频报错是There is no procedure with the name apoc.create.addLabels registered for this database instance这是 APOC 插件没装或没启用。还有一类更隐蔽GraphRAG 索引阶段报openai.BadRequestError: Error code: 400 - invalid embedding dimension根因是 one-api 渠道里 embedding 模型和 chat 模型混用同一个 Key或者 api_base 指向了 ollama 的 11434 而不是 one-api 的 3000。这些报错单独看都不难但叠在一起就会让人怀疑人生。下面我把整条链路拆成可复制的步骤重点放在 docker-compose 网络配置、one-api 渠道参数、neo4j 连接验证命令以及如何把 endpoint 切到 TaoToken 统一 Key 通道完成端到端问答。你跟着做至少能少走我踩过的那些弯路。2. TaoToken 前置统一 Key 通道与 one-api 渠道配置在讲 neo4j 之前得先把模型入口理顺。本地 ollama 适合跑开源模型但 GraphRAG 的实体抽取对模型指令遵循能力要求不低qwen3:8b 在长文档上容易漏实体。我的做法是ollama 只保留 embedding 模型 bge-m3chat 模型走 TaoToken 的统一 Key 通道这样既省本地显存又能拿到更稳的抽取质量。TaoToken 的 API 地址是https://taotoken.net/api兼容 OpenAI 的/v1/chat/completions和/v1/embeddings你只需要在 one-api 里新建一个渠道把 Base URL 填成它再把申请到的 Key 贴进去。具体操作登录 one-api 后台默认账号 root、密码 123456进去后点「渠道」→「新建渠道」。类型选 OpenAI名称随便写比如taotoken-chatBase URL 填https://taotoken.net/api密钥填你的 TaoToken Key。模型列表里手动加上你要用的模型 ID比如claude-sonnet-4-5或gpt-4o保存后点「测试」确认连通。如果你还要用 embedding再建一个渠道Base URL 同样模型填text-embedding-3-large之类。这里有个细节one-api 的渠道测试走的是/v1/models如果 TaoToken 那边模型列表没返回测试会失败但实际调用可能正常别被吓到。建完渠道后去「令牌」页面新建一个令牌额度设无限复制出来的sk-xxx就是 GraphRAG 要用的 API Key。这个 Key 同时能调 chat 和 embedding省得你在 settings.yaml 里维护两套凭证。如果你更习惯用 Coding Plan 做长期编码任务也可以在 TaoToken 后台开一个 Coding Plan把额度集中管理不过 GraphRAG 这种批处理场景用按量 Key 更划算。记住一个原则one-api 里渠道的 Base URL 必须带/api后缀而 GraphRAG 里填的 api_base 要带/v1两者别搞混。3. 可复制配置docker-compose 打通 ollama、one-api 与 neo4j网络不通的根因通常是每个容器各自docker run默认 bridge 网络里只能用容器名互访但宿主机脚本又用 IP 访问导致一半通一半不通。我改成 docker-compose 统一编排所有服务放进同一个自定义网络容器名即主机名。下面这份 compose 文件你可以直接改路径和密码后用。version: 3.8 services: ollama: image: ollama/ollama:latest container_name: ollama restart: always ports: - 11434:11434 volumes: - /home/ubuntu/ollama-data:/root/.ollama networks: - ragnet deploy: resources: reservations: devices: - driver: nvidia device_ids: [4] capabilities: [gpu] one-api: image: justsong/one-api container_name: one-api restart: always ports: - 3000:3000 environment: - TZAsia/Shanghai - SQL_DSNroot:123456tcp(mysql:3306)/oneapi volumes: - /home/ubuntu/data/one-api:/data depends_on: - mysql networks: - ragnet mysql: image: mysql:8.0 container_name: one-api-mysql restart: always environment: - MYSQL_ROOT_PASSWORD123456 - MYSQL_DATABASEoneapi volumes: - /home/ubuntu/data/mysql:/var/lib/mysql networks: - ragnet neo4j: image: neo4j:5.21.2 container_name: neo4j-graphrag restart: always ports: - 7474:7474 - 7687:7687 volumes: - /home/ubuntu/neo4j/data:/data - /home/ubuntu/neo4j/logs:/logs - /home/ubuntu/neo4j/plugins:/plugins environment: - NEO4J_AUTHneo4j/YourStrongPassword123! - NEO4J_apoc_export_file_enabledtrue - NEO4J_apoc_import_file_enabledtrue - NEO4J_apoc_import_file_use__neo4j__configtrue - NEO4J_PLUGINS[apoc] - NEO4J_dbms_security_procedures_unrestrictedapoc.*,gds.* networks: - ragnet networks: ragnet: driver: bridge启动前先建好目录mkdir -p /home/ubuntu/neo4j/{data,logs,plugins}。然后docker compose up -d。验证 neo4j 是否就绪用docker logs neo4j-graphrag看到Remote interface available at http://localhost:7474/才算成功。接着进容器拉模型docker exec -it ollama ollama pull bge-m3:latestchat 模型不用拉走 TaoToken。one-api 首次启动后浏览器打开http://你的IP:3000用 root/123456 登录按上一节配好渠道和令牌。GraphRAG 的 settings.yaml 关键片段如下注意 api_base 指向 one-api 的容器名或宿主机 IPapi_key 填 one-api 生成的令牌models: default_chat_model: type: openai_chat api_base: http://one-api:3000/v1 auth_type: api_key api_key: sk-你的one-api令牌 model: claude-sonnet-4-5 request_timeout: 600 model_supports_json: true concurrent_requests: 10 async_mode: threaded retry_strategy: native max_retries: 10 tokens_per_minute: auto requests_per_minute: auto default_embedding_model: type: openai_embedding api_base: http://ollama:11434/v1 auth_type: api_key api_key: ollama model: bge-m3:latest request_timeout: 600 concurrent_requests: 10 async_mode: threaded retry_strategy: native max_retries: 10 tokens_per_minute: null requests_per_minute: null如果你把 embedding 也切到 TaoToken就把 api_base 改成http://one-api:3000/v1api_key 换成同一个令牌model 填text-embedding-3-large。这样 chat 和 embedding 走同一个 Key 通道维度一致性更有保障。4. 验证请求与成功结果从索引到 neo4j 图谱落库配置改完先别急着跑全量索引用一个小文档验证链路。在 ragtest 目录下建input文件夹放一个几百字的 txt。执行python -m graphrag init --root ./生成 settings.yaml 和 prompts 后把上面的模型配置覆盖进去。然后跑索引python -m graphrag index --root ./。如果 chat 和 embedding 都通你会看到extract_graph工作流逐步推进最后在output目录生成documents.parquet、text_units.parquet、entities.parquet、relationships.parquet等文件。接下来验证 neo4j 连接。在宿主机执行docker exec -it neo4j-graphrag cypher-shell -u neo4j -p YourStrongPassword123! RETURN 1 AS ok;返回ok为 1 就说明数据库可用。再验证 APOCRETURN apoc.version();能返回版本号即可。如果报过程未注册检查 compose 里NEO4J_PLUGINS的引号格式必须是[apoc]这种 JSON 数组且 plugins 目录挂载正确。导入脚本用 Python 跑核心是neo4j://neo4j-graphrag:7687这个地址——如果你在宿主机跑脚本就写neo4j://localhost:7687如果在另一个容器里跑写容器名。批量导入时用UNWIND $rows AS value配合driver.execute_query每批 1000 条。导入完成后打开http://你的IP:7474用 neo4j 账号登录执行MATCH (n:__Entity__) RETURN n LIMIT 50能看到实体节点和 RELATED 关系说明图谱落库成功。最后做端到端问答验证。GraphRAG 的 local search 和 global search 都走 chat 模型此时 chat 的 api_base 指向 one-apione-api 再转发到 TaoToken。执行python -m graphrag query --root ./ --method local --query 文档里提到了哪些核心概念如果返回带引用的答案整条链路就通了。我实测下来把 chat 切到 TaoToken 后实体抽取的完整度比纯本地 qwen3:8b 明显提升长文档漏抽的情况少了很多。5. 本篇常见错排查401、local proxy failed 与 reading choices第一个高频错误是401 Unauthorized。在 GraphRAG 里出现通常是 settings.yaml 的 api_key 没填对或者 one-api 令牌额度用尽。排查顺序先用 curl 直接打 one-api 的/v1/models带上令牌看是否返回模型列表如果这一步就 401说明令牌或渠道有问题去 one-api 后台看渠道测试结果。如果 curl 通但 GraphRAG 报 401检查 yaml 里 api_key 有没有多余空格或引号。第二个是local proxy failed或Connection refused。这基本是网络问题。如果你在宿主机跑 GraphRAGapi_base 写http://localhost:3000/v1如果在容器里跑写http://one-api:3000/v1。别在宿主机脚本里写容器名DNS 解析不了。同理 neo4j 的 URI宿主机用 localhost容器内用容器名。我踩过的坑是 compose 里 one-api 依赖 mysql但 mysql 没起来时 one-api 会反复重启导致端口时通时断docker compose logs one-api能看到数据库连接失败。第三个是Error reading choices或返回结构解析失败。这通常发生在 one-api 转发 TaoToken 时某些模型返回的 JSON 字段和 OpenAI 标准略有差异。解决办法是在 one-api 渠道设置里开启「强制格式化」或换一个模型 ID 测试。另外 GraphRAG 的model_supports_json: true要和你实际模型能力匹配如果模型不支持 JSON mode 却开了会报解析错误改成 false 即可。第四个是 neo4j 导入时报Unknown function apoc.text.upperCamelCase。这是 APOC 没启用回到 compose 确认NEO4J_PLUGINS[apoc]和NEO4J_dbms_security_procedures_unrestrictedapoc.*,gds.*两行都在然后docker compose down docker compose up -d重建容器。注意 plugins 目录权限如果挂载后容器内读不到用chmod 755放开。第五个是 embedding 维度不匹配。GraphRAG 默认用cl100k_base做 token 编码但 embedding 模型输出的向量维度必须和索引时一致。如果你中途换了 embedding 模型必须删掉output和cache重新索引否则查询阶段会报维度错误。我建议 embedding 固定用 bge-m3 或 text-embedding-3-large别频繁换。6. 语义一致 CTA把 endpoint 切到 TaoToken 后的长期用法整条链路跑通后你会发现最省心的做法是把 chat 和 embedding 都收敛到 TaoToken 的统一 Key 通道。one-api 在这里的角色是聚合和转发你可以在它里面同时挂本地 ollama 渠道和 TaoToken 渠道按模型名分流。比如bge-m3走 ollamaclaude-sonnet-4-5走 TaoTokenGraphRAG 的 settings.yaml 只需要指向 one-api 一个地址。这样以后换模型只改 one-api 渠道不用动 GraphRAG 配置。如果你打算长期跑知识图谱和 Agent 任务可以去 TaoToken 后台开一个 Coding Plan把常用模型的额度打包比按量调用更可控。API Key 在控制台的 API Keys 页面管理接入文档里有各语言的示例代码模型对话页面可以直接测试模型连通性。Claude Code 这类工具也能通过配置 Base URL 和 Key 接到同一个通道具体参考文档里的 Anthropic 兼容说明。最后留一个实用技巧neo4j 导入脚本里的batched_import函数把 batch_size 从 1000 调到 500在内存小的机器上更稳虽然慢一点但不容易 OOM。GraphRAG 索引阶段如果文档多把concurrent_requests降到 5避免 one-api 触发上游限流。这些参数没有标准答案按你机器的实际情况调跑通一次之后就有手感了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进