跳转至

06 通过设置思考框架,引导模型进行更好的推理

你好,我是黄佳。

在上节课中,我们通过5个实用的例子,向你展示了与AI模型交互的5大基本原则。你应该已经意识到,精心设计的提示可以极大地影响和改善AI模型的输出质量。通过使用不同的模型,我们也领略到了它们各自的特点和适用场景。

在这节课里,我们将着眼于一个更高层次的提示工程方法——设置思考框架,来帮助AI模型进行更有条理、更可靠的推理。我在LangChain实战课的 0405 讲里也提到过“少样本”(Few-Shot Learning)和“思维链”(Chain-of-Thought)这两个概念。

  • 少样本,旨在使模型能够从极少量的数据样本中学习并泛化新任务。
  • 思维链,就是一个通过思考框架来构建提示词、引导模型完成复杂任务的典型解决方案。

今天,我们就继续从少样本和思维链开始,来继续探讨如何通过设计思考框架,来引导AI模型进行深入、完善的推理,提高在各种复杂任务上的表现。

同时,我们还将介绍一些其他的思考框架,比如自我一致性、反思和对话等等。通过这些方法,我们可以让AI模型不仅能够得出答案,还能解释其推理过程,甚至对自己的思路进行质疑和改进。从这些思考框架中,你可以看到学术界和产业界对于通过提示词来提升大模型,都进行了哪些角度的思考、探索和尝试。

现在,我们就开始提示工程的进阶之旅。

思考框架 1:少样本提示(Few-Shot Prompting)

少样本提示是一种通过在提示中提供少量示例,来引导模型完成特定任务的方法。这种方法对于资源有限的场景尤为重要,它可以让模型快速理解任务要求,无需大量的训练数据。

在提示工程中,少样本指的是给大模型几个例子,去照猫画虎。

下面是一个使用少样本提示进行情感分析的提示示例。

few_shot_prompt = """
请对以下句子的情感进行分类,可以分为积极、中性或消极三类。

示例1:
句子: 这部电影真是太棒了,我非常喜欢!
情感: 积极

示例2: 
句子: 天气还可以,不冷不热。
情感: 中性

示例3:
句子: 这家餐厅的服务太差了,再也不来了。
情感: 消极

句子: 这次旅行很不错,风景优美,吃得也不错。
情感:
"""

完整代码如下:

import os
from openai import OpenAI

client = OpenAI()

def get_completion(messages, model="gpt-3.5-turbo"):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0,
    )
    return response.choices[0].message.content

few_shot_prompt = """
请对以下句子的情感进行分类,可以分为积极、中性或消极三类。

示例1:
句子: 这部电影真是太棒了,我非常喜欢!
情感: 积极

示例2:
句子: 天气还可以,不冷不热。
情感: 中性

示例3:
句子: 这家餐厅的服务太差了,再也不来了。
情感: 消极

句子: 这次旅行很不错,风景优美,吃得也不错。
情感:
"""

messages = [
    {"role": "system", "content": "你是一个情感分析模型,可以判断句子的情感是积极、中性还是消极。"},
    {"role": "user", "content": few_shot_prompt}
]

response = get_completion(messages)
print(response)

输出结果:

积极

在这儿,我在提示中提供了3个示例,分别展示了如何对积极、中性、消极的句子进行情感分类。然后我给出一个新的句子,让模型对其情感进行判断。这样,我们是通过大语言模型的能力,通过少样本的自然语言提示方法,完成了一个传统的 NLP 任务

类似的,这种少样本提示的方法也可以应用于其他各种NLP任务,如文本总结、命名实体识别、关系抽取等。如果你想了解这些传统的NLP任务的更多细节,你可以参考任意一本NLP入门书籍,而且网络上也有大量的数据集,可以用来测试大模型在传统NLP任务上的能力。

思考框架 2:思维链(Chain-of-Thought)

思维链则特别适用于解决需要复杂推理的问题。这种技术通过引导模型模拟人类解决问题的思考过程来工作。具体来说,模型被提示以一种连贯和逐步的方式表达其解题思路,这有助于模型更加结构化和深入地处理问题。

