概要
neo4jを使ってGraphRAGを実装している時のお話です。
チュートリアルに従って以下のようなコードを書くと思います。
graph = Neo4jGraph(
url=neo4j_uri,
username=neo4j_username,
password=neo4j_password
)
vector_index = Neo4jVector.from_existing_graph(
OpenAIEmbeddings(),
search_type="hybrid",
node_label="Document",
text_node_properties=["text"],
embedding_node_property="embedding"
)
llm = ChatOpenAI(temperature=0, model_name="gpt-4o-mini-2024-07-18")
# ~~~省略~~~
if __name__ == "__main__":
question = "〇〇について教えて"
answer = chain.invoke({"question": question})
print(answer)
で、実行すると
Failed to write data to connection ResolvedIPv4Address(('12.345.67.890', 7687)) (ResolvedIPv4Address(('12.345.67.890', 7687)))
Failed to write data to connection IPv4Address(('aa111111.databases.neo4j.io', 7687)) (ResolvedIPv4Address(('12.345.67.890', 7687)))
Failed to write data to connection ResolvedIPv4Address(('12.345.67.890', 7687)) (ResolvedIPv4Address(('12.345.67.890', 7687)))
Failed to write data to connection IPv4Address(('aa111111.databases.neo4j.io', 7687)) (ResolvedIPv4Address(('12.345.67.890', 7687)))
こんなエラーが出てきます。なんか2*2匹いるし。なんだろう。
バージョンはneo4j=5.25.0、Python=3.13.0 です。
原因:コネクションが生き残っているせい
参考記事
曰く、Neo4jのドライバーがクローズされていないために発生しているっぽい。
でも
graph.driver.close()
graph.close()
こんなプロパティないんですよね。ふむ?
解決
以下のコードで解決しました。
if __name__ == "__main__":
question = "〇〇について教えて"
answer = chain.invoke({"question": question})
print(answer)
graph._driver.close()
vector_index._driver.close()
これでFailed to write data to connection は出なくなりました。
が、なんか怪しいのでジピ先生に聞いてみました。
• _driver.close() は何か
_driver.close() は、クラス内部で使用されている Neo4jドライバーの接続を閉じるメソッドを呼び出すコードです。
_driver は プライベート属性であり、クラスの内部実装として扱われます。
このメソッドを呼び出すと、Neo4jデータベースとの接続が終了します。
• なぜ使わないほうがいいのか
プライベート属性への直接アクセスは非推奨です。
カプセル化の原則に反し、クラス設計者が意図したデータ保護や内部実装の隠蔽を破壊します。
将来的な変更に弱い: ライブラリの更新で内部実装が変わると、コードが動作しなくなるリスクがあります。
メンテナンス性の低下: 他の開発者がコードを理解しにくくなり、バグの原因となります。
• 代替案は何か
接続管理クラス(例: Neo4jConnectionManager)を作成する
接続のライフサイクルを明示的に管理し、プライベート属性に依存しない方法でドライバーをクローズします。
コンテキストマネージャを利用して、接続の開始と終了を自動的に管理できます。
ということらしいです。目下のエラーが気になって(そもそも動作自体にはもともと影響無し)対応したのですが、実際の実装の時にはもっと考慮した方が良さそうです。
あとneo4j公式のQAにこんな質問がありました。
You should definitely not close the connection after every query. Connections are relatively heavyweight objects that involve setup and network roundtrips to authenticate you. If you close the connection after every query, your experience will be that queries are a lot slower as your code does a lot of extra unnecessary work.
(毎回クエリごとに接続を切るのはやめたほうがいいよ。接続って結構重いオブジェクトで、設定とか認証のためにネットワークの往復が必要なんだ。だから毎回切ってたら、無駄な処理が増えてクエリが遅く感じるようになるよ。)
だから可能な限りコネクションは維持した方が良さそうですね。このあたりも実装においてはしっかり作るようにしたいと思います。
以上です。