AI大模型實戰篇:AI Agent設計模式,ReAct

0 評論 3586 瀏覽 9 收藏 20 分鐘

隨著人工智能技術的不斷進步,AI Agent設計模式逐漸成為研究和應用的熱點。ReAct模式作為AI Agent設計模式的起點,以其模擬人類思考和行動過程的特點,為各種智能應用提供了一種有效的實現途徑。

《大佬們都在關注的AI Agent,到底是什么?用5W1H分析框架拆解AI Agent(下篇)》中,風叔簡單介紹了AI Agent的八種設計模式。對于這八種設計模式,風叔整理了一張圖,來闡明它們之間的關系。

ReAct模式最早出現的Agent設計模式,目前也是應用最廣泛的。從ReAct出發,有兩條發展路線:

一條更偏重Agent的規劃能力,包括REWOO、Plan & Execute、LLM Compiler。

另一條更偏重反思能力,包括Basic Reflection、Reflexion、Self Discover、LATS。

在后續文章中,風叔將沿著上圖的脈絡,結合產品流程和源代碼,詳細介紹這八種AI Agent設計模式。

為什么選擇結合源代碼呢?因為在AI大模型時代,很多的概念和方法都太新了。只有結合源代碼,產品經理才能真正理解背后的原理和邏輯,才能知道什么能做,什么不能做,AI的邊界在哪里,以及該如何與人類經驗配合。

下面,我們先從ReAct模式開始。

一、ReAct的概念

ReAct的概念來自論文《ReAct: Synergizing Reasoning and Acting in Language Models》,這篇論文提出了一種新的方法,通過結合語言模型中的推理(reasoning)和行動(acting)來解決多樣化的語言推理和決策任務。ReAct 提供了一種更易于人類理解、診斷和控制的決策和推理過程。

它的典型流程如下圖所示,可以用一個有趣的循環來描述:思考(Thought)→ 行動(Action)→ 觀察(Observation),簡稱TAO循環。

  • 思考(Thought):面對一個問題,我們需要進行深入的思考。這個思考過程是關于如何定義問題、確定解決問題所需的關鍵信息和推理步驟。
  • 行動(Action):確定了思考的方向后,接下來就是行動的時刻。根據我們的思考,采取相應的措施或執行特定的任務,以期望推動問題向解決的方向發展。
  • 觀察(Observation):行動之后,我們必須仔細觀察結果。這一步是檢驗我們的行動是否有效,是否接近了問題的答案。
  • 循環迭代

如果觀察到的結果并不匹配我們預期的答案,那么就需要回到思考階段,重新審視問題和行動計劃。這樣,我們就開始了新一輪的TAO循環,直到找到問題的解決方案。

和ReAct相對應的是Reasoning-Only和Action-Only。在Reasoning-Only的模式下,大模型會基于任務進行逐步思考,并且不管有沒有獲得結果,都會把思考的每一步都執行一遍。在Action-Only的模式下,大模型就會處于完全沒有規劃的狀態下,先進行行動再進行觀察,基于觀察再調整行動,導致最終結果不可控。

假設我們正在構建一個智能助手,用于管理我們的日程安排。

在reason-only模式中,智能助手專注于分析和推理,但不直接采取行動。

  • 你告訴智能助手:“我明天有個會議?!?/li>
  • 智能助手分析這句話,確定明天的會議時間、地點等細節。
  • 它可能會提醒你:“明天下午3點有個會議,在公司會議室。”

在action-only模式中,智能助手專注于執行任務,但不做深入的推理或分析。

  • 你告訴智能助手:“把我明天的會議改到上午10點?!?/li>
  • 智能助手立即執行這個任務,將會議時間修改為上午10點。
  • 它可能會簡單確認:“您的會議已改到明天上午10點?!?/li>

在ReAct模式中,智能助手結合推理和行動,形成一個循環的感知-動作循環。不僅分析了你的需求(推理),還實際修改了日程安排(行動)。

  • 你告訴智能助手:“我明天有個會議,但我想提前到上午10點。”
  • 智能助手首先分析這句話,確定會議的原始時間和地點(感知階段)。
  • 然后,它更新你的日程安排,將會議時間改為上午10點(決策和動作執行階段)。
  • 最后,智能助手確認修改,并提供額外的信息:“您的會議已成功改到明天上午10點。提醒您,會議地點不變。

二、ReAct的實現過程

下面,風叔通過實際的源碼,詳細介紹ReAct模式的實現方法。在手機端閱讀源代碼的體驗不太好,建議大家在PC端打開。

大家可以私信風叔,或者在評論區留言“ReAct源碼”,獲取ReAct設計模式的示例源代碼。

