نقاط نهاية استجابات OpenAI ذاتية الاستضافة

Note

سيتوفر مساعدو الاستضافة الذاتية لنقاط نهاية استجابات OpenAI في .NET قريبا.

Note

لا يتوفر حاليا مساعدو الاستضافة الذاتية لنقاط نهاية استجابات OpenAI ل Go.

استخدم agent-framework-hosting-responses لتحويل الطلبات والاستجابات على شكل استجابات OpenAI في نقطة نهاية يمتلكها التطبيق الخاص بك. يختار الخادم إطار عمل الويب والمسار والمصادقة والتخويل وخيارات الطلب وتخزين الجلسة.

pip install --pre agent-framework agent-framework-foundry agent-framework-hosting agent-framework-hosting-responses azure-identity

نموذج FastAPI هو تطبيق واحد. يعمل نفس المساعدين مع Django أو Flask أو Starlette أو دالات Azure أو إطار عمل آخر.

استضافة نقطة نهاية عامل

يحول هذا النموذج الطلب إلى قيم تشغيل إطار عمل العامل، ويطبق قائمة السماح للخيار المعرفة من قبل التطبيق، ويستمر في جلسة العمل المحدثة ضمن معرف الاستجابة الذي تم إنشاؤه حديثا.

app = FastAPI()
state = AgentState(
    create_agent,
    session_store=FileSessionStore(SESSIONS_DIR / "snapshots"),
)

ALLOWED_REQUEST_OPTIONS = frozenset({"max_tokens", "reasoning"})


@app.post("/responses", response_model=None)
async def responses(body: dict[str, Any] = Body(...)) -> JSONResponse | StreamingResponse:  # noqa: B008
    """Handle one OpenAI Responses-shaped request."""
    try:
        run = responses_to_run(body)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    session_id, is_conversation_id = responses_session_id(body)
    conversation_id = session_id if is_conversation_id else None
    response_id = create_response_id()

    # App-specific policy: allow only the request options this route is willing
    # to honor. This denies tools, tool_choice, deployment/persistence fields,
    # and all other caller-supplied options by default. Your app decides which
    # options are allowed, altered, or denied.
    options = {key: value for key, value in run["options"].items() if key in ALLOWED_REQUEST_OPTIONS}
    options["reasoning"] = {"effort": "medium", "summary": "auto"}
    options_for_run = cast(Any, options)

    target = await state.get_target()
    lookup_id = session_id or response_id
    # An unknown id supplied through `conversation` becomes a new session here. Production apps
    # can choose to require a separate "create conversation" API instead.
    session = await state.get_or_create_session(lookup_id)
    if run["stream"]:
        stream = target.run(
            run["messages"],
            stream=True,
            session=session,
            options=options_for_run,
        )
        if not isinstance(stream, ResponseStream):
            raise HTTPException(status_code=500, detail="agent did not return a response stream")

        async def stream_events() -> AsyncIterator[str]:
            async for event in responses_from_streaming_run(
                stream,
                response_id=response_id,
                conversation_id=conversation_id,
            ):
                yield event
            # `agent.run(..., stream=True)` updates the session while the stream
            # is consumed/finalized. Persist the selected continuation only
            # after finalization.
            if conversation_id is not None:
                # A stable conversation id is a mutable head. Apps must ensure
                # only one caller advances it at a time; AgentState does not
                # serialize concurrent runs for the same id.
                await state.set_session(conversation_id, session)
            else:
                await state.set_session(response_id, session)

        return StreamingResponse(
            stream_events(),
            media_type="text/event-stream",
        )

    result = await target.run(
        run["messages"],
        session=session,
        options=options_for_run,
    )
    # `agent.run(...)` updates the session. Persist the selected continuation
    # only after the run completes.

AgentState يحل الهدف ويحمل أو ينشئ جلسة عمل. احفظ جلسة العمل بعد التشغيل، أو بعد انتهاء تشغيل الدفق، لأن التشغيل يحدثها.

للحصول على التطبيق الكامل، بما في ذلك تعريف العامل وقائمة السماح بالخيار الطلب، راجع نموذج الاستجابات المحلي.

فهم تحويل استخدام الاستجابة

بالنسبة لاستجابات العامل وسير العمل، تحتفظ حزمة الاستضافة بعنصر OpenAI ResponseUsage أصلي صالح ل SDK دون تغيير عند توفره. لا يدمج استخدام الاستجابات الأصلي مع إطار عمل UsageDetailsالعامل .

عندما لا يتوفر الاستخدام الأصلي، يمكن للحزمة إعادة إنشاء استخدام الاستجابات من حقول إطار عمل العامل المطابقة دلاليا:

