从零构建Prism自定义AI提供者:解锁LLM集成新范式
你是否曾因现有AI服务无法满足特定业务需求而困扰?是否希望将内部自研模型或小众AI服务无缝接入Laravel应用?Prism的自定义提供者机制为你打开了可能性之门。本文将系统带你掌握从接口设计到错误处理的全流程开发技巧,让你在1小时内拥有生产级别的AI服务集成能力。
核心概念与架构设计
Prism通过统一抽象层实现多AI服务的无缝切换,其核心在于Provider抽象类定义的标准化接口。该架构采用"最小实现原则",允许开发者仅覆盖需要支持的功能方法,大幅降低集成门槛。
关键接口方法解析:
text(): 处理文本生成请求,返回完整文本响应stream(): 实现流式输出,返回Chunk生成器structured(): 支持结构化数据输出,需配合Schema使用handleRequestException(): 自定义错误处理逻辑,必须抛出异常
实战开发步骤
1. 创建提供者类
创建app/Prism/Providers/MyCustomProvider.php,继承抽象基类并实现核心方法:
namespace App\Prism\Providers;
use Generator;
use Prism\Prism\Providers\Provider;
use Prism\Prism\Text\Request as TextRequest;
use Prism\Prism\Text\Response as TextResponse;
use Prism\Prism\Text\Chunk;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http;
class MyCustomProvider extends Provider
{
protected PendingRequest $client;
public function __construct(protected string $apiKey, protected string $baseUrl)
{
$this->client = Http::baseUrl($baseUrl)
->withHeaders([
'Authorization' => "Bearer {$apiKey}",
'Content-Type' => 'application/json',
]);
}
public function text(TextRequest $request): TextResponse
{
try {
$response = $this->client->post('/completions', [
'model' => $request->model,
'prompt' => $request->prompt,
'max_tokens' => $request->maxTokens,
]);
return new TextResponse(
text: $response->json('choices.0.text'),
finishReason: $response->json('choices.0.finish_reason'),
usage: new \Prism\Prism\ValueObjects\Usage(
promptTokens: $response->json('usage.prompt_tokens'),
completionTokens: $response->json('usage.completion_tokens'),
),
);
} catch (\Illuminate\Http\Client\RequestException $e) {
$this->handleRequestException($request->model, $e);
}
}
public function stream(TextRequest $request): Generator
{
$response = $this->client->send('POST', '/stream', [
'body' => json_encode([
'model' => $request->model,
'prompt' => $request->prompt,
'stream' => true,
]),
]);
foreach (explode("\n", $response->body()) as $line) {
if (str_starts_with($line, 'data: ')) {
$data = json_decode(substr($line, 6), true);
yield new Chunk(
content: $data['choices.0.text'],
finishReason: $data['choices.0.finish_reason'],
);
}
}
}
}
2. 服务注册与配置
在AppServiceProvider中注册自定义提供者:
// app/Providers/AppServiceProvider.php
public function boot(): void
{
$this->app['prism-manager']->extend('my-custom-provider', function ($app, $config) {
return new \App\Prism\Providers\MyCustomProvider(
apiKey: $config['api_key'],
baseUrl: $config['base_url'] ?? 'https://api.my-custom-ai.com/v1',
);
});
}
添加配置到config/prism.php:
return [
'providers' => [
// ...现有提供者
'my-custom-provider' => [
'api_key' => env('MY_CUSTOM_PROVIDER_API_KEY'),
'base_url' => env('MY_CUSTOM_PROVIDER_BASE_URL'),
],
],
];
3. 高级错误处理
重写handleRequestException实现精细化错误控制:
public function handleRequestException(string $model, \Illuminate\Http\Client\RequestException $e): never
{
$statusCode = $e->response->status();
$responseBody = $e->response->json();
match (true) {
$statusCode === 429 => throw \Prism\Prism\Exceptions\PrismRateLimitedException::make(
rateLimits: [
'limit' => $e->response->header('X-RateLimit-Limit'),
'remaining' => $e->response->header('X-RateLimit-Remaining'),
'reset' => $e->response->header('X-RateLimit-Reset'),
],
retryAfter: $e->response->header('Retry-After'),
),
$statusCode === 400 && $responseBody['error']['type'] === 'invalid_schema' =>
throw new \Prism\Prism\Exceptions\PrismStructuredDecodingException(
message: $responseBody['error']['message'],
errors: $responseBody['error']['details'],
),
default => parent::handleRequestException($model, $e),
};
}
测试与验证策略
单元测试实现
// tests/Unit/Providers/MyCustomProviderTest.php
use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider as ProviderEnum;
it('can generate text with custom provider', function () {
// 模拟API响应
\Illuminate\Support\Facades\Http::fake([
'https://api.my-custom-ai.com/v1/completions' => \Illuminate\Support\Facades\Http::response([
'choices' => [['text' => 'Hello from custom AI', 'finish_reason' => 'stop']],
'usage' => ['prompt_tokens' => 5, 'completion_tokens' => 10],
], 200),
]);
$response = Prism::text()
->using('my-custom-provider', 'custom-model-1.0')
->withPrompt('Hello')
->asText();
expect($response->text)->toBe('Hello from custom AI')
->and($response->usage->promptTokens)->toBe(5);
});
集成测试检查清单
✅ 验证所有实现的方法是否按预期工作
✅ 测试异常状态码处理(429/400/500等)
✅ 确认流式输出正确解析
✅ 检查配置参数是否正确传递
✅ 验证工具调用和结构化输出功能(如实现)
性能优化与最佳实践
连接池管理
为高频调用优化HTTP客户端配置:
protected function client(): PendingRequest
{
return Http::baseUrl($this->baseUrl)
->withHeaders($this->headers)
->timeout(30)
->connectTimeout(5)
->pooled(); // 启用连接池
}
缓存策略实现
use Illuminate\Cache\CacheManager;
public function __construct(
protected string $apiKey,
protected CacheManager $cache,
) {}
public function embeddings(\Prism\Prism\Embeddings\Request $request): \Prism\Prism\Embeddings\Response
{
$cacheKey = 'prism:embeddings:'.md5(implode(',', $request->texts).$request->model);
if ($cached = $this->cache->get($cacheKey)) {
return new \Prism\Prism\Embeddings\Response(
embeddings: $cached['embeddings'],
usage: new \Prism\Prism\ValueObjects\EmbeddingsUsage(...$cached['usage']),
);
}
// API调用逻辑...
$this->cache->put($cacheKey, [
'embeddings' => $embeddings,
'usage' => $usage->toArray(),
], now()->hourly());
return $response;
}
安全最佳实践
- 敏感数据处理:避免日志记录API密钥,使用环境变量
- 输入验证:实现请求参数净化
protected function sanitizePrompt(string $prompt): string { return trim(preg_replace('/[\r\n]+/', ' ', $prompt)); } - 权限控制:添加请求签名验证
常见问题与解决方案
| 问题场景 | 解决方案 | 代码示例 |
|---|---|---|
| API响应格式不匹配 | 实现响应适配器 | $this->adaptResponse($rawResponse) |
| 模型不支持特定方法 | 抛出UnsupportedAction异常 | throw PrismException::unsupportedProviderAction('stream', self::class) |
| 长文本截断处理 | 实现自动分块逻辑 | $chunks = str_split($text, 4096); |
| 多模态输入支持 | 使用Media对象包装 | Image::fromLocalPath($path)->toArray() |
生产环境部署清单
- 完成单元测试覆盖率≥80%
- 实现请求重试机制
- 添加性能监控指标
// 在text()方法中添加 \Illuminate\Support\Facades\Log::info('Prism custom provider usage', [ 'model' => $request->model, 'duration' => microtime(true) - LARAVEL_START, ]); - 配置熔断保护
- 编写操作文档
通过这套开发框架,你可以将任何AI服务标准化为Prism兼容的提供者,实现"一次集成,处处可用"的效果。无论是企业内部模型、开源LLM还是新兴AI服务,Prism的抽象层都能确保你的Laravel应用保持一致的调用体验。
想进一步提升?考虑实现:
- 分布式追踪集成
- 动态模型路由
- A/B测试框架
- 成本计量系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



