paint-brush
Cách xây dựng một tác nhân với trợ lý OpenAI trong Python - Phần 1: Đàm thoạitừ tác giả@jeanmaried
3,575 lượt đọc
3,575 lượt đọc

Cách xây dựng một tác nhân với trợ lý OpenAI trong Python - Phần 1: Đàm thoại

từ tác giả Jean-Marie Dalmasso12m2024/02/09
Read on Terminal Reader

dài quá đọc không nổi

Bây giờ chúng tôi chạy vòng lặp while để kiểm tra trạng thái đã hoàn thành trong khi xử lý một số trường hợp lỗi. Việc thanh toán thực tế của API Trợ lý hơi mơ hồ, vì vậy để đảm bảo an toàn, tôi đã chọn hủy các lần chạy của mình sau 2 phút. Mặc dù có trạng thái hết hạn khi OpenAI hủy chạy sau 10 phút. Nếu quá trình chạy mất hơn 2 phút thì có thể bạn đang gặp sự cố.
featured image - Cách xây dựng một tác nhân với trợ lý OpenAI trong Python - Phần 1: Đàm thoại
Jean-Marie Dalmasso HackerNoon profile picture

Đây là phần đầu tiên trong loạt bài gồm nhiều phần về cách xây dựng Đại lý bằng API Trợ lý của OpenAI bằng SDK Python.

Đại lý là gì?

Theo cách tôi muốn nhìn nhận, tác nhân thực sự chỉ là một phần mềm tận dụng LLM (Mô hình ngôn ngữ lớn) và cố gắng bắt chước hành vi của con người. Điều đó có nghĩa là nó không chỉ có thể trò chuyện và hiểu ngôn ngữ mà còn có thể thực hiện các hành động có tác động đến thế giới thực. Những hành động này thường được gọi là công cụ.


Trong bài đăng trên blog này, chúng ta sẽ khám phá cách xây dựng một tác nhân bằng API Trợ lý của OpenAI bằng SDK Python của họ. Phần 1 sẽ chỉ là bộ xương của trợ lý. Đó là, chỉ là phần đàm thoại.


Tôi đã chọn xây dựng một ứng dụng CLI nhằm mục đích không phụ thuộc vào khuôn khổ. Chúng tôi sẽ cố tình gọi việc triển khai của mình là Tác nhân và gọi việc triển khai OpenAI SDK là Trợ lý để dễ dàng phân biệt giữa hai điều này.


Tôi sử dụng thuật ngữ công cụchức năng thay thế cho nhau khi nói đến các chức năng mà Tác nhân có thể gọi. Phần 2 sẽ trình bày chi tiết hơn về hàm callin.

Điều kiện tiên quyết

Để làm theo hướng dẫn này, bạn sẽ cần những điều sau:


  • Python3 được cài đặt trên máy của bạn
  • Khóa API OpenAI
  • Kiến thức cơ bản về lập trình Python

Khái niệm trợ lý OpenAI

Trợ lý : Trợ lý trong API Trợ lý là một thực thể được định cấu hình để phản hồi tin nhắn của người dùng. Nó sử dụng các hướng dẫn, mô hình đã chọn và các công cụ để tương tác với các chức năng và đưa ra câu trả lời.


Chủ đề : Chủ đề đại diện cho một cuộc trò chuyện hoặc cuộc đối thoại trong API Trợ lý. Nó được tạo cho mỗi tương tác của người dùng và có thể chứa nhiều Tin nhắn, đóng vai trò là nơi chứa đựng cuộc trò chuyện đang diễn ra.


Message : Message là một đơn vị giao tiếp trong Thread. Nó chứa văn bản (và các tệp có thể có trong tương lai) và được sử dụng để truyền tải các truy vấn của người dùng hoặc phản hồi của trợ lý trong Chủ đề.


Chạy : Chạy là một phiên bản Trợ lý đang xử lý một Chủ đề. Nó liên quan đến việc đọc Chủ đề, quyết định có nên gọi các công cụ hay không và tạo phản hồi dựa trên việc giải thích Thông báo của Chủ đề của mô hình.

Thiết lập môi trường phát triển

Bước đầu tiên là tạo môi trường ảo bằng venv và kích hoạt nó. Điều này sẽ đảm bảo rằng các phần phụ thuộc của chúng tôi được tách biệt khỏi quá trình cài đặt Python của hệ thống:

 python3 -m venv venv source venv/bin/activate


Hãy cài đặt phần phụ thuộc duy nhất của chúng ta: gói openai :

 pip install openai


