👆 👆👆 欢迎关注 👆👆👆
React Agent 是如何工作的?
- “Reason Only”(向内求索) :COT 类型的 Prompt,希望模型一步一步思考去解决问题。这类型 Prompt 非常擅长逻辑推理,能将问题拆解成一个一个小问题,逐一解决。但是没有灵活运用外部工具和外部知识的能力。向内求索型人格 :像一个思维严谨,思考问题很深入,但是不够开放,不喜欢接触新知识的人。
- “Act Only”(向外探索) :工具使用 Prompt,非常擅长借助外部工具和外部知识去解决问题,但是逻辑推理能力不强。向外探索人格 :像一个思维开放,非常喜欢探索新知识,尝试新工具,但是思想深度不够的人。
- “ReAct”(内外兼修) :就是将“Reason Only”(向内求索)和“Act Only”(向外探索)这两者相结合,
将严谨思维和探索的心态结合起来,优势互补,成为一个内外兼修的人 。
直观的理解就是,ReAct 就是结合了的 推理能力 + 工具使用能力/外部知识能力,利用大模型去解决难问题。
在 ReAct Prompting 中,使用 LLMs 以交错的方式生成推理轨迹和特定于任务的操作,从而实现两者之间更大的协同作用:
- 推理跟踪帮助模型诱导、跟踪和更新行动计划以及处理异常。
- 执行操作与外部源(例如知识库或环境)进行交互,以获取相关信息。
接下来深入研究 ReAct 框架的代码实现的工作原理,总共分三步。
第一步:
定义 Prompt 模版
TOOL_DESC = """{name\_for\_model}: Call this tool to interact with the {name\_for\_human} API. What is the {name\_for\_human} API useful for? {description\_for\_model} Parameters: {parameters} Format the arguments as a JSON object."""
REACT_PROMPT = """Answer the following questions as best you can. You have access to the following tools:
{tool\_descs}
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 be repeated zero or more times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: {query}"""
工具构建函数
def build\_planning\_prompt(TOOLS, query):
tool_descs = []
tool_names = []
for info in TOOLS:
tool_descs.append(
TOOL_DESC.format(
name_for_model=info['name\_for\_model'],
name_for_human=info['name\_for\_human'],
description_for_model=info['description\_for\_model'],
parameters=json.dumps(
info['parameters'], ensure_ascii=False),
)
)
tool_names.append(info['name\_for\_model'])
tool_descs = '\n\n'.join(tool_descs)
tool_names = ','.join(tool_names)
prompt = REACT_PROMPT.format(tool_descs=tool_descs, tool_names=tool_names, query=query)
return prompt
prompt_1 = build_planning_prompt(TOOLS[0:1], query="加拿大2023年人口统计数字是多少?")
print(prompt_1)
调用函数查看 response 结果
Answer the following questions as best you can. You have access to the following tools:
Search: Call this tool to interact with the google search API. What is the google search API useful for? useful for when you need to answer questions about current events. Parameters: [{"name": "query", "type": "string", "description": "search query of google", "required": true}] Format the arguments as a JSON object.
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 [Search]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: 加拿大2023年人口统计数字是多少?
初始化模型
# 国内连 hugginface 网络不好,这段代码可能需要多重试
checkpoint = "Qwen/Qwen-7B-Chat"
TOKENIZER = AutoTokenizer.from_pretrained(checkpoint, trust_remote_code=True)
MODEL = AutoModelForCausalLM.from_pretrained(checkpoint, device_map="auto", trust_remote_code=True).eval()
MODEL.generation_config = GenerationConfig.from_pretrained(checkpoint, trust_remote_code=True)
MODEL.generation_config.do_sample = False # greedy
设置 "Observation" 为 stop word
stop = ["Observation:", "Observation:\n"]
react_stop_words_tokens = [TOKENIZER.encode(stop_) for stop_ in stop]
response_1, _ = MODEL.chat(TOKENIZER, prompt_1, history=None, stop_words_ids=react_stop_words_tokens)
print(response_1)
模型在预测到要生成的下一个词是 "Observation" 时马上停止生成,此时这个 prompt 后会生成如下的结果
Thought: 我应该使用搜索工具帮助我完成任务。search api能完成搜索的任务。
Action: Search
Action Input: {"query": "加拿大 2023年人口统计数字"}
Observation:
第二步:
从输出中解析需要使用的工具和入参
def parse\_latest\_plugin\_call(text: str) -> Tuple[str, str]:
i = text.rfind('\nAction:')
j = text.rfind('\nAction Input:')
k = text.rfind('\nObservation:')
if 0 <= i < j: # If the text has `Action` and `Action input`,
if k < j: # but does not contain `Observation`,
# then it is likely that `Observation` is ommited by the LLM,
# because the output text may have discarded the stop word.
text = text.rstrip() + '\nObservation:' # Add it back.
k = text.rfind('\nObservation:')
if 0 <= i < j < k:
plugin_name = text[i + len('\nAction:'):j].strip()
plugin_args = text[j + len('\nAction Input:'):k].strip()
return plugin_name, plugin_args
return '', ''
调用对应工具
def use\_api(tools, response):
use_toolname, action_input = parse_latest_plugin_call(response)
if use_toolname == "":
return "no tool founds"
used_tool_meta = list(filter(lambda x: x["name\_for\_model"] == use_toolname, tools))
if len(used_tool_meta) == 0:
return "no tool founds"
api_output = used_tool_meta[0]["tool\_api"](action_input)
return api_output
api_output = use_api(TOOLS, response_1)
print(api_output)
工具返回的结果
根据加拿大统计局预测,加拿大人口今天(2023年6月16日)预计将超过4000万。 联邦统计局使用模型来实时估计加拿大的人口,该计数模型预计加拿大人口将在北美东部时间今天下午3点前达到4000万。 加拿大的人口增长率目前为2.7%。
第三步:
根据工具返回结果继续调用模型
拼接上述返回答案,形成新的prompt,并获得生成最终结果
prompt_2 = prompt_1 + response_1 + ' ' + api_output
stop = ["Observation:", "Observation:\n"]
react_stop_words_tokens = [TOKENIZER.encode(stop_) for stop_ in stop]
response_2, _ = MODEL.chat(TOKENIZER, prompt_2, history=None, stop_words_ids=react_stop_words_tokens)
print(prompt_2, response_2)
续写,生成最终答案
按照 ReAct 格式继续生成 Thought 和 Final Answer,得到最终答案。
Answer the following questions as best you can. You have access to the following tools:
Search: Call this tool to interact with the google search API. What is the google search API useful for? useful for when you need to answer questions about current events. Parameters: [{"name": "query", "type": "string", "description": "search query of google", "required": true}] Format the arguments as a JSON object.
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 [Search]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: 加拿大2023年人口统计数字是多少?Thought: 我应该使用搜索工具帮助我完成任务。search api能完成搜索的任务。
Action: Search
Action Input: {"query": "加拿大 2023年人口统计数字"}
Observation: 根据加拿大统计局预测,加拿大人口今天(2023年6月16日)预计将超过4000万。 联邦统计局使用模型来实时估计加拿大的人口,该计数模型预计加拿大人口将在北美东部时间今天下午3点前达到4000万。 加拿大的人口增长率目前为2.7%。
Thought: I now know the final answer.
Final Answer: 加拿大2023年人口统计数字预计为4000万。
总结
def main(query, choose\_tools):
# 组织 prompt
prompt = build_planning_prompt(choose_tools, query)
# 设置 stop word,模型停止生成
stop = ["Observation:", "Observation:\n"]
react_stop_words_tokens = [TOKENIZER.encode(stop_) for stop_ in stop]
response, _ = MODEL.chat(TOKENIZER, prompt, history=None, stop_words_ids=react_stop_words_tokens)
while "Final Answer:" not in response: # 出现final Answer时结束
api_output = use_api(choose_tools, response) # 抽取入参并执行api
api_output = str(api_output) # 部分api工具返回结果非字符串格式需进行转化后输出
if "no tool founds" == api_output:
break
print("\033[32m" + response + "\033[0m" + "\033[34m" + ' ' + api_output + "\033[0m")
prompt = prompt + response + ' ' + api_output # 合并api输出
response, _ = MODEL.chat(TOKENIZER, prompt, history=None, stop_words_ids=react_stop_words_tokens) # 继续生成
print("\033[32m" + response + "\033[0m")
- 核心思想是利用了大模型的续写能力,按照 ReAct 的步骤进行续写。
- 设置停止词,当生成与停止词一样的输出时,模型停止生成。
- 进行工具和参数解析,并调用工具。
- 将工具调用结果进行 ReAct 格式进行拼装,调用模型继续进行续写生成。
- 直到遇见
Final Answer:,获得最终答案。 - 使用一次工具,至少要调用 2 次大模型。
推荐阅读
- OpenAI Function Calling 与 LangChain Agent 工作原理及区别
- OpenAI 内部工程师揭秘:如何通过 API 将大模型集成到自己的应用程序中
- ReAct Agent 回答 RAG 系统中的复杂问题
- 如何使用大模型调用函数
参考资料:
Qwen 7B大模型ReAct Prompt详解以及LLM 智能体Agent实战 (https://zhuanlan.zhihu.com/p/664477178?utm\_id=0)
ReAct Prompting 原理简要介绍 (https://github.com/QwenLM/Qwen-7B/blob/main/examples/react\_prompt.md)
基于 model.chat 接口(对话模式)的 ReAct Prompting 实现 (https://github.com/QwenLM/Qwen-7B/blob/main/examples/langchain\_tooluse.ipynb)
基于 model.generate 接口(续写模式)的 ReAct Prompting 实现 (https://github.com/QwenLM/Qwen-7B/blob/main/examples/react\_demo.py)
🧐 分享、点赞、在看 ,给个 3连击 呗! 👇