قيمة الاستخدام حقل إطار عمل العامل
الرموز المميزة للإدخال input_token_count
الرموز المميزة للإخراج output_token_count
الرموز المميزة لإدخال قراءة ذاكرة التخزين المؤقت cache_read_input_token_count
الرموز المميزة لإدخال كتابة ذاكرة التخزين المؤقت cache_creation_input_token_count
الرموز المميزة للإخراج المنطقي reasoning_output_token_count

يتم الاحتفاظ بالقيم الصفرية الصريحة. إذا total_tokens كان غائبا أثناء وجود كل من عدد الإدخال والإخراج، فإن الحزمة تشتقها كمدخل بالإضافة إلى الإخراج.

إذا كان استخدام إطار عمل العامل المتوفر غير مكتمل أو غير متناسق دلاليا مع مخطط الاستجابات، فإن الحزمة تحذف الاستخدام. لا يخمن، أو ينسخ عدادا إلى آخر، أو يفشل في استجابة ناجحة بخلاف ذلك. يظل العدد العددي الذي تم تكوينه بشكل غير جيد خطأ.

عملية إعادة الإعمار هذه خسارة مقصودة لأن استخدام إطار عمل العامل محايد للموفر واستخدام استجابات OpenAI له شكل أكثر ثراء ومخصصا للموفر. لذلك قد لا تظهر العدادات الخاصة بالموفر التي أبلغ عنها عامل مستضاف، مثل الاستخدام الخاص Anthropic، في الاستجابة التي تلقاها تطبيق الاستدعاء. لا يوفر هذا التحويل إمكانية التشغيل التفاعلي بين إصدارات مختلفة من OpenAI SDK قيد التشغيل في نفس العملية.

استضافة نقطة نهاية سير عمل

WorkflowState يحل سير العمل، ولكن التطبيق الخاص بك يمتلك تخزين نقطة التحقق وتعيين من معرف استجابة إلى نقطة تحقق. يستعيد هذا النموذج نقطة التحقق المحددة من قبل مخول previous_response_id، ثم يحفظ مؤشرا للاستجابة التالية.

app = FastAPI()
state = WorkflowState(workflow_builder, cache_target=False)


@app.post("/responses", response_model=None)
async def responses(body: dict[str, Any] = Body(...)) -> JSONResponse:  # noqa: B008
    """Handle one OpenAI Responses-shaped request for the workflow."""
    try:
        run = responses_to_run(body)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc

    # This sample demonstrates only Responses `previous_response_id`
    # continuation, so reject `conversation` instead of treating it as a
    # checkpoint cursor.
    previous_response_id, is_conversation_id = responses_session_id(body)
    if is_conversation_id:
        raise HTTPException(
            status_code=400,
            detail="This server supports previous_response_id continuation only; conversation is not implemented.",
        )
    response_id = create_response_id()

    target = await state.get_target()
    if previous_response_id and (checkpoint_cursor := checkpoint_cursor_store.get(previous_response_id)) is not None:
        # Restore first. Workflow.run does not allow `message` and
        # `checkpoint_id` in the same call.
        await target.run(
            checkpoint_id=checkpoint_cursor["checkpoint_id"],
            checkpoint_storage=checkpoint_storage_for(checkpoint_cursor["storage_id"]),
        )

    storage_id = response_id
    checkpoint_storage = checkpoint_storage_for(storage_id)
    result = await target.run(
        message=workflow_prompt_from_messages(run["messages"]),
        checkpoint_storage=checkpoint_storage,
    )

    latest = await checkpoint_storage.get_latest(workflow_name=target.name)
    if latest is not None:
        # Responses `previous_response_id` can point to any response id. Store
        # the current response id as the cursor for this workflow continuation.
        cursor = CheckpointCursor(checkpoint_id=latest.checkpoint_id, storage_id=storage_id)
        checkpoint_cursor_store.set_many({response_id: cursor})

    return JSONResponse(
        responses_from_run(
            response_from_workflow_result(result),
            response_id=response_id,
        )
    )

التخزين المدعوم بالملفات للعينة مخصص للتطوير المحلي. استخدم التخزين الدائم عندما يمكن إعادة تشغيل النسخ المتماثلة أو توسيع نطاقها.

Important

تعامل مع previous_response_id و conversation كمدخل غير موثوق به. مصادقة المتصل وتخويله قبل استخدام أي قيمة لتحميل جلسة عمل أو نقطة تحقق أو حفظها. تم إهمال حقل الطلب القديم conversation_id ؛ استخدم حقل استجابات conversation OpenAI بدلا من ذلك.

للحصول على تنسيق سلكي أوسع، راجع نقاط النهاية المتوافقة مع OpenAI.

الخطوات التالية

انتقل إلى أبعد من ذلك: