Kịch bản nhập vai dạng văn bản với API LLM
Các kịch bản nhập vai dạng văn bản hoạt động tốt nhất khi mô hình đóng vai người dẫn chuyện với các quy tắc, và mã của bạn sở hữu trạng thái. Bài viết này hướng dẫn bạn qua một công cụ nhỏ bằng Python: prompt, truyền phát, bộ nhớ và ước tính chi phí cho một phiên dài.

Điểm chính
- Chia nhỏ công việc: mô hình viết câu chuyện, mã của bạn lưu trữ sự thật, kho đồ và luật chơi.
- Phản hồi theo luồng và giữ mỗi luồng ngắn đủ để giải phóng khe đồng thời của nó nhanh chóng.
- Cửa sổ 100.000 token chứa một phiên rất dài, nhưng việc tóm tắt giúp giảm chi phí và hạn chế tình trạng trôi dạt ngữ cảnh.
- Một phiên 200 lượt với 6,000 token đầu vào và 400 token đầu ra mỗi lượt tốn khoảng $0.22.
Bắt đầu với người dẫn chuyện, không phải cốt truyện
Những người xây dựng kịch bản nhập vai dạng văn bản thường bắt đầu bằng cách viết một cốt truyện. Điều đó ngược lại. Một kịch bản là một vòng lặp giữa người chơi và người dẫn chuyện, và người dẫn chuyện cần một mô tả công việc trước khi cần một câu chuyện. Mô hình phải biết ai là người nói, nó có thể quyết định gì cho người chơi, một câu trả lời nên dài bao nhiêu, và nó không bao giờ nên làm gì. Mọi thứ khác có thể được cải biên.
Một prompt hệ thống tốt cho một công cụ kịch bản có bốn phần. Đầu tiên là vai trò: người dẫn truyện, người phỏng vấn, máy tính trên tàu, hoặc một nhân vật trong vở kịch. Thứ hai là luật thế giới trong vài câu đơn giản: tông giọng, bối cảnh, những gì tồn tại và những gì không. Thứ ba là quy tắc định dạng, vì mã của bạn có thể cần phân tích chúng: ví dụ, "kết thúc mỗi câu trả lời bằng một dòng bắt đầu bằng OPTIONS:". Thứ tư là các ranh giới. API văn bản ở đây được tinh chỉnh để trả lời mà không từ chối nội dung cho mục đích người lớn hợp pháp, khiến nó phù hợp với tiểu thuyết tối tăm, các nhân vật phản diện đáng sợ và các cốt truyện có nhiều sắc thái đạo đức. Nó vẫn có một ranh giới cứng: không có nội dung tình dục liên quan đến người chưa thành niên. Hãy đặt quy tắc đó vào điều khoản sử dụng của riêng bạn, và thực hiện bộ lọc độ tuổi trong ứng dụng của bạn nếu các kịch bản của bạn hướng đến người lớn.
Giữ prompt dưới khoảng 600 từ. Các danh sách luật dài thường chỉ được tuân thủ một nửa. Nếu bạn cần nhiều luật, hãy xếp hạng chúng và loại bỏ phần ba cuối cùng.
Vòng lặp hoạt động với truyền phát
Truyền phát (streaming) quan trọng hơn trong đóng vai so với hầu hết các trường hợp sử dụng khác. Một người chơi đọc một câu chuyện muốn các từ xuất hiện trong khi mô hình đang viết, và một câu trả lời 300 token cảm thấy chậm hơn thực tế nếu nó xuất hiện cùng một lúc. API truyền phát qua các sự kiện do máy chủ gửi với stream: true, và các SDK của OpenAI xử lý việc phân tích cú pháp. Endpoint tương thích với OpenAI, ID mô hình là uncensored, và đường dẫn cơ sở cho trang web này là https://api.sdxlapi.com/v1.
from openai import OpenAI
client = OpenAI(base_url="https://api.sdxlapi.com/v1", api_key="YOUR_KEY")
SYSTEM = """You are the narrator of a noir mystery set in a rainy harbour city.
Write in second person, present tense, 120 to 200 words per reply.
Never decide what the player says or does. Invent details freely, but stay
consistent with the STATE block you are given.
End every reply with one line: OPTIONS: a | b | c"""
state = {"place": "Pier 9 office", "time": "23:40", "inventory": ["brass key"], "facts": []}
history = []
def turn(player_text: str) -> str:
history.append({"role": "user", "content": player_text})
msgs = [{"role": "system", "content": SYSTEM + "\nSTATE: " + str(state)}] + history[-20:]
stream = client.chat.completions.create(
model="uncensored", messages=msgs, stream=True,
temperature=0.9, top_p=0.95, max_tokens=500,
presence_penalty=0.3,
)
reply = ""
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
piece = chunk.choices[0].delta.content
reply += piece
print(piece, end="", flush=True)
history.append({"role": "assistant", "content": reply})
return reply
turn("I check the desk drawer.")Các cài đặt được tinh chỉnh cho văn học. temperature khoảng 0.8 đến 1.0 mang lại văn phong đa dạng, và top_p gần 0.95 loại bỏ các lựa chọn từ ngữ kỳ lạ nhất. Một hình phạt hiện diện nhỏ hạn chế việc người dẫn chuyện quay lại lặp lại cùng những hình ảnh đó. Đặt max_tokens để giới hạn độ dài câu trả lời; nó là tùy chọn, nhưng nếu không có nó, một người dẫn chuyện thích nói chuyện có thể viết nhiều hơn mức bạn muốn. Đối với một cảnh bạn muốn phát lại chính xác trong quá trình kiểm thử, hãy truyền seed và giữ các tham số khác không đổi.
Để mã sở hữu sự thật
Lỗi phổ biến nhất trong các phiên đóng vai dài là trôi dạt. Chìa khóa nằm trong túi người chơi ở lượt 5 và trên bàn ở lượt 40. Người chủ quán trọ được gọi là Mara, sau đó là Marta. Một mô hình đọc 30.000 token lịch sử thường sẽ hiểu đúng điều này, nhưng "thường" không phải là một thiết kế. Hãy lưu trữ các sự thật trong một cấu trúc nhỏ mà chương trình của bạn cập nhật, và đưa cấu trúc đó vào mọi yêu cầu, như dòng STATE ở trên đã làm.
Bạn cập nhật trạng thái như thế nào? Yêu cầu mô hình báo cáo các thay đổi sau mỗi lượt, trong một cuộc gọi thứ hai, rẻ hơn sử dụng chế độ JSON. Cuộc gọi đầu tiên viết câu chuyện cho người chơi. Cuộc gọi thứ hai đọc đoạn văn mới và chỉ trả về những gì đã thay đổi.
import json
def extract_changes(passage: str) -> dict:
r = client.chat.completions.create(
model="uncensored",
response_format={"type": "json_object"},
temperature=0,
max_tokens=250,
messages=[
{"role": "system", "content":
'Read the passage. Return JSON: {"place": str|null, "inventory_add": [str], '
'"inventory_remove": [str], "new_facts": [str]}. Only report what the passage states.'},
{"role": "user", "content": passage},
],
)
return json.loads(r.choices[0].message.content)
def apply(ch: dict):
if ch.get("place"):
state["place"] = ch["place"]
for item in ch.get("inventory_add", []):
if item not in state["inventory"]:
state["inventory"].append(item)
state["inventory"] = [i for i in state["inventory"] if i not in ch.get("inventory_remove", [])]
state["facts"] = (state["facts"] + ch.get("new_facts", []))[-30:]Giới hạn danh sách các sự thật, giống như cách bạn cắt lát dữ liệu, nếu không khối trạng thái sẽ phát triển cho đến khi nặng bằng chính lịch sử mà nó dự định thay thế. Nếu một câu trả lời JSON không thể phân tích cú pháp, hãy bỏ qua cập nhật cho lượt đó và tiếp tục; việc bỏ lỡ một sự thật thì rẻ hơn một lỗi hệ thống.
Xúc xắc, sức khỏe và bộ đếm thời gian được xử lý tốt hơn trong mã thông thường. Khi người chơi thử một điều gì đó mạo hiểm, hãy tung xúc xắc trong Python và nói cho người dẫn chuyện về kết quả: "Nỗ lực của người chơi thất bại." Mô hình sau đó viết các hậu quả. Điều này giữ cho mô hình không lặng lẽ cho phép người chơi thắng mọi lúc, điều mà các mô hình ngôn ngữ có xu hướng làm.
Bộ nhớ cho các phiên chạy trong nhiều giờ
Cửa sổ ngữ cảnh là 100.000 token, tính cả prompt và kết quả hoàn thành (completion) cùng nhau. Một câu trả lời điển hình gồm 250 token cộng với tin nhắn 30 token của người chơi là khoảng 280 token mỗi lượt, vì vậy lịch sử thô đơn thuần có thể chứa hàng trăm lượt. Bạn có thể gửi tất cả chúng. Nhiều nhà phát triển vẫn không làm vậy vì hai lý do: chi phí tăng lên theo mỗi lượt, vì toàn bộ lịch sử được tính phí là đầu vào mỗi lần, và các chi tiết cũ có thể làm phân tán sự chú ý của mô hình.
Một bố cục hợp lý giữ ba lớp:
- Đã ghim: prompt hệ thống và khối trạng thái. Luôn được gửi.
- Tóm tắt: một đoạn văn hoặc hai đoạn văn liên tục về những gì đã xảy ra, được viết lại sau mỗi 10 đến 15 lượt bằng một lệnh gọi ngắn.
- Các lượt gần đây: 10 đến 20 tin nhắn gần đây nhất nguyên văn, để văn phong giữ nhịp điệu của nó.
Khi cửa sổ của các lượt gần đầy, hãy gộp mười lượt cũ nhất vào bản tóm tắt và loại bỏ chúng. Lệnh gọi tóm tắt có thể sử dụng nhiệt độ thấp và giới hạn 300 token. Người chơi hiếm khi nhận thấy sự nén dữ liệu, miễn là tên, lời hứa và các mối đe dọa chưa được giải quyết đều được đưa vào bản tóm tắt. Hãy nói rõ điều đó trong prompt tóm tắt: "giữ lại tên, khoản nợ, vết thương, các câu hỏi chưa được giải đáp".
Nếu bạn muốn một nhân vật giữ một giọng điệu ổn định, hãy cho nó một thẻ phong cách ngắn trong lớp đã ghim: ba dòng ví dụ về lời nói của nó và hai điều nó không bao giờ nói. Các ví dụ ngắn định hình giọng điệu tốt hơn tính từ.
Chi phí cho một phiên
Giá là $0.15 cho mỗi 1M token đầu vào và $0.50 cho mỗi 1M token đầu ra, với tín dụng trả trước và không có gói đăng ký. Hãy làm việc qua một phiên dài. Giả sử 200 lượt, với lớp ghim, bản tóm tắt và các lượt gần đây cộng lại thành trung bình 6.000 token đầu vào mỗi yêu cầu, và 400 token đầu ra mỗi câu trả lời (câu trả lời câu chuyện cộng với một phần nhỏ cho các lệnh gọi trạng thái và tóm tắt).
Đầu vào: 200 x 6,000 = 1,200,000 token, tức là 1.2 x $0.15 = $0.18. Đầu ra: 200 x 400 = 80,000 token, tức là 0.08 x $0.50 = $0.04. Phiên tốn $0.22.
So sánh phiên bản lười biếng gửi lại toàn bộ lịch sử thô. Đến lượt 200, lịch sử là khoảng 56,000 token, và trong suốt phiên, đầu vào trung bình là khoảng 28,000 token mỗi yêu cầu. Đó là 200 x 28,000 = 5,600,000 token đầu vào, hoặc 5.6 x $0.15 = $0.84, cộng với cùng $0.04 cho đầu ra, tạo thành $0.88. Vẫn có thể chi trả, nhưng gấp bốn lần hóa đơn mà không có lợi ích về chất lượng. Đối với một ứng dụng có nhiều người chơi, khoảng cách đó quyết định biên lợi nhuận của bạn.
Các yêu cầu bị lỗi hoặc bị từ chối không bị tính phí, và tín dụng không bao giờ hết hạn, nên số dư chưa sử dụng chỉ đơn giản là chờ đợi. Các khoản nạp tiền chạy từ $20 đến $500 theo nguyên số đô la, với 5% thưởng từ $50, 10% từ $100 và 15% từ $200. Xem trang giá cả để xem danh sách đầy đủ các tùy chọn.
Chạy nhiều người chơi cùng lúc
Một khóa đầy đủ cho phép 8 yêu cầu đồng thời và 300 yêu cầu mỗi phút. Một yêu cầu truyền phát giữ khe của nó cho đến khi luồng kết thúc, vì vậy với 8 khe và các câu trả lời mất khoảng mười giây, bạn có thể phục vụ khoảng 48 luồng truyền phát câu trả lời mỗi phút, giả sử không có gì khác đang chạy. Đó là một quy tắc ước lượng, không phải là một số liệu đo lường chính xác, vì tốc độ thay đổi tùy theo tải và độ dài câu trả lời. Điểm mấu chốt là hãy lập kế hoạch cho nó: nếu trò chơi của bạn có nhiều người chơi đồng thời hơn số khe, hãy xếp hàng các yêu cầu trên máy chủ của bạn và hiển thị trạng thái "người dẫn chuyện đang suy nghĩ" thay vì kích hoạt mọi thứ cùng một lúc.
Khi bạn vượt quá giới hạn, API trả về HTTP 429. Mã là rate_limited cho các yêu cầu mỗi phút và concurrency cho quá nhiều cùng một lúc. Quá tải tạm thời cũng trả về 429, với tiêu đề Retry-After: 5. Một lệnh thử lại đơn giản chờ năm giây đó là đủ cho một trò chơi giải trí nhẹ nhàng. Hãy đọc các tiêu đề phản hồi: X-RateLimit-Limit-Requests và X-RateLimit-Limit-Concurrency cho thấy giới hạn của bạn, và X-Balance-USD cho biết số dư còn lại, mà bạn có thể hiển thị trên màn hình quản trị.
Không chạy cuộc gọi trích xuất trạng thái song song với lượt người chơi tiếp theo. Chờ nó, hoặc người dẫn chuyện sẽ viết từ các sự thật cũ. Một sự thỏa hiệp tốt là bắt đầu trích xuất ngay khi luồng kết thúc và để người chơi đọc trong khi nó chạy.
Kết nối các cảnh với quy trình xử lý hình ảnh của bạn
Trang web này được xây dựng cho những người sử dụng các mô hình văn bản xung quanh các quy trình làm việc của Stable Diffusion, và các kịch bản phù hợp với điều đó. API chỉ xử lý văn bản: nó không tạo ra hình ảnh, âm thanh hoặc video. Nhưng người dẫn truyện có thể viết một mô tả cảnh mà quy trình của bạn chuyển đổi thành prompt hình ảnh. Thêm một lệnh gọi JSON nữa sau khi thay đổi cảnh lớn, yêu cầu một mô tả hình ảnh ngắn gọn về bối cảnh với ánh sáng, tâm trạng và góc máy, sau đó truyền chuỗi đó vào bất kỳ công cụ hình ảnh nào mà bạn đang chạy. Trang tài liệu hiển thị hình dạng yêu cầu cho các lệnh gọi viết prompt, và bài viết về trình tạo prompt SD đi sâu hơn vào khía cạnh đó.
Để thử công cụ, hãy tạo tài khoản và nhận dùng thử. Người dùng mới có thể nhận $0.50 tín dụng dùng thử chỉ với một cú nhấp chuột trên trang khóa API, có giá trị trong 7 ngày mà không cần thẻ. Theo các số liệu trên, đó là đủ cho một vài phiên đầy đủ. Khóa dùng thử bị giới hạn ở 2 yêu cầu đồng thời và 60 yêu cầu mỗi phút, phù hợp cho việc kiểm thử cá nhân.
Giữ tiểu thuyết trong tiểu thuyết. Các kịch bản chủ đề người lớn là sử dụng hợp pháp khi tất cả những người tham gia đều là người lớn; quy tắc duy nhất không bao giờ thay đổi là không có nội dung tình dục liên quan đến người chưa thành niên. Các prompt gửi đến API không được sử dụng để huấn luyện.
Hướng dẫn khác
Hỏi đáp
Mô hình có thể đóng bất kỳ nhân vật hoặc thể loại nào không?
Đây là mô hình văn bản chung được tinh chỉnh để không từ chối các yêu cầu người lớn hợp pháp, vì vậy kinh dị, tội phạm, kịch và hài kịch đều ổn. Giới hạn cứng duy nhất là nội dung tình dục liên quan đến người chưa thành niên.
Phiên có thể dài bao lâu trước khi cửa sổ ngữ cảnh đầy?
Cửa sổ là 100,000 token cho prompt cộng với kết quả hoàn thành. Với lớp tóm tắt, bạn có thể chạy xa hơn thế trong thực tế, vì các lượt cũ được nén lại.
Tôi có nên truyền phát các câu trả lời không?
Có, đối với bất cứ thứ gì người dùng đọc trực tiếp. Truyền phát cho bạn biết mức sử dụng vào cuối, và một luồng chiếm một trong các khe đồng thời của bạn cho đến khi nó kết thúc.
Có cách nào để làm cho một cảnh lặp lại chính xác không?
Sử dụng cùng seed với các thông điệp và cài đặt giống hệt nhau. Đây là một công cụ kiểm tra và không đảm bảo đầu ra giống hệt nhau về byte.
Khóa của bạn chỉ cách một biểu mẫu
Tạo tài khoản, sao chép khóa, thay đổi URL cơ sở. Đó là toàn bộ thiết lập.