Tạo một tệp main.py Hãy điền một số logic thời gian chạy cơ bản cho ứng dụng CLI của chúng ta:

 while True: user_input = input("User: ") if user_input.lower() == 'exit': print("Exiting the assistant...") break print(f"Assistant: You said {user_input}")


Hãy dùng thử bằng cách chạy python3 main.py :

 python3 main.py User: hi Assistant: You said hi


Như bạn có thể thấy, CLI chấp nhận tin nhắn của Người dùng làm đầu vào và Trợ lý thiên tài của chúng ta vẫn chưa có não 🧠 nên anh ấy chỉ lặp lại tin nhắn đó ngay lập tức. Vẫn chưa thông minh lắm.

Đại lý

Bây giờ, cuộc vui 😁 (hay đau đầu 🤕) bắt đầu. Tôi sẽ cung cấp tất cả nội dung nhập cần thiết cho lớp cuối cùng ngay bây giờ, để bạn không phải đắn đo xem mọi thứ đến từ đâu vì tôi đã liên tục nhập các mẫu mã để cho ngắn gọn. Hãy bắt đầu bằng cách xây dựng một lớp Agent trong một tệp mới agent.py :

 import time import openai from openai.types.beta.threads.run import Run class Agent: def __init__(self, name: str, personality: str): self.name = name self.personality = personality self.client = openai.OpenAI(api_key="sk-*****") self.assistant = self.client.beta.assistants.create( name=self.name, model="gpt-4-turbo-preview" )


Trong hàm tạo lớp, chúng tôi khởi tạo ứng dụng khách OpenAI làm thuộc tính lớp bằng cách chuyển khóa API OpenAI của chúng tôi. Tiếp theo, chúng tôi tạo một thuộc tính lớp assistant ánh xạ tới Trợ lý mới tạo của chúng tôi. Chúng tôi lưu trữ namepersonality thuộc tính lớp để sử dụng sau này.


Đối số name mà chúng ta đang chuyển đến phương thức tạo chỉ nhằm mục đích xác định Trợ lý trong bảng điều khiển OpenAI và AI thực sự không biết về điều đó vào thời điểm này. Bạn thực sự phải chuyển tên cho instructions mà chúng ta sẽ thấy sau.


Bạn đã có thể đặt instructions khi tạo Trợ lý, nhưng điều đó thực sự sẽ khiến Trợ lý của bạn kém linh hoạt hơn trước những thay đổi linh hoạt.


Bạn có thể cập nhật Trợ lý bằng cách gọi client.beta.assistants.update nhưng có một nơi tốt hơn để chuyển các giá trị động mà chúng ta sẽ thấy khi chuyển sang Chạy.


Lưu ý rằng nếu bạn chuyển đi instructions ở đây khi tạo Cuộc chạy, instructions của Trợ lý sẽ bị ghi đè bằng instructions của cuộc chạy. Chúng không bổ sung cho nhau, vì vậy hãy chọn một tùy theo nhu cầu của bạn: Cấp độ Trợ lý cho hướng dẫn tĩnh hoặc Cấp độ Chạy cho hướng dẫn động.


Đối với mô hình, tôi đã chọn mô hình gpt-4-turbo-preview để chúng ta có thể thêm chức năng gọi trong phần 2 của loạt bài này. Bạn có thể sử dụng gpt-3.5-turbo nếu bạn muốn tiết kiệm một vài xu trong khi vẫn cảm thấy đau đầu vì thất vọng khi chúng tôi triển khai các công cụ.


GPT 3.5 có công cụ gọi điện rất tệ; những giờ tôi đã mất để cố gắng giải quyết nó cho phép tôi nói điều đó. 😝 Tôi sẽ để nó ở đó và nói thêm về điều này sau.

Tạo chủ đề, thêm tin nhắn và truy xuất tin nhắn cuối cùng

Sau khi tạo một tổng đài viên, chúng ta sẽ cần bắt đầu một chuỗi hội thoại.

 class Agent: # ... (rest of code) def create_thread(self): self.thread = self.client.beta.threads.create()


Và chúng tôi sẽ muốn có cách thêm tin nhắn vào chuỗi đó:

 class Agent: # ... (rest of code) def add_message(self, message): self.client.beta.threads.messages.create( thread_id=self.thread.id, role="user", content=message )


Lưu ý rằng hiện tại, chỉ có thể thêm tin nhắn với vai trò user . Tôi tin rằng OpenAI có kế hoạch thay đổi điều này trong phiên bản tương lai vì điều này khá hạn chế.


