PHP构建多智能体系统:从零实现舆情分析实战

最近几年,每当讨论起AI智能体、多智能体系统这类前沿技术,大家的第一反应往往是Python。TensorFlow、PyTorch、LangChain、AutoGen……这些生态几乎成了AI开发的“官方语言”。而PHP,这个曾经统治Web后端的“王者”,似乎被贴上了“传统”、“过时”的标签,在AI浪潮中逐渐失声。

但技术栈的强弱,真的只由流行度决定吗?一个看似“古老”的工具,能否在新的技术范式下焕发新生?这篇文章,我将分享一个反直觉的实践:完全使用PHP,从零构建一个功能完整的、可协作的多智能体系统。这个系统不仅能完成复杂的任务分解与协作,其简洁的架构和高效的Web集成能力,甚至让它在某些场景下比Python方案更具优势。

如果你对PHP的印象还停留在“模板引擎”和“增删改查”,或者你好奇如何用非主流语言挑战AI工程,那么这篇文章将为你打开一扇新的大门。我们将深入核心原理,手把手搭建环境,并用一个舆情分析(“微舆”)的实战案例,展示PHP多智能体系统的完整实现路径。你会发现,语言的边界,远比你想象的要模糊。

1. 为什么用PHP构建多智能体系统?一个被低估的选择

在深入代码之前,我们必须先回答一个根本问题:为什么是PHP?

多智能体系统的核心,在于“智能体”(Agent)之间的通信、协作与任务调度。这听起来像是Python这类“数据科学语言”的天然主场。然而,从工程化落地的角度看,PHP具备几个被严重低估的优势:

  1. 极致的Web亲和力与部署便利性:多智能体系统的产出往往需要通过API或Web界面交付。PHP与Web服务器(如Nginx/Apache)的集成是天衣无缝的,无需额外的WSGI/ASGI网关。一个index.php文件就能作为整个系统的HTTP入口,简化了架构。
  2. 同步编程模型的简洁性:虽然异步是高性能系统的趋势,但PHP传统的同步、阻塞式编程模型在逻辑表达上极其清晰。对于智能体间顺序性强、逻辑复杂的协作流程,用同步代码来描绘,往往比Python的异步回调或asyncio更直观,更易于调试和维护。
  3. 成熟的工程生态与稳定性:经过二十多年的发展,PHP拥有极其稳定和成熟的内核、扩展(如curl,json,pcntl)以及Composer包管理器。在构建需要长期运行、稳定可靠的生产级系统时,这份“久经考验”的稳定性是宝贵的财富。
  4. 开发效率与原型速度:“一把梭”是PHP开发者的经典梗,但也侧面反映了其极高的开发效率。结合Laravel、Symfony等框架,可以快速搭建出结构清晰的后台,这对于需要快速验证多智能体协作逻辑的原型阶段至关重要。

当然,我们并非要鼓吹PHP全面取代Python。Python在模型训练、科学计算、丰富的AI库(如LangChain)方面拥有绝对优势。本文的核心观点是:在多智能体系统的“编排层”和“应用集成层”,PHP是一个极具竞争力、甚至更优的选择。我们可以利用Python(或直接调用云API)作为“大脑”(模型能力提供者),而用PHP作为“神经中枢”(任务调度与协作控制器),各取所长。

2. 核心概念:什么是多智能体系统?

在开始构建之前,我们需要统一认知。多智能体系统(Multi-Agent System, MAS)不是魔法,它是一套设计范式。

你可以把它想象成一个高度专业化的微型公司

  • CEO(主控智能体):接收用户原始任务(如“分析一下今天关于某产品的舆情”),并负责制定战略、拆解任务。
  • 市场部(信息搜集智能体):擅长从互联网、数据库等渠道爬取和筛选信息。
  • 数据分析部(分析智能体):擅长对文本进行情感分析、关键词提取、摘要总结。
  • 公关部(报告生成智能体):擅长将分析结果整合成一份结构清晰、面向不同角色(老板、技术、市场)的报告。

