1+ # Copyright (c) "Neo4j"
2+ # Neo4j Sweden AB [https://neo4j.com]
3+ # #
4+ # Licensed under the Apache License, Version 2.0 (the "License");
5+ # you may not use this file except in compliance with the License.
6+ # You may obtain a copy of the License at
7+ # #
8+ # https://www.apache.org/licenses/LICENSE-2.0
9+ # #
10+ # Unless required by applicable law or agreed to in writing, software
11+ # distributed under the License is distributed on an "AS IS" BASIS,
12+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ # See the License for the specific language governing permissions and
14+ # limitations under the License.
15+ #
16+ """
17+ Example demonstrating how to create multiple domain-specific tools from retrievers.
18+
19+ This example shows:
20+ 1. How to create multiple tools from the same retriever type for different use cases
21+ 2. How to provide custom parameter descriptions for each tool
22+ 3. How type inference works automatically while descriptions are explicit
23+ """
24+
25+ import neo4j
26+ from typing import cast , Any , Optional
27+ from unittest .mock import MagicMock
28+
29+ from neo4j_graphrag .retrievers .base import Retriever
30+ from neo4j_graphrag .types import RawSearchResult
31+
32+
33+ class MockVectorRetriever (Retriever ):
34+ """A mock vector retriever for demonstration purposes."""
35+
36+ VERIFY_NEO4J_VERSION = False
37+
38+ def __init__ (self , driver : neo4j .Driver , index_name : str ):
39+ super ().__init__ (driver )
40+ self .index_name = index_name
41+
42+ def get_search_results (
43+ self ,
44+ query_vector : Optional [list [float ]] = None ,
45+ query_text : Optional [str ] = None ,
46+ top_k : int = 5 ,
47+ effective_search_ratio : int = 1 ,
48+ filters : Optional [dict [str , Any ]] = None ,
49+ ) -> RawSearchResult :
50+ """Get vector search results (mocked for demonstration)."""
51+ # Return empty results for demo
52+ return RawSearchResult (records = [], metadata = {"index" : self .index_name })
53+
54+
55+ def main () -> None :
56+ """Demonstrate creating multiple domain-specific tools from retrievers."""
57+
58+ # Create mock driver (in real usage, this would be actual Neo4j driver)
59+ driver = cast (Any , MagicMock ())
60+
61+ # Create retrievers for different domains using the same retriever type
62+ # In practice, these would point to different vector indexes
63+
64+ # Movie recommendations retriever
65+ movie_retriever = MockVectorRetriever (
66+ driver = driver ,
67+ index_name = "movie_embeddings"
68+ )
69+
70+ # Product search retriever
71+ product_retriever = MockVectorRetriever (
72+ driver = driver ,
73+ index_name = "product_embeddings"
74+ )
75+
76+ # Document search retriever
77+ document_retriever = MockVectorRetriever (
78+ driver = driver ,
79+ index_name = "document_embeddings"
80+ )
81+
82+ # Convert each retriever to a domain-specific tool with custom descriptions
83+
84+ # 1. Movie recommendation tool
85+ movie_tool = movie_retriever .convert_to_tool (
86+ name = "movie_search" ,
87+ description = "Find movie recommendations based on plot, genre, or actor preferences" ,
88+ parameter_descriptions = {
89+ "query_text" : "Movie title, plot description, genre, or actor name" ,
90+ "query_vector" : "Pre-computed embedding vector for movie search" ,
91+ "top_k" : "Number of movie recommendations to return (1-20)" ,
92+ "filters" : "Optional filters for genre, year, rating, etc." ,
93+ "effective_search_ratio" : "Search pool multiplier for better accuracy"
94+ }
95+ )
96+
97+ # 2. Product search tool
98+ product_tool = product_retriever .convert_to_tool (
99+ name = "product_search" ,
100+ description = "Search for products matching customer needs and preferences" ,
101+ parameter_descriptions = {
102+ "query_text" : "Product name, description, or customer need" ,
103+ "query_vector" : "Pre-computed embedding for product matching" ,
104+ "top_k" : "Maximum number of product results (1-50)" ,
105+ "filters" : "Filters for price range, brand, category, availability" ,
106+ "effective_search_ratio" : "Breadth vs precision trade-off for search"
107+ }
108+ )
109+
110+ # 3. Document search tool
111+ document_tool = document_retriever .convert_to_tool (
112+ name = "document_search" ,
113+ description = "Find relevant documents and knowledge articles" ,
114+ parameter_descriptions = {
115+ "query_text" : "Question, keywords, or topic to search for" ,
116+ "query_vector" : "Semantic embedding for document retrieval" ,
117+ "top_k" : "Number of relevant documents to retrieve (1-10)" ,
118+ "filters" : "Document type, date range, or department filters"
119+ }
120+ )
121+
122+ # Demonstrate that each tool has distinct, meaningful descriptions
123+ tools = [movie_tool , product_tool , document_tool ]
124+
125+ for tool in tools :
126+ print (f"\n === { tool .get_name ().upper ()} ===" )
127+ print (f"Description: { tool .get_description ()} " )
128+ print ("Parameters:" )
129+
130+ params = tool .get_parameters ()
131+ for param_name , param_def in params ["properties" ].items ():
132+ required = "required" if param_name in params .get ("required" , []) else "optional"
133+ print (f" - { param_name } ({ param_def ['type' ]} , { required } ): { param_def ['description' ]} " )
134+
135+ # Show how the same parameter type gets different contextual descriptions
136+ print (f"\n === PARAMETER COMPARISON ===" )
137+ print ("Same parameter 'query_text' with different contextual descriptions:" )
138+
139+ for tool in tools :
140+ params = tool .get_parameters ()
141+ query_text_desc = params ["properties" ]["query_text" ]["description" ]
142+ print (f" { tool .get_name ()} : { query_text_desc } " )
143+
144+ print (f"\n Same parameter 'top_k' with different contextual descriptions:" )
145+ for tool in tools :
146+ params = tool .get_parameters ()
147+ top_k_desc = params ["properties" ]["top_k" ]["description" ]
148+ print (f" { tool .get_name ()} : { top_k_desc } " )
149+
150+
151+ if __name__ == "__main__" :
152+ main ()
0 commit comments