Bây giờ, chúng ta có thể nhận được tin nhắn cuối cùng trong chuỗi:

 class Agent: # ... (rest of code) def get_last_message(self): return self.client.beta.threads.messages.list( thread_id=self.thread.id ).data[0].content[0].text.value


Tiếp theo, chúng ta tạo một phương thức run_agent điểm vào để kiểm tra những gì chúng ta có cho đến nay. Hiện tại, phương thức run_agent chỉ trả về tin nhắn cuối cùng trong chuỗi. Nó không thực sự thực hiện Chạy. Nó vẫn còn thiếu não.

 class Agent: # ... (rest of code) def run_agent(self): message = self.get_last_message() return message


Quay lại main.py , chúng ta tạo tác nhân và luồng đầu tiên. Chúng tôi thêm một tin nhắn vào chủ đề. Sau đó trả lại tin nhắn đó cho người dùng, nhưng lần này, đến từ chuỗi trực tiếp đó.

 from agent import Agent agent = Agent(name="Bilbo Baggins", personality="You are the accomplished and renowned adventurer from The Hobbit. You act like you are a bit of a homebody, but you are always up for an adventure. You worry a bit too much about breakfast.") agent.create_thread() while True: user_input = input("User: ") if user_input.lower() == 'exit': print("Exiting the agent...") break agent.add_message(user_input) answer = agent.run_agent() print(f"Assistant: {answer}")


Hãy chạy nó:

 python3 main.py User: hi Assistant: hi


Vẫn chưa thông minh lắm. Gần giống một con vẹt 🦜 hơn là một người Hobbit. Trong phần tiếp theo, niềm vui thực sự bắt đầu.

Tạo và bỏ phiếu cho một cuộc chạy

Khi tạo một lần chạy, bạn cần truy xuất định kỳ đối tượng Run để kiểm tra trạng thái của lần chạy. Đây được gọi là bỏ phiếu, và nó thật tệ. Bạn cần thăm dò ý kiến để xác định xem đại lý của bạn nên làm gì tiếp theo. OpenAI có kế hoạch bổ sung hỗ trợ phát trực tuyến để làm cho việc này đơn giản hơn. Trong lúc chờ đợi, tôi sẽ hướng dẫn bạn cách thiết lập bỏ phiếu trong phần tiếp theo.


Lưu ý _ trên các tên phương thức sau đây là tiêu chuẩn trong Python để chỉ ra rằng phương thức này được thiết kế để sử dụng nội bộ và không được truy cập trực tiếp bằng mã bên ngoài.


Trước tiên, hãy tạo một phương thức trợ giúp _create_run để tạo Run và cập nhật run_agent để gọi phương thức này:

 class Agent: # ... (rest of code) def get_breakfast_count_from_db(self): return 1 def _create_run(self): count = self.get_breakfast_count_from_db() return self.client.beta.threads.runs.create( thread_id=self.thread.id, assistant_id=self.assistant.id, instructions=f""" Your name is: {self.name} Your personality is: {self.personality} Metadata related to this conversation: {{ "breakfast_count": {count} }} """, ) def run_agent(self): run = self._create_run() # add this line message = self.get_last_message() return message


Lưu ý cách chúng tôi chuyển thread.idassistant.id để tạo một lượt chạy.


Hãy nhớ lúc đầu tôi đã nói rằng có một nơi tốt hơn để truyền các hướng dẫn và dữ liệu động? Đó sẽ là tham số instructions khi tạo Run. Trong trường hợp của chúng tôi, chúng tôi có thể lấy count bữa sáng từ cơ sở dữ liệu. Điều này sẽ cho phép bạn dễ dàng chuyển các dữ liệu động có liên quan khác nhau mỗi khi bạn muốn kích hoạt câu trả lời.


Bây giờ, đại lý của bạn nhận thức được thế giới đang thay đổi xung quanh nó và có thể hành động tương ứng. Tôi muốn có một đối tượng JSON siêu dữ liệu trong hướng dẫn của mình để lưu giữ bối cảnh động có liên quan. Điều này cho phép tôi truyền dữ liệu ít dài dòng hơn và ở định dạng mà LLM thực sự hiểu rõ.


Đừng chạy cái này vội; nó sẽ không hoạt động vì chúng tôi không đợi quá trình chạy hoàn tất khi chúng tôi nhận được tin nhắn cuối cùng, vì vậy nó vẫn sẽ là tin nhắn cuối cùng của người dùng.


