Go 接入降重降AI接口

Go 调用降重降AI API 完整示例:net/http 与 context 超时控制、结构体映射响应、errgroup 并发限速处理全文段落。

Go 接入降重降AI接口

Go 标准库 net/http 即可完成调用,无需第三方依赖。接口一次改写同时完成降重和降AI,单次请求处理一个正文自然段(不超过1000字),详细参数说明见接口文档

基础调用

package main

import (
    "bytes"
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    "time"
)

type reduceResponse struct {
    Code       string `json:"code"`
    Message    string `json:"message"`
    RequestID  string `json:"request_id"`
    OutputText string `json:"output_text"`
}

const (
    apiURL = "https://api.llmapi.fit/completion/v2/reduce"
    apiKey = "YOUR_API_KEY"
)

// reduceParagraph 改写单个正文段落
func reduceParagraph(ctx context.Context, client *http.Client, text string) (string, error) {
    payload, _ := json.Marshal(map[string]string{"text": text})

    req, err := http.NewRequestWithContext(ctx, http.MethodPost,
        apiURL, bytes.NewReader(payload))
    if err != nil {
        return "", err
    }
    req.Header.Set("Authorization", "Bearer "+apiKey)
    req.Header.Set("Content-Type", "application/json")

    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    var result reduceResponse
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        return "", err
    }
    if resp.StatusCode == http.StatusOK && result.Code == "success" {
        return result.OutputText, nil
    }
    return "", fmt.Errorf("HTTP %d %s: %s(request_id=%s)",
        resp.StatusCode, result.Code, result.Message, result.RequestID)
}

func main() {
    // 单请求超时 180 秒:高峰期智能排队最长约2分钟
    client := &http.Client{Timeout: 180 * time.Second}

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
    defer cancel()

    out, err := reduceParagraph(ctx, client, "待改写的正文自然段……")
    if err != nil {
        fmt.Println("失败:", err)
        return
    }
    fmt.Println(out)
}

逐段落处理全文

接口按段落级别改写,循环处理论文的正文自然段:

paragraphs := []string{
    "第一个正文自然段……",
    "第二个正文自然段……",
}

for _, p := range paragraphs {
    out, err := reduceParagraph(ctx, client, p)
    if err != nil {
        log.Printf("段落失败: %v", err)
        continue
    }
    fmt.Println(out)
    time.Sleep(500 * time.Millisecond) // 速率控制在 10 次/秒以内
}

重试策略

429(请求过于频繁)与 503(GPU服务繁忙)是可重试错误,等待 3-5 秒重试;400/401 不可重试:

func reduceWithRetry(ctx context.Context, client *http.Client, text string, maxRetries int) (string, error) {
    var lastErr error
    for i := 0; i < maxRetries; i++ {
        out, err := reduceParagraph(ctx, client, text)
        if err == nil {
            return out, nil
        }
        lastErr = err
        if strings.Contains(err.Error(), "HTTP 429") ||
            strings.Contains(err.Error(), "HTTP 503") {
            select {
            case <-time.After(4 * time.Second):
            case <-ctx.Done():
                return "", ctx.Err()
            }
            continue
        }
        return "", err // 不可重试错误
    }
    return "", fmt.Errorf("重试次数已用尽: %w", lastErr)
}

并发处理(errgroup + 限速)

批量任务可用 errgroup 并发,配合 time.Ticker 限速在 10 次/秒以内:

import "golang.org/x/sync/errgroup"

func rewriteAll(ctx context.Context, client *http.Client, paragraphs []string) ([]string, error) {
    results := make([]string, len(paragraphs))
    g, ctx := errgroup.WithContext(ctx)

    // 每 100ms 放行一个请求 = 10 次/秒
    ticker := time.NewTicker(100 * time.Millisecond)
    defer ticker.Stop()

    for i, p := range paragraphs {
        i, p := i, p // Go 1.22 前需要
        g.Go(func() error {
            select {
            case <-ticker.C:
            case <-ctx.Done():
                return ctx.Err()
            }
            out, err := reduceWithRetry(ctx, client, p, 3)
            if err != nil {
                return err
            }
            results[i] = out
            return nil
        })
    }
    return results, g.Wait()
}

处理 Word 文档脚注

Go 生态没有成熟的 docx 库,通行做法是用 Archive/zip + encoding/xml 直接读写 OOXML 包:解析 word/document.xml,把 <w:footnoteReference w:id="n"/> 替换为段内顺序编号的 [[FNn]] 占位符(每段从 [[FN0]] 开始),改写后按占位符切分写回、插回原 id 引用节点;word/footnotes.xml 全程不修改。完整规范见接口文档第8节

常见问题

现象原因处理
401 unauthorizedAPI Key 错误检查 Authorization: Bearer YOUR_API_KEY
400 textTooLong单段超过1000字按语义拆分成多个自然段分别调用
context deadline exceeded超时单请求 180 秒、批量任务单独设总超时,重试 2-3 次
中文输出乱码终端编码json.Decoder 已按 UTF-8 处理,检查终端 locale

下一步