首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >PHP Neuron 手撸 FFmpeg AI Agent 智能体

PHP Neuron 手撸 FFmpeg AI Agent 智能体

作者头像
Tinywan
发布2026-07-01 16:04:46
发布2026-07-01 16:04:46
60
举报
文章被收录于专栏:开源技术小栈开源技术小栈

前言

作为一名 PHP 开发者,你是否曾想过用最熟悉的语言构建一个智能视频处理助手 Agent?

今天,我将分享如何使用 Webman + Neuron AI + FFmpeg 构建一个智能视频处理助手,让你用自然语言就能完成各种视频编辑操作。

一个完整可落地的实现方案,适合视频处理、短视频平台、内容管理系统等场景。

技术栈选择与优势

  • PHP 8.2+ :开发语言
  • webman:基于 Workerman 的高性能 PHP 框架,适合长连接、异步任务和 API 服务
  • Neuron AI:PHP 专属 Agent 框架(https://github.com/neuron-core/neuron-ai),支持 Tool Calling、中间件、内存管理、多模态(V3 已支持音频输入输出),非常适合构建生产级 Agent
  • LLM 支持:OpenAI、Qwen(通义千问)、DeepSeek、腾讯元宝等,可轻松切换

核心实现思路

  1. 定义 FFmpeg Tools 每个常见操作封装成一个 Tool 类,继承或实现 Neuron AI 的 Tool 接口,描述清楚功能和参数。
  2. 构建 FFmpegAgent 继承 Neuron AI 的 Agent 类,注入多个 Tools,并编写系统指令(System Prompt)。
  3. webman 接口层 自然语言指令 → 调用 Agent → 返回处理结果和文件路径。

核心代码实现

Step.1 创建 FFmpegAgent 智能体

代码语言:javascript
复制
<?php
/**
 * @desc FFmpeg AI Agent - 通过自然语言处理音视频文件
 * @author Tinywan(ShaoBo Wan)
 *
 * 功能:
 * - 视频转码 (支持多种分辨率)
 * - 视频合并
 * - 视频截图
 * - 音频提取
 * - 添加水印 (图片/文字)
 * - 视频信息查询
 */
declare(strict_types=1);

namespace app\neuron;

use NeuronAI\Agent\Agent;
use NeuronAI\Agent\SystemPrompt;
use NeuronAI\Providers\AIProviderInterface;
use NeuronAI\Providers\ZAI\ZAI;
use app\neuron\tool\VideoTranscodeTool;
use app\neuron\tool\VideoConcatTool;
use app\neuron\tool\VideoScreenshotTool;
use app\neuron\tool\AudioExtractTool;
use app\neuron\tool\VideoWatermarkTool;
use app\neuron\tool\VideoInfoTool;

class FFmpegAgent extends Agent
{

    /**
     * @desc provider
     * @author Tinywan(ShaoBo Wan)
     * @return AIProviderInterface
     */
    protectedfunction provider(): AIProviderInterface
    {
        returnnew ZAI(
            key: '7312460bbad94517a18981c93a12c2ea.xxxxxxxxxxxxx',
            model: 'glm-5.1',
            parameters: [],
        );
    }

    /**
     * @desc instructions
     * @author Tinywan(ShaoBo Wan)
     * @return string
     */
    publicfunction instructions(): string
    {
        return (string)new SystemPrompt(
            background: [
                '你是 FFmpeg Agent,专业的音视频处理助手。',
            ],
            steps: [
                '理解用户需求',
                '调用工具处理',
                '直接返回结果',
            ],
            toolsUsage: [
                '使用绝对路径',
                '输出文件路径要完整',
                '错误要明确',
                '只输出最终结果,不要输出思考过程',
            ],
        );
    }

    /**
     * @desc 注册可用的音视频处理工具
     * @return array
     * @author Tinywan(ShaoBo Wan)
     */
    protectedfunction tools(): array
    {
        return [
            new VideoInfoTool(), // 视频信息查询
            new VideoScreenshotTool(), // 视频截图
            new VideoWatermarkTool(), // 添加水印
            new VideoTranscodeTool(), // 视频转码
            new VideoConcatTool(), // 视频合并
            new AudioExtractTool(), // 音频提取
        ];
    }
}

Step.2 实现视频信息查询工具

代码语言:javascript
复制
<?php
/**
 * @desc 视频信息查询工具
 * @author Tinywan(ShaoBo Wan)
 */
declare(strict_types=1);

namespace app\neuron\tool;

use NeuronAI\Tools\Tool;
use NeuronAI\Tools\ToolProperty;
use NeuronAI\Tools\PropertyType;

class VideoInfoTool extends Tool
{
    publicfunction __construct()
    {
        parent::__construct(
            'video_info',
            '获取视频文件的详细信息,包括分辨率、时长、帧率、编码格式等'
        );
    }

    protectedfunction properties(): array
    {
        return [
            new ToolProperty(
                name: 'input_file',
                type: PropertyType::STRING,
                description: '输入视频/音频文件的完整路径',
                required: true
            ),
        ];
    }

    publicfunction __invoke(string $input_file): string
    {
        if (!file_exists($input_file)) {
            return"错误: 文件不存在 - {$input_file}";
        }

        $cmd = sprintf('ffprobe -v quiet -print_format json -show_format -show_streams %s', escapeshellarg($input_file));

        $output = shell_exec($cmd);

        if (!$output) {
            return"错误: 无法获取文件信息,请确保 ffprobe 已正确安装";
        }

        $info = json_decode($output, true);

        if (!$info) {
            return"错误: 无法解析文件信息";
        }

        return$this->formatVideoInfo($input_file, $info);
    }

    privatefunction formatVideoInfo(string $filename, array $info): string
    {
        $lines = [];
        $lines[] = "文件信息: " . basename($filename);
        $lines[] = str_repeat('-', 50);

        if (isset($info['format'])) {
            $format = $info['format'];
            $lines[] = "格式名称: " . ($format['format_name'] ?? '未知');
            $lines[] = "文件大小: " . $this->formatFileSize($format['size'] ?? 0);
            $lines[] = "时长: " . $this->formatDuration($format['duration'] ?? 0);
            $lines[] = "比特率: " . $this->formatBitrate($format['bit_rate'] ?? 0);
        }

        $lines[] = "";
        $lines[] = "流信息:";

        $videoCount = 0;
        $audioCount = 0;

        foreach ($info['streams'] ?? [] as $stream) {
            if ($stream['codec_type'] === 'video') {
                $videoCount++;
                $lines[] = "";
                $lines[] = "  视频流 #{$videoCount}:";
                $lines[] = "     编码: " . ($stream['codec_name'] ?? '未知');
                $lines[] = "     分辨率: " . (($stream['width'] ?? 0) . 'x' . ($stream['height'] ?? 0));
                $lines[] = "     帧率: " . $this->formatFrameRate($stream['r_frame_rate'] ?? '0/0');
                $lines[] = "     像素格式: " . ($stream['pix_fmt'] ?? '未知');
            } elseif ($stream['codec_type'] === 'audio') {
                $audioCount++;
                $lines[] = "";
                $lines[] = "  音频流 #{$audioCount}:";
                $lines[] = "     编码: " . ($stream['codec_name'] ?? '未知');
                $lines[] = "     采样率: " . (($stream['sample_rate'] ?? 0) . ' Hz');
                $lines[] = "     声道: " . ($stream['channels'] ?? 0);
            }
        }

        $lines[] = "";
        $lines[] = str_repeat('-', 50);

        return implode("\n", $lines);
    }

    privatefunction formatFileSize(int|string $bytes): string
    {
        $bytes = (int)$bytes;
        if ($bytes >= 1073741824) {
            return round($bytes / 1073741824, 2) . ' GB';
        }
        if ($bytes >= 1048576) {
            return round($bytes / 1048576, 2) . ' MB';
        }
        if ($bytes >= 1024) {
            return round($bytes / 1024, 2) . ' KB';
        }
        return $bytes . ' bytes';
    }

    privatefunction formatDuration(float|string $seconds): string
    {
        $seconds = (float)$seconds;
        $totalSeconds = (int)$seconds;
        $hours = intdiv($totalSeconds, 3600);
        $remaining = $totalSeconds % 3600;
        $minutes = intdiv($remaining, 60);
        $secs = $remaining % 60;

        if ($hours > 0) {
            return sprintf('%d:%02d:%02d', $hours, $minutes, $secs);
        }

        return sprintf('%d:%02d', $minutes, $secs);
    }

    privatefunction formatBitrate(int|string $bitrate): string
    {
        $bitrate = (int)$bitrate;
        if ($bitrate >= 1000000) {
            return round($bitrate / 1000000, 2) . ' Mbps';
        }
        return round($bitrate / 1000, 2) . ' Kbps';
    }

    privatefunction formatFrameRate(string $frameRate): string
    {
        $parts = explode('/', $frameRate);

        if (count($parts) === 2 && (int)$parts[1] > 0) {
            $fps = (int)$parts[0] / (int)$parts[1];
            return number_format($fps, 2) . ' fps';
        }

        return $frameRate;
    }
}

其余工具大差不差

Step.3 控制器接入

代码语言:javascript
复制
<?php
/**
 * @desc FFmpeg Agent 控制器
 * @author Tinywan(ShaoBo Wan)
 */
declare(strict_types=1);

namespace app\controller;

use NeuronAI\Chat\Messages\UserMessage;
use NeuronAI\Chat\Messages\ContentBlocks\TextContent;
use NeuronAI\Chat\Messages\ContentBlocks\ReasoningContent;
use support\Request;
use support\Response;

class FfmpegController
{
    /**
     * FFmpeg Agent 聊天页面
     */
    publicfunction index(): Response
    {
        return view('ffmpeg/index');
    }

    /**
     * 聊天 API
     */
    publicfunction chat(Request $request): Response
    {
        $message = $request->post('message', '');

        if (empty($message)) {
            return json(['error' => '消息不能为空'], 400);
        }

        $agent = \app\neuron\FFmpegAgent::make();

        // 普通响应
        try {
            $response = $agent->chat(new UserMessage($message));
            $message = $response->getMessage();

            // 只获取文本内容,排除思考过程
            $content = '';
            foreach ($message?->getContentBlocks() ?? [] as $block) {
                if ($block instanceof TextContent && !($block instanceof ReasoningContent)) {
                    $content .= $block->content;
                }
            }

            if (empty($content)) {
                $content = '处理完成,但没有返回内容';
            }

            return json([
                'success' => true,
                'message' => $content,
            ]);
        } catch (\Exception $e) {
            return json([
                'success' => false,
                'error' => $e->getMessage(),
            ], 500);
        }
    }

}

使用效果演示

获取视频信息

视频截图

视频转码

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-04-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 技术栈选择与优势
  • 核心实现思路
  • 核心代码实现
    • Step.1 创建 FFmpegAgent 智能体
    • Step.2 实现视频信息查询工具
    • Step.3 控制器接入
  • 使用效果演示
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档