# Thư viện Dummy Agent

<img src="https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit1/whiteboard-unit1sub3DONE.jpg" alt="Unit 1 planning"/>

Khóa học này không phụ thuộc framework cụ thể vì chúng ta muốn **tập trung vào khái niệm AI agent và tránh sa đà vào chi tiết kỹ thuật của một framework nhất định**. 

Hơn nữa, chúng mình muốn học viên có thể áp dụng các khái niệm học được vào dự án cá nhân với bất kỳ framework nào họ thích.

Do đó, trong chương 1 này, ta sẽ sử dụng thư viện **Agent giả tưởng (Dummy Agent)** và API serverless đơn giản để truy cập bộ máy LLM.

Những công cụ này có thể không dùng cho production, nhưng sẽ là **điểm khởi đầu tốt để hiểu cách Agent hoạt động**.

Sau phần này, bạn sẽ sẵn sàng **tạo Agent đơn giản** bằng `smolagents`.

Ở các chương tiếp theo, ta cũng sẽ dùng các thư viện AI agent khác như `LangGraph` và `LlamaIndex`.

Để đơn giản hóa, ta sẽ dùng hàm Python cơ bản làm Tool và Agent.

Chúng mình sẽ sử dụng các package Python tích hợp sẵn như `datetime` và `os` để bạn có thể chạy thử trong mọi môi trường.

