Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .antigravitycli/26cee464-e7a7-497a-b06f-b4af6a61e4a6.json
2 changes: 1 addition & 1 deletion README-ja.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Learn Claude Code -- 真の Agent のための Harness Engineering

[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md)
[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md) | [Tiếng Việt](./README-vi.md)

## Agency はモデルから生まれる。Agent プロダクト = モデル + Harness

Expand Down
377 changes: 377 additions & 0 deletions README-vi.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README-zh.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Learn Claude Code -- 真正的 Agent Harness 工程

[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md)
[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md) | [Tiếng Việt](./README-vi.md)

## Agency 来自模型,Agent 产品 = 模型 + Harness

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md)
[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md) | [Tiếng Việt](./README-vi.md)
# Learn Claude Code -- Harness Engineering for Real Agents
<a href="https://trendshift.io/repositories/19746" target="_blank"><img src="https://trendshift.io/api/badge/repositories/19746" alt="shareAI-lab%2Flearn-claude-code | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
## Agency Comes from the Model. An Agent Product = Model + Harness.
Expand Down
116 changes: 116 additions & 0 deletions docs/vi/s01-the-agent-loop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# s01: Vòng lặp Agent (The Agent Loop)

`[ s01 ] s02 > s03 > s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12`

> *"Một vòng lặp & Bash là tất cả những gì bạn cần"* -- một công cụ + một vòng lặp = một agent.
>
> **Lớp Harness**: Vòng lặp -- kết nối đầu tiên của model với thế giới thực.

## Vấn đề (Problem)

Một mô hình ngôn ngữ có thể tư duy về code, nhưng nó không thể *chạm* vào thế giới thực -- không thể đọc file, chạy kiểm thử, hoặc kiểm tra lỗi. Nếu không có vòng lặp, mỗi lần gọi công cụ bạn lại phải copy-paste kết quả ngược trở lại một cách thủ công. Bạn chính là vòng lặp đó.

## Giải pháp (Solution)

```
+--------+ +-------+ +---------+
| User | ---> | LLM | ---> | Tool |
| prompt | | | | execute |
+--------+ +---+---+ +----+----+
^ |
| tool_result |
+----------------+
(vòng lặp cho đến khi stop_reason != "tool_use")
```

Một điều kiện thoát kiểm soát toàn bộ luồng. Vòng lặp chạy cho đến khi model dừng gọi các công cụ.

## Cách hoạt động (How It Works)

1. Prompt của người dùng trở thành tin nhắn đầu tiên.

```python
messages.append({"role": "user", "content": query})
```

2. Gửi các tin nhắn + định nghĩa công cụ tới LLM.

```python
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
```

3. Thêm phản hồi của trợ lý vào danh sách. Kiểm tra `stop_reason` -- nếu model không gọi công cụ, chúng ta đã hoàn thành.

```python
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return
```

4. Thực thi từng yêu cầu gọi công cụ, thu thập kết quả, thêm vào danh sách dưới dạng tin nhắn của người dùng. Quay lại bước 2.

```python
results = []
for block in response.content:
if block.type == "tool_use":
output = run_bash(block.input["command"])
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})
```

Tất cả được gộp vào một hàm:

```python
def agent_loop(query):
messages = [{"role": "user", "content": query}]
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})

if response.stop_reason != "tool_use":
return

results = []
for block in response.content:
if block.type == "tool_use":
output = run_bash(block.input["command"])
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})
```

Đó là toàn bộ một agent trong chưa đầy 30 dòng code. Mọi thứ khác trong khóa học này sẽ được xây dựng chồng lên trên -- mà không thay đổi vòng lặp này.

## Những gì đã thay đổi (What Changed)

| Thành phần | Trước | Sau |
|----------------|-----------|---------------------------------|
| Vòng lặp Agent | (không có)| `while True` + stop_reason |
| Công cụ | (không có)| `bash` (một công cụ) |
| Tin nhắn | (không có)| Danh sách tích lũy |
| Luồng điều khiển| (không có)| `stop_reason != "tool_use"` |

## Dùng thử (Try It)

```sh
cd learn-claude-code
python agents/s01_agent_loop.py
```

