TR ▾

OpenAI uyumlu sansürsüz LLM API'siuncensoredapis.com

Ana SayfaAgent'lar

Sansürsüz LLM API'si Üzerine Agent'lar: Fonksiyon Çağırma, Çerçeveler ve Koruma

Bir agent bir döngüdür: model bir fonksiyon ister, kodunuz onu çalıştırır ve sonuç konuşmaya geri döner. Sansürsüz LLM API'si standart sohbet formatında fonksiyon çağırma kullandığı için bu döngüyü kırk satırda oluşturabilir, ardından aynı uç noktayı LangChain veya LlamaIndex'e bağlayabilirsiniz. Bu rehber her ikisini de ve fonksiyon yürütmesini güvenli hale getiren kontrolleri kapsar.

güncellendi

Ana noktalar

  1. Fonksiyonları istekte iletin, finish_reason değerinin tool_calls'a eşit olup olmadığına dikkat edin, fonksiyonları çalıştırın, tool mesajlarını ekleyin ve model metinle yanıt verene kadar tekrarlayın.
  2. LangChain için base_url ile ChatOpenAI gerekir; LlamaIndex için api_base ve fonksiyon çağırma bayrağı ayarlanmış OpenAILike gerekir.
  3. Model çağrıları önerir; kodunuz bunların çalışıp çalışmayacağına karar verir. İzin listesine ekleyin, doğrulayın, zaman aşımı uygulayın ve döngüyü sınırlandırın.
  4. Agent'lar istek sayısını çarpar, bu yüzden dakika başına 300 istek limiti ve 100.000 token bağlam penceresi için bütçe yapın.

Fonksiyon çağırma döngüsünün anatomisi

Çerçeveleri ve her agent'ın aynı dört şeyi yapmasını kaldırın. Sohbeti ve bir tools listesi gönderirsiniz. Model ya metin döner (tamamlandı anlamında) ya da tool_calls döner (bir şeyin yürütülmesini ister). Her çağrıyı çalıştırın ve her çağrı için bir tool mesajı ekleyin, çağrının id'sini alıntılayın. Ardından daha uzun sohbetle modeli tekrar sorgulayın.

Dal için sinyal finish_reason değeridir. Bu değer tool_calls ise, asistan mesajının content alanı null olarak ayarlanmıştır ve bir tool_calls dizisi içerir; her girdi bir id, bir fonksiyon adı ve bir arguments dizesi (JSON formatında) barındırır. Başvuru sayfası tam yapıyı gösterir. İki detay sıkça hata yapılmasına neden olur: argümanlar ayrıştırılması gereken bir dizedir ve çağrıları içeren asistan mesajı, araç sonuçlarınızdan önce, sıralı bir şekilde geçmişte kalmalıdır.

Sıfırdan tam bir döngü

Aşağıdaki betik bir fonksiyon tanımlar, onu JSON şemasında açıklar ve model düz metin üretene kadar döngü kurar. API'yi işaret eden resmi Python SDK'sını kullanır. Her geçişte asistan mesajını, ardından her çağrı için bir tool mesajını ve bir adım sayacını ekler; bu sayaç kontrolsüz davranışı durdurur.

import json, os
from openai import OpenAI

client = OpenAI(base_url="https://api.uncensoredapis.com/v1", api_key=os.environ["API_KEY"])

TICKETS = {"T-101": "open", "T-102": "closed"}

def ticket_status(ticket_id: str) -> str:
    return TICKETS.get(ticket_id, "unknown ticket")

TOOLS = [{
    "type": "function",
    "function": {
        "name": "ticket_status",
        "description": "Look up the status of a support ticket by id.",
        "parameters": {
            "type": "object",
            "properties": {"ticket_id": {"type": "string"}},
            "required": ["ticket_id"],
        },
    },
}]
REGISTRY = {"ticket_status": ticket_status}

messages = [{"role": "user", "content": "Are tickets T-101 and T-102 still open?"}]

for step in range(6):                      # hard cap on loop turns
    r = client.chat.completions.create(
        model="uncensored", messages=messages, tools=TOOLS, max_tokens=400)
    msg = r.choices[0].message
    messages.append(msg.model_dump(exclude_none=True))
    if r.choices[0].finish_reason != "tool_calls":
        print(msg.content)
        break
    for call in msg.tool_calls:
        fn = REGISTRY.get(call.function.name)
        try:
            args = json.loads(call.function.arguments)
            result = fn(**args) if fn else f"error: unknown tool {call.function.name}"
        except Exception as exc:
            result = f"error: {exc}"
        messages.append({"role": "tool", "tool_call_id": call.id, "content": str(result)})
