PHP 接入降重降AI接口

PHP 调用降重降AI API 完整示例:Guzzle 带重试的基础调用、逐段落处理全文、Laravel 队列批量任务实践。

PHP 接入降重降AI接口

PHP 8+ 推荐用 Guzzle 调用降重降AI接口(cURL 原生写法亦可,Guzzle 处理超时与异常更省事)。接口一次改写同时完成降重和降AI,单次请求处理一个正文自然段(不超过1000字),详细参数说明见接口文档

安装依赖

composer require guzzlehttp/guzzle

基础调用

以下代码包含超时控制与错误重试,可直接复制运行(替换 API_KEY):

<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

$client = new Client([
    'base_uri' => 'https://api.llmapi.fit',
    'timeout'  => 180, // 高峰期智能排队最长约2分钟
]);

function reduceParagraph(Client $client, string $apiKey, string $text, int $maxRetries = 3): string
{
    $attempt = 0;
    while (true) {
        $attempt++;
        try {
            $resp = $client->post('/completion/v2/reduce', [
                'headers' => ['Authorization' => "Bearer {$apiKey}"],
                'json'    => ['text' => $text],
            ]);

            $data = json_decode((string) $resp->getBody(), true);
            if (($data['code'] ?? '') === 'success') {
                return $data['output_text'];
            }
            throw new RuntimeException(
                '业务错误: ' . ($data['code'] ?? 'unknown') . ' ' . ($data['message'] ?? '')
            );

        } catch (RequestException $e) {
            $status = $e->getResponse()?->getStatusCode();

            // 429 请求过于频繁 / 503 GPU服务繁忙:等待后重试
            if (in_array($status, [429, 503], true) && $attempt < $maxRetries) {
                sleep(4);
                continue;
            }
            // 400 参数或余额问题、401 密钥问题:不可重试
            throw $e;
        }
    }
}

$rewritten = reduceParagraph($client, 'YOUR_API_KEY', '待改写的正文自然段……');
echo $rewritten, PHP_EOL;

逐段落处理全文

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

$paragraphs = [
    '第一个正文自然段……',
    '第二个正文自然段……',
];

$rewritten = [];
foreach ($paragraphs as $p) {
    $rewritten[] = reduceParagraph($client, $apiKey, $p);
    usleep(500000); // 500ms,速率控制在 10 次/秒以内
}

Laravel 队列批量处理

批量改写整篇论文(几十上百段)时,建议丢进队列异步处理,避免 HTTP 请求超时:

// app/Jobs/RewriteParagraph.php
class RewriteParagraph implements ShouldQueue
{
    public $timeout = 300;

    public function __construct(
        public Document $document,
        public int $paragraphIndex,
        public string $text,
    ) {}

    public function handle(): void
    {
        $client = app(Client::class);
        $result = reduceParagraph($client, config('services.llmapi.key'), $this->text);

        $this->document->updateParagraph($this->paragraphIndex, $result);
    }
}

// 分发时用 RateLimiter 控制速率(10 次/秒以内)
foreach ($paragraphs as $i => $p) {
    RateLimiter::attempt(
        "reduce:{$document->id}",
        $perSecond = 10,
        fn() => RewriteParagraph::dispatch($document, $i, $p),
        1
    );
}

处理 Word 文档脚注(PhpWord)

论文段落常带脚注引用,直接发送会丢失。正确做法是把脚注引用替换为 [[FNn]] 占位符,改写后原样保留,写回时恢复(完整规范见接口文档第8节)。

phpoffice/phpword 读取段落文本后,可用正则把脚注引用标记替换为段内顺序编号的占位符(每段从 [[FN0]] 开始);写回时按占位符切分文本,在占位符处插回原脚注引用节点。word/footnotes.xml 条目文件全程不修改,保存时 Word 按引用出现顺序自动重排编号。

PhpWord 对脚注写回的支持有限,复杂文档(脚注+尾注+批注并存)建议直接操作 OOXML 包(ZipArchive + DOMDocument)。

常见问题

现象原因处理
401 unauthorizedAPI Key 错误检查 Authorization: Bearer YOUR_API_KEY
400 textTooLong单段超过1000字按语义拆分成多个自然段分别调用
cURL error 28超时timeout 保持 180,重试 2-3 次
JSON 中文转义json_encode 默认转 UnicodeGuzzle 的 json 选项已正确处理 UTF-8,无需干预

下一步