基于阿里通义千问实现ReAct

问题背景

目前,国内大模型基本上都还不支持类似openai的自定义函数绑定,即:llm.bindtools方法,也就无法实现自定义ReAct Agent,如果要使用国内大模型,需要考虑自己实现。本文基于国内的阿里通义千问,实现了类似OpenAI 的ReAct Agent效果。

实现思路
  1. 使用通义千问的原生接口调用方法,以千问大模型支持的方式来调用工具
  2. 使用langchain的react agent提示词模板,传给通义大模型,实现大模型的ReAct推理方式
  3. 解析通义大模型的返回
  4. 根据大模型返回内容,判断是否已经拿到了最终推理结果,如果没有,就继续递归调用,如果有,返回最终推理结果。
实现代码

以下是实现代码

from langchain_core.output_parsers import JsonOutputParser
from langchain_core.tools import tool
from operator import itemgetter
from dashscope import Generation
from langchain_core.utils.function_calling import convert_to_openai_tool


def get_model_output(**kwargs):
    """通过ReAct递归推理,获取最终答案"""

    question = kwargs.get('question')
    tools = kwargs.get('tools')
    msgs = kwargs.get('msgs', [])
    verbose = kwargs.get('verbose', False)
    n = kwargs.get('n', 1)

    tool_names = [tool.name for tool in tools]
    system_prompt = "Answer the following questions as best you can. You have access to the following tools:"
    react_prompt = f"""
    Use the following format:
    Question: the input question you must answer
    Thought: you should always think about what to do
    Action: the action to take, should be one of [{tool_names}]
    Action Input: the input to the action
    Observation: the result of the action
    ... (this Thought/Action/Action Input/Observation can repeat N times)
    Thought: I now know the final answer
    Final Answer: the final answer to the original input question

    Begin!

    Question: {question}
    Thought:"""

    # 初始化通义大模型输入消息
    if not msgs:
        msgs = [{"role": "system", "content": system_prompt}, {"role": "user", "content": react_prompt}]

    # 调用通义大模型的原生接口获取返回
    llm_response = Generation.call(
        model="qwen-turbo",
        messages=msgs,
        tools=[convert_to_openai_tool(tool) for tool in tools],
        result_format='message'
    )

    res_content = llm_response.output.choices[0].message["content"]
    if verbose == True:
        print("第{}轮ReAct:{}".format(n, res_content))

    if res_content.find("Action Input:") != -1:
        n = n + 1
        # 没拿到Final answer就根据模型店返回继续调用工具
        res_chain = call_tool(res_content, tools)

        # 重新生成msgs
        react_prompt = react_prompt + res_content + "\nObservation:" + str(res_chain) + "\n"
        msgs = [{"role": "system", "content": system_prompt}, {"role": "user", "content": react_prompt}]

        # 递归调用本方法
        res_content = get_model_output(question=question, tools=tools, n=n, msgs=msgs, verbose=verbose)

    return res_content


def call_tool(res_content, tools):
    """根据大模型返回内容和给定的工具集,执行工具调用,返回工具输出内容"""

    # 创建一个工具MAP
    tool_map = {tool.name: tool for tool in tools}

    # 创建JSON输出解析器
    parser = JsonOutputParser()

    # 从大模型回复中解析本次调用的工具名称
    tool_call = str(res_content.split("Action:")[1].split("Action Input:")[0]).strip()

    # 从大模型回复中解析本次调用工具的入参
    tool_call_arguments = str(res_content.split("Action:")[1].split("Action Input:")[1]).strip()

    # 组装本次工具调用的schema
    model_output = {"name": tool_call, "arguments": parser.parse(tool_call_arguments)}
    chosen_tool = tool_map[model_output["name"]]

    # 创建工具调用链
    chain = itemgetter("arguments") | chosen_tool

    # 返回工具调用链的计算结果
    return chain.invoke(model_output)


@tool
def add(first_int: int, second_int: int) -> int:
    """将两个数相加。"""
    return first_int + second_int


@tool
def minus(first_int: int, second_int: int) -> int:
    """将两个数相减"""
    return first_int - second_int


@tool
def multiply(first_int: int, second_int: int) -> int:
    """将两个数相乘"""
    return first_int * second_int


@tool
def exponentiate(base: int, exponent: int) -> int:
    """对底数求指数幂"""
    return base ** exponent

# 创建工具集
tools = [add, minus, multiply, exponentiate]
question = "4加22乘以10的结果3次方减去10086等于多少?"

react_result = get_model_output(
    question=question,
    tools=tools,
    verbose=True
)

# print(react_result)

以下是开启调试结果后,每次大模型的推理、行动输出

第1轮ReAct: 我们需要按照数学中的运算顺序来解决这个问题:先进行乘法和指数运算,然后做加法,最后做减法。所以,我们需要分别计算22乘以10的结果,这个结果的3次方,然后从得到的结果中减去10086。我需要使用`multiply`工具来计算乘法,`exponentiate`工具来计算指数,以及`minus`工具来进行最后的减法。
    Action: multiply
    Action Input: {"first_int": 22, "second_int": 10}
第2轮ReAct:Thought: 现在我得到了22乘以10的结果。下一步,我需要计算这个结果的3次方,再进行减法操作。我会用`exponentiate`工具来计算220的三次方。
	Action: exponentiate
	Action Input: {"base": 220, "exponent": 3}
第3轮ReAct:Thought: 我已经得到了220的三次方。接下来,我需要将这个结果与4相加,然后减去10086。我将使用`add`和`minus`工具来进行这些运算。
	Action: add
	Action Input: {"first_int": 10648000, "second_int": 4}
第4轮ReAct:Thought: 现在我知道了10648004的结果。接下来我需要从这个结果中减去10086。
	Action: minus
	Action Input: {"first_int": 10648004, "second_int": 10086}
第5轮ReAct:Thought: 我现在知道了最终答案。
	Final Answer: 4加22乘以10的结果3次方减去10086等于10637918。
  • 13
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值