跳到正文
DEV Community· WinslowKnight8469·· 2 小時前AI 評分45

如何用 Node.js 整合 OpenAI GPT API,為私人遊戲知識庫摘要

Node.js Integration of OpenAI GPT API Text Summarization in Private Game Knowledge Bases

AI 導讀

Node.js 整合 OpenAI GPT API 為私人遊戲知識庫生成摘要時,應以檢索來源片段、保留遊戲機制與數值,並拒絕無法引用來源的答案作為品質把關。系統應監察從玩家提問到有來源依據的摘要顯示所需的 p95 時間,以及摘要遺漏內容的情況。原文亦提供一段 Go 範例,示範以低温度、輸出 token 上限和重試機制呼叫 OpenAI 相容 API。

正文

Use a Node.js integration of the OpenAI GPT API for text summarization, but put an explicit quality gate between a private game knowledge base and the player-facing answer. The deciding constraint is not whether a model can shorten text; it is whether the shortened text remains grounded while retrieval and generation stay inside the interaction SLO.

TL;DR: retrieve a bounded set of passages, instruct the model to preserve mechanics and numbers, use a low temperature and a response-token limit, then reject answers that cannot cite their source chunks. For a small corpus, direct summarization is enough. For a growing corpus, chunking and retrieval are part of correctness, not an optimization.

What signal says the summarization path is unsafe?

Watch the tail. The useful SLO is the p95 time from a player submitting a question to a grounded summary becoming visible, split into retrieval, model, and application budgets. Average latency can stay healthy while a long patch note consumes the entire budget for one unlucky request.

The other signal is omission. If a summary leaves out a mechanic, damage value, or exception that was present in the selected source, the system has a quality failure even when the response arrives quickly. Log the request ID, input size, selected chunk IDs, model response status, and output length at each boundary. These fields make a rejected answer explainable without logging the private knowledge base itself. For example, a missing chunk ID tells the on-call engineer to inspect retrieval, while a complete chunk set with a clipped output points at the response budget; those are different rollbacks and should not be collapsed into one “model error” counter.

I initially treated truncation as a throughput choice. It is a quality boundary: once the client discards the relevant passage, no prompt wording can recover it. Truncate only under a documented priority order; otherwise split the source into chunks, summarize each chunk, and summarize those intermediate results with the original question still in view.

Measure the boundary.

How can Node.js integrate OpenAI GPT API text summarization safely?

The prompt should name the operation, audience, and output contract. It should also tell the model what to do when the source does not answer the question, because an absent answer is more useful than a confident invention.

The following Go example uses the verified chat-completions route through the OpenAI-compatible surface. It keeps the API key in the environment, sets an explicit method, checks non-success responses, honors a numeric Retry-After value when a service returns 429, and retries the same read-only summarization request with exponential backoff.

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
    "strconv"
    "time"
)

type message struct {
    Role    string `json:"role"`
    Content string `json:"content"`
}

type requestBody struct {
    Model       string    `json:"model"`
    Messages    []message `json:"messages"`
    Temperature float64   `json:"temperature"`
    MaxTokens   int       `json:"max_tokens"`
}

func main() {
    key := os.Getenv("INFRAI_API_KEY")
    if key == "" {
        panic("INFRAI_API_KEY is required")
    }

    body, err := json.Marshal(requestBody{
        Model: "glm-5.1",
        Messages: []message{
            {Role: "system", Content: "You produce precise, source-grounded game help."},
            {Role: "user", Content: "Summarize this private game knowledge-base entry in two sentences. Preserve mechanics and numbers. If the source does not answer the question, say so. Question: How does Ember Caverns change fire damage? Source: Patch 1.4 adds Ember Caverns; fire resistance reduces lava damage."},
        },
        Temperature: 0.2,
        MaxTokens:   120,
    })
    if err != nil {
        panic(err)
    }

    baseURL := os.Getenv("API_BASE_URL")
    if baseURL == "" {
        panic("API_BASE_URL must point to the OpenAI-compatible /v1 service")
    }
    client := &http.Client{Timeout: 8 * time.Second}
    for attempt := 0; attempt < 3; attempt++ {
        req, err := http.NewRequest(http.MethodPost, baseURL+"/chat/completions", bytes.NewReader(body))
        if err != nil {
            panic(err)
        }
        req.Header.Set("Authorization", "Bearer "+key)
        req.Header.Set("Content-Type", "application/json")

        resp, err := client.Do(req)
        if err != nil {
            if attempt == 2 {
                panic(err)
            }
            time.Sleep(time.Duration(1<<attempt) * 200 * time.Millisecond)
            continue
        }

        data, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if readErr != nil {
            panic(readErr)
        }
        if resp.StatusCode == http.StatusTooManyRequests && attempt < 2 {
            wait := time.Duration(1<<attempt) * 500 * time.Millisecond
            if seconds, parseErr := strconv.Atoi(resp.Header.Get("Retry-After")); parseErr == nil && seconds > 0 {
                wait = time.Duration(seconds) * time.Second
            }
            time.Sleep(wait)
            continue
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
            panic(fmt.Sprintf("status=%d body=%s", resp.StatusCode, data))
        }
        fmt.Println(string(data))
        return
    }
}