1. `Tạo một file tên là hello.py in ra "Hello, World!"`
2. `Liệt kê tất cả các file Python trong thư mục này`
3. `Nhánh git hiện tại là gì?`
4. `Tạo một thư mục tên là test_output và ghi 3 file vào đó`
100 changes: 100 additions & 0 deletions docs/vi/s02-tool-use.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# s02: Sử dụng công cụ

`s01 > [ s02 ] s03 > s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12`

> *"Thêm một công cụ có nghĩa là thêm một trình xử lý (handler)"* -- vòng lặp vẫn giữ nguyên; các công cụ mới được đăng ký vào bản đồ điều phối (dispatch map).
>
> **Lớp khung (Harness layer)**: Điều phối công cụ -- mở rộng những gì mô hình có thể tiếp cận.

## Vấn đề

Chỉ với `bash`, agent phải thực hiện mọi thứ thông qua shell. `cat` cắt bớt nội dung một cách khó dự đoán, `sed` thất bại với các ký tự đặc biệt, và mỗi lần gọi bash là một bề mặt bảo mật không bị giới hạn. Các công cụ chuyên dụng như `read_file` và `write_file` cho phép bạn thực thi việc đóng gói đường dẫn (path sandboxing) ở cấp độ công cụ.

Điểm mấu chốt: thêm công cụ không yêu cầu thay đổi vòng lặp.

## Giải pháp

```
+--------+ +-------+ +-----------------------+
| User | ---> | LLM | ---> | Điều phối công cụ |
| prompt | | | | { |
+--------+ +---+---+ | bash: run_bash |
^ | read: run_read |
| | write: run_wr |
+-----------+ edit: run_edit |
tool_result | } |
+-----------------------+

Bản đồ điều phối là một từ điển: {tool_name: handler_function}.
Một lần tra cứu thay thế bất kỳ chuỗi if/elif nào.
```

## Cách hoạt động

1. Mỗi công cụ có một hàm xử lý (handler function). Việc đóng gói đường dẫn (path sandboxing) ngăn chặn việc thoát khỏi không gian làm việc (workspace escape).

```python
def safe_path(p: str) -> Path:
# Đảm bảo đường dẫn nằm trong WORKDIR
path = (WORKDIR / p).resolve()
if not path.is_relative_to(WORKDIR):
raise ValueError(f"Đường dẫn thoát khỏi không gian làm việc: {p}")
return path

def run_read(path: str, limit: int = None) -> str:
text = safe_path(path).read_text()
lines = text.splitlines()
if limit and limit < len(lines):
lines = lines[:limit]
return "\n".join(lines)[:50000]
```

2. Bản đồ điều phối liên kết tên công cụ với các trình xử lý.

```python
TOOL_HANDLERS = {
"bash": lambda **kw: run_bash(kw["command"]),
"read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
"write_file": lambda **kw: run_write(kw["path"], kw["content"]),
"edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"],
kw["new_text"]),
}
```

3. Trong vòng lặp, tra cứu trình xử lý theo tên. Thân vòng lặp không thay đổi so với s01.

```python
for block in response.content:
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
output = handler(**block.input) if handler \
else f"Công cụ không xác định: {block.name}"
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
```

Thêm một công cụ = thêm một trình xử lý + thêm một mục nhập lược đồ (schema entry). Vòng lặp không bao giờ thay đổi.

## Những gì đã thay đổi so với s01

| Thành phần | Trước (s01) | Sau (s02) |
|--------------------|-----------------------|--------------------------------|
| Công cụ | 1 (chỉ bash) | 4 (bash, read, write, edit) |
| Điều phối | Gọi bash được mã hóa cứng | Từ điển `TOOL_HANDLERS` |
| An toàn đường dẫn | Không có | Sandbox `safe_path()` |
| Vòng lặp Agent | Không đổi | Không đổi |

## Thử nghiệm

```sh
cd learn-claude-code
python agents/s02_tool_use.py
```