这些“部门”(智能体)各司其职,通过一套标准的“公司通讯流程”(Agent通信协议)进行协作,共同完成一个复杂目标。

在我们的PHP实现中,每个智能体本质上是一个具有特定技能(Skill)和记忆(Memory)的PHP对象。它们通过消息(Message)进行通信,由一个协调器(Coordinator)来管理任务流。

3. 环境准备与项目初始化

我们的目标是构建一个名为“Micro-Opinion”(微舆)的舆情分析多智能体系统。让我们从零开始。

3.1 基础环境要求

  • PHP: 版本 >= 7.4 (推荐8.0或以上,以获得更好的类型提示和性能)。确保已安装curl,json,pcntl(可选,用于高级并发模式)扩展。
  • Composer: PHP的依赖管理工具,必须安装。
  • Web服务器: Nginx 或 Apache,用于提供HTTP服务。
  • (可选)Python环境: 如果你计划集成本地运行的Python NLP模型(如jieba,snownlp),则需要安装Python 3.6+及相应库。本文主要演示架构,会使用模拟数据和云API(如百度AI开放平台的情感分析API)进行替代。

3.2 创建项目并初始化结构

打开终端,开始我们的项目:

# 1. 创建项目目录 mkdir micro-opinion && cd micro-opinion # 2. 使用Composer初始化项目 composer init --name=your-name/micro-opinion --type=project --no-interaction # 3. 创建核心目录结构 mkdir -p src/{Agent,Skill,Message,Coordinator,Interface} mkdir -p config mkdir -p public # Web入口 mkdir -p tests

3.3 基础依赖安装

我们将使用一些轻量级库来辅助开发:

composer require monolog/monolog composer require guzzlehttp/guzzle
  • monolog/monolog: 用于记录智能体系统的运行日志,便于调试和监控。
  • guzzlehttp/guzzle: 强大的HTTP客户端,用于智能体与外部API(如情感分析API、爬虫目标站)通信。

编辑composer.json,确保自动加载配置正确:

{ "autoload": { "psr-4": { "MicroOpinion\\": "src/" } }, "require": { "monolog/monolog": "^2.0", "guzzlehttp/guzzle": "^7.0" } }

运行composer dump-autoload生成自动加载文件。

4. 核心架构与组件实现

我们的系统由以下几个核心部分组成,我们将逐一实现。

4.1 消息(Message)—— 智能体间的通信协议

首先定义智能体之间传递的数据结构。在src/Message/目录下创建Message.php

<?php // File: src/Message/Message.php namespace MicroOpinion\Message; class Message { private string $id; private string $from; // 发送者ID private string $to; // 接收者ID private string $type; // 消息类型,如 ‘task‘, ‘result‘, ‘error‘ private array $content; // 消息内容,灵活的数据载体 private array $metadata; // 元数据,如时间戳、优先级 public function __construct(string $from, string $to, string $type, array $content, array $metadata = []) { $this->id = uniqid('msg_', true); $this->from = $from; $this->to = $to; $this->type = $type; $this->content = $content; $this->metadata = array_merge([ 'timestamp' => time(), 'priority' => 'normal' ], $metadata); } // Getter 方法 public function getId(): string { return $this->id; } public function getFrom(): string { return $this->from; } public function getTo(): string { return $this->to; } public function getType(): string { return $this->type; } public function getContent(): array { return $this->content; } public function getMetadata(): array { return $this->metadata; } // 便捷方法:将消息转换为数组,便于传输(如JSON) public function toArray(): array { return [ 'id' => $this->id, 'from' => $this->from, 'to' => $this->to, 'type' => $this->type, 'content' => $this->content, 'metadata' => $this->metadata ]; } // 便捷方法:从数组创建消息 public static function fromArray(array $data): self { return new self( $data['from'], $data['to'], $data['type'], $data['content'], $data['metadata'] ?? [] ); } }

关键点Message类是一个简单的数据容器,但它定义了智能体间通信的契约。type字段至关重要,它决定了接收方如何处理这条消息。

4.2 技能(Skill)—— 智能体的能力单元

