Open DiaQusNet opened 1 month ago
Well you cannot use OllamaFunctions
with AgentExecutor
due to unsupported messages type in ChatOllama
(ToolAgentAction) but you can still use OllamaFunction to call functions by doing some custom changes as shown below, let me know if you understand the code below because I have made some major changes in OllamaFunctions
to make it work. Please try it on your machine and let me know if it works. I would also suggest using React which is being powered by Ollama
:
from langchain_experimental.llms.ollama_functions import OllamaFunctions,convert_to_ollama_tool
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.pydantic_v1 import (
BaseModel,
Field
)
from langchain_core.messages import AIMessage, ToolCall
from langchain_core.outputs import ChatGeneration, ChatResult
from langchain_core.prompts import SystemMessagePromptTemplate,HumanMessagePromptTemplate
from langchain_core.pydantic_v1 import BaseModel
from langchain_community.chat_models.ollama import ChatOllama
from langchain.agents.output_parsers import ToolsAgentOutputParser
class CustomOllamaFunctions(OllamaFunctions):
def _generate(
self,
messages,
stop,
run_manager,
**kwargs,
):
functions = kwargs.get("functions", [])
if "functions" in kwargs:
del kwargs["functions"]
if "function_call" in kwargs:
functions = [
fn for fn in functions if fn["name"] == kwargs["function_call"]["name"]
]
if not functions:
raise ValueError(
"If `function_call` is specified, you must also pass a "
"matching function in `functions`."
)
del kwargs["function_call"]
response_message = ChatOllama(model="llama3", temperature=0, format="json")._generate(
messages, stop=stop, run_manager=run_manager, **kwargs
)
chat_generation_content = response_message.generations[0].text
if not isinstance(chat_generation_content, str):
raise ValueError("OllamaFunctions does not support non-string output.")
try:
parsed_chat_result = json.loads(chat_generation_content)
except json.JSONDecodeError:
raise ValueError(
f"""'{self.model}' did not respond with valid JSON.
Please try again.
Response: {chat_generation_content}"""
)
called_tool_name = parsed_chat_result["tool"]
called_tool = next(
(fn for fn in functions if fn["name"] == called_tool_name), None
)
if called_tool is None:
raise ValueError(
f"Failed to parse a function call from {self.model} output: "
f"{chat_generation_content}"
)
called_tool_arguments = parsed_chat_result["tool_input"]
response_message_with_functions = AIMessage(
content="",
tool_calls=[
ToolCall(
name=called_tool_name,
args=called_tool_arguments if called_tool_arguments else {},
id=f"call_{str(uuid.uuid4()).replace('-', '')}",
)
],
)
return ChatResult(
generations=[ChatGeneration(message=response_message_with_functions)]
)
os.environ["TAVILY_API_KEY"] = ''
class MultiplyInput(BaseModel):
"""Input for the Multiply tool."""
name = "Multiply"
description = "Multiply two integers together."
first_int: int = Field(description="The first integer number to be multiplied e.g 4")
second_int: int = Field(description="The second integer to be multiplied. e.g 7")
class SearchinWebInput(BaseModel):
"""Input for the Multiply tool."""
name = "SearchinWebInput"
description = "Use Tavily to search information in Internet."
query: str = Field(description="The query used to search in Internet. " "e.g. what is the weather in San Francisco?")
def multiply(first_int: int, second_int: int) -> int:
"""Multiply two integers together."""
return first_int * second_int
def search_in_web(query: str) -> str:
"""Use Tavily to search information in Internet."""
search = TavilySearchResults(max_results=2)
context = search.invoke(query)
result = ""
for i in context:
result += f"In site:{i['url']}, context shows:{i['content']}.\n"
return result
llm = CustomOllamaFunctions(model="llama3", temperature=0, format="json")
tools = [MultiplyInput,SearchinWebInput]
tools = [convert_to_ollama_tool(fn) for fn in tools]
system_message = """
You have access to the following tools to answer the user question:
{tools}
You must always select one of the above tools and respond with only a JSON object matching the following schema:
{{
"tool": <name of the selected tool>,
"tool_input": <parameters for the selected tool, matching the tool's JSON schema>
}}
Double check that in your json, you only have `tool` and `tool_input`.
""".format(tools=json.dumps(tools, indent=2))
prompt = ChatPromptTemplate(messages=[SystemMessagePromptTemplate.from_template(system_message,template_format='jinja2'),
HumanMessagePromptTemplate.from_template('{input}')])
all_tools = {'Multiply':multiply,'SearchinWebInput':search_in_web}
llm_with_tools = llm.bind_tools(tools=tools)
agent = prompt | llm_with_tools | ToolsAgentOutputParser()
action = agent.invoke({'input':'Using the search tool tell me Who is elon musk?'})[0]
tool_used,tool_input = action.tool,action.tool_input
print(all_tools[tool_used](**tool_input))
@keenborder786 Agent successfully returned search information with your codes. Thanks. I know the complexity is due to Ollama has not support function calling yet. So OllamaFunctions will continue to imporve? It will be easier to build agent if Ollama support function calling in the future?
@DiaQusNet Agreed therefore keep the issue open
@keenborder786 Thank you. Is it possible to use tools with llama.cpp as well? because i don't think there is anything in llama.cpp like ollamafunctions. also, i prefer llama.cpp because it is faster, almost twice, for same model.
though i'm able to use kobold.cpp for tools, however, it only works for first step "AI message" and does not go to tool message, and did not throw error like ollama or llama.cpp, so I assumed that maybe tools can be used with kobold.cpp however, they don't work like I want, or perhaps tools don't work at all and kobold just don't throw error.
update with llama.cpp, now it is not throwing error after updating llama.cpp. However, now the output is like the kobold.cpp's
(ai) D:\work\project\ai>python agent1.py
{
'agent': {
'messages': [
AIMessage(
content=" To answer your question, I'll first list the tables in the database and then query the schema of the table
that seems most relevant to find out which country's customers spent the most.\n\n```sql\nPRAGMA
table_info(sqlite_master);\n```\n\nLet's assume we have a table named `transactions` that contains information about customer
transactions. Now, I will query the schema of this table to find out which columns are available:\n\n```sql\nPRAGMA
table_info(transactions);\n```\n\nAssuming the `transactions` table has columns like `customer_id`, `amount`, and `country`, I'll
write a SQL query to find the country with the highest total spent by its customers.\n\n```sql\nSELECT country, SUM(amount) as
total_spent\nFROM transactions\nGROUP BY country\nORDER BY total_spent DESC\nLIMIT 1;\n```\n\nThis query groups all transactions by
their respective countries and calculates the total amount spent in each country. It then orders the results in descending order
based on the total spent, and limits the output to only the top result (the country with the highest total spent).",
response_metadata={
'token_usage': {'completion_tokens': 263, 'prompt_tokens': 265, 'total_tokens': 528},
'model_name': 'gemini-pro',
'system_fingerprint': None,
'finish_reason': 'stop',
'logprobs': None
},
id='run-45dbe68d-a9a6-42af-b355-a363e5c74406-0',
usage_metadata={'input_tokens': 265, 'output_tokens': 263, 'total_tokens': 528}
)
]
}
}
----
This is from the code of agent part from the page agents but i think output should be "agent"... action ... agent ... action ... until you get the correct answer
Take a look at #22339 which should have addressed this issue. The PR was approved and merged yesterday but a release is yet to be cut from it and should happen in the next few days.
In the meantime, you may try and install langchain-experimental
directly from langchain's source like this:
pip install git+https://github.com/langchain-ai/langchain.git\#egg=langchain-experimental\&subdirectory=libs/experimental
I hope this helps.
Checked other resources
Example Code