第一步 準備Prompt模板

在實現ReAct模式的時候,首先需要設計一個清晰的Prompt模板,主要包含以下幾個元素:

  • 思考(Thought):這是推理過程的文字展示,闡明我們想要LLM幫我們做什么,為了達成目標的前置條件是什么
  • 行動(Action):根據思考的結果,生成與外部交互的指令文字,比如需要LLM進行外部搜索
  • 行動參數(Action Input):用于展示LLM進行下一步行動的參數,比如LLM要進行外部搜索的話,行動參數就是搜索的關鍵詞。主要是為了驗證LLM是否能提取準確的行動參數
  • 觀察(Observation):和外部行動交互之后得到的結果,比如LLM進行外部搜索的話,那么觀察就是搜索的結果。

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}"""

第二步 構建Agent

一個ReAct Agent需要定義好以下元素

  • llm:背后使用的LLM大模型
  • tools:后續會用到的Tools集合
  • stop:什么情況下ReAct Agent停止循環
class LLMSingleActionAgent {
  llm: AzureLLM
  tools: StructuredTool[]
  stop: string[]
  private _prompt: string = '{input}'
  constructor({ llm, tools = [], stop = [] }: LLMSingleActionAgentParams) {
    this.llm = llm
    this.tools = tools
    if (stop.length > 4)
      throw new Error('up to 4 stop sequences')
    this.stop = stop
  }
 }

第三步 定義Tools

Tools有兩個最重要的參數,name和description。

Name就是函數名,description是工具的自然語言描述,LLM 根據description來決定是否需要使用該工具。工具的描述應該非常明確,說明工具的功能、使用的時機以及不適用的情況。

export abstract class StructuredTool {
  name: string
  description: string
  constructor(name: string, description: string) {
    this.name = name
    this.description = description
  }

  abstract call(arg: string, config?: Record<string, any>): Promise<string>

  getSchema(): string {
    return `${this.declaration} | ${this.name} | ${this.description}`
  }

  abstract get declaration(): string
}

我們先簡單地將兩個描述信息拼接一下,為Agent提供4個算數工具:

1. Addition Tool: A tool for adding two numbers
2. Subtraction Tool: A tool for subtracting two numbers
3. Division Tool: A tool for dividing two numbers
4.MultiplicationTool: Atoolformultiplyingtwonumbers

一個很有意思的事情是,這幾個算數工具函數并不需要實際的代碼,大模型可以僅靠自身的推理能力就完成實際的算數運算。當然,對于更復雜的工具函數,還是需要進行詳細的代碼構建。

第四步 循環執行

執行器executor是在Agent的運行時,協調各個組件并指導操作。還記得ReAct模式的流程嗎?Thought、Action、Observation、循環,Executor的作用就是執行這個循環。

class AgentExecutor {
  agent: LLMSingleActionAgent
  tools: StructuredTool[] = []
  maxIterations: number = 15

  constructor(agent: LLMSingleActionAgent) {
    this.agent = agent
  }
  
  addTool(tools: StructuredTool | StructuredTool[]) {
    const _tools = Array.isArray(tools) ? tools : [tools]
    this.tools.push(..._tools)
  }
}

executor會始終進行如下事件循環直到目標被解決了或者思考迭代次數超過了最大次數:

  • 根據之前已經完成的所有步驟(Thought、Action、Observation)和 目標(用戶的問題),規劃出接下來的Action(使用什么工具以及工具的輸入)
  • 檢測是否已經達成目標,即Action是不是ActionFinish。是的話就返回結果,不是的話說明還有行動要完成
  • 根據Action,執行具體的工具,等待工具返回結果。工具返回的結果就是這一輪步驟的Observation
  • 保存當前步驟到記憶上下文,如此反復
async call(input: promptInputs): Promise<AgentFinish> {
    const toolsByName = Object.fromEntries(
      this.tools.map(t => [t.name, t]),
    )

    const steps: AgentStep[] = []
    let iterations = 0

    while (this.shouldContinue(iterations)) {
      const output = await this.agent.plan(steps, input)
      console.log(iterations, output)

      // Check if the agent has finished
      if ('returnValues' in output)
        return output

      const actions = Array.isArray(output)
        ? output as AgentAction[]
        : [output as AgentAction]

      const newSteps = await Promise.all(
        actions.map(async (action) => {
          const tool = toolsByName[action.tool]

          if (!tool)
            throw new Error(`${action.tool} is not a valid tool, try another one.`)

          const observation = await tool.call(action.toolInput)

          return { action, observation: observation ?? '' }
        }),
      )

      steps.push(...newSteps)

      iterations++
    }

    return {
      returnValues: { output: 'Agent stopped due to max iterations.' },
      log: '',
    }
  }

第五步 實際運行

我們提出一個問題,看看Agent怎么通過ReAct方式進行解決。 “一種減速機的價格是750元,一家企業需要購買12臺。每臺減速機運行一小時的電費是0.5元,企業每天運行這些減速機8小時。請計算企業購買及一周運行這些減速機的總花費。”

describe('agent', () => {
  const llm = new AzureLLM({
    apiKey: Config.apiKey,
    model: Config.model,
  })
  const agent = new LLMSingleActionAgent({ llm })
  agent.setPrompt(REACT_PROMPT)
  agent.addStop(agent.observationPrefix)
  agent.addTool([new AdditionTool(), new SubtractionTool(), new DivisionTool(), new MultiplicationTool()])

  const executor = new AgentExecutor(agent)
  executor.addTool([new AdditionTool(), new SubtractionTool(), new DivisionTool(), new MultiplicationTool()])
  it('test', async () => {
    const res = await executor.call({ input: '一種減速機的價格是750元,一家企業需要購買12臺。每臺減速機運行一小時的電費是0.5元,企業每天運行這些減速機8小時。請計算企業購買及一周運行這些減速機的總花費。' })
    expect(res).toMatchInlineSnapshot(`
      {
        "log": "Final Answer: The total cost of purchasing and operating the gearboxes for a week is 9336 yuan.",
        "returnValues": {
          "output": "The total cost of purchasing and operating the gearboxes for a week is 9336 yuan.",
        },
      }
    `)
  }, { timeout: 50000 })
})

我們來看一下Agent的輸出,以及Agent在這個過程,是如何思考和行動的??梢钥吹?,通過Thought、Action、Observation的循環,AI Agent很好地一步步完成最終答案的輸出。

Question:一種減速機的價格是750元,一家企業需要購買12臺。每臺減速機運行一小時的電費是0.5元,企業每天運行這些減速機8小時。請計算企業購買及一周運行這些減速機的總花費
Thought:I need to calculate the total cost of purchasing and operating the gearboxes for a week.
Action: Multiplication Tool
Action Input: [750, 12]
Observation: 9000
Thought: Now I need to calculate the cost of operating the gearboxes for a day.
Action: Multiplication Tool
ActionInput:[0.5,8,12]
Observation: 48
Thought: Now I need to calculate the cost of operating the gearboxes for a week.
Action: Multiplication Tool
Action Input: [48, 7]
Observation: 336
Thought: Now I need to calculate the total cost of purchasing and operating the gearboxes for a week.
Action: Addition Tool
Action Input: [9000, 336]
Observation: 9336

總結

在AI Agent的多種實現模式中,ReAct模式是最早出現、也是目前使用最廣泛的模式。ReAct的核心思想就是模擬人思考和行動的過程,通過Thought、Action、Observation的循環,一步步解決目標問題。

ReAct模式也存在很多的不足:

  • 首先是LLM大模型的通病,即產出內容不穩定,不僅僅是輸出內容存在波動,也體現在對復雜問題的分析,解決上存在一定的波動
  • 然后是成本,采用ReAct方式,我們是無法控制輸入內容的。因為在任務提交給LLM后,LLM對任務的拆解、循環次數是不可控的。因此存在一種可能性,過于復雜的任務導致Token過量消耗。
  • 最后是響應時間,比起大部分API接口毫秒級的響應,LLM響應時間是秒級以上。在ReAct模式下,這個時間變得更加不可控。因為無法確定需要拆分多少步驟,需要訪問多少次LLM模型。因此在在秒級接口響應的背景下,做成同步接口顯然是不合適的,需要采用異步的方式。而異步方式,又會影響用戶體驗,對應用場景的選擇又造成了限制。

但是無論如何,ReAct框架提出了一種非常好的思路,讓現有的應用得到一次智能化的進化機會?,F在很多場景已經有了非常成熟的ReAct Agent應用,比如智能客服、知識助手、個性化營銷、智能銷售助理等等。

在下一篇文章中,風叔將介紹另一種AI Agent設計模式,REWOO。

作者:風叔,微信公眾號:風叔云

本文由@風叔 原創發布于人人都是產品經理,未經作者許可,禁止轉載。

題圖來自Unsplash,基于CC0協議。

該文觀點僅代表作者本人,人人都是產品經理平臺僅提供信息存儲空間服務。

更多精彩內容,請關注人人都是產品經理微信公眾號或下載App
評論
評論請登錄
  1. 目前還沒評論,等你發揮!