LangChain.js 与 Neo4j 图数据库集成指南:图谱、向量检索与对话记忆实战
2026/9/13 11:36:18 网站建设 项目流程

LangChain.js 与 Neo4j 图数据库集成指南:图谱、向量检索与对话记忆实战

【免费下载链接】langchainjsThe agent engineering platform项目地址: https://gitcode.com/GitHub_Trending/la/langchainjs

本篇指南围绕@langchain/neo4j集成包展开,介绍如何在 LangChain.js 应用中接入 Neo4j(及兼容 Bolt 协议的 Memgraph)图数据库:包括图数据库封装Neo4jGraph、基于向量索引的Neo4jVectorStore、会话消息历史存储Neo4jChatMessageHistory,以及"自然语言 → Cypher → 答案"的问答链GraphCypherQAChain。读完本文,你将掌握从安装、初始化、写入图数据、混合检索到搭建完整图问答应用的完整方案。

包概览与适用场景

@langchain/neo4j是 LangChain.js 官方提供的 Neo4j 图数据库集成包,源码位于 libs/providers/langchain-neo4j,核心入口统一从 src/index.ts 导出。它同时兼容 Memgraph(一款同样使用 Bolt 协议的内存图数据库)。该包提供的组件可分为四类:

组件类型典型用途
Neo4jGraph/MemgraphGraph图数据库封装连接管理、Schema 自省、Cypher 查询、结构化图文档写入
Neo4jVectorStore向量存储基于 Neo4j 向量索引的相似度检索、混合检索、元数据过滤
Neo4jChatMessageHistory聊天历史存储将会话消息持久化到图数据库,支持滑动窗口
GraphCypherQAChain问答链面向图数据的自然语言问答(LLM 生成 Cypher 查询)

从 package.json 可以看到,该包将neo4j-driver(^6.2.0)作为直接依赖自动安装,将@langchain/core作为必要 peer dependency、@langchain/classic作为可选 peer dependency(仅GraphCypherQAChain需要)。因此按文档安装时只需显式安装两个包即可:

npm install @langchain/neo4j @langchain/core

如需使用GraphCypherQAChain,额外安装@langchain/classic。运行时要求 Node.js >= 20(见 package.json)。

Neo4jGraph:图数据库的基础封装

Neo4jGraph是对 Neo4j 驱动的轻量封装,提供连接管理、Schema 自省和查询执行三类能力。

初始化与基本用法