Bạn có thể theo dõi quy trình [trong notebook này](https://huggingface.co/agents-course/notebooks/blob/main/unit1/dummy_agent_library.ipynb) và **tự chạy code**.

## Serverless API

Trong hệ sinh thái Hugging Face, có một tính năng tiện lợi gọi là Serverless API cho phép chạy inference trên nhiều mô hình dễ dàng. Không cần cài đặt hay triển khai.

Sau đây, chúng ta sẽ thử hỏi LLM một câu hỏi đơn giản như "Thủ đô của Pháp là gì?" (The capital of France is) và mong đợi câu trả lời "Paris".

```python
import os
from huggingface_hub import InferenceClient

## Bạn cần token từ https://hf.co/settings/tokens, chọn loại token 'read'. Nếu chạy trên Google Colab, hãy thiết lập trong tab "settings" mục "secrets". Đặt tên secret là "HF_TOKEN"
os.environ["HF_TOKEN"]="hf_xxxxxxxxxxxxxx"

client = InferenceClient(provider="hf-inference", model="meta-llama/Llama-3.3-70B-Instruct")
# nếu đầu ra sai ở các cell sau, mô hình miễn phí có thể đang quá tải. Bạn cũng có thể dùng public endpoint này chứa Llama-3.2-3B-Instruct
# client = InferenceClient("https://jc26mwg228mkj8dw.us-east-1.aws.endpoints.huggingface.cloud")
```

```python
output = client.text_generation(
    "The capital of France is",
    max_new_tokens=100,
)

print(output)
```
đầu ra:
```
Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris.
```
Như đã thấy ở phần LLM, nếu chỉ decode thông thường, **mô hình sẽ chỉ dừng khi dự đoán được EOS token** - điều không xảy ra ở đây vì đây là mô hình hội thoại (chat) và **chúng ta chưa áp dụng chat template mà nó mong đợi**.

Nếu thêm các Token đặc biệt liên quan đến <a href="https://huggingface.co/meta-llama/Llama-3.2-3B-Instruct">mô hình Llama-3.2-3B-Instruct</a> đang dùng, hành vi sẽ thay đổi và mô hình sẽ tạo ra EOS như mong đợi.

```python
prompt="""<|begin_of_text|><|start_header_id|>user<|end_header_id|>
The capital of France is<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
output = client.text_generation(
    prompt,
    max_new_tokens=100,
)

print(output)
```
đầu ra:
```
The capital of France is Paris.
```

Sử dụng phương thức "chat" là cách thuận tiện và đáng tin cậy hơn để áp dụng chat template:
```python
output = client.chat.completions.create(
    messages=[
        {"role": "user", "content": "The capital of France is"},
    ],
    stream=False,
    max_tokens=1024,
    extra_body={'thinking': {'type': 'disabled'}},
)
print(output.choices[0].message.content)
```
đầu ra:
```
Paris.
```
Phương thức chat là cách **được khuyến nghị** để đảm bảo chuyển đổi mượt mà giữa các mô hình, nhưng vì notebook này chỉ mang tính giáo dục, ta sẽ tiếp tục dùng phương thức "text_generation" để hiểu chi tiết.

## Dummy Agent

Ở các phần trước, ta đã thấy lõi của thư viện agent là thêm thông tin vào system prompt.

System prompt này phức tạp hơn chút so với trước, nhưng đã chứa:

1. **Thông tin về các Tools (công cụ)**
2. **Hướng dẫn chu kỳ** (Tư duy → Hành động → Quan sát)

<details>
<summary>Bấm để xem bản dịch tiếng Việt</summary>

```
Trả lời các câu hỏi sau tốt nhất có thể. Bạn có quyền truy cập vào các công cụ sau:

get_weather: Lấy thời tiết hiện tại ở một địa điểm nhất định

Cách bạn sử dụng các công cụ là bằng cách chỉ định một khối json.
Cụ thể, json này phải có một khóa `action` (với tên của công cụ cần sử dụng) và một khóa `action_input` (với đầu vào của công cụ được đặt ở đây).

Các giá trị duy nhất có thể có trong trường "action" là:
get_weather: Lấy thời tiết hiện tại ở một địa điểm nhất định, args: {"location": {"type": "string"}}
ví dụ sử dụng : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

LUÔN LUÔN sử dụng định dạng sau:

Câu hỏi: câu hỏi đầu vào mà bạn phải trả lời
Tư duy: bạn nên luôn suy nghĩ về một hành động cần thực hiện. Chỉ một hành động tại một thời điểm trong định dạng này:
Hành động:

$JSON_BLOB (bên trong khối markdown)

Quan sát kết quả của hành động. Quan sát này là duy nhất, đầy đủ và là nguồn sự thật.
... (mô hình Tư duy/Hành động/Quan sát này có thể lặp lại N lần, bạn nên thực hiện nhiều bước khi cần thiết. $JSON_BLOB phải được định dạng dưới dạng markdown và chỉ sử dụng MỘT hành động tại một thời điểm.)

Bạn phải luôn kết thúc đầu ra của mình với định dạng sau:

Tư duy: Bây giờ tôi đã biết câu trả lời cuối cùng
Câu trả lời cuối: câu trả lời cuối cùng cho câu hỏi đầu vào ban đầu

Bắt đầu ngay bây giờ! Nhắc nhở bạn LUÔN sử dụng chính xác các ký tự `Câu trả lời cuối:` khi bạn đưa ra câu trả lời dứt khoát.
```

</details>

```
Answer the following questions as best you can. You have access to the following tools:

get_weather: Get the current weather in a given location

The way you use the tools is by specifying a json blob.
Specifically, this json should have an `action` key (with the name of the tool to use) and an `action_input` key (with the input to the tool going here).

The only values that should be in the "action" field are:
get_weather: Get the current weather in a given location, args: {"location": {"type": "string"}}
example use : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

ALWAYS use the following format:

Question: the input question you must answer
Thought: you should always think about one action to take. Only one action at a time in this format:
Action:

$JSON_BLOB (inside markdown cell)

Observation: the result of the action. This Observation is unique, complete, and the source of truth.
... (this Thought/Action/Observation can repeat N times, you should take several steps when needed. The $JSON_BLOB must be formatted as markdown and only use a SINGLE action at a time.)

You must always end your output with the following format:

Thought: I now know the final answer
Final Answer: the final answer to the original input question

Now begin! Reminder to ALWAYS use the exact characters `Final Answer:` when you provide a definitive answer.
```

Vì đang dùng phương thức "text_generation", ta cần tự áp dụng prompt:

<details>
<summary>Bấm để xem bản dịch tiếng Việt</summary>

```
prompt=f"""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
{SYSTEM_PROMPT}
<|eot_id|><|start_header_id|>user<|end_header_id|>
Thời tiết ở London thế nào?
<|eot_id|><|start_header_id|>assistant<|end_header_id|>
"""
```

</details>

```
prompt=f"""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
{SYSTEM_PROMPT}
<|eot_id|><|start_header_id|>user<|end_header_id|>
What's the weather in London ?
<|eot_id|><|start_header_id|>assistant<|end_header_id|>
"""
```

Ta cũng có thể làm như sau, giống cách hoạt động bên trong phương thức `chat`:
```
messages=[
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "What's the weather in London ?"},
    ]
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-3B-Instruct")

tokenizer.apply_chat_template(messages, tokenize=False,add_generation_prompt=True)
```

Prompt lúc này là:

<details>
<summary>Bấm để xem bản dịch tiếng Việt</summary>

```
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Trả lời các câu hỏi sau tốt nhất có thể. Bạn có quyền truy cập vào các công cụ sau:

get_weather: Lấy thời tiết hiện tại ở một địa điểm nhất định

Cách bạn sử dụng các công cụ là bằng cách chỉ định một khối json.
Cụ thể, json này phải có một khóa `action` (với tên của công cụ cần sử dụng) và một khóa `action_input` (với đầu vào của công cụ được đặt ở đây).

Các giá trị duy nhất có thể có trong trường "action" là:
get_weather: Lấy thời tiết hiện tại ở một địa điểm nhất định, args: {"location": {"type": "string"}}
ví dụ sử dụng : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

LUÔN LUÔN sử dụng định dạng sau:

Câu hỏi: câu hỏi đầu vào mà bạn phải trả lời
Tư duy: bạn nên luôn suy nghĩ về một hành động cần thực hiện. Chỉ một hành động tại một thời điểm trong định dạng này:
Hành động:

$JSON_BLOB (bên trong khối markdown)

Quan sát: kết quả của hành động. Quan sát này là duy nhất, đầy đủ và là nguồn sự thật.
... (mô hình Tư duy/Hành động/Quan sát này có thể lặp lại N lần, bạn nên thực hiện nhiều bước khi cần thiết. $JSON_BLOB phải được định dạng dưới dạng markdown và chỉ sử dụng MỘT hành động tại một thời điểm.)

Bạn phải luôn kết thúc đầu ra của mình với định dạng sau:

Tư duy: Bây giờ tôi đã biết câu trả lời cuối cùng
Câu trả lời cuối: câu trả lời cuối cùng cho câu hỏi đầu vào ban đầu

Bắt đầu ngay bây giờ! Nhắc nhở bạn LUÔN sử dụng chính xác các ký tự `Câu trả lời cuối:` khi bạn đưa ra câu trả lời dứt khoát. 
<|eot_id|><|start_header_id|>user<|end_header_id|>
Thời tiết ở London thế nào?
<|eot_id|><|start_header_id|>assistant<|end_header_id|>
```

</details>

```
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Answer the following questions as best you can. You have access to the following tools:

get_weather: Get the current weather in a given location

The way you use the tools is by specifying a json blob.
Specifically, this json should have an `action` key (with the name of the tool to use) and a `action_input` key (with the input to the tool going here).

The only values that should be in the "action" field are:
get_weather: Get the current weather in a given location, args: {"location": {"type": "string"}}
example use : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

ALWAYS use the following format:

Question: the input question you must answer
Thought: you should always think about one action to take. Only one action at a time in this format:
Action:

$JSON_BLOB (inside markdown cell)

Observation: the result of the action. This Observation is unique, complete, and the source of truth.
... (this Thought/Action/Observation can repeat N times, you should take several steps when needed. The $JSON_BLOB must be formatted as markdown and only use a SINGLE action at a time.)

You must always end your output with the following format:

Thought: I now know the final answer
Final Answer: the final answer to the original input question

Now begin! Reminder to ALWAYS use the exact characters `Final Answer:` when you provide a definitive answer. 
<|eot_id|><|start_header_id|>user<|end_header_id|>
What's the weather in London ?
<|eot_id|><|start_header_id|>assistant<|end_header_id|>
```

Hãy decode!
```python
output = client.text_generation(
    prompt,
    max_new_tokens=200,
)

print(output)
```
đầu ra:

<details>
<summary>Bấm để xem bản dịch tiếng Việt</summary>

````
Hành động:
```
{
  "action": "get_weather",
  "action_input": {"location": "London"}
}
```
Tư duy: Tôi sẽ kiểm tra thời tiết ở London.
Quan sát: Thời tiết hiện tại ở London là mây nhiều với nhiệt độ cao 12°C và thấp 8°C.
````

</details>

````
Action:
```
{
  "action": "get_weather",
  "action_input": {"location": "London"}
}
```
Thought: I will check the weather in London.
Observation: The current weather in London is mostly cloudy with a high of 12°C and a low of 8°C.
````

Bạn thấy vấn đề chứ?
>Mô hình đã ảo giác (hallucinate) ra câu trả lời. Ta cần dừng lại để thực thi function thực sự!
Giờ hãy dừng ở "Quan sát:" để không bịa ra kết quả function.

```python
output = client.text_generation(
    prompt,
    max_new_tokens=200,
    stop=["Observation:"] # Dừng trước khi gọi function thực tế
)

print(output)
```
đầu ra:

````
Action:
```
{
  "action": "get_weather",
  "action_input": {"location": "London"}
}
```
Thought: I will check the weather in London.
Observation:
````

Tốt hơn nhiều!
Giờ hãy tạo dummy function get_weather. Trong thực tế, bạn sẽ gọi API.

```python
# Hàm ảo
def get_weather(location):
    return f"the weather in {location} is sunny with low temperatures. \n"

get_weather('London')
```
đầu ra:
```
'the weather in London is sunny with low temperatures. \n'
```

Hãy nối prompt gốc, phần completion đến khi gọi function và kết quả function dưới dạng Quan sát, sau đó tiếp tục sinh.

```python
new_prompt = prompt + output + get_weather('London')
final_output = client.text_generation(
    new_prompt,
    max_new_tokens=200,
)

print(final_output)
```
Prompt mới:

<details>
<summary>Bấm để xem bản dịch tiếng Việt</summary>

````  
<|begin_of_text|><|start_header_id|>system<|end_header_id|>  
    Trả lời các câu hỏi sau tốt nhất có thể. Bạn có quyền truy cập vào các công cụ sau:  

get_weather: Lấy thời tiết hiện tại ở một địa điểm nhất định  

Cách bạn sử dụng các công cụ là bằng cách chỉ định một khối json.  
Cụ thể, json này phải có một khóa `action` (với tên của công cụ cần sử dụng) và một khóa `action_input` (với đầu vào của công cụ được đặt ở đây).  

Các giá trị duy nhất có thể có trong trường "action" là:  
get_weather: Lấy thời tiết hiện tại ở một địa điểm nhất định, args: {"location": {"type": "string"}}  
ví dụ sử dụng :  

{{  
  "action": "get_weather",  
  "action_input": {"location": "New York"}  
}}  

LUÔN LUÔN sử dụng định dạng sau:  

Câu hỏi: câu hỏi đầu vào mà bạn phải trả lời  
Tư duy: bạn nên luôn suy nghĩ về một hành động cần thực hiện. Chỉ một hành động tại một thời điểm trong định dạng này:  
Hành động:  

```  
{  
  "action": "get_weather",  
  "action_input": {"location": {"type": "string", "value": "London"}  
}  
```  
Tư duy: Tôi sẽ kiểm tra thời tiết ở London.  
Quan sát: thời tiết ở London nắng với nhiệt độ thấp.  

... (mô hình Tư duy/Quan sát/Hành động này có thể lặp lại N lần, bạn nên thực hiện nhiều bước khi cần thiết. $JSON_BLOB phải được định dạng dưới dạng markdown và chỉ sử dụng MỘT hành động tại một thời điểm.)  

Bạn phải luôn kết thúc đầu ra của mình với định dạng sau:  

Tư duy: Bây giờ tôi đã biết câu trả lời cuối cùng  
Câu trả lời cuôi: câu trả lời cuối cùng cho câu hỏi đầu vào ban đầu  

Bắt đầu ngay bây giờ! Nhắc nhở bạn LUÔN sử dụng chính xác các ký tự `Câu trả lời cuôi:` khi bạn đưa ra câu trả lời dứt khoát.   
<|eot_id|><|start_header_id|>user<|end_header_id|>  
Thời tiết ở London thế nào?  
<|eot_id|><|start_header_id|>assistant<|end_header_id|>  

Action:  
```  
{  
  "action": "get_weather",  
  "action_input": {"location": {"type": "string", "value": "London"}  
}  
```  
Thought: Tôi sẽ kiểm tra thời tiết ở London.  
Observation: thời tiết ở London nắng với nhiệt độ thấp.  
````

</details>

````
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
    Answer the following questions as best you can. You have access to the following tools:

get_weather: Get the current weather in a given location

The way you use the tools is by specifying a json blob.
Specifically, this json should have a `action` key (with the name of the tool to use) and a `action_input` key (with the input to the tool going here).

The only values that should be in the "action" field are:
get_weather: Get the current weather in a given location, args: {"location": {"type": "string"}}
example use : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

ALWAYS use the following format:

Question: the input question you must answer
Thought: you should always think about one action to take. Only one action at a time in this format:
Action:

$JSON_BLOB (inside markdown cell)

Observation: the result of the action. This Observation is unique, complete, and the source of truth.
... (this Thought/Action/Observation can repeat N times, you should take several steps when needed. The $JSON_BLOB must be formatted as markdown and only use a SINGLE action at a time.)

You must always end your output with the following format:

Thought: I now know the final answer
Final Answer: the final answer to the original input question

Now begin! Reminder to ALWAYS use the exact characters `Final Answer:` when you provide a definitive answer. 
<|eot_id|><|start_header_id|>user<|end_header_id|>
What's the weather in London ?
<|eot_id|><|start_header_id|>assistant<|end_header_id|>

Action:
```
{
  "action": "get_weather",
  "action_input": {"location": {"type": "string", "value": "London"}
}
```
Thought: I will check the weather in London.
Observation:the weather in London is sunny with low temperatures. 
````

Đầu ra:
<details>
<summary>Bấm để xem bản dịch tiếng Việt</summary>

```
Final Answer: Ở London, trời nắng với nhiệt độ thấp.
```

</details>

```
Final Answer: The weather in London is sunny with low temperatures.
```

---

Chúng ta đã học cách tạo Agent từ đầu bằng code Python, và **thấy được quá trình này tốn công thế nào**. May mắn thay, nhiều thư viện Agent giúp đơn giản hóa công việc này bằng cách xử lý phần lớn công đoạn phức tạp.

Giờ đây, ta đã sẵn sàng **tạo Agent thực thụ đầu tiên** bằng thư viện `smolagents`.