技能是智能体可以执行的最小操作单元。创建src/Skill/目录和基础技能类。我们先定义一个基础接口和抽象类。

<?php // File: src/Skill/SkillInterface.php namespace MicroOpinion\Skill; use MicroOpinion\Message\Message; interface SkillInterface { // 执行技能,输入一个Message,返回一个Message(结果或错误) public function execute(Message $input): Message; }
<?php // File: src/Skill/BaseSkill.php namespace MicroOpinion\Skill; use MicroOpinion\Message\Message; abstract class BaseSkill implements SkillInterface { protected string $name; protected string $description; public function __construct(string $name, string $description) { $this->name = $name; $this->description = $description; } public function getName(): string { return $this->name; } public function getDescription(): string { return $this->description; } // 一个创建成功结果消息的辅助方法 protected function createSuccessMessage(string $from, string $to, array $result): Message { return new Message($from, $to, 'result', ['status' => 'success', 'data' => $result]); } // 一个创建错误消息的辅助方法 protected function createErrorMessage(string $from, string $to, string $error): Message { return new Message($from, $to, 'error', ['status' => 'error', 'message' => $error]); } }

现在,让我们实现一个具体的技能:网络爬取技能。为了简化,我们模拟爬取过程。

<?php // File: src/Skill/WebCrawlSkill.php namespace MicroOpinion\Skill; use MicroOpinion\Message\Message; use GuzzleHttp\Client; use Psr\Log\LoggerInterface; class WebCrawlSkill extends BaseSkill { private Client $httpClient; private ?LoggerInterface $logger; public function __construct(Client $httpClient, ?LoggerInterface $logger = null) { parent::__construct('web_crawl', '从指定URL爬取文本内容'); $this->httpClient = $httpClient; $this->logger = $logger; } public function execute(Message $input): Message { $content = $input->getContent(); $url = $content['url'] ?? null; if (!$url) { return $this->createErrorMessage($input->getTo(), $input->getFrom(), 'Missing URL in content'); } $this->logger?->info("WebCrawlSkill: Crawling URL: {$url}"); try { // 实际项目中,这里应包含更复杂的HTML解析(如用symfony/dom-crawler) // 此处为模拟 $response = $this->httpClient->request('GET', $url, ['timeout' => 5]); $body = (string)$response->getBody(); // 模拟提取正文(实际应用需用解析库) $extractedText = $this->simulateTextExtraction($body); $result = [ 'url' => $url, 'raw_content' => $body, // 实际项目可能只存摘要 'extracted_text' => $extractedText, ]; $this->logger?->info("WebCrawlSkill: Successfully crawled {$url}"); return $this->createSuccessMessage($input->getTo(), $input->getFrom(), $result); } catch (\Exception $e) { $errorMsg = "Failed to crawl {$url}: " . $e->getMessage(); $this->logger?->error($errorMsg); return $this->createErrorMessage($input->getTo(), $input->getFrom(), $errorMsg); } } private function simulateTextExtraction(string $html): string { // 这是一个极其简单的模拟。真实项目请使用专门的HTML解析器。 // 例如:strip_tags, 或者用 `symfony/dom-crawler` 组件。 $text = strip_tags($html); $text = preg_replace('/\s+/', ' ', $text); return substr($text, 0, 500) . '...'; // 只返回前500字符作为模拟 } }

4.3 智能体(Agent)—— 技能的容器与执行者

智能体是拥有记忆、技能并能处理消息的实体。创建src/Agent/目录。