else:
    print("stopped: step limit reached")

Döngünün hatada ne yaptığını fark edin: hatalar, dize olarak araç sonuçlarına dönüşür. Bu, modelin sorunu okumasını ve tekrar denemesini sağlar (örneğin, hatalı bir bilet kimliğini düzeltmek gibi), böylece programınız çökmez. for ... else bloğu yalnızca nihai bir yanıt olmadan sınırın aşılması durumunda tetiklenir; bu, modelin döngüye girmesinin tespit edilme yoludur.

İstemleri odaklı tutun. Bir fonksiyon çağrıldığında ve doğrudan yanıt verildiğinde ne zaman olduğunu belirten kısa bir sistem mesajı, gereksiz çağrıları azaltır ve her atlanan çağrı bir tur, token ve hız limitinizde bir slot tasarrufu sağlar.

Fonksiyon yürütmenin korunması

Model çıktısı güvenilmeyen bir girdidir. Model tarafından seçilen bir fonksiyon adı veya argüman yanlış, tuhaf veya modelin bir web sayfasından veya kullanıcıdan okuduğu metinden etkilenebilir olabilir. Her çağrıyı halk internetinden gelen bir istek gibi ele alın.

  • İsimle izin listesi. Fonksiyonu oluşturduğunuz bir kayıt defterinden arayın. Kodu içe aktarmak veya değerlendirmek için bu dizeyi asla kullanmayın.
  • Okumayı yazmadan ayırın. Salt okuma araçları serbestçe çalışır. E-posta gönderen, para harcayan, veri silen veya dosyalara dokunan her şey açık insan onayı gerektirir.
  • Argümanları ilan ettiğiniz şemaya göre doğrulayın: gerekli anahtarlar, ekstra anahtar yok, türler ve uzunluk sınırları.
  • Zaman ve çıktıyı sınırlayın. Zaman aşımı uygulayın ve sonuçları geri beslemeden önce kısaltın, böylece büyük bir araç çıktısı 100.000 token bağlam pencerenizi yiyemez.
  • İterasyon ve maliyeti sınırlayın. usage bloğunu kullanarak görev başına adım ve oturum başı token sayısını sınırlayın.
  • Her çağrıyı ad, argümanlar ve sonuçla birlikte günlüğe kaydedin, böylece davranışı denetleyebilirsiniz.
import json
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutTimeout

ALLOWED = {"ticket_status", "lookup_invoice"}      # read-only tools only
NEEDS_APPROVAL = {"refund_order"}                  # side effects wait for a human
POOL = ThreadPoolExecutor(max_workers=4)

def validate(args: dict, schema: dict) -> None:
    props = schema["properties"]
    for key in schema.get("required", []):
        if key not in args:
            raise ValueError(f"missing argument: {key}")
    for key, val in args.items():
        if key not in props:
            raise ValueError(f"unexpected argument: {key}")
        if props[key]["type"] == "string" and not isinstance(val, str):
            raise ValueError(f"{key} must be a string")
        if isinstance(val, str) and len(val) > 200:
            raise ValueError(f"{key} too long")

def run_tool(name, raw_args, registry, schemas, approve=lambda n, a: False):
    if name in NEEDS_APPROVAL:
        args = json.loads(raw_args)
        if not approve(name, args):
            return "denied: this action needs human approval"
    elif name not in ALLOWED:
        return f"error: tool {name} is not permitted"
    args = json.loads(raw_args)
    validate(args, schemas[name]["parameters"])
    future = POOL.submit(registry[name], **args)
    try:
        return str(future.result(timeout=5))[:2000]    # time and size limits
    except FutTimeout:
        return "error: tool timed out"

Araç sonuçlarını da güvenilmeyen olarak ele alın. Bir araç, talimatlar içeren metin döndürürse, model bunları takip edebilir. Bu tür içeriği tool mesajında açık sınırlayıcılarla sarın ve sistem isteminde araç çıktısının veri olduğunu, komut olmadığını belirtin.

Modelin kullanabileceği fonksiyon şemaları yazma

