สร้างแชทบอทจาก API LLM: คู่มือ Python
โมเดล Abliterated เป็นโมเดลภาษาขนาดใหญ่ที่กลไกการปฏิเสธความปลอดภัยถูกลบออก ทำให้โมเดลสามารถตอบคำถามที่อ่อนไหว ผู้ใหญ่ หรือเฉพาะทางได้โดยไม่ติดตัวกรองเนื้อหา วิธีนี้มอบการควบคุมเสียงและขอบเขตความรู้ของโมเดลให้กับนักพัฒนาอย่างเต็มที่ ซึ่งมีคุณค่าอย่างยิ่งสำหรับการเขียนโค้ด การแสดงบทบาทสมมติ และการเขียนสร้างสรรค์ที่มาตรฐานอาจบล็อกผลลัพธ์ที่มีประโยชน์
อัปเดต

ประเด็นสำคัญ
- API เป็นแบบ stateless ดังนั้นโค้ดของคุณจะเป็นเจ้าของประวัติการสนทนาและส่งประวัตินั้นในทุกคำขอ
- การสตรีมมิง การเรียกใช้ฟังก์ชัน และโหมด JSON ใช้พารามิเตอร์มาตรฐานของ OpenAI ทั้งหมด
- ตัดหรือสรุปประวัติก่อนที่จำนวนโทเคนจะเข้าใกล้หน้าต่างบริบท 100,000 โทเคน
- การแชท 1,000 ครั้ง ด้วยอินพุต 2,000 โทเคนและเอาต์พุต 400 โทเคน มีค่าใช้จ่ายประมาณ $0.50
แนวคิดหลัก: แชทบอทคือลูปและรายการ
โมเดลภาษาไม่จำสิ่งใดระหว่างการเรียกใช้งาน ทุกการเรียก POST /v1/chat/completions เป็นอิสระต่อกัน สิ่งที่ดูเหมือนความจำคือโปรแกรมของคุณส่งข้อความก่อนหน้าอีกครั้ง โดยแต่ละข้อความมีบทบาทกำกับ: system สำหรับคำสั่งคงที่ user สำหรับสิ่งที่ผู้ใช้พิมพ์ และ assistant สำหรับคำตอบของโมเดลก่อนหน้า
ดังนั้นสถาปัตยกรรมทั้งหมดจึงเรียบง่าย เก็บเป็นรายการ เพิ่มข้อความผู้ใช้ ส่งรายการ เพิ่มคำตอบ ทำซ้ำ สิ่งอื่นๆ ทั้งหมดในคู่มือนี้ ตั้งแต่สตรีมมิงไปจนถึงเครื่องมือ เป็นเพียงรูปแบบต่างๆ ของลูปนี้
API ที่นี่เข้ากันได้กับ OpenAI ดังนั้นคุณใช้แพ็กเกจ openai ทั่วไป ตั้งค่า Base URL เป็น https://api.abliterated.cc/v1 ใช้คีย์ของคุณ และตั้งค่าโมเดลเป็น uncensored รับคีย์จากหน้า API key; เครดิตทดลองใช้ฟรี $0.50 เพียงพอสำหรับข้อความทดสอบสั้นๆ หลายร้อยข้อความ
ขั้นตอนที่ 1: แชทบอทคอนโซลที่ใช้งานได้
ติดตั้ง SDK ด้วย pip install openai แล้วรันสิ่งนี้
from openai import OpenAI
client = OpenAI(
base_url="https://api.abliterated.cc/v1",
api_key="YOUR_KEY",
)
messages = [
{"role": "system", "content": "You are a concise, friendly support assistant for a bike shop."}
]
while True:
text = input("you: ")
if text.strip() in {"quit", "exit"}:
break
messages.append({"role": "user", "content": text})
resp = client.chat.completions.create(
model="uncensored",
messages=messages,
temperature=0.7,
)
answer = resp.choices[0].message.content
messages.append({"role": "assistant", "content": answer})
print("bot:", answer)
นี่คือแชทบอทที่สมบูรณ์ ข้อความระบบกำหนดหน้าที่ รายการเก็บความจำ และ temperature ควบคุมความหลากหลายของคำตอบ ตั้งค่าให้เข้าใกล้ 0.2 สำหรับบอทสนับสนุนข้อเท็จจริง และเพิ่มเข้าใกล้ 1.0 สำหรับบอทสร้างสรรค์
เขียนพรอมต์ระบบที่กำหนดหน้าที่
ข้อความระบบเป็นจุดที่ถูกที่สุดในการปรับปรุงแชทบอท ให้กระชับและเฉพาะเจาะจง กำหนดบทบาท กลุ่มเป้าหมาย ความยาวและน้ำเสียงของการตอบกลับ และสิ่งที่ควรทำเมื่อบอทไม่รู้คำตอบ คำสั่งเช่น "คุณตอบคำถามสำหรับร้านขายจักรยาน ตอบกลับไม่เกินสามประโยค หากคำถามเกี่ยวกับราคาซ่อม ให้บอกว่าช่างต้องตรวจสอบจักรยาน" ทำได้ดีกว่าข้อความทั่วไปหนึ่งหน้า
สองนิสัยที่คุ้มค่า ประการแรก ใส่กฎในรูปของการกระทำ ไม่ใช่ทัศนคติ: "ถามคำถามเพื่อชี้แจงหนึ่งข้อเมื่อคำขอคลุมเครือ" แทนที่จะเป็น "ช่วยเหลือผู้ใช้" ประการที่สอง ทดสอบกับอินพุตแบบป่วน เช่น ข้อความว่าง ข้อความยาวมาก และคำขอที่ไม่เกี่ยวข้องกับผลิตภัณฑ์ของคุณ ปรับปรุงพรอมต์ตามสิ่งที่คุณเห็น แล้วเก็บอินพุตเหล่านั้นเป็นรายการทดสอบ regression
เนื่องจากข้อความระบบจะถูกส่งใหม่ในทุกเทิร์น ทุกคำในข้อความนั้นจะถูกเรียกเก็บเงินในทุกคำขอ พรอมต์ขนาด 400 โทเคนใน 1,000 การแชท ซึ่งแต่ละครั้งมี 5 เทิร์น จะเพิ่ม 2,000,000 โทเคนอินพุต หรือคิดเป็น 2 x $0.15 = $0.30 ซึ่งน้อย แต่แสดงให้เห็นว่าพรอมต์ที่ยาวเกินไปจะสะสมค่าใช้จ่ายเมื่อมีปริมาณมาก
ขั้นตอนที่ 2: สตรีมคำตอบ
การรอคำตอบเต็มรูปแบบรู้สึกช้าในหน้าต่างแชท ตั้งค่า stream: true และพิมพ์ชิ้นส่วนเมื่อได้รับ การสตรีมใช้เหตุการณ์ที่ส่งจากเซิร์ฟเวอร์ (SSE) และลงท้ายด้วยบล็อกการใช้งาน ดังนั้นคุณจึงสามารถบันทึกจำนวนโทเคนได้
from openai import OpenAI
client = OpenAI(base_url="https://api.abliterated.cc/v1", api_key="YOUR_KEY")
messages = [{"role": "user", "content": "Explain tire pressure in two sentences."}]
stream = client.chat.completions.create(
model="uncensored",
messages=messages,
stream=True,
)
reply = ""
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
part = chunk.choices[0].delta.content
reply += part
print(part, end="", flush=True)
if getattr(chunk, "usage", None):
print("\ntokens:", chunk.usage.prompt_tokens, chunk.usage.completion_tokens)
อย่าลืมเพิ่ม reply ที่ประกอบเสร็จแล้วลงในประวัติของคุณ มิฉะนั้นเทิร์นถัดไปจะไม่รู้ว่าบอทพูดอะไรไป
ขั้นตอนที่ 3: จัดการหน้าต่างบริบทก่อนที่มันจะขยายใหญ่
หน้าต่างบริบทมีขนาด 100,000 โทเคน ซึ่งใช้ร่วมกันระหว่างพรอมต์และการสร้างข้อความ ส่วนใหญ่แล้วแชทบอทจะไม่เข้าใกล้ขีดจำกัดนี้ แต่ค่าใช้จ่ายเพิ่มขึ้นตามประวัติเพราะคุณจ่ายสำหรับพรอมต์ทั้งหมดในทุกเทิร์น นโยบายง่ายๆ ช่วยควบคุมทั้งสองอย่างได้:
- เก็บข้อความระบบไว้เสมอ
- เก็บข้อความล่าสุด 10 ถึง 20 ข้อความแบบตรงตัว
- เมื่อรายการเกินขีดจำกัดขนาดที่คุณเลือก เช่น 8,000 โทเคนโดยประมาณ ส่งบล็อกที่เก่าที่สุดไปยังโมเดลด้วยคำสั่ง "สรุปการสนทนานี้ให้เหลือไม่เกิน 150 คำ" และแทนที่บล็อกนั้นด้วยสรุปเป็นข้อความระบบ
การประมาณการอย่างหยาบว่าหนึ่งโทเคนต่อสี่อักขระภาษาอังกฤษก็เพียงพอสำหรับทริกเกอร์ ตัวเลขการใช้งานในการตอบกลับแต่ละครั้งให้ตัวเลขที่ถูกต้องหลังจากทราบผล ดังนั้นคุณจึงสามารถปรับขีดจำกัดนี้ได้
หากคุณตัดแต่งโดยการลบข้อความที่เก่าที่สุด ให้ลบเป็นคู่ระหว่างผู้ใช้และผู้ช่วย คำตอบที่ไม่มีคำถาม หรือคำถามที่ไม่มีคำตอบ จะทำให้โมเดลสับสนมากกว่าที่คุณคาดคิด
ขั้นตอนที่ 4: มอบเครื่องมือให้บอท
การเรียกใช้ฟังก์ชันช่วยให้โมเดลสามารถเรียกใช้โค้ดของคุณให้ทำงานบางอย่างได้ เช่น ค้นหาข้อมูลคำสั่งซื้อ คุณอธิบายฟังก์ชันผ่าน tools โมเดลจะตอบกลับด้วยรายการ tool_calls คุณจึงเรียกใช้ฟังก์ชันนั้นและส่งผลลัพธ์กลับเป็นข้อความ role: "tool"
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.abliterated.cc/v1", api_key="YOUR_KEY")
tools = [{
"type": "function",
"function": {
"name": "get_order_status",
"description": "Look up the status of an order by its id",
"parameters": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"],
},
},
}]
def get_order_status(order_id):
return {"order_id": order_id, "status": "shipped"}
messages = [{"role": "user", "content": "Where is order A1042?"}]
resp = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = resp.choices[0].message
if msg.tool_calls:
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = get_order_status(**args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
final = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools
)
print(final.choices[0].message.content)
ควรตรวจสอบอาร์กิวเมนต์ทุกครั้งก่อนเรียกใช้ฟังก์ชันที่มีผลข้างเคียง โมเดลจะเสนอการเรียกใช้; โค้ดของคุณจะเป็นผู้ตัดสินใจว่าจะดำเนินการหรือไม่
ขั้นตอนที่ 5: ผลลัพธ์ที่มีโครงสร้างด้วยโหมด JSON
เมื่อขั้นตอนถัดไปในโปรแกรมของคุณต้องการข้อมูลแทนข้อความทั่วไป เช่น การจัดหมวดหมู่ข้อความ ให้ใช้โหมด JSON พร้อม response_format นอกจากนี้ควรระบุคำว่า JSON และคีย์ที่คาดหวังไว้ในพรอมต์ของคุณด้วย
curl https://api.abliterated.cc/v1/chat/completions \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "uncensored",
"response_format": {"type": "json_object"},
"messages": [
{"role": "system", "content": "Reply with JSON: {\"intent\": string, \"urgent\": boolean}"},
{"role": "user", "content": "My brakes squeal and I ride tomorrow morning."}
]
}'
ยังควรห่อ json.loads ด้วย try block หากการแยกวิเคราะห์ล้มเหลว ให้ลองใหม่หนึ่งครั้งโดยเพิ่มข้อความข้อผิดพลาด; คำขอที่ล้มเหลวจะไม่คิดเงิน
หมายเหตุเกี่ยวกับข้อผิดพลาด ค่าใช้จ่าย และการปรับใช้
จัดการข้อผิดพลาด การลองใหม่ และขีดจำกัดอัตรา
ผู้ใช้จริงส่งคำขอในเวลาที่อาจไม่เหมาะสม และเครือข่ายอาจล้มเหลว ควรห่อหุ้มการเรียกใช้เพื่อให้คำตอบที่ไม่ดีเพียงครั้งเดียวไม่ทำให้การสนทนาพังทลาย
import time
from openai import OpenAI, APIError, RateLimitError
client = OpenAI(base_url="https://api.abliterated.cc/v1", api_key="YOUR_KEY")
def chat(messages, tries=3):
for attempt in range(tries):
try:
resp = client.chat.completions.create(
model="uncensored", messages=messages, temperature=0.7
)
return resp.choices[0].message.content
except RateLimitError:
time.sleep(2 ** attempt)
except APIError as exc:
if attempt == tries - 1:
raise
time.sleep(1)
return "Sorry, the assistant is busy. Please try again in a moment."
คีย์เต็มรูปแบบอนุญาตให้ทำคำขอพร้อมกัน 8 คำขอและ 300 คำขอต่อนาที; คีย์ทดลองอนุญาตให้ทำ 2 และ 60 หากบอทของคุณให้บริการผู้ใช้จำนวนมากในเวลาเดียวกัน ให้ใส่คำขอในคิวพร้อมขีดจำกัด worker ต่ำกว่า 8 แทนที่จะพึ่งพาการลองใหม่ เนื่องจากคำขอที่ล้มเหลวไม่ถูกคิดค่า การลองใหม่จึงไม่มีต้นทุนโทเคน ให้จำนวนการลองใหม่มีค่าน้อยเพื่อให้การหยุดทำงานจริงล้มเหลวเร็ว
บันทึกส่วนหัว X-Request-Id ในแต่ละรอบการสนทนา เมื่อคำตอบดูผิด id ช่วยค้นหาคำขอที่แน่นอนได้
ค่าใช้จ่ายและการปรับใช้
ราคาอยู่ที่ $0.15 ต่อโทเคนอินพุต 1M และ $0.50 ต่อโทเคนเอาต์พุต 1M สมมติว่าการสนทนากับฝ่ายสนับสนุนใช้โทเคนอินพุตรวม 2,000 และโทเคนเอาต์พุต 400
- อินพุต: 0.002M x $0.15 = $0.0003
- เอาต์พุต: 0.0004M x $0.50 = $0.0002
- หนึ่งการสนทนา: $0.0005 ดังนั้น 1,000 การสนทนามีค่าใช้จ่าย $0.50
ตรวจสอบอัตราปัจจุบันและโบนัสการเติมเงินที่ หน้าราคา สำหรับการใช้งานจริง ให้เก็บคีย์ไว้บนเซิร์ฟเวอร์ของคุณเสมอ ไม่ใช่ในโค้ดเบราว์เซอร์ และเฝ้าดูเฮดเดอร์การตอบกลับ X-Balance-USD เพื่อแจ้งเตือนตัวเองก่อนเครดิตจะหมด คีย์แบบเต็มอนุญาตให้ทำคำขอพร้อมกันได้สูงสุด 8 คำขอ และ 300 คำขอต่อนาที ดังนั้นจึงควรจัดคิวคำขอที่พุ่งขึ้นกะทันหัน (burst)แทนที่จะส่งทั้งหมดพร้อมกัน เอกสาร ระบุพารามิเตอร์ที่รองรับทั้งหมด
คู่มือเพิ่มเติม
ถาม-ตอบ
ฉันจำเป็นต้องใช้เฟรมเวิร์กเพื่อสร้างแชทบอทหรือไม่?
ไม่ SDK ของ OpenAI และรายการข้อความก็เพียงพอสำหรับบอทที่ทำงานได้ เพิ่มเฟรมเวิร์กในภายหลังก็ต่อเมื่อคุณต้องการฟีเจอร์เสริมของมันเท่านั้น
API จำข้อความก่อนหน้านี้ได้หรือไม่?
ไม่ แต่ละคำขอเป็นอิสระต่อกัน ดังนั้นโค้ดของคุณจึงต้องส่งประวัติซ้ำในทุกครั้ง
ฉันจะหยุดไม่ให้ประวัติยาวเกินไปได้อย่างไร?
เก็บข้อความระบบและรอบการสนทนาล่าสุดไว้ และสรุปข้อความเก่าให้เป็นบันทึกสั้นๆ เมื่อรายการเกินขนาดที่คุณกำหนด
บอทสามารถเรียกใช้ฟังก์ชันของฉันเองได้หรือไม่?
ใช่ ส่ง tools และ tool_choice, รันฟังก์ชันเมื่อการตอบกลับมี tool_calls และส่งผลลัพธ์กลับเป็นข้อความเครื่องมือ
คีย์ของคุณอยู่ห่างแค่แบบฟอร์มเดียว
สร้างบัญชี คัดลอกคีย์ และเปลี่ยน base URL นั่นคือการตั้งค่าทั้งหมด