Hãy giải quyết vấn đề này bằng cách xây dựng cơ chế bỏ phiếu của chúng ta. Trước tiên, chúng ta sẽ cần một cách để truy xuất một lần chạy nhiều lần và dễ dàng, vì vậy hãy thêm phương thức _retrieve_run :

 class Agent: # ... (rest of code) def _retrieve_run(self, run: Run): return self.client.beta.threads.runs.retrieve( run_id=run.id, thread_id=self.thread.id)


Lưu ý cách chúng ta cần chuyển cả run.idthread.id để tìm một lần chạy cụ thể.


Thêm phương thức _poll_run vào lớp Agent của chúng tôi:

 class Agent: # ... (rest of code) def _cancel_run(self, run: Run): self.client.beta.threads.runs.cancel( run_id=run.id, thread_id=self.thread.id) def _poll_run(self, run: Run): status = run.status start_time = time.time() while status != "completed": if status == 'failed': raise Exception(f"Run failed with error: {run.last_error}") if status == 'expired': raise Exception("Run expired.") time.sleep(1) run = self._retrieve_run(run) status = run.status elapsed_time = time.time() - start_time if elapsed_time > 120: # 2 minutes self._cancel_run(run) raise Exception("Run took longer than 2 minutes.")


🥵 Phew, nhiều lắm... Hãy giải nén nó nhé.


_poll_run nhận đối tượng Run làm đối số và trích xuất status Run hiện tại. Tất cả các trạng thái sẵn có có thể được tìm thấy trong tài liệu OpenAI. Chúng tôi sẽ chỉ sử dụng một số phù hợp với mục đích hiện tại của chúng tôi.


Bây giờ chúng tôi chạy vòng lặp while để kiểm tra trạng thái đã hoàn thành trong khi xử lý một số trường hợp lỗi. Việc thanh toán thực tế của API Trợ lý hơi mơ hồ, vì vậy để đảm bảo an toàn, tôi đã chọn hủy các lần chạy của mình sau 2 phút.


Mặc dù có trạng thái expired khi OpenAI hủy chạy sau 10 phút. Nếu quá trình chạy mất hơn 2 phút thì có thể bạn đang gặp sự cố.


Vì tôi cũng không muốn bỏ phiếu cứ sau vài mili giây nên tôi điều tiết yêu cầu của mình bằng cách chỉ bỏ phiếu mỗi 1 giây cho đến khi chạm mốc 2 phút và hủy lượt chạy của mình. Bạn có thể điều chỉnh điều này cho phù hợp.


Mỗi lần lặp lại sau độ trễ, chúng tôi lại tìm nạp lại trạng thái Chạy.


Bây giờ, hãy kết nối tất cả những thứ đó vào phương thức run_agent của chúng ta. Bạn sẽ nhận thấy trước tiên chúng tôi tạo cuộc chạy bằng _create_run sau đó chúng tôi thăm dò ý kiến với _poll_run cho đến khi nhận được câu trả lời hoặc đưa ra lỗi và cuối cùng khi quá trình bỏ phiếu kết thúc, chúng tôi truy xuất thông báo cuối cùng từ chuỗi mà bây giờ sẽ là từ tác nhân.


Sau đó, chúng tôi đưa tin nhắn trở lại vòng lặp thời gian chạy để nó có thể được gửi lại cho người dùng.

 class Agent: # ... (rest of code) def run_agent(self): run = self._create_run() self._poll_run(run) # add this line message = self.get_last_message() return message


Voilà, bây giờ khi bạn điều hành lại đại lý của mình, bạn sẽ nhận được phản hồi từ Đại lý thân thiện của chúng tôi:

 python3 main.py User: hi Assistant: Hello there! What adventure can we embark on today? Or perhaps, before we set out, we should think about breakfast. Have you had yours yet? I've had mine, of course – can't start the day without a proper breakfast, you know. User: how many breakfasts have you had? Assistant: Ah, well, I've had just 1 breakfast today. But the day is still young, and there's always room for a second, isn't there? What about you? How can I assist you on this fine day?


Trong phần 2, chúng tôi sẽ bổ sung thêm khả năng cho Agent của chúng tôi gọi các công cụ.


Bạn có thể tìm thấy mã đầy đủ trên GitHub của tôi.


Cảm ơn bạn đã đọc. Rất vui khi được nghe bất kỳ suy nghĩ và phản hồi nào trong phần bình luận. Theo dõi tôi trên Linkedin để biết thêm nội dung như thế này: https://www.linkedin.com/in/jean-marie-dalmasso-1b5473141/