Như đã đề cập, nhúng văn bản rất tuyệt vời trong việc mã hóa văn bản phi cấu trúc. Mặt khác, chúng không tuyệt vời trong việc xử lý thông tin có cấu trúc và các hoạt động như lọc , sắp xếp hoặc tổng hợp . Hãy tưởng tượng một câu hỏi đơn giản như:
Bộ phim nào được đánh giá cao nhất ra mắt năm 2024?
Để trả lời câu hỏi này, trước tiên chúng ta phải lọc theo năm phát hành, sau đó sắp xếp theo xếp hạng. Chúng ta sẽ xem xét cách tiếp cận ngây thơ với nhúng văn bản thực hiện như thế nào và sau đó trình bày cách xử lý những câu hỏi như vậy. Bài đăng trên blog này cho thấy rằng khi xử lý các hoạt động dữ liệu có cấu trúc như lọc, sắp xếp hoặc tổng hợp, bạn cần sử dụng các công cụ khác cung cấp cấu trúc như biểu đồ kiến thức.
os.environ["NEO4J_URI"] = "bolt://44.204.178.84:7687" os.environ["NEO4J_USERNAME"] = "neo4j" os.environ["NEO4J_PASSWORD"] = "minimums-triangle-saving" graph = Neo4jGraph(refresh_schema=False)
os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")
Cơ sở dữ liệu chứa 10.000 bộ phim, nhưng các nhúng văn bản vẫn chưa được lưu trữ. Để tránh tính toán nhúng cho tất cả các phim, chúng tôi sẽ gắn nhãn 1.000 phim được đánh giá cao nhất bằng nhãn phụ có tên là Target :
graph.query(""" MATCH (m:Movie) WHERE m.imdbRating IS NOT NULL WITH m ORDER BY m.imdbRating DESC LIMIT 1000 SET m:Target """)
Sau đây là ví dụ về văn bản chúng tôi sẽ nhúng cho bộ phim The Wolf of Wall Street :
plot: Based on the true story of Jordan Belfort, from his rise to a wealthy stock-broker living the high life to his fall involving crime, corruption and the federal government. title: Wolf of Wall Street, The year: 2013 imdbRating: 8.2
embedding = OpenAIEmbeddings(model="text-embedding-3-small") neo4j_vector = Neo4jVector.from_existing_graph( embedding=embedding, index_name="movies", node_label="Target", text_node_properties=["plot", "title", "year", "imdbRating"], embedding_node_property="embedding", )
Trong ví dụ này, chúng tôi sử dụng mô hình text-embedding-3-small của OpenAI để tạo nhúng. Chúng tôi khởi tạo đối tượng Neo4jVector bằng phương thức from_existing_graph. Tham số node_label lọc các nút cần mã hóa, cụ thể là các nút được gắn nhãn Target . Tham số text_node_properties xác định các thuộc tính nút cần nhúng, bao gồm plot , title , year và imdbRating . Cuối cùng, embedding_node_property xác định thuộc tính nơi các nhúng được tạo sẽ được lưu trữ, được chỉ định là embedding .
pretty_print( neo4j_vector.similarity_search( "What is a movie where a little boy meets his hero?" ) )
plot: A young boy befriends a giant robot from outer space that a paranoid government agent wants to destroy. title: Iron Giant, The year: 1999 imdbRating: 8.0 plot: After the death of a friend, a writer recounts a boyhood journey to find the body of a missing boy. title: Stand by Me year: 1986 imdbRating: 8.1 plot: A young, naive boy sets out alone on the road to find his wayward mother. Soon he finds an unlikely protector in a crotchety man and the two have a series of unexpected adventures along the way. title: Kikujiro (Kikujirô no natsu) year: 1999 imdbRating: 7.9 plot: While home sick in bed, a young boy's grandfather reads him a story called The Princess Bride. title: Princess Bride, The year: 1987 imdbRating: 8.1
pretty_print( neo4j_vector.similarity_search( "Which movies are from year 2016?" ) )
plot: Six short stories that explore the extremities of human behavior involving people in distress. title: Wild Tales year: 2014 imdbRating: 8.1 plot: A young man who survives a disaster at sea is hurtled into an epic journey of adventure and discovery. While cast away, he forms an unexpected connection with another survivor: a fearsome Bengal tiger. title: Life of Pi year: 2012 imdbRating: 8.0 plot: Based on the true story of Jordan Belfort, from his rise to a wealthy stock-broker living the high life to his fall involving crime, corruption and the federal government. title: Wolf of Wall Street, The year: 2013 imdbRating: 8.2 plot: After young Riley is uprooted from her Midwest life and moved to San Francisco, her emotions - Joy, Fear, Anger, Disgust and Sadness - conflict on how best to navigate a new city, house, and school. title: Inside Out year: 2015 imdbRating: 8.3
pretty_print( neo4j_vector.similarity_search("Which movie has the highest imdb score?") )
plot: A silent film production company and cast make a difficult transition to sound. title: Singin' in the Rain year: 1952 imdbRating: 8.3 plot: A film about the greatest pre-Woodstock rock music festival. title: Monterey Pop year: 1968 imdbRating: 8.1 plot: This movie documents the Apollo missions perhaps the most definitively of any movie under two hours. Al Reinert watched all the footage shot during the missions--over 6,000,000 feet of it, ... title: For All Mankind year: 1989 imdbRating: 8.2 plot: An unscrupulous movie producer uses an actress, a director and a writer to achieve success. title: Bad and the Beautiful, The year: 1952 imdbRating: 7.9
Nếu bạn quen thuộc với xếp hạng IMDb, bạn biết rằng có rất nhiều phim đạt điểm trên 8,3. Tựa phim được xếp hạng cao nhất trong cơ sở dữ liệu của chúng tôi thực sự là một loạt phim — Band of Brothers — với xếp hạng ấn tượng là 9,6. Một lần nữa, nhúng văn bản hoạt động kém khi sắp xếp kết quả.
pretty_print(neo4j_vector.similarity_search("How many movies are there?"))
plot: Ten television drama films, each one based on one of the Ten Commandments. title: Decalogue, The (Dekalog) year: 1989 imdbRating: 9.2 plot: A documentary which challenges former Indonesian death-squad leaders to reenact their mass-killings in whichever cinematic genres they wish, including classic Hollywood crime scenarios and lavish musical numbers. title: Act of Killing, The year: 2012 imdbRating: 8.2 plot: A meek Hobbit and eight companions set out on a journey to destroy the One Ring and the Dark Lord Sauron. title: Lord of the Rings: The Fellowship of the Ring, The year: 2001 imdbRating: 8.8 plot: While Frodo and Sam edge closer to Mordor with the help of the shifty Gollum, the divided fellowship makes a stand against Sauron's new ally, Saruman, and his hordes of Isengard. title: Lord of the Rings: The Two Towers, The year: 2002 imdbRating: 8.7
Đánh giá thế hệ câu lệnh Cypher. Trích từ bài đăng trên blog của tôi về .
class MovieCountInput(BaseModel): min_year: Optional[int] = Field( description="Minimum release year of the movies" ) max_year: Optional[int] = Field( description="Maximum release year of the movies" ) min_rating: Optional[float] = Field(description="Minimum imdb rating") grouping_key: Optional[str] = Field( description="The key to group by the aggregation", enum=["year"] )
@tool("movie-count", args_schema=MovieCountInput) def movie_count( min_year: Optional[int], max_year: Optional[int], min_rating: Optional[float], grouping_key: Optional[str], ) -> List[Dict]: """Calculate the count of movies based on particular filters""" filters = [ ("t.year >= $min_year", min_year), ("t.year <= $max_year", max_year), ("t.imdbRating >= $min_rating", min_rating), ] # Create the parameters dynamically from function inputs params = { extract_param_name(condition): value for condition, value in filters if value is not None } where_clause = " AND ".join( [condition for condition, value in filters if value is not None] ) cypher_statement = "MATCH (t:Target) " if where_clause: cypher_statement += f"WHERE {where_clause} " return_clause = ( f"t.`{grouping_key}`, count(t) AS movie_count" if grouping_key else "count(t) AS movie_count" ) cypher_statement += f"RETURN {return_clause}" print(cypher_statement) # Debugging output return graph.query(cypher_statement, params=params)
class MovieListInput(BaseModel): sort_by: str = Field( description="How to sort movies, can be one of either latest, rating", enum=["latest", "rating"], ) k: Optional[int] = Field(description="Number of movies to return") description: Optional[str] = Field(description="Description of the movies") min_year: Optional[int] = Field( description="Minimum release year of the movies" ) max_year: Optional[int] = Field( description="Maximum release year of the movies" ) min_rating: Optional[float] = Field(description="Minimum imdb rating")
@tool("movie-list", args_schema=MovieListInput) def movie_list( sort_by: str = "rating", k : int = 4, description: Optional[str] = None, min_year: Optional[int] = None, max_year: Optional[int] = None, min_rating: Optional[float] = None, ) -> List[Dict]: """List movies based on particular filters""" # Handle vector-only search when no prefiltering is applied if description and not min_year and not max_year and not min_rating: return neo4j_vector.similarity_search(description, k=k) filters = [ ("t.year >= $min_year", min_year), ("t.year <= $max_year", max_year), ("t.imdbRating >= $min_rating", min_rating), ] # Create parameters dynamically from function arguments params = { key.split("$")[1]: value for key, value in filters if value is not None } where_clause = " AND ".join( [condition for condition, value in filters if value is not None] ) cypher_statement = "MATCH (t:Target) " if where_clause: cypher_statement += f"WHERE {where_clause} " # Add the return clause with sorting cypher_statement += " RETURN t.title AS title, t.year AS year, t.imdbRating AS rating ORDER BY " # Handle sorting logic based on description or other criteria if description: cypher_statement += ( "vector.similarity.cosine(t.embedding, $embedding) DESC " ) params["embedding"] = embedding.embed_query(description) elif sort_by == "rating": cypher_statement += "t.imdbRating DESC " else: # sort by latest year cypher_statement += "t.year DESC " cypher_statement += " LIMIT toInteger($limit)" params["limit"] = k or 4 print(cypher_statement) # Debugging output data = graph.query(cypher_statement, params=params) return data
Hàm này lấy danh sách phim dựa trên nhiều bộ lọc tùy chọn: mô tả, phạm vi năm, xếp hạng tối thiểu và tùy chọn sắp xếp. Nếu chỉ có mô tả được đưa ra mà không có bộ lọc nào khác, hàm sẽ thực hiện tìm kiếm độ tương đồng chỉ mục vectơ để tìm phim có liên quan. Khi áp dụng các bộ lọc bổ sung, hàm sẽ xây dựng truy vấn Cypher để khớp phim dựa trên các tiêu chí đã chỉ định, chẳng hạn như năm phát hành và xếp hạng IMDb, kết hợp chúng với độ tương đồng dựa trên mô tả tùy chọn. Sau đó, kết quả được sắp xếp theo điểm tương đồng, xếp hạng IMDb hoặc năm và giới hạn ở k phim.
llm = ChatOpenAI(model='gpt-4-turbo') tools = [movie_count, movie_list] llm_with_tools = llm.bind_tools(tools) # System message sys_msg = SystemMessage(content="You are a helpful assistant tasked with finding and explaining relevant information about movies.") # Node def assistant(state: MessagesState): return {"messages": [llm_with_tools.invoke([sys_msg] + state["messages"])]}
# Graph builder = StateGraph(MessagesState) # Define nodes: these do the work builder.add_node("assistant", assistant) builder.add_node("tools", ToolNode(tools)) # Define edges: these determine how the control flow moves builder.add_edge(START, "assistant") builder.add_conditional_edges( "assistant", # If the latest message (result) from assistant is a tool call -> tools_condition routes to tools # If the latest message (result) from assistant is a not a tool call -> tools_condition routes to END tools_condition, ) builder.add_edge("tools", "assistant") react_graph = builder.compile()
messages = [ HumanMessage( content="What are the some movies about a girl meeting her hero?" ) ] messages = react_graph.invoke({"messages": messages}) for m in messages["messages"]: m.pretty_print()
messages = [ HumanMessage( content="What are the movies from the 90s about a girl meeting her hero?" ) ] messages = react_graph.invoke({"messages": messages}) for m in messages["messages"]: m.pretty_print()
messages = [ HumanMessage( content="How many movies are from the 90s have the rating higher than 9.1?" ) ] messages = react_graph.invoke({"messages": messages}) for m in messages["messages"]: m.pretty_print()
messages = [ HumanMessage( content="How many were movies released per year made after the highest rated movie?" ) ] messages = react_graph.invoke({"messages": messages}) for m in messages["messages"]: m.pretty_print()
Mặc dù nhúng văn bản rất tuyệt vời để tìm kiếm trong dữ liệu phi cấu trúc, nhưng chúng lại không hiệu quả khi nói đến các hoạt động có cấu trúc như lọc , sắp xếp và tổng hợp . Các tác vụ này yêu cầu các công cụ được thiết kế cho dữ liệu có cấu trúc, cung cấp độ chính xác và tính linh hoạt cần thiết để xử lý các hoạt động này. Điểm mấu chốt là việc mở rộng bộ công cụ trong hệ thống của bạn cho phép bạn giải quyết nhiều truy vấn của người dùng hơn, giúp ứng dụng của bạn mạnh mẽ và linh hoạt hơn. Kết hợp các phương pháp tiếp cận dữ liệu có cấu trúc và các kỹ thuật tìm kiếm văn bản phi cấu trúc có thể cung cấp phản hồi chính xác và phù hợp hơn, cuối cùng là nâng cao trải nghiệm của người dùng trong các ứng dụng RAG.
Để tìm hiểu thêm về chủ đề này, hãy tham gia cùng chúng tôi tại NODES 2024 vào ngày 7 tháng 11, hội nghị dành cho nhà phát triển trực tuyến miễn phí của chúng tôi về các ứng dụng thông minh, biểu đồ kiến thức và AI.