### [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組件
回顧上一小節的流程

- 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

- 從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**

* 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。條件邊: 決定

- 極簡範例

#### 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

- 範例
```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

#### 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())
```

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
```