### [AI / ML領域相關學習筆記入口頁面](https://hackmd.io/@YungHuiHsu/BySsb5dfp) #### [Deeplearning.ai GenAI/LLM系列課程筆記](https://learn.deeplearning.ai/) ##### GenAI - [Large Language Models with Semantic Search。大型語言模型與語義搜索 ](https://hackmd.io/@YungHuiHsu/rku-vjhZT) - [LangChain for LLM Application Development。使用LangChain進行LLM應用開發](https://hackmd.io/1r4pzdfFRwOIRrhtF9iFKQ) - [Finetuning Large Language Models。微調大型語言模型](https://hackmd.io/@YungHuiHsu/HJ6AT8XG6) ##### RAG - [Preprocessing Unstructured Data for LLM Applications。大型語言模型(LLM)應用的非結構化資料前處理](https://hackmd.io/@YungHuiHsu/BJDAbgpgR) - [Building and Evaluating Advanced RAG。建立與評估進階RAG](https://hackmd.io/@YungHuiHsu/rkqGpCDca) - [[GenAI][RAG] Multi-Modal Retrieval-Augmented Generation and Evaluaion。多模態的RAG與評估 ](https://hackmd.io/@YungHuiHsu/B1LJcOlfA) ##### AI Agents 原理可參考這篇綜論: [How Agents for LLM Perform Task Planning。大型語言模型的代理如何進行任務規劃](https://hackmd.io/@YungHuiHsu/rkK52BkQp) - 相關framework選擇 - [AI Agents in LangGraph](https://hackmd.io/@YungHuiHsu/BJTKpkEHC) - [Building Agentic RAG with LlamaIndex](https://learn.deeplearning.ai/courses/building-agentic-rag-with-llamaindex/lesson/1/introduction) - [Multi AI Agent Systems with crewAI](https://learn.deeplearning.ai/courses/multi-ai-agent-systems-with-crewai/lesson/1/introduction) - [Functions, Tools and Agents with LangChain](https://learn.deeplearning.ai/courses/functions-tools-agents-langchain/lesson/1/introduction) - Agent相關 - [Long-Term Agentic Memory With LangGraph](https://hackmd.io/@YungHuiHsu/S1f1cyOnke) --- # [AI Agents in LangGraph](https://learn.deeplearning.ai/courses/ai-agents-in-langgraph/lesson/1/introduction) * [Build an Agent from Scratch。從零開始建立AI代理](https://hackmd.io/@YungHuiHsu/BJTKpkEHC) * [LangGraph Components](https://hackmd.io/@YungHuiHsu/BySTFuhSC) * Agentic Search Tools * Persistence and Streaming * Human in the loop * Essay Writer --- ## LanaGraph Components。 LanaGraph組件 回顧上一小節的流程 ![image](https://hackmd.io/_uploads/rkF7j_nHC.png =800x) - LangChain的PromptTemplate - [hub/hwchase17/react](https://smith.langchain.com/hub/hwchase17/react) - 使用方式 ```python= # set the LANGCHAIN_API_KEY environment variable (create key in settings) from langchain import hub prompt = hub.pull("hwchase17/react") ``` - 提示模板 - `{tools}`可動態帶入指定工具 - `[{tool_names}]` ```poython= Answer the following questions as best you can. You have access to the following tools: {tools} 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: {input} Thought:{agent_scratchpad} ``` #### LangChain: Tools ![image](https://hackmd.io/_uploads/Hy0epdnSA.png =800x) - 從langchain_community中調用tools ```python= # get a tool from the library from langchain_community.tools.tavily_search import \ TavilySearchResults tool = TavilySearchResults(max_results=2) self.tools = {t.name: t for t in tools} self.model = model.bind_tools([tool]) ``` #### New in LangGraph - **Cyclic Graphs** - **Persistence** - **Human-in-the-loop** ![image](https://hackmd.io/_uploads/Hk7T6OhrR.png) * LangGraph is an extension of LangChain that supports graphs. * Single and Multi-agent flows are described and represented as graphs. * Allows for extremely controlled “flows” * Built-in persistence allows for human-in-the-loop workflows ### Graph 基本組件 * Nodes。節點: 代理或功能 * Edges。邊: 連接節點 * Conditional edges。條件邊: 決定 ![image](https://hackmd.io/_uploads/H1S8Aunr0.png =400x) - 極簡範例 ![image](https://hackmd.io/_uploads/SkNuC_nrC.png =600x) #### Data/State * **代理狀態**可被圖的所有部分訪問 Agent state Is accessible to all parts of the graph * 是圖的本地狀態 it is local to the graph * 可存儲在持久層中 Can be stored in a persistence layer ![image](https://hackmd.io/_uploads/HkjCkYhB0.png =400x) - 範例 ```python= # Simple class AgentState(TypedDict): messages: Annotated[Sequence[BaseMessage], operator.add] # Complex class AgentState(TypedDict): input: str chat_history: list[BaseMessage] agent_outcome: Union[AgentAction, AgentFinish, None] intermediate_steps: Annotatedf[list[tuple[AgentAction, str]], operator.add] ``` - Simple 範例 - 當 `messages` 欄位被更新時,它不是覆蓋原來的值,而是將新的消息添加到已有的消息中。 通過 `Annotated` 和 `operator.add` 來實現的。 - Complex 範例 - 更複雜的 `AgentState` 結構,包括 `input`、`chat_history`、`agent_outcome` 和 `intermediate_steps` - `input`、`chat_history` 和 `agent_outcome` 這三個欄位都沒有進行特別的註釋,這意味著當這些欄位被更新時,新值會覆蓋現有的值 - 唯一例外的是 `intermediate_steps` 欄位,它被註釋了 `operator.add`,這意味著當更新這個欄位時,新的值會添加到現有的值中,而不是覆蓋它 - 為什麼 `intermediate_steps` 要添加而不是覆蓋 - `intermediate_steps` 欄位被設計為追蹤代理行動和觀察的過程 - 這些步驟在代理執行過程中不斷累加,所以每次更新時需要將新步驟添加到現有步驟中,而不是覆蓋掉 - 保留完整的執行記錄,幫助追蹤代理的行動和決策過程 :::info 類似RL中記錄所有STATE ::: - State ![image](https://hackmd.io/_uploads/BysBQFnBC.png =500x) #### Lab 使用LangGraph進行Agent設計實作 :::success 這個範例`Agent`類別的主要功能是處理LLM與外部工具的互動。它使用狀態機(State)來管理調用流程,確保在發現錯誤(例如無效的工具名稱)時能夠適當處理並進行重試。整體流程是: 1. 使用模型生成訊息。 2. 檢查訊息中是否有工具調用。 3. 執行工具調用或重新請求模型生成訊息 4. 重複此流程直到結束。 ::: ```python= class Agent: def __init__(self, model, tools, system=""): self.system = system graph = StateGraph(AgentState) graph.add_node("llm", self.call_openai) graph.add_node("action", self.take_action) graph.add_conditional_edges( "llm", self.exists_action, {True: "action", False: END} ) graph.add_edge("action", "llm") graph.set_entry_point("llm") self.graph = graph.compile() self.tools = {t.name: t for t in tools} self.model = model.bind_tools(tools) def exists_action(self, state: AgentState): result = state['messages'][-1] return len(result.tool_calls) > 0 def call_openai(self, state: AgentState): messages = state['messages'] if self.system: messages = [SystemMessage(content=self.system)] + messages message = self.model.invoke(messages) return {'messages': [message]} def take_action(self, state: AgentState): tool_calls = state['messages'][-1].tool_calls results = [] for t in tool_calls: print(f"Calling: {t}") if not t['name'] in self.tools: # check for bad tool name from LLM print("\n ....bad tool name....") result = "bad tool name, retry" # instruct LLM to retry if bad else: result = self.tools[t['name']].invoke(t['args']) results.append(ToolMessage(tool_call_id=t['id'], name=t['name'], content=str(result))) print("Back to the model!") return {'messages': results} ``` 1. 類別初始化 (`__init__` 方法) - **參數**: - `model`: LLM模型,用於生成回應。 - `tools`: 一個工具列表,這些工具可以被LLM調用。 - `system`: (可選)系統訊息,作為對話的一部分提供給模型。 - **初始化**: - 設定`system`參數為類別屬性。 - 建立`StateGraph`對象來管理代理的狀態和行為。這是一個狀態機,用於控制代理的流程。 - 向狀態圖中添加節點和邊: - 節點 `"llm"`:調用OpenAI模型的函數`call_openai`。 - 節點 `"action"`:執行工具調用的函數`take_action`。 - 條件邊:從`"llm"`節點到`"action"`節點,當`exists_action`返回`True`時,進行此轉換;如果返回`False`,則結束(END)。 - 邊:從`"action"`節點回到`"llm"`節點。 - 設定進入點為`"llm"`。 - 將狀態圖編譯為可執行形式並儲存至`self.graph`。 - 將工具轉換為字典格式,儲存在`self.tools`中。 - 將模型與工具綁定,儲存在`self.model`中。 2. 方法 `exists_action` - **用途**:檢查模型生成的最後一個訊息中是否存在工具調用。 - **邏輯**: - 取得最後一條訊息`result`。 - 如果`result`中包含工具調用,返回`True`,否則返回`False`。 3. 方法 `call_openai` - **用途**:調用OpenAI模型並返回其輸出。 - **邏輯**: - 構建訊息列表,將`system`訊息(如果存在)添加到訊息序列的最前面。 - 調用模型,傳入這些訊息,並取得模型回應。 - 返回字典,包含模型生成的新訊息。 4. 方法 `take_action` - **用途**:執行LLM請求的工具調用,並返回結果。 - **邏輯**: - 取得最後一條訊息中的所有工具調用。 - 初始化`results`列表來儲存每個工具調用的結果。 - 對每個工具調用進行迭代: - 檢查工具名稱是否存在於`self.tools`中。 - 如果名稱無效,回傳"bad tool name, retry",並要求LLM重試。 - 如果名稱有效,調用相應的工具並傳入參數。 - 將每個工具調用的結果儲存為`ToolMessage`對象,並添加到`results`列表中。 - 返回包含這些結果訊息的字典。 ```python= prompt = """You are a smart research assistant. Use the search engine to look up information. \ You are allowed to make multiple calls (either together or in sequence). \ Only look up information when you are sure of what you want. \ If you need to look up some information before asking a follow up question, you are allowed to do that! """ model = ChatOpenAI(model="gpt-3.5-turbo") #reduce inference cost abot = Agent(model, [tool], system=prompt) messages = [HumanMessage(content="What is the weather in sf?")] result = abot.graph.invoke({"messages": messages}) ``` ```python= # Note, the query was modified to produce more consistent results. # Results may vary per run and over time as search information and models change. query = "Who won the super bowl in 2024? In what state is the winning team headquarters located? \ What is the GDP of that state? Answer each question." messages = [HumanMessage(content=query)] model = ChatOpenAI(model="gpt-4o") # requires more advanced model abot = Agent(model, [tool], system=prompt) result = abot.graph.invoke({"messages": messages}) ``` - 繪製graph流程簡圖 ```python= from IPython.display import Image Image(abot.graph.get_graph().draw_png()) ``` ![image](https://hackmd.io/_uploads/rkzJY4DoC.png) class Agent的mermaid詳細邏輯流程圖 ```mermaid graph TD A[llm: call_openai] -->|Model Response| B{exists_action} B -- True --> C[action: take_action] B -- False --> D[END] C --> E{Check Tool Name} E -- Valid --> F[Invoke Tool] E -- Invalid --> G["Return 'bad tool name, retry'"] F --> H["ToolMessage: ToolCallResult"] G --> H H -->|Return to LLM| A style A fill:#B3CDE3,stroke:#000,stroke-width:2px style B fill:#F0E1D6,stroke:#000,stroke-width:2px style C fill:#DECBE4,stroke:#000,stroke-width:2px style D fill:#F0E68C,stroke:#000,stroke-width:2px style E fill:#FFE4E1,stroke:#000,stroke-width:2px style F fill:#E6FFFA,stroke:#000,stroke-width:2px style G fill:#FFD700,stroke:#000,stroke-width:2px style H fill:#FFE4E1,stroke:#000,stroke-width:2px ```