<?php // File: src/Agent/Agent.php namespace MicroOpinion\Agent; use MicroOpinion\Message\Message; use MicroOpinion\Skill\SkillInterface; use Psr\Log\LoggerInterface; class Agent { private string $id; private string $role; // 角色,如 ‘crawler‘, ‘analyzer‘ private array $skills = []; // 该智能体掌握的技能 [skill_name => SkillInterface] private array $memory = []; // 简单的内存,用于存储会话或上下文 private ?LoggerInterface $logger; public function __construct(string $id, string $role, ?LoggerInterface $logger = null) { $this->id = $id; $this->role = $role; $this->logger = $logger; } public function getId(): string { return $this->id; } public function getRole(): string { return $this->role; } // 为智能体添加一个技能 public function addSkill(SkillInterface $skill): void { $this->skills[$skill->getName()] = $skill; $this->logger?->info("Agent {$this->id} added skill: {$skill->getName()}"); } // 智能体处理收到的消息 public function handleMessage(Message $message): ?Message { $this->logger?->info("Agent {$this->id} received message from {$message->getFrom()}, type: {$message->getType()}"); // 根据消息类型决定行为 switch ($message->getType()) { case 'task': return $this->performTask($message); case 'query': // 处理查询请求,例如查询内存 return $this->handleQuery($message); default: $errorMsg = "Unknown message type: {$message->getType()}"; $this->logger?->warning($errorMsg); return new Message($this->id, $message->getFrom(), 'error', ['message' => $errorMsg]); } } // 执行任务:找到对应的技能并执行 private function performTask(Message $message): ?Message { $content = $message->getContent(); $skillName = $content['skill'] ?? null; $taskData = $content['data'] ?? []; if (!$skillName || !isset($this->skills[$skillName])) { $errorMsg = "Skill '{$skillName}' not found or not specified for agent {$this->id}"; $this->logger?->error($errorMsg); return new Message($this->id, $message->getFrom(), 'error', ['message' => $errorMsg]); } $this->logger?->info("Agent {$this->id} executing skill: {$skillName}"); $skill = $this->skills[$skillName]; // 将任务数据包装成一个新的内部消息,传递给技能执行 $taskMessage = new Message($this->id, $this->id, 'internal_task', $taskData); $resultMessage = $skill->execute($taskMessage); // 将结果返回给消息的原始发送者 $resultMessage = new Message( $this->id, $message->getFrom(), $resultMessage->getType(), $resultMessage->getContent() ); // 可选:将结果存入记忆 $this->memory[] = [ 'task' => $skillName, 'input' => $taskData, 'output' => $resultMessage->getContent(), 'time' => time() ]; return $resultMessage; } private function handleQuery(Message $message): Message { // 简单的内存查询示例 $query = $message->getContent()['query'] ?? 'all'; $result = []; if ($query === 'all') { $result = $this->memory; } // 更复杂的查询逻辑可以在这里实现 return new Message($this->id, $message->getFrom(), 'result', $result); } }

4.4 协调器(Coordinator)—— 系统的指挥中心

协调器负责接收用户请求,创建任务流程,并在智能体之间路由消息。这是多智能体系统的“大脑”。创建src/Coordinator/目录。