思维链的关键优点包括:

  1. 增强透明度和可解释性:通过展示解决问题的逐步推理,用户可以更容易地理解模型的决策过程和逻辑。这种透明度对于提升用户对AI输出的信任至关重要。
  2. 提高复杂问题解决能力:对于数学问题、逻辑推理题或需要深入分析的情况,思维链可以帮助模型按部就班地解决问题,尤其是在需要多步骤推理的场景中。
  3. 改善泛化能力:通过练习不同类型的思维链,模型可以学习如何适应和解决各种未知或不熟悉的问题,这提高了其在面对新领域任务时的适应性和效果。

下面我们用思维链来设计一个进行数学应用题求解的简单系统。在这个CoT的提示设计中,我们希望模型能够:

  • 重述问题
  • 思考解题步骤
  • 给出最终答案

所以我们将这样设计提示词:

prompt = f"""
请解决以下应用题,并按照以下格式给出回答:

问题:
[问题描述]

解决:
1) [重述问题]
2) [解题步骤1]
3) [解题步骤2]
...

答案:[最终答案]

题目如下:
{{question}}
"""

好了,思维框架搭建完毕后,可以随便找个题目测试一下! (当然,这个框架可以搞定任意数学应用题。)

question = “一个水池有一进水管和一出水管。进水管每小时可注水180升,出水管每小时可排水120升。如果水池的容积是1200升,水池在开始注水时是空的,那么几小时后水池会注满水?”

完整代码如下:

from openai import OpenAI

client = OpenAI()

def get_completion(messages, model="gpt-3.5-turbo"):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0,
    )
    return response.choices[0].message.content

prompt = """
请解决以下应用题,并按照以下格式给出回答:

问题:
[问题描述]

解决:
1) [重述问题]
2) [解题步骤1]
3) [解题步骤2]
...

答案: [最终答案]

题目如下:
{question}
"""

def solve_problem(question):
    messages = [
        {"role": "system", "content": "你是一个擅长解决数学应用题的智能助手。"},
        {"role": "user", "content": prompt.format(question=question)}
    ]
    return get_completion(messages)

question = "一个水池有一进水管和一出水管。进水管每小时可注水180升,出水管每小时可排水120升。如果水池的容积是1200升,水池在开始注水时是空的,那么几小时后水池会注满水?"

result = solve_problem(question)
print(result)

输出结果:

问题:
一个水池有一进水管和一出水管。进水管每小时可注水180升,出水管每小时可排水120升。如果水池的容积是1200升,水池在开始注水时是空的,那么几小时后水池会注满水?

解决:
1) 这个问题是关于一个水池的进水和出水。水池容积为1200升,有一个进水管每小时注水180升,一个出水管每小时排水120升。问水池从空开始,多久能注满水。
2) 我们需要计算净注水速度,即进水速度减去出水速度。净注水速度 = 180升/小时 - 120升/小时 = 60升/小时。 
3) 要计算注满1200升需要的时间,我们用水池容积除以净注水速度。时间 = 1200升 ÷ 60升/小时 = 20小时。

答案: 从水池开始注水且为空时,需要20小时才能注满这个1200升的水池。

这就是思维链框架的具体实现过程。通过设计合适的提示,我们引导模型进行逐步推理,得出最终答案,从而提高在数学应用题等复杂任务上的表现。

思考框架 3:自洽性(Self-Consistency)

我们可以在CoT的基础上进一步优化,例如使用自洽性(或称自我一致性)框架来综合多个推理路径,来增强模型的推理能力。

自洽性框架源自斯坦福大学的一篇论文《Self-Consistency Improves Chain of Thought Reasoning in Language Models》。它的核心思想是让模型生成多个候选答案,然后通过答案之间的一致性来选出最佳答案。这个过程有点类似于人类的自我交叉验证,可以提高推理的可靠性。