1. `Đọc tệp requirements.txt`
2. `Tạo một tệp tên là greet.py với hàm greet(name)`
3. `Chỉnh sửa greet.py để thêm docstring vào hàm`
4. `Đọc greet.py để kiểm tra việc chỉnh sửa đã hoạt động`
96 changes: 96 additions & 0 deletions docs/vi/s03-todo-write.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# s03: TodoWrite

`s01 > s02 > [ s03 ] s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12`

> *"Một agent không có kế hoạch sẽ bị mất phương hướng"* -- liệt kê các bước trước, sau đó mới thực hiện.
>
> **Lớp khung (Harness layer)**: Lập kế hoạch -- giữ cho mô hình đi đúng hướng mà không cần lập trình sẵn lộ trình.

## Vấn đề

Đối với các tác vụ nhiều bước, mô hình dễ bị mất dấu. Nó lặp lại công việc, bỏ sót các bước hoặc đi chệch hướng. Các cuộc hội thoại dài làm cho điều này tệ hơn -- system prompt bị mờ nhạt dần khi kết quả công cụ lấp đầy ngữ cảnh. Một công việc tái cấu trúc (refactoring) 10 bước có thể hoàn thành các bước 1-3, sau đó mô hình bắt đầu tự ý ứng biến vì nó đã quên các bước từ 4-10.

## Giải pháp

```
+--------+ +-------+ +-------------+
| User | ---> | LLM | ---> | Công cụ |
| prompt | | | | + todo |
+--------+ +---+---+ +------+------+
^ |
| tool_result |
+-----------------+
|
+-----------+-----------+
| Trạng thái TodoManager|
| [ ] tác vụ A |
| [>] tác vụ B <- đang làm |
| [x] tác vụ C |
+-----------------------+
|
nếu số_vòng_từ_khi_todo >= 3:
chèn <reminder> vào tool_result
```

## Cách hoạt động

1. TodoManager lưu trữ các mục với các trạng thái. Tại một thời điểm chỉ có một mục có thể ở trạng thái `in_progress` (đang thực hiện).

```python
class TodoManager:
def update(self, items: list) -> str:
validated, in_progress_count = [], 0
for item in items:
status = item.get("status", "pending")
if status == "in_progress":
in_progress_count += 1
validated.append({"id": item["id"], "text": item["text"],
"status": status})
if in_progress_count > 1:
raise ValueError("Chỉ một tác vụ có thể ở trạng thái in_progress")
self.items = validated
return self.render()
```

2. Công cụ `todo` được đưa vào bản đồ điều phối giống như bất kỳ công cụ nào khác.

```python
TOOL_HANDLERS = {
# ...các công cụ cơ bản...
"todo": lambda **kw: TODO.update(kw["items"]),
}
```

3. Một lời nhắc nhở (nag reminder) sẽ được chèn vào nếu mô hình trải qua 3+ vòng lặp mà không gọi `todo`.

```python
if rounds_since_todo >= 3 and messages:
last = messages[-1]
if last["role"] == "user" and isinstance(last.get("content"), list):
last["content"].insert(0, {
"type": "text",
"text": "<reminder>Cập nhật các việc cần làm (todo) của bạn.</reminder>",
})
```

Ràng buộc "chỉ một việc đang thực hiện tại một thời điểm" buộc agent phải tập trung tuần tự. Lời nhắc nhở tạo ra tính trách nhiệm.

## Những gì đã thay đổi so với s02

| Thành phần | Trước (s02) | Sau (s03) |
|--------------------|----------------------|--------------------------------|
| Công cụ | 4 | 5 (+todo) |
| Lập kế hoạch | Không có | TodoManager với các trạng thái |
| Chèn nhắc nhở | Không có | `<reminder>` sau 3 vòng lặp |
| Vòng lặp Agent | Điều phối đơn giản | + bộ đếm số_vòng_từ_khi_todo |

## Thử nghiệm

```sh
cd learn-claude-code
python agents/s03_todo_write.py
```

1. `Tái cấu trúc tệp hello.py: thêm gợi ý kiểu (type hints), chuỗi tài liệu (docstrings) và main guard`
2. `Tạo một gói Python với __init__.py, utils.py và tests/test_utils.py`
3. `Kiểm tra tất cả các tệp Python và sửa các lỗi phong cách trình bày (style issues)`
Loading