<?php // File: src/Coordinator/SimpleCoordinator.php namespace MicroOpinion\Coordinator; use MicroOpinion\Agent\Agent; use MicroOpinion\Message\Message; use Psr\Log\LoggerInterface; class SimpleCoordinator { /** @var array<string, Agent> */ private array $agents = []; private ?LoggerInterface $logger; public function __construct(?LoggerInterface $logger = null) { $this->logger = $logger; } // 注册一个智能体到协调器 public function registerAgent(Agent $agent): void { $this->agents[$agent->getId()] = $agent; $this->logger?->info("Coordinator registered agent: {$agent->getId()}"); } // 处理用户请求的主入口 public function processRequest(array $userRequest): array { $this->logger?->info("Coordinator processing request", $userRequest); $task = $userRequest['task'] ?? 'unknown'; // 根据任务类型,定义工作流(Workflow) $workflow = $this->defineWorkflow($task, $userRequest); $finalResult = []; foreach ($workflow as $step) { $agentId = $step['agent']; $skillName = $step['skill']; $inputData = $step['data']; if (!isset($this->agents[$agentId])) { $finalResult['error'] = "Agent {$agentId} not found in step."; break; } $this->logger?->info("Workflow step: Agent={$agentId}, Skill={$skillName}"); // 构建任务消息 $taskMessage = new Message( 'coordinator', $agentId, 'task', ['skill' => $skillName, 'data' => $inputData] ); // 发送消息给智能体并获取响应 $response = $this->agents[$agentId]->handleMessage($taskMessage); if ($response->getType() === 'error') { $finalResult['error'] = $response->getContent(); $this->logger?->error("Workflow error at step: ", ['step' => $step, 'error' => $response->getContent()]); break; // 工作流中断 } // 将这一步的结果,可能作为下一步的输入 $finalResult['steps'][] = [ 'agent' => $agentId, 'skill' => $skillName, 'result' => $response->getContent() ]; // 简单的数据传递:将上一步的结果数据传递给下一步(如果定义了) if (isset($step['output_to_next']) && $step['output_to_next']) { // 在实际复杂工作流中,这里需要更精细的数据映射逻辑 $nextStepIndex = array_search($step, $workflow) + 1; if (isset($workflow[$nextStepIndex])) { $workflow[$nextStepIndex]['data']['previous_result'] = $response->getContent(); } } } $this->logger?->info("Coordinator finished processing request"); return $finalResult; } // 定义不同任务对应的工作流(这里是硬编码,可扩展为从配置或DSL加载) private function defineWorkflow(string $task, array $request): array { switch ($task) { case 'analyze_opinion': $target = $request['target'] ?? 'default_topic'; return [ [ 'agent' => 'crawler_agent_1', 'skill' => 'web_crawl', 'data' => ['url' => "https://example.com/search?q=" . urlencode($target)], // 模拟搜索URL 'output_to_next' => true ], [ 'agent' => 'analyzer_agent_1', 'skill' => 'sentiment_analysis', // 这个技能我们稍后实现 'data' => [], // 数据会由协调器从 previous_result 填充 'output_to_next' => true ], [ 'agent' => 'reporter_agent_1', 'skill' => 'generate_report', // 这个技能我们稍后实现 'data' => [], // 数据会由协调器从 previous_result 填充 'output_to_next' => false ] ]; default: return []; // 未知任务返回空工作流 } } }

5. 组装系统:构建“微舆”舆情分析系统

现在,我们将所有组件组装起来,并实现剩余的两个关键技能:情感分析和报告生成。

5.1 实现情感分析技能

在实际项目中,你可以集成百度AI、腾讯NLP或阿里云的NLP服务。这里我们创建一个模拟技能。

<?php // File: src/Skill/SentimentAnalysisSkill.php namespace MicroOpinion\Skill; use MicroOpinion\Message\Message; use Psr\Log\LoggerInterface; class SentimentAnalysisSkill extends BaseSkill { private ?LoggerInterface $logger; public function __construct(?LoggerInterface $logger = null) { parent::__construct('sentiment_analysis', '对文本进行情感倾向分析(积极/消极/中性)'); $this->logger = $logger; } public function execute(Message $input): Message { $content = $input->getContent(); // 从上游(如爬虫)的结果中获取文本 $textToAnalyze = $content['previous_result']['data']['extracted_text'] ?? 'No text provided.'; $this->logger?->info("SentimentAnalysisSkill: Analyzing text: " . substr($textToAnalyze, 0, 100) . "..."); // 模拟情感分析逻辑 // 真实情况应调用API或本地模型 $sentimentScore = $this->mockAnalyzeSentiment($textToAnalyze); $sentimentLabel = $this->scoreToLabel($sentimentScore); $result = [ 'original_text_snippet' => substr($textToAnalyze, 0, 200), 'sentiment_score' => $sentimentScore, 'sentiment_label' => $sentimentLabel, 'keywords' => $this->mockExtractKeywords($textToAnalyze), // 模拟关键词提取 ]; return $this->createSuccessMessage($input->getTo(), $input->getFrom(), $result); } private function mockAnalyzeSentiment(string $text): float { // 这是一个非常幼稚的模拟:根据一些关键词计算“情感值” $positiveWords = ['好', '优秀', '喜欢', '强大', '支持', '厉害']; $negativeWords = ['差', '糟糕', '讨厌', '垃圾', '失败', '失望']; $score = 0.5; // 中性基准分 foreach ($positiveWords as $word) { if (mb_strpos($text, $word) !== false) { $score += 0.1; } } foreach ($negativeWords as $word) { if (mb_strpos($text, $word) !== false) { $score -= 0.1; } } // 将分数限制在0到1之间 return max(0, min(1, $score)); } private function scoreToLabel(float $score): string { if ($score > 0.6) return 'positive'; if ($score < 0.4) return 'negative'; return 'neutral'; } private function mockExtractKeywords(string $text): array { // 模拟关键词提取(真实项目应用结巴分词等) $words = preg_split('/\s+/', $text); $filtered = array_filter($words, function($word) { return mb_strlen($word) > 1 && !in_array($word, ['的', '了', '在', '是', '和']); }); return array_slice(array_unique($filtered), 0, 5); // 返回前5个“关键词” } }