自洽性的主要优点包括:

  1. 提高可靠性:通过生成多个候选答案并比较其一致性,可以减少偶然错误和异常值的影响,提高结果的可靠性。
  2. 鲁棒性更强:自洽性可以帮助模型应对不确定性和模糊性,即使部分候选答案有误,只要多数答案是正确的,仍然可以得到可靠的结果。
  3. 探索多样性:通过鼓励模型生成不同的候选答案,自洽性可以帮助发现问题的多种解法,提供更全面的信息。

下面我们就用自洽性来设计一个更为复杂的问题求解系统。

from openai import OpenAI

client = OpenAI()

def get_completion(messages, model="gpt-3.5-turbo"):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=1.5,
        n=3,  # 生成3个候选答案
    )
    return [choice.message.content for choice in response.choices]

def self_consistency(question, solve_prompt):
    messages = [
        {"role": "system", "content": "你是一个善于解决问题的助手,解决问题方法不超过50字。"},
        {"role": "user", "content": solve_prompt.format(question=question)}
    ]
    
    candidate_answers = get_completion(messages)
    
    print(f"候选答案:\n{chr(10).join(candidate_answers)}\n")
    
    vote_prompt = f"""
    请对以下候选答案进行投票,选出最佳答案:
    
    候选答案:
    {chr(10).join(candidate_answers)}
    
    最佳答案是:"""
    
    messages = [
        {"role": "system", "content": "你是一个善于评判问题答案的助手,请返回最佳答案的阿拉伯数字编号。"},
        {"role": "user", "content": vote_prompt}
    ]

    best_answer_index = int(get_completion(messages)[0]) - 1
    best_answer = candidate_answers[best_answer_index]
    
    print(f"最佳答案的编号是: {best_answer_index + 1}")
    
    return best_answer

question = "马桶阻塞的最佳解决方案?"
solve_prompt = """
请解决以下应用题,并给出详细的解题步骤:
问题:
{question}
"""

result = self_consistency(question, solve_prompt)

在这个程序中,我们首先定义了两个辅助函数。

  • get_completion:用于向OpenAI发送请求并返回生成的答案,其中n参数设为3,表示生成3个候选答案。
  • self_consistency:实现了自洽性的核心逻辑。首先将问题和求解提示发送给模型,生成3个候选答案。然后把这些候选答案构造成一个投票提示,要求模型从中选出最佳答案。最后返回模型票选出的最佳答案作为最终结果。

通过这种自洽性的方法,我们可以综合多个候选答案的优点,得到更加可靠和全面的解答。同时,这种方法也能够在一定程度上减少偶然错误和异常值的影响。

输出如下:

候选答案:
#1
最佳解决方案是使用马桶通,按照说明书向堵塞处轻推或者使用高压气泵进行清理。如无效,需寻求专业卫生间卫生公司帮助。
#2
使用专门的马桶疏通器进行疏通可处方便简单来解决阻塞问题。参考以下步骤:)
1. 穿戴手套垫塑料袋。
2. 插入疏通器并用有规律地推动手柄​​。
3. 冲洗马桶确认问题是否解决。
#3
1. 先准备橡胶活塞,将其置于马桶下水口;  
2. 快速向下按压数次,产生压力推通堵塞物;  
3. 可尝试旋转活塞,增加清理效果;  
4. 如果活塞无效,试试倒入热水+盐溶液;  
5. 如果依然无效,选择使用化学马桶疏通剂。

最佳答案的编号是: 1

果然,1 作为最佳答案给出是靠谱的,2 中的穿戴手套疏通马桶果然不是最佳选择,3 中的准备橡胶活塞简直就无厘头了,很难想象这是GPT-3.5的水准,我有点愕然!因此,自洽性的做法,可以避免异常错误的干扰,毕竟大模型时不时地会来个错误。而这种自洽性的思路也可以推广到其他需要逐步推理和验证的任务中,提高AI助手的可靠性和实用性。

当然,自洽性方法的缺点是计算开销较大,在实际应用中需要根据任务的复杂度和对效率的要求,权衡是否采用这种方法。

思考框架 4:辩论与自我反思(Debate and Self-Reflection)

自我辩论框架的思想是让模型扮演正反两个角色,针对同一个问题进行辩论,然后再作出最终判断。通过这种自我辩论,模型可以更全面地考虑问题,并减少偏见。

