ragalicious-app / utils /graph_chains.py
mickkhaw's picture
Merge changes from main
bb8f798
raw
history blame
7.95 kB
from langchain.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from .db import shortlisted_recipes_to_string
def get_grader_chain(llm_model):
class GradeRecipes(BaseModel):
"""Binary score for relevance check on retrieved documents."""
binary_score: str = Field(
description="Document representing recipes are generally relevant to the criteria in the question, 'yes' or 'no'"
)
integer_score: int = Field(
description="Degree to which Documents are relevant to the question, integers from 1 to 100"
)
# LLM with function call
structured_llm_grader = llm_model.with_structured_output(GradeRecipes)
# Prompt
system = """You are a grader assessing relevance of a retrieved cooking recipe document to a user question. \n
It does not need to be a stringent test. The goal is to filter out completely erroneous or irrelevant retrievals. \n
If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant. \n
Give a binary score 'yes' or 'no' score to indicate whether the recipe document is relevant to the question.
Also give a integer score from 1 to 100 to indicate the degree to which the recipe document is relevant to the question.
"""
grade_prompt = ChatPromptTemplate.from_messages(
[
("system", system),
("human", "Retrieved recipe document: \n\n {document} \n\n User question: {question}"),
]
)
retrieval_grader = grade_prompt | structured_llm_grader
return retrieval_grader
def get_recipe_url_extractor_chain(llm_model):
class RecipeUrlsSchema(BaseModel):
urls: list[str] = Field(description="A list of urls pointing to specific recipes")
structured_llm_grader = llm_model.with_structured_output(RecipeUrlsSchema)
pydantic_parser = PydanticOutputParser(pydantic_object=RecipeUrlsSchema)
format_instructions = pydantic_parser.get_format_instructions()
RECIPE_SEARCH_PROMPT = """
Your goal is to understand and parse out the full http urls in the context corresponding to each recipe.
{format_instructions}
Context:
{context}
"""
prompt = ChatPromptTemplate.from_template(
template=RECIPE_SEARCH_PROMPT, partial_variables={"format_instructions": format_instructions}
)
retriever = prompt | structured_llm_grader
return retriever
def get_recipe_selection_chain(llm_model):
class RecipeSelectionSchema(BaseModel):
asking_for_recipe_suggestions: str = Field(
description="Whether the User Question is asking for recipe suggestions based on some criteria, 'yes' or 'no'"
)
referring_to_specific_recipe: str = Field(
description="Whether the User Question is asking about one specific recipe (but NOT asking to just show a specific recipe), 'yes' or 'no'"
)
referring_to_shortlisted_recipes: str = Field(
description="Whether the User Question is asking generally about the 3 shortlisted recipes, 'yes' or 'no'"
)
show_specific_recipe: str = Field(
description="Whether the User Question is asking asking to show a specific recipe, 'yes' or 'no'"
)
specific_recipe_url: str = Field(
description="URL of the specific recipe that the User Question is directed to, if any "
)
# LLM with function call
structured_llm_grader = llm_model.with_structured_output(RecipeSelectionSchema)
pydantic_parser = PydanticOutputParser(pydantic_object=RecipeSelectionSchema)
format_instructions = pydantic_parser.get_format_instructions()
# Prompt
RECIPE_SELECTION_PROMPT = """
You are a helpful assistant attempting to categorize the nature of the User question
based on the last message sent to he user and the provided context.
{format_instructions}
User Question:
{question}
Last message provided to the user:
{last_message}
Context:
{context}
"""
prompt = ChatPromptTemplate.from_template(
template=RECIPE_SELECTION_PROMPT, partial_variables={"format_instructions": format_instructions}
)
chain = prompt | structured_llm_grader
return chain
def get_question_type_chain(llm_model):
class RecipeSelectionChanceSchema(BaseModel):
asking_for_recipe_suggestions: int = Field(
description="The likelihood / chance that the User Question is asking for recipe suggestions based on some criteria, integers from 1 to 100"
)
referring_to_specific_recipe: int = Field(
description="The likelihood / chance that the User Question is asking specific questions about a single specific recipe, integers from 1 to 100"
)
referring_to_shortlisted_recipes: int = Field(
description="The likelihood / chance that the User Question is asking generally about more than one recipe provided in the last message, integers from 1 to 100"
)
show_specific_recipe: int = Field(
description="The likelihood / chance that the User Question is asking to show the full recipe for a specific recipe, integers from 1 to 100"
)
send_text: int = Field(
description="The likelihood / chance that the User Question is to send a SMS or text, integers from 1 to 100"
)
specific_recipe_url: str = Field(
description="URL of the specific recipe that the User Question is directed to, if any "
)
# LLM with function call
structured_llm_grader = llm_model.with_structured_output(RecipeSelectionChanceSchema)
pydantic_parser = PydanticOutputParser(pydantic_object=RecipeSelectionChanceSchema)
format_instructions = pydantic_parser.get_format_instructions()
# Prompt
RECIPE_SELECTION_PROMPT = """
You are a helpful assistant attempting to categorize the nature of the User question
based on the last message sent to he user and the provided context.
Note that if there were recipe suggesetions in the last message provided to the user,
it is highly likely that the user is asking questions referring to shortlisted recipes.
If the last message was a full single recipe, it is generally likely that the user
is asking questions referring to specific recipe.
If the user is asking to show the full recipe, it is highly likely that they are asking
to show a specific recipe and less likely that they are asking for anything else.
{format_instructions}
User Question:
{question}
Last message provided to the user:
{last_message}
Context:
{context}
"""
prompt = ChatPromptTemplate.from_template(
template=RECIPE_SELECTION_PROMPT, partial_variables={"format_instructions": format_instructions}
)
chain = prompt | structured_llm_grader
return chain
def get_selected_recipe(llm_model, question, shortlisted_recipes, messages):
selected_recipe = None
recipe_selection_chain = get_recipe_selection_chain(llm_model)
recipe_selection_response = recipe_selection_chain.invoke(
{
"question": question,
"context": shortlisted_recipes_to_string(shortlisted_recipes),
"last_message": messages[-1] if messages else "",
}
)
if (
recipe_selection_response.referring_to_specific_recipe == "yes"
and recipe_selection_response.specific_recipe_url
):
selected_recipe = next(
(r for r in shortlisted_recipes if r["url"] == recipe_selection_response.specific_recipe_url)
)
return selected_recipe