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