Tutorial 5
Let’s build a conversational agent
import datetime
import requests
import wikipedia
from pydantic import BaseModel, Field
from langchain.tools import tool
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.tools.render import format_tool_to_openai_function
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
from langchain.prompts import MessagesPlaceholder
from langchain.agents.format_scratchpad import format_to_openai_functions
from langchain.schema.agent import AgentFinish
from langchain.schema.runnable import RunnablePassthrough
from langchain.agents import AgentExecutor
from langchain.memory import ConversationBufferMemory
import panel as pn
pn.extension()
import panel as pn
import param# Define the input schema
class OpenMeteoInput(BaseModel):
latitude: float = Field(..., description="Latitude of the location to fetch weather data for")
longitude: float = Field(..., description="Longitude of the location to fetch weather data for")
@tool(args_schema=OpenMeteoInput)
def get_current_temperature(latitude: float, longitude: float) -> dict:
"""Fetch current temperature for given coordinates."""
BASE_URL = "https://api.open-meteo.com/v1/forecast"
# Parameters for the request
params = {
'latitude': latitude,
'longitude': longitude,
'hourly': 'temperature_2m',
'forecast_days': 1,
}
# Make the request
response = requests.get(BASE_URL, params=params)
if response.status_code == 200:
results = response.json()
else:
raise Exception(f"API Request failed with status code: {response.status_code}")
current_utc_time = datetime.datetime.utcnow()
time_list = [datetime.datetime.fromisoformat(time_str.replace('Z', '+00:00')) for time_str in results['hourly']['time']]
temperature_list = results['hourly']['temperature_2m']
closest_time_index = min(range(len(time_list)), key=lambda i: abs(time_list[i] - current_utc_time))
current_temperature = temperature_list[closest_time_index]
return f'The current temperature is {current_temperature}°C'@tool
def search_wikipedia(query: str) -> str:
"""Run Wikipedia search and get page summaries."""
page_titles = wikipedia.search(query)
summaries = []
for page_title in page_titles[: 3]:
try:
wiki_page = wikipedia.page(title=page_title, auto_suggest=False)
summaries.append(f"Page: {page_title}\nSummary: {wiki_page.summary}")
except (
self.wiki_client.exceptions.PageError,
self.wiki_client.exceptions.DisambiguationError,
):
pass
if not summaries:
return "No good Wikipedia Search Result was found"
return "\n\n".join(summaries)functions = [format_tool_to_openai_function(f) for f in tools]
model = ChatOpenAI(temperature=0).bind(functions=functions)
prompt = ChatPromptTemplate.from_messages([
("system", "You are helpful but sassy assistant"),
("user", "{input}"),
])
chain = prompt | model | OpenAIFunctionsAgentOutputParser()def run_agent(user_input):
intermediate_steps = []
while True:
result = chain.invoke({
"input": user_input,
"agent_scratchpad": format_to_openai_functions(intermediate_steps)
})
if isinstance(result, AgentFinish):
return result
tool = {
"search_wikipedia": search_wikipedia,
"get_current_temperature": get_current_temperature,
}[result.tool]
observation = tool.run(result.tool_input)
intermediate_steps.append((result, observation))def run_agent(user_input):
intermediate_steps = []
while True:
result = agent_chain.invoke({
"input": user_input,
"intermediate_steps": intermediate_steps
})
if isinstance(result, AgentFinish):
return result
tool = {
"search_wikipedia": search_wikipedia,
"get_current_temperature": get_current_temperature,
}[result.tool]
observation = tool.run(result.tool_input)
intermediate_steps.append((result, observation))Entering new AgentExecutor chain…
Invoking: search_wikipedia with {'query': 'langchain'}
Page: LangChain Summary: LangChain is a framework designed to simplify the creation of applications using large language models (LLMs). As a language model integration framework, LangChain’s use-cases largely overlap with those of language models in general, including document analysis and summarization, chatbots, and code analysis.
Page: Prompt engineering Summary: Prompt engineering is the process of structuring text that can be interpreted and understood by a generative AI model. A prompt is natural language text describing the task that an AI should perform.A prompt for a text-to-text model can be a query such as “what is Fermat’s little theorem?”, a command such as “write a poem about leaves falling”, a short statement of feedback (for example, “too verbose”, “too formal”, “rephrase again”, “omit this word”) or a longer statement including context, instructions, and input data. Prompt engineering may involve phrasing a query, specifying a style, providing relevant context or assigning a role to the AI such as “Act as a native French speaker”. A prompt may include a few examples for a model to learn from, such as “maison -> house, chat -> cat, chien ->”, an approach called few-shot learning.When communicating with a text-to-image or a text-to-audio model, a typical prompt is a description of a desired output such as “a high-quality photo of an astronaut riding a horse” or “Lo-fi slow BPM electro chill with organic samples”. Prompting a text-to-image model may involve adding, removing, emphasizing and re-ordering words to achieve a desired subject, style, layout, lighting, and aesthetic.
Page: Sentence embedding Summary: In natural language processing, a sentence embedding refers to a numeric representation of a sentence in the form of a vector of real numbers which encodes meaningful semantic information.State of the art embeddings are based on the learned hidden layer representation of dedicated sentence transformer models. BERT pioneered an approach involving the use of a dedicated [CLS] token prepended to the beginning of each sentence inputted into the model; the final hidden state vector of this token encodes information about the sentence and can be fine-tuned for use in sentence classification tasks. In practice however, BERT’s sentence embedding with the [CLS] token achieves poor performance, often worse than simply averaging non-contextual word embeddings. SBERT later achieved superior sentence embedding performance by fine tuning BERT’s [CLS] token embeddings through the usage of a siamese neural network architecture on the SNLI dataset. Other approaches are loosely based on the idea of distributional semantics applied to sentences. Skip-Thought trains an encoder-decoder structure for the task of neighboring sentences predictions. Though this has been shown to achieve worse performance than approaches such as InferSent or SBERT. An alternative direction is to aggregate word embeddings, such as those returned by Word2vec, into sentence embeddings. The most straightforward approach is to simply compute the average of word vectors, known as continuous bag-of-words (CBOW). However, more elaborate solutions based on word vector quantization have also been proposed. One such approach is the vector of locally aggregated word embeddings (VLAWE), which demonstrated performance improvements in downstream text classification tasks. LangChain is a framework designed to simplify the creation of applications using large language models (LLMs). It is a language model integration framework that can be used for various tasks such as document analysis and summarization, chatbots, and code analysis. LangChain helps developers leverage the power of language models in their applications.
Finished chain.
{‘input’: ‘what is langchain?’, ‘output’: ‘LangChain is a framework designed to simplify the creation of applications using large language models (LLMs). It is a language model integration framework that can be used for various tasks such as document analysis and summarization, chatbots, and code analysis. LangChain helps developers leverage the power of language models in their applications.’}
Entering new AgentExecutor chain… Hello Bob! How can I assist you today?
Finished chain.
{‘input’: ‘my name is bob’, ‘output’: ‘Hello Bob! How can I assist you today?’}
Entering new AgentExecutor chain… Hello Bob! How can I assist you today?
Finished chain.
{‘input’: ‘my name is bob’, ‘chat_history’: [HumanMessage(content=‘my name is bob’), AIMessage(content=‘Hello Bob! How can I assist you today?’)], ‘output’: ‘Hello Bob! How can I assist you today?’}
Entering new AgentExecutor chain… Your name is Bob.
Finished chain.
{‘input’: ‘whats my name’, ‘chat_history’: [HumanMessage(content=‘my name is bob’), AIMessage(content=‘Hello Bob! How can I assist you today?’), HumanMessage(content=‘whats my name’), AIMessage(content=‘Your name is Bob.’)], ‘output’: ‘Your name is Bob.’}
Entering new AgentExecutor chain…
Invoking: get_current_temperature with {'latitude': 48.7758, 'longitude': 9.1829}
The current temperature is 9.5°CThe current temperature in Stuttgart is 9.5°C.
Finished chain.
{‘input’: ‘whats the weather in stuttgart?’, ‘chat_history’: [HumanMessage(content=‘my name is bob’), AIMessage(content=‘Hello Bob! How can I assist you today?’), HumanMessage(content=‘whats my name’), AIMessage(content=‘Your name is Bob.’), HumanMessage(content=‘whats the weather in stuttgart?’), AIMessage(content=‘The current temperature in Stuttgart is 9.5°C.’)], ‘output’: ‘The current temperature in Stuttgart is 9.5°C.’}
class cbfs(param.Parameterized):
def __init__(self, tools, **params):
super(cbfs, self).__init__( **params)
self.panels = []
self.functions = [format_tool_to_openai_function(f) for f in tools]
self.model = ChatOpenAI(temperature=0).bind(functions=self.functions)
self.memory = ConversationBufferMemory(return_messages=True,memory_key="chat_history")
self.prompt = ChatPromptTemplate.from_messages([
("system", "You are helpful but sassy assistant"),
MessagesPlaceholder(variable_name="chat_history"),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad")
])
self.chain = RunnablePassthrough.assign(
agent_scratchpad = lambda x: format_to_openai_functions(x["intermediate_steps"])
) | self.prompt | self.model | OpenAIFunctionsAgentOutputParser()
self.qa = AgentExecutor(agent=self.chain, tools=tools, verbose=False, memory=self.memory)
def convchain(self, query):
if not query:
return
inp.value = ''
result = self.qa.invoke({"input": query})
self.answer = result['output']
self.panels.extend([
pn.Row('User:', pn.pane.Markdown(query, width=450)),
pn.Row('ChatBot:', pn.pane.Markdown(self.answer, width=450, styles={'background-color': '#F6F6F6'}))
])
return pn.WidgetBox(*self.panels, scroll=True)
def clr_history(self,count=0):
self.chat_history = []
return cb = cbfs(tools)
inp = pn.widgets.TextInput( placeholder='Enter text here…')
conversation = pn.bind(cb.convchain, inp)
tab1 = pn.Column(
pn.Row(inp),
pn.layout.Divider(),
pn.panel(conversation, loading_indicator=True, height=400),
pn.layout.Divider(),
)
dashboard = pn.Column(
pn.Row(pn.pane.Markdown('# QnA_Bot')),
pn.Tabs(('Conversation', tab1))
)
dashboardCongratulations! You have completed this tutorial 👍
Next, you may want to go back to the lab’s website
Jan Kirenz