mirror of
https://github.com/openai/openai-cookbook
synced 2024-11-17 15:29:46 +00:00
184 lines
6.1 KiB
Python
184 lines
6.1 KiB
Python
from langchain.agents import (
|
|
Tool,
|
|
AgentExecutor,
|
|
LLMSingleActionAgent,
|
|
AgentOutputParser,
|
|
)
|
|
from langchain.prompts import BaseChatPromptTemplate
|
|
from langchain import SerpAPIWrapper, LLMChain
|
|
from langchain.chat_models import ChatOpenAI
|
|
from typing import List, Union
|
|
from langchain.schema import AgentAction, AgentFinish, HumanMessage
|
|
from langchain.memory import ConversationBufferWindowMemory
|
|
import openai
|
|
import re
|
|
import streamlit as st
|
|
|
|
from database import get_redis_results, get_redis_connection
|
|
from config import RETRIEVAL_PROMPT, CHAT_MODEL, INDEX_NAME, SYSTEM_PROMPT
|
|
|
|
|
|
redis_client = get_redis_connection()
|
|
|
|
|
|
def answer_user_question(query):
|
|
|
|
results = get_redis_results(redis_client, query, INDEX_NAME)
|
|
|
|
results.to_csv("results.csv")
|
|
|
|
search_content = ""
|
|
for x, y in results.head(3).iterrows():
|
|
search_content += y["title"] + "\n" + y["result"] + "\n\n"
|
|
|
|
retrieval_prepped = RETRIEVAL_PROMPT.format(
|
|
SEARCH_QUERY_HERE=query, SEARCH_CONTENT_HERE=search_content
|
|
)
|
|
|
|
retrieval = openai.ChatCompletion.create(
|
|
model=CHAT_MODEL,
|
|
messages=[{"role": "user", "content": retrieval_prepped}],
|
|
max_tokens=500,
|
|
)
|
|
|
|
# Response provided by GPT-3.5
|
|
return retrieval["choices"][0]["message"]["content"]
|
|
|
|
|
|
def answer_question_hyde(query):
|
|
|
|
hyde_prompt = """You are OracleGPT, an helpful expert who answers user questions to the best of their ability.
|
|
Provide a confident answer to their question. If you don't know the answer, make the best guess you can based on the context of the question.
|
|
|
|
User question: {USER_QUESTION_HERE}
|
|
|
|
Answer:"""
|
|
|
|
hypothetical_answer = openai.ChatCompletion.create(
|
|
model=CHAT_MODEL,
|
|
messages=[
|
|
{
|
|
"role": "user",
|
|
"content": hyde_prompt.format(USER_QUESTION_HERE=query),
|
|
}
|
|
],
|
|
)["choices"][0]["message"]["content"]
|
|
# st.write(hypothetical_answer)
|
|
results = get_redis_results(redis_client, hypothetical_answer, INDEX_NAME)
|
|
|
|
results.to_csv("results.csv")
|
|
|
|
search_content = ""
|
|
for x, y in results.head(3).iterrows():
|
|
search_content += y["title"] + "\n" + y["result"] + "\n\n"
|
|
|
|
retrieval_prepped = RETRIEVAL_PROMPT.replace("SEARCH_QUERY_HERE", query).replace(
|
|
"SEARCH_CONTENT_HERE", search_content
|
|
)
|
|
retrieval = openai.ChatCompletion.create(
|
|
model=CHAT_MODEL,
|
|
messages=[{"role": "user", "content": retrieval_prepped}],
|
|
max_tokens=500,
|
|
)
|
|
|
|
return retrieval["choices"][0]["message"]["content"]
|
|
|
|
|
|
# Set up a prompt template
|
|
class CustomPromptTemplate(BaseChatPromptTemplate):
|
|
# The template to use
|
|
template: str
|
|
# The list of tools available
|
|
tools: List[Tool]
|
|
|
|
def format_messages(self, **kwargs) -> str:
|
|
# Get the intermediate steps (AgentAction, Observation tuples)
|
|
# Format them in a particular way
|
|
intermediate_steps = kwargs.pop("intermediate_steps")
|
|
thoughts = ""
|
|
for action, observation in intermediate_steps:
|
|
thoughts += action.log
|
|
thoughts += f"\nObservation: {observation}\nThought: "
|
|
# Set the agent_scratchpad variable to that value
|
|
kwargs["agent_scratchpad"] = thoughts
|
|
# Create a tools variable from the list of tools provided
|
|
kwargs["tools"] = "\n".join(
|
|
[f"{tool.name}: {tool.description}" for tool in self.tools]
|
|
)
|
|
# Create a list of tool names for the tools provided
|
|
kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools])
|
|
formatted = self.template.format(**kwargs)
|
|
return [HumanMessage(content=formatted)]
|
|
|
|
|
|
class CustomOutputParser(AgentOutputParser):
|
|
def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
|
|
# Check if agent should finish
|
|
if "Final Answer:" in llm_output:
|
|
return AgentFinish(
|
|
# Return values is generally always a dictionary with a single `output` key
|
|
# It is not recommended to try anything else at the moment :)
|
|
return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
|
|
log=llm_output,
|
|
)
|
|
# Parse out the action and action input
|
|
regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
|
|
match = re.search(regex, llm_output, re.DOTALL)
|
|
if not match:
|
|
raise ValueError(f"Could not parse LLM output: `{llm_output}`")
|
|
action = match.group(1).strip()
|
|
action_input = match.group(2)
|
|
# Return the action and action input
|
|
return AgentAction(
|
|
tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output
|
|
)
|
|
|
|
|
|
def initiate_agent(tools):
|
|
prompt = CustomPromptTemplate(
|
|
template=SYSTEM_PROMPT,
|
|
tools=tools,
|
|
# This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically
|
|
# The history template includes "history" as an input variable so we can interpolate it into the prompt
|
|
input_variables=["input", "intermediate_steps", "history"],
|
|
)
|
|
|
|
# Initiate the memory with k=2 to keep the last two turns
|
|
# Provide the memory to the agent
|
|
memory = ConversationBufferWindowMemory(k=2)
|
|
|
|
output_parser = CustomOutputParser()
|
|
|
|
llm = ChatOpenAI(temperature=0)
|
|
|
|
# LLM chain consisting of the LLM and a prompt
|
|
llm_chain = LLMChain(llm=llm, prompt=prompt)
|
|
|
|
tool_names = [tool.name for tool in tools]
|
|
agent = LLMSingleActionAgent(
|
|
llm_chain=llm_chain,
|
|
output_parser=output_parser,
|
|
stop=["\nObservation:"],
|
|
allowed_tools=tool_names,
|
|
)
|
|
|
|
agent_executor = AgentExecutor.from_agent_and_tools(
|
|
agent=agent, tools=tools, verbose=True, memory=memory
|
|
)
|
|
|
|
return agent_executor
|
|
|
|
|
|
def ask_gpt(query):
|
|
response = openai.ChatCompletion.create(
|
|
model=CHAT_MODEL,
|
|
messages=[
|
|
{
|
|
"role": "user",
|
|
"content": "Please answer my question.\nQuestion: {}".format(query),
|
|
}
|
|
],
|
|
temperature=0,
|
|
)
|
|
return response["choices"][0]["message"]["content"]
|