ประเด็นสำคัญ
- ChatOpenAI ทำงานได้เหมือนเดิม: ตั้งค่า base_url, api_key และ model เป็น
uncensored - API รองรับ tool calling ดังนั้น bind_tools และ prebuilt agent จึงใช้งานได้ทั้งคู่
- จำกัดจำนวนรอบ loop และตรวจสอบความถูกต้องของ tool arguments; โมเดลแบบเปิดน้ำหนักบางครั้งอาจทำผิดพลาดได้
- เคารพขีดจำกัดคำขอพร้อมกัน 8 รายการบนคีย์แบบเต็มเมื่อคุณรัน agents พร้อมกัน
สิ่งที่คุณต้องมีก่อนเริ่ม
คุณต้องการ Python 3.10 ขึ้นไป คีย์ API และแพ็กเกจจำนวนหนึ่ง คีย์ได้จากหน้า API key: ลงทะเบียนด้วย Google หรืออีเมลและรหัสผ่าน แล้วขอเครดิตทดลองใช้ $0.50 ด้วยคลิกเดียว คีย์ทดลองอนุญาตให้ทำคำขอพร้อมกัน 2 คำขอและ 60 คำขอต่อนาที ซึ่งเพียงพอสำหรับทุกอย่างในบทความนี้หากคุณรัน agent เพียงตัวเดียว
pip install langchain-openai langchain-core langgraph
export API_KEY="paste-your-key-here"API เปิดใช้งาน POST /v1/chat/completions และ GET /v1/models LangChain ต้องการเพียงอันแรก โมเดลเป็นโมเดลแบบเปิดน้ำหนักเพียงตัวเดียวที่ปรับแต่งมาเพื่อตอบคำถามโดยไม่ปฏิเสธเนื้อหาสำหรับการใช้งานผู้ใหญ่ที่ถูกต้องตามกฎหมาย ดำเนินการโดยผู้ให้บริการอิสระและไม่ใช่ API ทางการของผู้ขายโมเดลใด ๆ เอกสารระบุเฮดเดอร์และขีดจำกัดไว้ หากต้องการเปิดดูข้างๆ editor ของคุณ
วิธีการที่ชิ้นส่วนต่างๆ จับคู่กับ API
การเข้าใจสิ่งที่ LangChain ส่งผ่านเครือข่ายช่วยได้มาก HumanMessage จะกลายเป็นข้อความที่มี role user, AIMessage จะกลายเป็น assistant, และเมื่อมีการเรียกใช้เครื่องมือ (tool calls) สิ่งเหล่านั้นจะปรากฏในข้อความของผู้ช่วยเป็นอาร์เรย์ tool_calls. ToolMessage จะกลายเป็น role tool พร้อม ID ของการเรียกที่ตรงกัน. พรอมต์ระบบจะถูกส่งเป็น role system เป็นลำดับแรก.
การจับคู่รูปแบบนี้สำคัญต่อการดีบัก. หากเอเจนต์ทำงานแปลกๆ ให้พิมพ์รายการข้อความก่อนแต่ละการเรียก และอ่านเหมือนบทสนทนา. คุณมักจะพบปัญหาภายในหนึ่งนาที: ผลลัพธ์ของฟังก์ชันว่างเปล่า, ID ของการเรียกที่ไม่ตรงกัน, หรือประวัติที่ยาวนานกว่าที่คุณคาดไว้. คำว่า agentic อาจทำให้ดูน่าอัศจรรย์ แต่ทุกขั้นตอนคือคำขอ chat completion ธรรมดา.
ขั้นตอนที่ 1: เชื่อมต่อ ChatOpenAI
การผสานรวมทั้งหมดมีเพียงสามอาร์กิวเมนต์. LangChain สร้างคำขอแบบ OpenAI มาตรฐาน ดังนั้นเซิร์ฟเวอร์จึงไม่สามารถแยกความแตกต่างได้.
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.uncensoredvideo.cc/v1",
api_key=os.environ["API_KEY"],
temperature=0.7,
max_retries=2,
)
print(llm.invoke("Name three beats of a heist scene, one line each.").content)หากสิ่งนี้พิมพ์สามบรรทัด แสดงว่าการเชื่อมต่อทำงานได้ หากได้ 401 ให้ตรวจสอบคีย์ หากได้ 404 ให้ตรวจสอบว่า URL ลงท้ายด้วย /v1 และไม่มีพาธเพิ่มเติม max_retries เป็นตัวเลือกไคลเอนต์ และช่วยจัดการกับคำตอบ 429 ชั่วคราวที่ส่ง Retry-After: 5
การทำงานแบบสตรีมมิงทำงานในลักษณะเดียวกับที่คุณใช้กับโมเดล OpenAI ใดๆ: เรียกใช้ llm.stream(...) และวนลูป ผลลัพธ์สุดท้ายจะรวมข้อมูลการใช้งาน ดังนั้นคุณจึงสามารถบันทึกจำนวนโทเคนต่อคำขอได้
ขั้นตอนที่ 2: กำหนดเครื่องมือ
เอเจนต์คือลูป: โมเดลอ่านบทสนทนา, ตอบหรือขอใช้เครื่องมือ, โค้ดของคุณรันเครื่องมือ, และผลลัพธ์ถูกส่งกลับไปยังโมเดล. เครื่องมือด้านล่างคือฟังก์ชัน Python ธรรมดา. พวกมันถูกออกแบบให้เรียบง่าย เพราะจุดประสงค์คือเพื่อดูกลไกการทำงาน.
from langchain_core.tools import tool
@tool
def word_count(text: str) -> int:
"""Count the words in a piece of text."""
return len(text.split())
@tool
def spoken_minutes(words: int, words_per_minute: int = 150) -> float:
"""Estimate how many minutes a script takes to read aloud."""
return round(words / words_per_minute, 2)
tools = [word_count, spoken_minutes]docstring และ type hints จะกลายเป็นคำอธิบายเครื่องมือและสคีม่าที่โมเดลเห็น เขียนไว้เหมือนคุณกำลังอธิบายงานให้เพื่อนร่วมงาน docstring ที่คลุมเครือเป็นสาเหตุหลักที่สุดที่ agent เลือกเครื่องมือผิด หากคุณเขียนสคริปต์วิดีโอ รูปแบบเดียวกันนี้สามารถปรับขนาดไปยังเครื่องมือที่ค้นหา shot list หรือตรวจสอบงบประมาณความยาวได้ ซึ่งเป็นหัวข้อที่กล่าวถึงใน หน้าเครื่องมือสร้างสคริปต์ Sora 2
ขั้นตอนที่ 3: รัน agent loop ด้วยตนเอง
ตัวช่วยที่สร้างไว้ล่วงหน้าซ่อนลูปนี้ไว้. การเขียนลูปด้วยตนเองเพียงครั้งเดียวจะทำให้การหาข้อบกพร่องในภายหลังง่ายขึ้น.
from langchain_core.messages import HumanMessage, ToolMessage
by_name = {t.name: t for t in tools}
llm_tools = llm.bind_tools(tools)
messages = [HumanMessage(
"Here is a draft: 'The vault door hums. Nina counts to three, "
"then cuts the power.' How many words is it, and how long is it read aloud?"
)]
for step in range(6):
ai = llm_tools.invoke(messages)
messages.append(ai)
if not ai.tool_calls:
break
for call in ai.tool_calls:
result = by_name[call["name"]].invoke(call["args"])
messages.append(ToolMessage(content=str(result), tool_call_id=call["id"]))
print(messages[-1].content)รายละเอียดสามประการสำคัญ ค่า range(6) หยุดโมเดลที่สับสนจากการวนลูปตลอดไป และยังจำกัดค่าใช้จ่ายของคุณด้วย tool_call_id ต้องตรงกับการเรียกที่มันตอบ และโมเดลสามารถขอใช้เครื่องมือหลายรายการในคำตอบเดียว ซึ่งเป็นเหตุผลที่ลูปภายในจัดการกับรายการ
เบื้องหลัง คำขอจะประกอบด้วยอาร์เรย์ tools และคำตอบจะประกอบด้วย tool_calls โดยผลลัพธ์จะถูกส่งกลับเป็นข้อความที่มี role: "tool". คุณสามารถบังคับให้ใช้เครื่องมือเฉพาะได้โดยใช้ tool_choice ซึ่ง LangChain เปิดใช้งานเป็นอาร์กิวเมนต์ให้กับ bind_tools.
ขั้นตอนที่ 4: ใช้ agent LangGraph ที่สร้างไว้ล่วงหน้า
เมื่อเข้าใจลูปด้วยตนเองแล้ว เอเจนต์ที่สร้างไว้ล่วงหน้าจะช่วยลดจำนวนบรรทัดโค้ด. มันใช้โมเดลและเครื่องมือเดียวกัน.
import os
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.uncensoredvideo.cc/v1",
api_key=os.environ["API_KEY"],
)
agent = create_react_agent(llm, tools)
out = agent.invoke({"messages": [("user", "How long is a 450 word script read aloud?")]})
print(out["messages"][-1].content)LangChain และ LangGraph เปลี่ยน API ระหว่างการปล่อยเวอร์ชัน และตัวช่วยบางตัวถูกเปลี่ยนชื่อหรือย้าย หากการ import ล้มเหลว ให้ตรวจสอบเอกสาร LangChain ปัจจุบันสำหรับ agent constructor ที่เทียบเท่า ส่วนการเชื่อมต่อ ChatOpenAI พร้อม base URL ยังคงเหมือนเดิม
การเลือกระหว่างลูปและกราฟ
ลูปที่เขียนด้วยตนเองเพียงพอสำหรับเอเจนต์ที่เลือกจากเครื่องมือไม่กี่ตัวแล้วหยุด. มันอ่านง่าย, บันทึก log ง่าย และไม่มีข้อผิดพลาดจากเฟรมเวิร์ก. ให้ใช้ LangGraph เมื่อคุณต้องการการแยกสาขาที่ขึ้นอยู่กับสถานะ: ขั้นตอนการทบทวนที่ส่งงานกลับเพื่อทำซ้ำ, การอนุมัติจากมนุษย์ก่อนที่เครื่องมือที่มีความเสี่ยงจะทำงาน, หรือเอเจนต์ย่อยเฉพาะทางหลายตัวที่ส่งผลลัพธ์ถึงกัน.
ไม่ว่าคุณจะเลือกอะไร ให้รักษาคอมโพเนนต์แต่ละส่วนให้เล็กและมอบหมายงานเดียว. โหนดวางแผนที่สร้างลิสต์ตัวเลขสั้นๆ เป็น JSON, โหนดการทำงานที่รันเครื่องมือ, และโหนดการเขียนที่สร้างข้อความสุดท้าย จะทดสอบได้ง่ายกว่าพรอมต์ขนาดใหญ่เพียงตัวเดียว. พวกมันยังช่วยให้คุณใช้ temperature ที่แตกต่างกัน: ต่ำสำหรับการวางแผนและการใช้เครื่องมือ, และสูงขึ้นสำหรับขั้นตอนการเขียน. เนื่องจากโหนดแต่ละตัวคือการเรียก chat completion ธรรมดา คีย์, URL และชื่อโมเดลเดียวกันจึงใช้ได้กับทั้งหมด และต้นทุนคือผลรวมของการเรียกทั้งหมด.
นิสัยสุดท้าย: พิมพ์ยอดรวมโทเคนที่ใช้ไปสะสม. เอเจนต์ที่ดูประหยัดในการสาธิตอาจใช้เงินมากกว่าสิบเท่าในงานจริงที่ยุ่งเหยิง, และข้อมูลการใช้งานในคำตอบแต่ละรายการทำให้เห็นได้ในการรันครั้งแรก ก่อนที่มันจะกลายเป็นความประหลาดใจในยอดเงินของคุณ.
หน่วยความจำ, การทดสอบ, และผลลัพธ์ที่มีโครงสร้าง
ลูปข้างต้นจะลืมทุกอย่างเมื่อสคริปต์สิ้นสุดลง หน่วยความจำที่ง่ายที่สุดคือรายการข้อความเอง: เก็บไว้ระหว่างรอบการสนทนาของผู้ใช้ เพิ่มคำถามใหม่แต่ละข้อ และตัดข้อความที่เก่าที่สุดออกเมื่อรายการยาวขึ้น สำหรับเซสชันที่ยาวนาน ให้แทนที่ข้อความเก่าด้วยสรุปสั้นๆ ที่เขียนโดยโมเดล เนื่องจากอินพุตคิดค่าตามโทเคน สรุป 500 โทเคนจึงดีกว่าประวัติดิบ 20,000 โทเคนทั้งในแง่ต้นทุนและความโฟกัส
การทดสอบ agents ส่วนใหญ่เป็นการตรวจสอบพฤติกรรมที่สังเกตได้ ตั้งค่า temperature เป็น 0 หรือส่งค่า seed คงที่ ซึ่ง API ส่งต่อให้ เพื่อให้รันซ้ำได้มากขึ้น จากนั้นเขียนคำถามไม่กี่ข้อที่รู้ว่าจะเรียกใช้ tool ไหน และตรวจสอบชื่อ tool และอาร์กิวเมนต์ในรายการข้อความ ไม่ใช่ข้อความตอบกลับที่แน่นอน รันสิ่งเหล่านี้หลังการเปลี่ยนแปลง prompt หรือคำอธิบาย tool ทุกครั้ง การเปลี่ยน docstring อาจเปลี่ยน tool ที่โมเดลเลือก และการทดสอบเป็นวิธีที่ถูกที่สุดในการตรวจจับ
บันทึกสามสิ่งสำหรับการรันทุกครั้ง: จำนวนขั้นตอน, การเรียกใช้เครื่องมือพร้อมอาร์กิวเมนต์, และการใช้งานโทเคน. เมื่อการรันล้มเหลว, สามบรรทัดนี้บอกคุณว่าโมเดลเลือกไม่ดี, เครื่องมือล้มเหลว, หรือลูปหมดขั้นตอน.
ผลลัพธ์ที่มีโครงสร้างและโหมด JSON
Agent มักต้องการส่งคืนข้อมูล ไม่ใช่ข้อความบรรยาย API รองรับโหมด JSON ผ่าน response_format: {"type":"json_object"} ใน LangChain คุณสามารถขอใช้ด้วย llm.bind(response_format={"type": "json_object"}) บอกโมเดลใน prompt ว่าคำตอบต้องเป็นวัตถุ JSON และระบุชื่อคีย์ที่คุณคาดหวัง จากนั้นแยกวิเคราะห์สตริงและตรวจสอบความถูกต้องด้วย Pydantic อย่าไว้วางใจขั้นตอนการแยกวิเคราะห์ว่าจะสำเร็จเสมอไป ให้ห่อหุ้มมันด้วยการ retry ที่ส่งข้อความข้อผิดพลาดกลับไปยังโมเดลหนึ่งครั้ง
จุดบกพร่องและวิธีแก้ไข
- อาร์กิวเมนต์เครื่องมือที่ไม่ถูกต้อง โมเดลแบบเปิดน้ำหนักบางครั้งส่งสตริงเมื่อคุณประกาศเป็นจำนวนเต็ม ตรวจสอบอาร์กิวเมนต์ ส่งข้อความข้อผิดพลาดสั้นๆ จากเครื่องมือ และให้โมเดลแก้ไขตัวเองในขั้นตอนถัดไป
- ลูปที่ควบคุมไม่ได้ จำกัดจำนวนการวนลูปเสมอ บันทึกแต่ละขั้นตอนขณะพัฒนา
- Agent แบบขนาน คีย์แบบเต็มอนุญาตให้ทำคำขอพร้อมกัน 8 คำขอ หากคุณรันแบบ batch ให้ตั้งค่า
config={"max_concurrency": 4}บนllm.batchเพื่อเหลือพื้นที่สำรอง การเกินขีดจำกัดจะส่ง 429 พร้อมรหัสconcurrency; การเกิน 300 คำขอต่อนาทีจะส่งrate_limited - สตรีมมิงพร้อมเครื่องมือ คำขอแบบสตรีมจะครอบครองสล็อตการทำงานพร้อมกันจนกว่าสตรีมจะสิ้นสุด ดังนั้นอย่าเริ่มคำขอใหม่จากภายใน handler ของสตรีมบนคีย์ทดลอง
- ประวัติที่ยาวขึ้น ทุกขั้นตอนจะส่งรายการข้อความทั้งหมดอีกครั้ง หน้าต่างบริบทมีขนาด 100,000 โทเคน แต่คุณต้องจ่ายสำหรับอินพุตในแต่ละคำขอ
ต้นทุนการรันเอเจนต์
พิจารณาเอเจนต์แบบเรียบง่ายที่ทำ 4 คำขอโมเดลเพื่อเสร็จสิ้นงาน โดยมีพรอมต์เฉลี่ย 3,000 โทเคนและเอาต์พุต 200 โทเคนต่อคำขอ อินพุต: 4 x 3,000 = 12,000 โทเคน ซึ่งคิดเป็น 12,000 x $0.15 / 1,000,000 = $0.0018 เอาต์พุต: 4 x 200 = 800 โทเคน ซึ่งคิดเป็น $0.0004 การรันหนึ่งครั้งมีค่าใช้จ่าย $0.0022 ดังนั้นการรัน 1,000 ครั้งมีค่าใช้จ่าย $2.20 เครดิตทดลองใช้ $0.50 ครอบคลุมการรันดังกล่าวได้ประมาณ 227 ครั้ง คำขอที่ล้มเหลวหรือถูกปฏิเสธจะไม่ถูกเรียกเก็บเงิน และเครดิตไม่หมดอายุ ดู ราคา สำหรับจำนวนเงินเติมและโบนัส เว็บไซต์นี้รับเฉพาะคริปโต USDT บน TRC20 หรือ USDC บน Base