自我辩论的主要优点包括:

  1. 全面性:通过正反双方的辩论,可以从不同角度全面审视问题,有助于发现隐藏的假设、逻辑漏洞或遗漏的信息。
  2. 客观性:辩论过程要求模型必须站在不同立场上思考问题,这有助于克服单一视角的局限性,提高客观性和中立性。
  3. 说服力:通过论证和反驳,模型可以更有说服力地支持其观点。这种论证能力对于需要解释和 justify 的任务尤为重要。

下面就是一种通过自我辩论和反思来改进问题解答的提示设置方法。

初始答案提示(initial_answer_prompt):

请解答以下问题:
问题:{question}

这个提示直接要求AI助手解答给定的问题。它将问题嵌入到提示中,并将其发送给一个被设定为“善于解决数学应用题”的AI助手。这一步旨在获得问题的初步答案。

得到初步答案之后,继续设定辩论提示,对初始答案进行批评性的思考。

辩论提示(debate_prompt):

请针对以下问题和给出的答案,提出反对意见或质疑,并给出理由:
问题:{question}
答案:{initial_answer}

这个提示将问题和初始答案提供给另一个AI助手,并要求其从不同角度对初始答案进行质疑或提出反对意见。这一步的目的是发现初始答案可能存在的问题或不足之处。这里的AI助手被设定为“善于从不同角度思考问题”,以鼓励其进行批判性思考。

反思提示(reflect_prompt):

请根据以下问题、最初给出的答案以及提出的质疑,进行反思并给出更全面的答案:
问题:{question}
最初答案:{initial_answer}
质疑:{debate}

这个提示将问题、初始答案和辩论意见提供给第三个AI助手,要求其在综合考虑所有信息的基础上,进行反思并给出一个更加全面的答案。这一步的目的是改进和完善初始答案,纠正其中可能存在的错误或遗漏,并尝试回应辩论中提出的质疑。这里的AI助手被设定为“善于反思和改进”,以促使其进行深入的思考和自我完善。

通过这三个提示的设置,实现了一个自我辩论和反思的过程。

  • 获得初始答案
  • 对初始答案进行质疑和辩论
  • 在辩论的基础上进行反思,给出更全面的答案

完整示例代码如下:

import os
import openai

openai.api_key = os.getenv("OPENAI_API_KEY")

def get_completion(messages, model="gpt-3.5-turbo"):
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=0, 
    )
    return response.choices[0].message["content"]

def debate_and_reflect(question):
    initial_answer_prompt = f"""
    请解答以下问题:
    问题: {question}
    """

    messages = [
        {"role": "system", "content": "你是一个善于解决数学应用题的助手。"},
        {"role": "user", "content": initial_answer_prompt}
    ]

    initial_answer = get_completion(messages)

    debate_prompt = f"""
    请针对以下问题和给出的答案,提出反对意见或质疑,并给出理由:
    问题: {question}
    答案: {initial_answer}
    """

    messages = [
        {"role": "system", "content": "你是一个善于从不同角度思考问题的助手。"},
        {"role": "user", "content": debate_prompt}
    ]

    debate = get_completion(messages)

    reflect_prompt = f"""
    请根据以下问题、最初给出的答案以及提出的质疑,进行反思并给出更全面的答案:
    问题: {question}
    最初答案: {initial_answer}
    质疑: {debate}
    """

    messages = [
        {"role": "system", "content": "你是一个善于反思和改进的助手。"},
        {"role": "user", "content": reflect_prompt}
    ]

    final_answer = get_completion(messages)

    return final_answer

question = "远程工作是否将成为大多数行业的常态?"
result = debate_and_reflect(question)
print(result)

这个程序的核心是debate_and_reflect函数,其通过上述几个提示,实现了自我辩论的逻辑。

程序输出如下:

初始答案: 

远程工作已经在许多行业中变得越来越普遍,尤其是在信息技术、媒体、市场营销等领域。随着技术的不断发展和全球化的趋势,远程工作的普及可能会继续增加。然而,是否会成为大多数行业的常态还取决于具体的行业特点、公司文化以及员工需求等因素。一些行业可能更适合远程工作,而另一些行业可能需要更多的面对面交流和团队合作。因此,远程工作是否会成为大多数行业的常态可能会因行业而异。

