上面已经提到了,RAT的实现原理其实就是 RAG + COT。其实现步骤如下:
(1)生成初始CoT:给定一个任务提示,LLM首先零样本(zero-shot)生成一个初始的、分步的思维链(CoT),其中包含多个推理步骤。
(2)检索相关信息:对于CoT中的每一步,RAT使用当前和之前所有的思维步骤,以及原始任务提示,生成一个查询(query)。然后,这个查询被用来从外部知识库中检索相关信息。
(3)修正思维步骤:利用检索到的信息,LLM逐步修正CoT中的每一步。这意味着,每一步的修正都基于当前步骤和之前所有已修正步骤的信息。
(4)生成最终回答:修正完所有的思维步骤后,LLM会根据这些修正后的步骤生成最终的输出。
伪代码实现如下:
上面有了RAT的原理和基本步骤,可能还是有点懵。别急,下面我们看下RAT的具体实现代码。
完整实现代码:https://github.com/CraftJarvis/RAT
下面是RAT的基本流程部分:
#RATFunction
newline_char='\n'
defrat(question):
print(f"{datetime.now()}[INFO]获取草稿...")
draft=get_draft(question)
print(draft)
print(f"{datetime.now()}[INFO]处理草稿...")
draft_paragraphs=split_draft(draft)
answer=""
fori,pinenumerate(draft_paragraphs):
print(str(i)*80)
print(f"{datetime.now()}[INFO]修改第{i+1}/{len(draft_paragraphs)}部分...")
answer=answer+'\n\n'+p
print(f"{datetime.now()}[INFO]生成对应Query...")
res=run_with_timeout(get_query_wrapper,3,question,answer)
ifnotres:
print(f"{datetime.now()}[INFO]跳过后续步骤...")
continue
else:
query=res
print(f">>>{i}/{len(draft_paragraphs)}Query:{query.replace(newline_char,'')}")
print(f"{datetime.now()}[INFO]获取网页内容...")
#content=get_content(query)
res=run_with_timeout(get_content_wrapper,5,query)
ifnotres:
print(f"{datetime.now()}[INFO]跳过后续步骤...")
continue
else:
content=res
forj,cinenumerate(content):
ifj>2:
break
print(f"{datetime.now()}[INFO]根据网页内容修改对应答案...[{j}/{min(len(content),3)}]")
#answer=get_revise_answer(question,answer,c)
res=run_with_timeout(get_revise_answer_wrapper,10,question,answer,c)
ifnotres:
print(f"{datetime.now()}[INFO]跳过后续步骤...")
continue
else:
diff_html=generate_diff_html(answer,res)
display(HTML(diff_html))
answer=res
print(f"{datetime.now()}[INFO]答案修改完成[{j}/{min(len(content),3)}]")
returndraft,answer对以上代码的解释:
(1)生成初始CoT:draft = get_draft(question)
(2)然后切分出CoT的步骤(论文中的T1、T2、... 、Tn):draft_paragraphs = split_draft(draft)
(3)生成对应Query:res = run_with_timeout(get_query_wrapper, 3, question, answer)
defget_query(question,answer):
query_prompt='''
Iwanttoverifythecontentcorrectnessofthegivenquestion,especiallythelastsentences.
Pleasesummarizethecontentwiththecorrespondingquestion.
ThissummarizationwillbeusedasaquerytosearchwithBingsearchengine.
ThequeryshouldbeshortbutneedtobespecifictopromiseBingcanfindrelatedknowledgeorpages.
Youcanalsousesearchsyntaxtomakethequeryshortandclearenoughforthesearchenginetofindrelevantlanguagedata.
Trytomakethequeryasrelevantaspossibletothelastfewsentencesinthecontent.
**IMPORTANT**
Justoutputthequerydirectly.DONOTaddadditionalexplanationsorintroducementintheanswerunlessyouareaskedto.
'''
query=openai_client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{
"role":"system",
"content":chatgpt_system_prompt
},
{
"role":"user",
"content":f"##Question:{question}\n\n##Content:{answer}\n\n##Instruction:{query_prompt}"
}
],
temperature=1.0
).choices[0].message.content
returnquery这里值得注意的一点,也是我觉得比较核心的一点:answer = answer + '\n\n' + p,在生成Query的时候,将之前的答案和对应的段落拼接在一起,然后一起总结。
(4)根据生成的Query检索内容:res = run_with_timeout(get_content_wrapper, 5, query)
defget_content(query):
res=get_search(query,1)
ifnotres:
print(">>>NogoodGoogleSearchResultwasfound")
returnNone
search_results=res[0]
link=search_results['link']#title,snippet
res=get_page_content(link)
ifnotres:
print(f">>>Nocontentwasfoundin{link}")
returnNone
retrieved_text=res
trunked_texts=chunk_texts(retrieved_text,1500)
trunked_texts=[trunked_text.replace('\n',"")fortrunked_textintrunked_texts]
returntrunked_texts(5)根据检索内容修正答案:res = run_with_timeout(get_revise_answer_wrapper, 10, question, answer, c)
defget_revise_answer(question,answer,content):
revise_prompt='''
IwanttorevisetheansweraccordingtoretrievedrelatedtextofthequestioninWIKIpages.
Youneedtocheckwhethertheansweriscorrect.
Ifyoufindsomeerrorsintheanswer,revisetheanswertomakeitbetter.
Ifyoufindsomenecessarydetailsareignored,addittomaketheanswermoreplausibleaccordingtotherelatedtext.
Ifyoufindtheanswerisrightanddonotneedtoaddmoredetails,justoutputtheoriginalanswerdirectly.
**IMPORTANT**
Trytokeepthestructure(multipleparagraphswithitssubtitles)intherevisedanswerandmakeitmorestructualforunderstanding.
Splittheparagraphswith`\n\n`characters.
Justoutputtherevisedanswerdirectly.DONOTaddadditionalexplanationsorannoucementintherevisedanswerunlessyouareaskedto.
'''
revised_answer=openai_client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{
"role":"system",
"content":chatgpt_system_prompt
},
{
"role":"user",
"content":f"##ExistingTextinWikiWeb:{content}\n\n##Question:{question}\n\n##Answer:{answer}\n\n##Instruction:{revise_prompt}"
}
],
temperature=1.0
).choices[0].message.content
returnrevised_answer代码中是以创意写作场景为例。
用 CoT 思想生成初始答案,答案需要使用 \n\n 进行分段。
将答案进行总结,生成相应的问题query。这个query会用来检索知识库或使用Bing搜索来检索相关内容。
根据检索到的内容,修正答案。包括:如果发现了错误,修正答案。如果发现答案不够完整,补充答案。如果答案正确且充分,直接输出原始答案。
RAT(Retrieval-Augmented Thoughts)方法固然如上所述在各个场景下都有更好的表现和通用性,但是也有不足之处:
(1)依赖基础模型:RAT的性能依赖于底层LLMs的CoT推理和RAG能力。对于较小或较弱的模型,RAT的效果可能会受限。
(2)知识库质量:RAT的有效性受限于检索到的知识库的质量和相关性。如果知识库与用户查询不相关,可能无法提供有用的信息。
(3)检索成本:从大型知识库中检索信息可能会带来较高的计算和维护成本,并且可能会影响检索的精确度。
(4)复杂性:RAT方法需要复杂的处理步骤,包括生成初始CoT、构建查询、检索信息以及迭代修正,增加了实现的复杂性。
(5)效率问题:RAT迭代修正过程影响生成速度,尤其是在需要大量检索和处理的情况下。
其中,成本问题和效率问题,是大家更加关心的。
| 欢迎光临 链载Ai (https://www.lianzai.com/) | Powered by Discuz! X3.5 |