Şema, modelin gördüğü tek dokümantasyondur, bu yüzden kamu API'siyle aynı özeni hak eder. Fonksiyonları görevle eşleşen fiiller ve isimlerle adlandırın, örneğin ticket_status veya lookup_invoice, run veya helper gibi genel etiketler yerine. description'i bir talimat olarak yazın: fonksiyon ne yapar, ne döndürür ve ne zaman kullanılmamalıdır.

  • Parametreleri az ve düz tutun. İki veya üç basit alan, on alan içeren iç içe bir nesneden çok daha sık doğru şekilde çağrılır.
  • Kapalı setler için enum kullanın, örneğin "enum": ["open", "closed"], böylece model bir değer uyduramaz.
  • Her özelliği bir birim veya örnek ile açıklayın: "T-101 gibi bir bilet kimliği" çıplak bir diziden daha iyidir.
  • Sıkı sonuçlar döndürün. Dizeler veya küçük JSON nesneleri en iyi sonucu verir; uzun dökümler bağlamı az kazançla tüketir.
  • Mod bayrağı olan tek bir araç yerine birkaç dar araç tercih edin.

Bir çağrı doğrulamayı başarısız olursa, yükseltmek yerine doğrulama mesajını araç sonucu olarak döndürün. Model genellikle bir sonraki turda çağrıyı onarır, çünkü neyin yanlış olduğunu okuyabilir. Aynı hatayı iki kez alırsa, döngüyü değil, açıklamayı yeniden yazın.

LangChain yapılandırması

LangChain'in ChatOpenAI sınıfı bir base_url kabul eder, bu yüzden özel bir sağlayıcı paketi gerekmez. model'i uncensored olarak ayarlayın, anahtarı ortamınızdan iletin ve araçları bind_tools ile bağlayın. langchain-openai ve langchain-core'i yükleyin.

import os
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool

@tool
def ticket_status(ticket_id: str) -> str:
    """Return the status of a support ticket."""
    return {"T-101": "open", "T-102": "closed"}.get(ticket_id, "unknown ticket")

llm = ChatOpenAI(
    model="uncensored",
    base_url="https://api.uncensoredapis.com/v1",
    api_key=os.environ["API_KEY"],
    temperature=0.3,
    max_tokens=400,
)
llm_with_tools = llm.bind_tools([ticket_status])

ai = llm_with_tools.invoke("What is the status of ticket T-101?")
for call in ai.tool_calls:
    print(call["name"], call["args"], "->", ticket_status.invoke(call["args"]))

invoke'ün sonucu, tool_calls listesi ayrıştırılmış adlar ve argümanlar içeren bir AIMessage'dir, böylece manuel JSON adımını atlamış olursunuz. Tam otonom bir döngü elde etmek için aynı llm nesnesini LangChain'in agent oluşturucularına verin; uç noktanın yalnızca fonksiyon çağırma yapması gerekir, bu da yapar. Bir agent araçlarını görmezden geliyorsa, docstring ve tür ipuçlarının mevcut olduğunu kontrol edin, çünkü bunlar modelin gördüğü şema haline gelir.

LlamaIndex yapılandırması

LlamaIndex sohbet tamamlama formatını takip eden sunucular için OpenAILike sağlar. Üç ayar önemlidir. api_base API'yi işaret eder, is_chat_model=True sohbet rotasını seçer ve is_function_calling_model=True çerçeveye araç gönderebileceğini söyler. Metin bölenleri ve hafıza arabelleklerinin doğru boyutlandırılması için context_window'i 100000 olarak ayarlayın. llama-index-llms-openai-like'i yükleyin.

import os
from llama_index.llms.openai_like import OpenAILike
from llama_index.core.tools import FunctionTool

def ticket_status(ticket_id: str) -> str:
    """Return the status of a support ticket."""
    return {"T-101": "open", "T-102": "closed"}.get(ticket_id, "unknown ticket")

llm = OpenAILike(
    model="uncensored",
    api_base="https://api.uncensoredapis.com/v1",
    api_key=os.environ["API_KEY"],
    is_chat_model=True,
    is_function_calling_model=True,
    context_window=100000,
    max_tokens=400,
)

tool = FunctionTool.from_defaults(fn=ticket_status)
response = llm.predict_and_call([tool], "Check ticket T-102 for me.")
print(response)

Buradan aynı llm nesnesini bir agent işçisine veya sorgu motoruna iletebilirsiniz. Hatırlayın ki, bilgi erişim borularındaki (pipeline) sistemler, bir isteme çok sayıda parça sığdırabilir; istem ve tamamlama arasında paylaşılan 100.000 tokenlik pencere ile, getirilen parça sayısını sınırlayın ve max_tokens değerini makul tutun.