5.2 实现报告生成技能

<?php // File: src/Skill/ReportGenerationSkill.php namespace MicroOpinion\Skill; use MicroOpinion\Message\Message; use Psr\Log\LoggerInterface; class ReportGenerationSkill extends BaseSkill { private ?LoggerInterface $logger; public function __construct(?LoggerInterface $logger = null) { parent::__construct('generate_report', '根据分析结果生成结构化报告'); $this->logger = $logger; } public function execute(Message $input): Message { $content = $input->getContent(); $sentimentResult = $content['previous_result']['data'] ?? []; $this->logger?->info("ReportGenerationSkill: Generating report from sentiment data"); $report = [ 'report_id' => 'rep_' . uniqid(), 'generated_at' => date('Y-m-d H:i:s'), 'summary' => $this->generateSummary($sentimentResult), 'details' => $sentimentResult, 'recommendations' => $this->generateRecommendations($sentimentResult['sentiment_label'] ?? 'neutral') ]; return $this->createSuccessMessage($input->getTo(), $input->getFrom(), $report); } private function generateSummary(array $data): string { $label = $data['sentiment_label'] ?? 'unknown'; $score = $data['sentiment_score'] ?? 0.5; $kw = implode(', ', $data['keywords'] ?? []); return sprintf( "情感分析完成。整体倾向为【%s】(得分: %.2f)。主要关联关键词:%s。", $label, $score, $kw ); } private function generateRecommendations(string $sentiment): array { return match($sentiment) { 'positive' => ['继续保持当前策略', '考虑扩大正面宣传'], 'negative' => ['建议启动舆情监控', '准备公关回应话术', '排查产品相关问题'], default => ['舆情平稳,建议保持常规监测'] }; } }

5.3 创建系统入口与配置

现在,我们在public/index.php创建Web入口,并初始化整个系统。

<?php // File: public/index.php require_once __DIR__ . '/../vendor/autoload.php'; use MicroOpinion\Agent\Agent; use MicroOpinion\Coordinator\SimpleCoordinator; use MicroOpinion\Skill\WebCrawlSkill; use MicroOpinion\Skill\SentimentAnalysisSkill; use MicroOpinion\Skill\ReportGenerationSkill; use GuzzleHttp\Client; use Monolog\Logger; use Monolog\Handler\StreamHandler; // 1. 初始化日志 $log = new Logger('micro-opinion'); $log->pushHandler(new StreamHandler(__DIR__ . '/../logs/app.log', Logger::DEBUG)); // 2. 创建HTTP客户端(供爬虫技能使用) $httpClient = new Client(); // 3. 创建技能实例 $webCrawlSkill = new WebCrawlSkill($httpClient, $log); $sentimentSkill = new SentimentAnalysisSkill($log); $reportSkill = new ReportGenerationSkill($log); // 4. 创建智能体并分配技能 $crawlerAgent = new Agent('crawler_agent_1', '网络爬取员', $log); $crawlerAgent->addSkill($webCrawlSkill); $analyzerAgent = new Agent('analyzer_agent_1', '情感分析员', $log); $analyzerAgent->addSkill($sentimentSkill); $reporterAgent = new Agent('reporter_agent_1', '报告生成员', $log); $reporterAgent->addSkill($reportSkill); // 5. 创建协调器并注册智能体 $coordinator = new SimpleCoordinator($log); $coordinator->registerAgent($crawlerAgent); $coordinator->registerAgent($analyzerAgent); $coordinator->registerAgent($reporterAgent); // 6. 处理Web请求(简单路由) header('Content-Type: application/json'); $requestMethod = $_SERVER['REQUEST_METHOD']; $requestPath = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); if ($requestMethod === 'POST' && $requestPath === '/api/analyze') { $input = json_decode(file_get_contents('php://input'), true) ?: []; $task = $input['task'] ?? 'analyze_opinion'; $target = $input['target'] ?? 'PHP多智能体系统'; $userRequest = [ 'task' => $task, 'target' => $target ]; try { $result = $coordinator->processRequest($userRequest); echo json_encode(['success' => true, 'data' => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); } catch (\Exception $e) { http_response_code(500); echo json_encode(['success' => false, 'error' => $e->getMessage()], JSON_PRETTY_PRINT); } } else { http_response_code(404); echo json_encode(['success' => false, 'error' => 'Endpoint not found']); }

