from langchain_experimental.llms.ollama_functions import OllamaFunctions
from langchain_core.messages import HumanMessage
from langchain_core.messages import AIMessage
from langchain_community.tools.ddg_search.tool import DuckDuckGoSearchRun
import json

search = DuckDuckGoSearchRun()

model = OllamaFunctions(model="mistral:7b-instruct-v0.3-q5_K_M", format="json")

model2 = OllamaFunctions(model="mistral:7b-instruct-v0.3-q5_K_M", format="json")

model = model.bind(
    functions=[
        {
            "name": "get_current_weather",
            "description": "Get the current weather in a given location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, " "e.g. San Francisco, CA",
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                    },
                },
                "required": ["location"],
            },
        }
    ]
)

# 使用 JsonOutputToolsParser 来试图解析结果
# https://python.langchain.com/docs/modules/model_io/chat/function_calling#binding-functions

# tool_chain = model | JsonOutputToolsParser(name="get_current_weather")
# result = tool_chain.invoke("what is the weather in Boston?")
# print(result)
# 这个方法是废的,因为这是针对OpenAI的
def get_current_weather(location):
    res = "真的是在被call哦,get_current_weather:"+location
    print(res)
    result = search.run("what is the current weather in "+location)
    print("==========================search的返回==================")
    print(result)
    return result


def chat(input):
    result=model.invoke(input)
    print("==========================模型的返回==================")
    print(result)
    print("==========================模型的返回==================")

    cur_tool_calls = result.tool_calls
    nums_of_tool_calls = len(cur_tool_calls)

    if nums_of_tool_calls!=0:
        # 判断字符串是否是可调用的函数
        #https://stackoverflow.com/questions/59912800/how-do-i-check-if-a-string-is-a-callable-name-in-python
        function_call_name = cur_tool_calls[0]['name']
        function_call_arguments = cur_tool_calls[0]['args']
        print(function_call_arguments)

        if callable(globals()[function_call_name]):
            print(f"{function_call_name} 是当前文件中可调用的函数。")
            location = function_call_arguments["location"]
            print("location:",location)
            res = eval(function_call_name)(location)
            result = model2.invoke(res+"\n\n 根据上下文,并且使用celsius作为单位,使用中文来总结,这个城市的天气是怎样的?")
            print("==========================最终的结论==================")
            print("\n")
            print(result.content)
            print("\n")
        else:
            print(f"{function_call_name} 不是当前文件中可调用的函数。")
    return result


    # 现在这个事情里有一个致命的缺陷就是,它一定以及肯定会启动一个tool call
    # 这太二逼了只能说

chat_history = []

while True:
    user_input = input("我:")
    myNewMsg = HumanMessage(content=user_input)
    chat_history.append(myNewMsg)
    resp = chat(chat_history)
    print("==============机器人的回复==================")
    print(resp.content)
    print("==============机器人的回复==================")
    respAImsg = AIMessage(content=resp.content)
    chat_history.append(respAImsg)
    print(chat_history)
    print(len(chat_history))

#https://python.langchain.com/v0.2/docs/tutorials/chatbot/
#然后看着这令人蛋疼的代码去一点点构建带history的对话机器人吧

首先给出调用的代码,这里利用了之前调用豆包的服务器端的代码的技巧

lanchain写的哪个代码啊,又臭又长,十分抽象

 https://github.com/lemonhall/RMMZ_GPT_movement/blob/main/server/main.py 

其实llm能处理对话的本质就是那个数组而已

DEFAULT_SYSTEM_TEMPLATE = """You have access to the following tools:

{tools}

你可以选择以上的其中之一的工具来回答用户,但如果用户的query与所使用的工具描述无关,则不必call任何工具。

比如:用户向你问好,你当然也要回答你好啊,而别使用工具。

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>
}}
"""  # noqa: E501

DEFAULT_SYSTEM_TEMPLATE_BAK = """You have access to the following tools:

{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>
}}
"""  # noqa: E501


然后我发现了为什么OllamaFunctions如此霸道的原因了,就是它原来的提示词,强制让llm必须使用其中一个工具

这就让这个llm变成了纯纯的一个天气查询器了

我需要解决的,就是何时,以及要不要调用的一个文本分类问题,你倒好,直接给我强制调用了,搞得llm像个智障

修改之后对话会变得较为正常,但文本分类问题依旧存在

其实还是有点智障的

但起码可以同时聊天气和其它的话题并保持上下文

否则成了一个专用的天气查询工具有个屁用啊

所以langchain给的那些个例子,可以说就是没用的东西


所以这真的很糟糕

工具调用本质上需要一整套的文本分类去调教,这才是最可怕的地方