Bütçe ile agent'ları işletme

Bir agent, tek bir kullanıcı mesajını birden fazla isteğe dönüştürür, bu nedenle kapasiteyi planlayın. Beş fonksiyon turu içeren bir görev, altı model çağrısı gerektirir. On eşzamanlı görev, kısa bir sürede altmış istek üretir ve dakika başına 300 istek sınırı, bir sohbet uygulamasının ulaşacağından çok daha erken gelir. İstemcinizin önüne hatalar ve hız limitleri kılavuzundaki token-havuzu kısıtlamasını yerleştirin.

Her araç sonucu eklendiği için bağlam her turda büyür. Kullanılan eski araç çıktılarını özetleyin veya atın, ve usage.prompt_tokens'a dikkat edin; 50.000'e yaklaştığında geçmişi sıkıştırın. Maliyet için, ortalama 3.000 istek tokenı ve 200 tamamlama tokenı olan 6 çağrılık açıklayıcı bir görev varsayın: bu 18.000 giriş tokenı (milyonda $0.25 başına yaklaşık $0.0045) ve 1.200 çıkış tokenı (milyonda $1.00 başına yaklaşık $0.0012) demektir. Bu token sayıları toplamlar için varsayımlardır, ölçüm değil. Uç noktaya yeniyseniz dokümantasyon ve ücretsiz deneme kredisi ile başlayın.

Bir agent'ı test etme ve hata ayıklama

Agent'lar tek bir fonksiyon için birim testlerin kaçırdığı şekillerde başarısız olur, bu yüzden konuşmayı test edin. Her çalıştırma için tam mesaj listesini, araç argümanları ve sonuçları dahil olmak üzere kaydedin ve günlükteki hataları tekrar oynatın. Üç alışkanlık hızla sonuç verir.

  1. Modeli taklit edin. İstemciyi, senaryolu tool_calls mesajları döndürecek şekilde taklit edin. Bu, kayıt defterinizi, doğrulamanızı ve döngü sonlandırmanızı ağ ve harcama olmadan test eder.
  2. Örnekleme sabitleme. Araç destekli görevler için 0.2 gibi düşük bir temperature kullanın. Daha düşük rastgelelik, daha tekrarlanabilir argüman JSON'u sağlar; daha sonra yazma ağırlıklı adımlar için bu değeri artırabilirsiniz.
  3. Yörüngeleri doğrulayın. Sabitlenmiş birkaç istem için, yalnızca nihai metni değil, hangi araçların ve hangi sırayla çağrıldığını kontrol edin.

finish_reason'a da dikkat edin. Bir araç çağrısının ortasında length değeri, argümanların kesildiği anlamına gelir; max_tokens'ı (32.000'e kadar) artırın ve JSON tamamlanacaktır. Akış, araçlarla da çalışır ancak her çağrı dizini için argüman parçalarını biriktirin ve akış kapanana kadar yalnızca ayrıştırın. Bu parçalar hakkında bir hatırlatma için referansın akış bölümüne bakın.

Sorular ve cevaplar

API, tek bir yanıtta paralel araç çağrılarını destekliyor mu?

Mesaj bir tool_calls dizisi içerir; bu nedenle dizideki her girdiyi işleyin ve her çağrı kimliği için bir araç mesajı gönderin.

Neden ajan döngüsü sonsuza kadar devam ediyor?

Genellikle araç hataları açıklayıcı değildir veya sistem istemi ne zaman durulması gerektiğini belirtmez. Bir adım sınırı ekleyin ve net hata dizeleri döndürün.

LangChain veya LlamaIndex'e ihtiyacım var mı?

Hayır. OpenAI SDK ile düz bir döngü yeterlidir; çerçeveler yalnızca aynı uç nokta üzerinde bir düzenleme katmanı ekler.

Modelin yıkıcı araçları çalıştırmasını nasıl durdurabilirim?

Onay adımı olmadan onları kaydetmeyin. Yalnızca kayıt defterinizdeki işlevler çalışabileceğinden, yazma işlemlerini insan onayının arkasına alın.

Anahtarınız tek bir formun uzağında

Bir hesap oluşturun, anahtarı kopyalayın ve base URL'i değiştirin. Kurulum bu kadar basittir.