辩论意见: 

我认为可以对这个答案提出一些质疑和反对意见。首先,虽然远程工作在一些行业中变得越来越普遍,但并不是所有行业都适合远程工作。例如,制造业、医疗保健、零售等需要实地操作或面对面服务的行业可能不太适合远程工作。在这些行业中,员工需要直接与设备、产品或客户进行互动,远程工作可能会影响效率和质量。

其次,远程工作虽然有很多优点,如节约通勤时间、提高灵活性等,但也存在一些挑战和问题。例如,远程工作可能导致员工之间沟通不畅、团队合作受限、员工孤立感增加等问题。在某些行业或岗位中,面对面交流和团队协作是至关重要的,远程工作可能无法完全替代这种模式。

最后,远程工作是否会成为大多数行业的常态还取决于公司文化和领导者的态度。一些公司可能更倾向于传统的办公模式,更看重员工之间的互动和团队精神,因此可能不会全面推行远程工作。因此,远程工作是否会成为大多数行业的常态还需要考虑到各种因素的综合影响,而不能简单地一概而论。

反思后的答案:

在对远程工作是否会成为大多数行业的常态进行反思后,我认为可以给出更全面的答案:

远程工作的普及程度将受到多种因素的影响,包括行业特点、技术发展、员工需求、公司文化等。在一些行业,如信息技术、媒体、市场营销等,远程工作已经成为一种普遍的工作模式,并且随着技术的不断进步和全球化的趋势,远程工作的普及可能会继续增加。然而,在其他行业,如制造业、医疗保健、零售等需要实地操作或面对面服务的行业,远程工作可能并不适合或无法完全替代传统的办公模式。

虽然远程工作有诸多优点,如节约通勤时间、提高灵活性等,但也存在挑战和问题,如沟通不畅、团队合作受限、员工孤立感增加等。在某些行业或岗位中,面对面交流和团队协作是至关重要的,远程工作可能需要与传统办公模式相结合,以满足不同的工作需求。

最后,远程工作是否会成为大多数行业的常态还取决于公司文化和领导者的态度。一些公司可能更倾向于传统的办公模式,更看重员工之间的互动和团队精神,因此可能不会全面推行远程工作。因此,远程工作的普及程度将因行业特点、公司文化、员工需求等多方面因素而异,需要综合考虑各种因素才能得出更准确的结论。

很明显,通过这种自我辩论的方式,模型的每一轮回答都有进步,从不同角度审视问题,发现可能存在的问题或不足,并综合多方面信息给出了更加全面和可靠的答案。你也可以通过为不同阶段的AI助手设定不同的角色和目标,引导其进行有针对性的思考和改进。

这种自我辩论和反思的提示设置,可以应用于各种需要深入思考和多角度分析的任务,以提高AI助手输出的质量和可靠性。

当然,自我辩论的缺点也是需要多轮交互,计算开销较大。在实践中,可以根据任务的复杂度和对效率的要求,权衡是否采用这种方法。此外,自我辩论的效果也取决于所使用的模型的辩论和反思能力,需要进行适当的提示工程和模型选择。

思考框架 5:自动提示优化(Automatic Prompt Engineering)

这里要介绍的最后一个思考框架是自动提示优化。这是一种通过迭代优化提示词来提高模型在特定任务上表现的方法。

怎么优化?让大模型自己来决定如何优化。它的核心思想就是将提示工程本身看作一个可优化的任务,通过机器学习的方法或者用大模型的能力来自动发现更有效的提示。

自动提示优化的主要优点包括:

  1. 提高效果:通过优化提示词更好地引导模型理解任务要求,生成更高质量的结果。
  2. 节省成本:自动化的提示优化可以减少人工设计提示的时间和精力成本。
  3. 发现新知识:在优化过程中可能会发现人工难以发现的提示模式和技巧。