import { Neo4jGraph } from "@langchain/neo4j"; const graph = await Neo4jGraph.initialize({ url: "bolt://localhost:7687", username: "neo4j", password: "password", database: "neo4j", // 可选,默认 "neo4j" }); // 获取数据库 schema console.log(graph.getSchema()); // 执行 Cypher 查询 const results = await graph.query("MATCH (n:Person) RETURN n.name LIMIT 10"); // 关闭连接 await graph.close();

Neo4jGraph.initialize的完整配置项定义在 src/graphs/neo4j_graph.ts:

配置项类型默认值说明
urlstring必填Bolt 连接地址,如bolt://localhost:7687
username/passwordstring必填数据库认证凭据
databasestring"neo4j"目标数据库名(Memgraph 默认为"memgraph"
timeoutMsnumber事务超时时间(毫秒),透传给驱动的事务配置
enhancedSchemabooleanfalse是否启用增强 Schema 自省

从源码看,initialize是一个异步工厂方法,内部执行三步:创建驱动实例(若 URL/凭据非法会抛出 "Could not create a Neo4j driver instance..." 错误)→verifyConnectivity()(调用driver.getServerInfo()验证连通性)→refreshSchema()刷新 Schema 缓存,随后将实例返回。其中refreshSchema依赖 Neo4j 的 APOC 插件(apoc.meta.data()),若插件缺失会抛出明确提示,要求安装 APOC 并在配置中放行该过程。

query方法返回记录数组(内部会把 Neo4j 的Int整数、NodeRelationshipPath等类型递归转换为普通 JS 对象,方便直接消费),默认使用WRITE路由,可通过第三参数指定READ路由。

Enhanced Schema:更细粒度的元数据自省

默认情况下getSchema()返回的是格式化后的精简 Schema 字符串(如Person {name: STRING, age: INTEGER})。开启enhancedSchema: true后,初始化过程会进一步调用apoc.meta.graphSample()并逐标签/关系类型执行统计分析,产出更丰富的属性信息:

const graph = await Neo4jGraph.initialize({ url: "bolt://localhost:7687", username: "neo4j", password: "password", enhancedSchema: true, });

从 src/graphs/neo4j_graph.ts 的实现看,增强模式的行为由几个关键阈值控制:

  • DISTINCT_VALUE_LIMIT = 10:字符串属性的去重值数量小于等于 10 时,Schema 中展示完整的Available options: ...取值列表;超过 10 则只给出一个Example示例值,避免 Schema 过长;
  • EXHAUSTIVE_SEARCH_LIMIT = 10000:标签对应节点数小于 1 万时走穷举统计(收集去重值、min/max、distinct count),否则只采样前 5 个节点,控制自省开销;
  • LIST_LIMIT = 128:LIST 类型属性的最小尺寸超过 128 时跳过,不纳入 Schema;
  • 数字类型(INTEGER/FLOAT/DATE 等)输出Min/Max与去重计数;BOOLEANPOINTDURATION属性不参与增强统计。

增强 Schema 还支持利用数据库已有 RANGE 索引,通过apoc.schema.properties.distinct快速获取去重值,避免全表扫描。

向图数据库写入结构化图文档

Neo4jGraph支持将"从文本中抽取出的节点与关系"以结构化图文档的形式写入数据库,这为构建知识图谱类应用(例如从文档抽取实体与关系后入库)提供了直接通路。

import { Neo4jGraph, Node, Relationship, GraphDocument, } from "@langchain/neo4j"; import { Document } from "@langchain/core/documents"; const source = new Document({ pageContent: "Alice works at Acme Corp.", metadata: { id: "doc1" }, }); const alice = new Node({ id: "alice", type: "Person", properties: { name: "Alice" }, }); const acme = new Node({ id: "acme", type: "Company", properties: { name: "Acme Corp" }, }); const worksAt = new Relationship({ source: alice, target: acme, type: "WORKS_AT", }); const graphDoc = new GraphDocument({ nodes: [alice, acme], relationships: [worksAt], source, }); await graph.addGraphDocuments([graphDoc]);

三种数据结构定义在 src/graphs/document.ts:

  • Node{ id, type, properties }type默认"Node",即图中的标签(Label);
  • Relationship{ source, target, type, properties },连接两个节点,type为关系类型;
  • GraphDocument{ nodes, relationships, source }source记录该图文档的来源Document

addGraphDocuments的底层实现(src/graphs/neo4j_graph.ts)有几个值得注意的细节:

  • source.metadata.id缺失,会自动用sha256(pageContent)生成稳定文档 ID;
  • 节点写入使用apoc.merge.node,以id为键做 MERGE(存在即更新,不存在则创建),保证幂等;
  • 关系类型会被replace(/ /g, "_").toUpperCase()规范化(空格转下划线、统一大写);
  • 可选配置AddGraphDocumentsConfig支持两个开关:
await graph.addGraphDocuments([graphDoc], { baseEntityLabel: true, // 为所有实体附加统一的基础标签 "__Entity__" includeSource: true, // 在图中同时创建 Document 节点并建立 (:Document)-[:MENTIONS]->(实体) 关系 });

baseEntityLabel: true时,写入前会自动创建__Entity__标签上的id唯一约束(对应源码导出的常量BASE_ENTITY_LABEL),并先 MERGE 实体节点、再通过apoc.create.addLabels追加其具体标签;includeSource: true则通过INCLUDE_DOCS_QUERY将文档内容与元数据一并入库并建立MENTIONS关系,便于溯源。

Neo4jVectorStore:基于向量索引的检索存储

Neo4jVectorStore是 Neo4j 官方 vector index 之上的向量存储实现,在初始化时会自动创建向量索引,支持向量检索、混合检索(向量 + 全文)以及元数据过滤。

从文档创建向量存储

import { Neo4jVectorStore } from "@langchain/neo4j"; import { OpenAIEmbeddings } from "@langchain/openai"; const embeddings = new OpenAIEmbeddings(); const vectorStore = await Neo4jVectorStore.fromDocuments(docs, embeddings, { url: "bolt://localhost:7687", username: "neo4j", password: "password", indexName: "vector", nodeLabel: "Chunk", textNodeProperty: "text", embeddingNodeProperty: "embedding", }); // 相似度检索 const results = await vectorStore.similaritySearch("What is Neo4j?", 4); // 带分数的相似度检索 const resultsWithScore = await vectorStore.similaritySearchWithScore( "What is Neo4j?", 4 ); // 关闭连接 await vectorStore.close();

Neo4jVectorStoreArgs的完整配置定义在 src/vectorstores/neo4j_vector.ts,常用项及源码中的默认值如下:

配置项默认值说明
url/username/password必填连接信息
database"neo4j"目标数据库
indexName"vector"向量索引名
nodeLabel"Chunk"存储向量的节点标签
textNodeProperty"text"文本内容属性名
embeddingNodeProperty"embedding"向量属性名
keywordIndexName"keyword"全文索引名(hybrid 模式用)
searchType"vector""vector""hybrid"
indexType"NODE""NODE""RELATIONSHIP"(后者查询关系向量)
preDeleteCollectionfalse初始化时先删除同名索引与数据
retrievalQuery自动生成自定义检索 Cypher(返回 text/metadata/score)
textNodeProperties[]多文本属性(fromExistingGraph等场景)
createIdIndextrue是否为节点id创建唯一约束

底层初始化流程(initialize)会:创建驱动 →verifyAuthentication验证凭据 → 用embeddings.embedQuery("foo")探测向量维度 → 检查同名索引是否存在,不存在则通过db.index.vector.createNodeIndex创建(相似度度量固定为cosine,即DistanceStrategy默认值)。

写入文档时(addVectors)使用MERGE (c:\Chunk` {id: row.id})id幂等写入,再通过db.create.setVectorProperty设置向量属性,批量事务按 1000 行切分。fromTextsfromDocuments` 语义一致,只是入参为纯文本数组。

Hybrid Search:向量 + 全文混合检索

searchType: "hybrid"时,检索同时执行向量查询与全文(full-text)查询,两者各自按最高分归一化后取最大值融合排序:

const vectorStore = await Neo4jVectorStore.fromDocuments(docs, embeddings, { url: "bolt://localhost:7687", username: "neo4j", password: "password", searchType: "hybrid", indexName: "vector", keywordIndexName: "keyword", nodeLabel: "Chunk", });

混合模式的实现细节(src/vectorstores/neo4j_vector.ts):

  • 初始化时若检测不到指定keywordIndexName的全文索引,会自动执行CREATE FULLTEXT INDEX keyword FOR (n:\Chunk`) ON EACH [n.`text`]` 创建;
  • 查询阶段先分别调用db.index.vector.queryNodesdb.index.fulltext.queryNodes,各自将原始分数除以组内最大分实现 0–1 归一化,再对两条结果取max(score)排序取 Top-K;
  • 全文查询的输入会先经过removeLuceneChars(源码位于 neo4j_vector.ts),将+ - & | ! ( ) { } [ ] ^ " ~ * ? : \等 Lucene 特殊字符替换为空格,避免查询语法被意外注入。

连接已有索引

若索引已在 Neo4j 中预先创建(例如由运维或其它流程建立),用fromExistingIndex直接接入,无需重新建索引:

const vectorStore = await Neo4jVectorStore.fromExistingIndex(embeddings, { url: "bolt://localhost:7687", username: "neo4j", password: "password", indexName: "my_existing_index", });

该方法会通过SHOW INDEXES校验索引存在性与维度匹配:索引不存在会抛出提示核对索引名;嵌入函数维度与索引维度不一致会明确报错。hybrid 模式则额外要求全文索引存在,且向量索引与全文索引必须指向同一个节点标签。

补充:源码还提供了fromExistingGraph静态方法,用于对已有图数据按指定文本属性批量生成向量并回填(db.create.setVectorProperty),适合对存量节点做离线向量化。

元数据过滤

similaritySearch/similaritySearchWithScore的第三个参数支持filter,按节点属性过滤检索结果(要求 Neo4j 5.18+):

const results = await vectorStore.similaritySearch("query", 4, { filter: { category: "science", year: { $gte: 2020 }, }, });

完整支持的操作符(源码 neo4j_vector.ts 中SUPPORTED_OPERATORS):

操作符Cypher 映射说明
$eq=相等(无操作符的裸值默认视为$eq
$ne<>不等
$lt/$lte/$gt/$gte<<=>>=大小比较
$in/$ninIN/NOT IN集合包含/不包含(元素须为 string/number/boolean)
$likeCONTAINS子串包含(注意:实现为 CONTAINS,值尾部通配符会被截掉)
$iliketoLower(...) CONTAINS大小写不敏感的子串包含
$between低值 <= n.field <= 高值区间过滤,值为[low, high]
$and/$orAND/OR逻辑组合,值为子过滤条件数组

过滤实现位于constructMetadataFilter(neo4j_vector.ts):多字段同时出现时自动以AND拼接;字段名需符合合法标识符正则,否则抛错;参数名按位置自动编号(param_1param_2_low等),杜绝 Cypher 注入。几个使用限制需要留意:

  • 过滤仅支持searchType: "vector",与 hybrid 检索互斥(组合使用会直接抛错);
  • 依赖版本检查(源码_verifyVersion):向量索引要求 Neo4j5.11+,元数据过滤要求5.18+;Aura 版本会单独做版本解析;
  • Enterprise 版会为过滤查询附加CYPHER runtime = parallel并行运行时前缀以提升性能。

Neo4jChatMessageHistory:图数据库中的对话记忆

Neo4jChatMessageHistory将聊天消息持久化到 Neo4j,适合多轮对话场景下的消息存取,与 LangChain 的RunnableWithMessageHistory等组件可无缝组合。

import { Neo4jChatMessageHistory } from "@langchain/neo4j"; import { HumanMessage, AIMessage } from "@langchain/core/messages"; const history = await Neo4jChatMessageHistory.initialize({ url: "bolt://localhost:7687", username: "neo4j", password: "password", sessionId: "my-session-id", // 可选,缺省时自动生成 UUID windowSize: 5, // 可选,默认 3 }); // 追加消息 await history.addMessage(new HumanMessage("Hello!")); await history.addMessage(new AIMessage("Hi there! How can I help you?")); // 读取消息 const messages = await history.getMessages(); // 清空历史 await history.clear(); // 关闭连接 await history.close();

配置项定义在 src/stores/message/neo4j.ts,除连接信息外还有三个可选参数:

配置项默认值说明
sessionId自动生成uuidv4()会话标识,同一会话共享历史
sessionNodeLabel"ChatSession"会话节点的标签
messageNodeLabel"ChatMessage"消息节点的标签
windowSize3读取时保留的消息窗口大小(读取最近消息条数)

从源码看,其存储模型是一个双向链表式的图结构:每个会话有一个ChatSession节点,通过LAST_MESSAGE关系指向最后一条消息;消息节点之间用NEXT关系串成链。addMessage会用一条 Cypher 完成"创建新消息节点、断开旧 LAST_MESSAGE、建立新 LAST_MESSAGE、并让旧末条消息指向新消息"的原子操作;getMessages通过<-[:NEXT*0..windowSize*2-1]-()沿链回溯最多windowSize条消息(每次读写各占一个单位),再借助@langchain/core/messagesmapStoredMessagesToChatMessages还原为标准BaseMessage对象(Human/AI/System 等类型由节点上的type属性区分)。

该组件继承自BaseListChatMessageHistory,因此可以直接传给 LangChain 的RunnableWithMessageHistory等记忆机制,用于多轮对话:

import { RunnableWithMessageHistory } from "@langchain/core/runnables"; const withHistory = new RunnableWithMessageHistory({ runnable: model, // 任意 Runnable(如 ChatOpenAI) getMessageHistory: () => history, inputMessagesKey: "input", historyMessagesKey: "history", }); await withHistory.invoke( { input: "继续刚才的话题" }, { configurable: { sessionId: "my-session-id" } } );

GraphCypherQAChain:自然语言驱动的图问答

GraphCypherQAChain是一个"Text-to-Cypher"问答链:将用户问题交给 LLM 生成 Cypher 查询,在图上执行查询,再把结果交给 LLM 综合成自然语言答案。它继承自@langchain/classicBaseChain,因此需要将@langchain/classic作为可选依赖安装(见 package.json 的 peerDependenciesMeta 声明)。

import { Neo4jGraph, GraphCypherQAChain } from "@langchain/neo4j"; import { ChatOpenAI } from "@langchain/openai"; const graph = await Neo4jGraph.initialize({ url: "bolt://localhost:7687", username: "neo4j", password: "password", }); const llm = new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 }); const chain = GraphCypherQAChain.fromLLM({ graph, llm, returnIntermediateSteps: true, }); const result = await chain.invoke({ query: "Who played in Pulp Fiction?" }); console.log(result.result);

也可以拆分"生成 Cypher"与"生成答案"两个环节使用不同模型(例如用更强的模型写 Cypher、用更经济的模型总结答案):

const chain = GraphCypherQAChain.fromLLM({ graph, cypherLLM: new ChatOpenAI({ model: "gpt-4o", temperature: 0 }), qaLLM: new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 }), });

fromLLM的可配置项(源码 src/chains/graph_qa/cypher.ts):

配置项默认值说明
graph必填Neo4jGraph实例(链会读取getSchema()作为 Cypher 生成上下文)
llm-单模型,同时承担 Cypher 生成与答案合成
cypherLLM/qaLLM-分别指定两个模型(与llm二选一)
cypherPrompt/qaPrompt内置模板自定义提示词模板
returnIntermediateStepsfalse返回intermediateSteps(含生成的 Cypher 与查询结果),便于调试
returnDirectfalsetrue时只返回查询结果、跳过答案合成
topK10限制查询返回的路径数量(追加到生成的 Cypher 中)
inputKey/outputKey"query"/"result"输入输出键名

链的执行分为三个阶段:① 用内置的CYPHER_GENERATION_PROMPT将图 Schema 与问题喂给 LLM 生成 Cypher;② 通过graph.query在图上执行;③ 将结果连同原问题交给CYPHER_QA_PROMPT合成最终答案。两个提示词模板定义在 src/chains/graph_qa/prompts.ts,均从 src/index.ts 导出,可复用或覆盖。该链的实现与提示词均有对应测试: tests/cypher.test.ts 与 tests/prompts.test.ts。

MemgraphGraph:Bolt 协议兼容的图数据库接入

MemgraphGraphNeo4jGraph的子类(源码 src/graphs/memgraph_graph.ts),用于接入同样基于 Bolt 协议的 Memgraph。与 Neo4j 的差异主要在 Schema 自省方式上:Memgraph 通过其 LLM 工具过程CALL llm_util.schema("raw")获取原始 Schema,再在客户端格式化为与Neo4jGraph一致的结构化形式。

import { MemgraphGraph } from "@langchain/neo4j"; const graph = await MemgraphGraph.initialize({ url: "bolt://localhost:7687", username: "memgraph", password: "password", }); console.log(graph.getSchema()); await graph.close();

MemgraphGraphConfig仅含url/username/password/database(默认"memgraph"),不支持enhancedSchema(其 Schema 输出格式为Node name: '...', Node properties: {...}(:Start)-[:TYPE]->(:End)形式的文本)。

安全注意事项

本文档涉及的数据库组件(图封装、向量存储、聊天历史)都接受连接凭据,并且GraphCypherQAChain会执行 LLM 生成的 Cypher 语句。官方安全文档(见 LangChain 安全文档)与源码中各组件的安全注释均强调:

  • 凭据最小化:使用仅包含必要权限的窄范围凭据,优先创建只读用户,防止被诱导执行删除、变更数据或读取敏感数据的操作;
  • 这尤其重要——Cypher 是强表达能力的查询语言,一旦 LLM 生成的语句被注入或误用,可能导致数据损坏或泄露。

从 @langchain/community 迁移

若此前使用@langchain/community中的 Neo4j 集成,迁移只需将分散的导入收敛到新包:

// 迁移前 import { Neo4jGraph } from "@langchain/community/graphs/neo4j_graph"; import { Neo4jVectorStore } from "@langchain/community/vectorstores/neo4j_vector"; import { Neo4jChatMessageHistory } from "@langchain/community/stores/message/neo4j"; import { GraphCypherQAChain } from "@langchain/community/chains/graph_qa/cypher"; // 迁移后 import { Neo4jGraph, Neo4jVectorStore, Neo4jChatMessageHistory, GraphCypherQAChain, } from "@langchain/neo4j";

本地开发与测试

若要在本仓库内开发调试该包,使用 pnpm 工作区命令(对应 package.json 中的 scripts):

# 安装依赖 pnpm install # 构建 pnpm --filter @langchain/neo4j build # 运行测试 pnpm --filter @langchain/neo4j test # Lint pnpm --filter @langchain/neo4j lint

仓库为各组件提供了完整的单元测试与集成测试参考实现:图与 Schema 相关见 graphs/tests/neo4j_graph.test.ts、graphs/tests/document.test.ts 与 graphs/tests/memgraph_graph.test.ts,向量检索见 vectorstores/tests/neo4j_vector.test.ts,消息历史见 stores/message/tests/neo4j.test.ts。阅读这些测试可以快速掌握各组件在真实(或模拟)数据库上的调用方式与边界行为。

【免费下载链接】langchainjsThe agent engineering platform项目地址: https://gitcode.com/GitHub_Trending/la/langchainjs

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询