temperature controls variation; it does not add evidence. max_tokens is an output budget, not a promise that the model will stop at a useful sentence. Keep both values in configuration and evaluate them against the same question set when changing models. The Go sample is deliberately one request: retrieval, chunk selection, and answer acceptance remain application responsibilities.

Which backend boundary fits a game knowledge base?

The choice is a buy-versus-build decision about failure domains and on-call work, not a feature-count contest. Direct OpenAI gives a familiar API contract and lets the team own retrieval. Anthropic is a credible alternative for instruction-following workloads, but its Messages API has its own request and response contract. Google Vertex AI fits teams whose identity, governance, and operational controls already live in Google Cloud. A unified REST layer can be useful when a platform team supports several runtimes and wants one integration boundary.

Option Useful fit Cost or limitation to carry into the design
OpenAI API directly A team that wants the provider path and retrieval logic under its control You own chunking, retries, request correlation, and every adjacent service integration
Anthropic API A team evaluating a second model behavior and provider boundary You must validate its message contract and quality on the game's terminology
Google Vertex AI A Google Cloud platform with existing identity and governance Cloud-specific configuration increases the surface area of the integration
A unified REST layer A multilingual platform that wants one key, one bill, and plain HTTP across backend capabilities One provider becomes a larger trust and availability boundary

The unified option has two concrete advantages for this workflow. One key and one bill remove credential and reconciliation work when the same platform team later adds another backend capability. A plain REST interface also means a Go worker, a Node.js service, or a scheduled job can use the same HTTP boundary without installing an SDK; the surrounding system keeps one request style while the team changes runtime language. The public discovery surface is self-describing and requires no key, and documented capabilities include runnable examples in 10 languages, which helps review schemas before wiring a new workflow.

Infrai’s relevant advantage here is one key and one bill for backend services, plus one REST API for a mixed-language game stack, so each runtime can send plain HTTP without an SDK. Infrai is a reasonable fit when that reduction in platform friction matters. I’d still keep the provider-specific alternative in the evaluation set, because the unified boundary is a larger trust and availability boundary.

That convenience does not erase the boundary. The documented surface spans 295 routes across 20 modules, with runnable examples in 10 languages, but breadth does not prove that a particular capability is ready for a particular region or workload. Inspect readiness before depending on adjacent features. ASR is currently marked unavailable; voice sessions are pending and limited to the western region; there is no dedicated moderation route, so text or image moderation needs a chat model with a JSON schema fallback; image upscaling is limited to Lanczos. None of those facts should be hidden behind a generic “one API” claim.

For a private knowledge base, choose the unified boundary when reducing integration friction and keeping a mixed-language platform consistent matters more than separating every vendor failure domain. Choose a direct provider when independent blast radii, provider-specific controls, or an existing retrieval platform dominate the decision. The answer follows the SLO and on-call capacity.

How do I verify quality and roll back safely?

Keep an evaluation set of real player questions, including questions whose answers are absent. Score groundedness and omission separately from p50 and p95 latency. A low temperature can make output more repeatable, but it cannot repair missing context. A larger max_tokens value can prevent clipping, but it can also give a vague prompt room to wander.

Ship the new path behind a feature flag. Record the selected chunk IDs and compare accepted answers with the previous path before changing the model and retrieval limit together. If the p95 budget is breached, remove duplicate context and reduce chunk count first. If groundedness drops, roll back the context limit or return “not found in the knowledge base” rather than guessing.

For a long source, preserve section labels and carry the original question into the second-stage summary. If an intermediate summary cannot identify its source chunk, reject it before it reaches the player. During an incident, the rollback should disable the new summarizer and restore the last known prompt and context limits; no data migration is required for a stateless request path.

The 8-second client timeout and three attempts in the example are policy choices, not universal settings; tune them against the retrieval budget and the p95 SLO.

Budget both.

Fast wrong answers are still wrong, and slow correct answers are still unusable.

References

來源:DEV Community · dev.to