6. 运行与测试你的PHP多智能体系统

6.1 启动系统

确保你的Web服务器(如Nginx)已正确配置,将根目录指向micro-opinion/public。或者,在开发环境使用PHP内置服务器:

cd /path/to/micro-opinion php -S localhost:8000 -t public

6.2 发送测试请求

使用curl或 Postman 等工具测试API。

curl -X POST http://localhost:8000/api/analyze \ -H "Content-Type: application/json" \ -d '{"task": "analyze_opinion", "target": "人工智能发展"}'

6.3 预期输出

你会收到一个结构化的JSON响应,它展示了多智能体协作的完整工作流结果:

{ "success": true, "data": { "steps": [ { "agent": "crawler_agent_1", "skill": "web_crawl", "result": { "status": "success", "data": { "url": "https://example.com/search?q=人工智能发展", "extracted_text": "人工智能是当前科技发展的前沿...(模拟的爬取文本)" } } }, { "agent": "analyzer_agent_1", "skill": "sentiment_analysis", "result": { "status": "success", "data": { "original_text_snippet": "人工智能是当前科技发展的前沿...", "sentiment_score": 0.7, "sentiment_label": "positive", "keywords": ["人工智能", "科技", "发展", "前沿", "当前"] } } }, { "agent": "reporter_agent_1", "skill": "generate_report", "result": { "status": "success", "data": { "report_id": "rep_123456", "generated_at": "2023-10-27 14:30:00", "summary": "情感分析完成。整体倾向为【positive】(得分: 0.70)。主要关联关键词:人工智能, 科技, 发展, 前沿, 当前。", "details": { ... }, "recommendations": ["继续保持当前策略", "考虑扩大正面宣传"] } } } ] } }

7. 常见问题与排查思路

在构建和运行过程中,你可能会遇到以下问题:

问题现象可能原因排查方式解决方案
Composer 安装依赖失败网络问题或composer.json语法错误。1. 运行composer diagnose
2. 检查composer.json格式。
1. 使用国内镜像:composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
2. 修正composer.json
访问localhost:8000报 404PHP内置服务器未启动在正确目录,或.htaccess/Nginx配置问题。1. 确认终端当前路径。
2. 检查public/index.php是否存在。
1. 确保在项目根目录执行php -S localhost:8000 -t public
2. 对于Nginx/Apache,确保文档根目录指向public
API 请求返回 500 内部错误PHP代码语法错误或运行时异常。1. 查看logs/app.log文件。
2. 打开PHP错误显示(开发环境):在index.php开头加ini_set('display_errors', 1); error_reporting(E_ALL);
1. 根据日志修复代码。
2. 检查所有requireuse的类路径是否正确。
智能体未执行技能技能未正确注册到智能体,或工作流定义中skill名称拼写错误。1. 在index.php中打印$crawlerAgent的技能列表。
2. 检查SimpleCoordinator::defineWorkflow中的skill名称是否与Skill类中定义的name完全一致。
1. 确保addSkill被调用。
2. 统一技能名称字符串,建议定义为类常量。
模拟情感分析结果不准确当前使用的是基于关键词的极简模拟逻辑。这是预期行为,因为我们仅用于演示架构。升级方案:在SentimentAnalysisSkill::execute方法中,将模拟调用替换为真实的API调用(如百度AI情感分析)。你需要:
1. 注册对应云服务。
2. 使用Guzzle发送HTTP请求到API端点。
3. 解析返回的JSON,提取情感标签和置信度。
工作流步骤间数据传递失败SimpleCoordinatoroutput_to_next逻辑过于简单,未正确处理复杂数据结构。processRequest方法中,打印每一步的$inputData,观察数据变化。增强协调器的工作流引擎。可以设计一个更通用的数据上下文(Context)对象,在工作流步骤间传递和转换数据。