这个框架会对初始提示进行迭代优化,以生成更适合推理任务的提示。它的优点是可以自动化地改进提示质量,但缺点是优化过程可能比较耗时。

自动提示优化的示例代码如下:

from openai import OpenAI

client = OpenAI()

def get_completion(messages, model="gpt-3.5-turbo"):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0,
    )
    return response.choices[0].message.content

def optimize_prompt(question, initial_prompt, iterations=3):
    current_prompt = initial_prompt
    
    for i in range(iterations):
        messages = [
            {"role": "system", "content": "你是一个擅长优化提示的助手。"},
            {"role": "user", "content": f"请优化以下提示,使其更适合解答特点领域的问题:\n\n当前提示:\n{current_prompt}\n\n问题:\n{question}"}
        ]
        
        current_prompt = get_completion(messages)
        
    return current_prompt

question = "我是一家餐馆的老板,最近有客人在就餐时不慎摔伤。我担心他会提出法律诉讼。在这种情况下,我有哪些法律责任?应该如何应对?"
initial_prompt = """
根据您提供的信息,我的建议如下:
首先,[...]
其次,[...]
再者,[...]
总之,[...]
如果您还有其他问题,欢迎随时向我咨询。
"""

optimized_prompt = optimize_prompt(question, initial_prompt)
print(f"优化后的提示:\n{optimized_prompt}")

在这个程序中,我们定义了optimize_prompt函数,它接受一个问题、一个初始提示和迭代次数作为参数。在每次迭代中,我们将当前提示和问题发送给一个提示优化模型,要求其优化提示以更好地解决给定问题。优化后的提示将作为下一次迭代的起点,重复这个过程指定的次数。

一个可能的经过优化的提示如下:

优化后的提示:
根据您提供的信息,这是一个法律方面的问题,我的建议并不能代表专业人士的意见。
具体建议如下:
首先,需了解从事[...]行业的相关许可。
其次,[...]
再者,[...]
总之,在整个过程中,请保持冷静和专业的态度,避免冲动行事或作出不利于自己的承诺。
如果您还有任何其他问题或顾虑,欢迎随时与我进一步讨论。我会尽我所能为您提供帮助和建议。

可以看出,经过优化的提示,具有领域相关性,再度传入大模型求解时,得到的答案会更具针对性。

通过这种自动提示优化的方法,可以发现更有效的提示模式,提高模型在特定任务上的表现。这在需要处理大量相似任务的场景下尤为有用,可以显著节省人工设计提示的成本。

自动提示优化并不那么容易,它的效果取决于优化模型的能力以及初始提示的质量。在实践中,可能需要多次尝试不同的初始提示和优化策略,才能得到满意的结果。此外,优化得到的提示可能过度依赖于特定的问题,泛化能力有限。

总结时刻

大模型的思考框架是一个非常活跃的研究领域,也是构建更强大的AI Agent的核心内容。目前的思考框架远远不止上述几种,我所选择的这几种框架,则是典型而又具有启发性的。

除这几种框架之外,还有多个思维链组合在一起的思维树框架、定向刺激提示框架、知识融合与多跳检索框架等等。广义上说,模型的记忆管理、利用外部知识库进行检索的RAG架构,基于知识图谱进行推理的方法以及工具调用(Function Calling/Tool Calls)等提示范式,还有主流的Agent设计框架 ReAct,也都可以视为思考框架的一种。总而言之,设计思考框架的目的就是为了让模型具有更好的推理能力和内容生成能力。

这些思考框架当然也可以彼此结合,相互组合甚至融合在一起,形成更强大的思维框架,比如少样本思维链或者自洽性辩论等等。

思考题

  1. 用文中的5个思维框架来解决不同类型(也就是不同于我给出的示例)的问题。
  2. 你能否自己设计少样本思维链的提示工程示例,并用它解决一些你手头上的实际问题。
  3. 阅读最新的大模型思考框架的论文,如果有很新颖的、有启发的思路,请一定在评论区分享它的特点,最好说说其优势和不足之处。

期待你的分享,欢迎与我交流。如果今天的内容让你有所收获,也欢迎你把这节课转发给有需要的朋友!我们下节课再见!