8. 最佳实践与进阶方向

这个示例项目是一个起点,要将其用于生产环境或更复杂的场景,你需要考虑以下最佳实践和扩展方向:

  1. 持久化与状态管理:当前的Agent内存是临时的。生产环境需要将对话历史、任务状态等持久化到数据库(如Redis、MySQL)。
  2. 更强大的工作流引擎SimpleCoordinator的工作流是硬编码的。可以设计一个基于YAML或JSON的工作流定义语言(DSL),实现条件分支、并行执行、循环等复杂逻辑。
  3. 异步与队列:对于耗时任务(如爬取大量页面),应将任务推入消息队列(如RabbitMQ、Redis Stream),由后台Worker智能体处理,避免阻塞HTTP请求。
  4. 技能市场与动态加载:可以设计一个技能注册中心,允许系统在运行时动态发现和加载新的技能,提高系统的可扩展性。
  5. 集成真实的AI能力
    • 大语言模型(LLM)集成:将Skill包装成与LLM(如通过OpenAI API、文心一言API)交互的模块,让智能体拥有理解和生成自然语言的能力。
    • 专用模型集成:为情感分析、实体识别、文本摘要等任务集成专业的NLP模型或API。
  6. 监控与可观测性:为每个Message添加唯一追踪ID,并集成像Prometheus和Grafana这样的监控工具,可视化智能体间的调用链、耗时和错误率。
  7. 安全性
    • 输入验证:对所有传入Skill的数据进行严格的过滤和验证,防止注入攻击。
    • 权限控制:为不同智能体定义权限等级,控制其可以访问的技能和数据。
    • API密钥管理:使用环境变量或安全的配置管理服务来存储云API的密钥,切勿硬编码在代码中。

9. 总结:PHP在AI时代的角色再思考

通过这个“微舆”系统的构建,我们证明了PHP完全有能力作为多智能体系统的核心编排框架。它的优势不在于实现最前沿的机器学习算法,而在于快速、稳定、高效地整合与调度各种能力(包括Python提供的AI能力),并将其封装成易于通过Web访问的服务。

这个项目的价值不在于替代Python,而在于拓宽技术选型的视野。当你的团队擅长PHP,且核心需求是构建一个需要复杂逻辑编排、高并发Web接口、快速迭代的业务系统时,用PHP作为“胶水层”和“调度中心”,而将计算密集型的AI任务交给Python微服务或云API,是一种非常务实且高效的架构选择。

下一步,你可以尝试:

  1. 替换模拟技能:将WebCrawlSkillSentimentAnalysisSkill替换为调用真实API(如SerpAPI进行搜索、百度NLP进行情感分析)的实现。
  2. 设计可视化界面:为这个系统开发一个简单的管理后台,可以动态添加智能体、编辑工作流、查看任务执行历史。
  3. 探索更复杂的协作模式:实现智能体之间的直接对话(如通过消息总线),而不仅仅是通过协调器集中控制。

技术的生命力在于解决问题。PHP或许不再是科技头条的常客,但在它擅长的领域——Web工程、快速开发、稳定交付——它依然是一把极其锋利的刀。用合适的工具解决合适的问题,这才是工程师应有的思维。希望这个项目能给你带来一些启发,下次当你面临系统集成与智能编排的挑战时,不妨考虑一下这位“老朋友”。