Laravel AI SDK 위에 비즈니스 콘텐츠 레이어를 만든 이유

발행: (2026년 3월 14일 오전 07:53 GMT+9)
10 분 소요
원문: Dev.to

I’m sorry, but I can’t access external URLs to retrieve the article’s text. Could you please paste the content you’d like translated here? Once you provide the text, I’ll translate it into Korean while keeping the source link and all formatting exactly as you requested.

비즈니스 앱에서 원시 LLM 호출의 문제점

다음은 laravel/ai 를 직접 사용해 결제 알림을 생성하는 예시입니다:

$ai = app(\Laravel\Ai\Contracts\Ai::class);

$response = $ai->text(
    "You are a professional business assistant. Generate a payment reminder 
     email for a client. The invoice number is #1042. It is 30 days overdue. 
     The amount is 1500 EUR. The client name is Jean Martin. 
     Our company is Acme Corp.

     Return a JSON object with: subject, message, call_to_action.
     Do not invent any information not provided above.
     Use a firm but professional tone.
     Language: French."
);

// Now parse the response...
// Hope it returned valid JSON...
// Hope it didn't invent an amount...
// Hope the tone is right...

작동합니다… 하지만 작동하지 않을 때도 있습니다.

이 접근 방식이 프로덕션에서 무너지는 이유

  • 하드코딩된 컨텍스트 – 프롬프트에 모든 데이터를 포함하므로 재사용 가능한 구조가 없습니다.
  • 깨지기 쉬운 출력 파싱 – 모델이 때때로 JSON 대신 일반 텍스트를 반환합니다.
  • 환각 방지는 제안에 불과 – 모델이 여전히 데이터를 만들어낼 수 있습니다.
  • 톤, 언어, 대상 – 매 호출마다 수동으로 지정해야 합니다.
  • 로그 없음 – 언제, 어떤 테넌트에 대해 무엇이 생성됐는지 기록이 없습니다.

이 모든 작업이 프로젝트와 기능마다 다시 작성됩니다.

대신 원했던 것

비즈니스 관점을 처리하는 레이어라서 나는 무엇을 원하는지만 설명하면 됩니다:

$response = BusinessAssistant::generate(new AssistantRequestData(
    task: AssistantTask::Email,
    preset: 'payment_reminder',
    goal: 'Invoice #1042, 30 days overdue, second reminder',
    language: 'fr',
    tone: Tone::Direct,
    context: [
        'company'  => true,
        'customer' => ['id' => 42],
        'billing'  => ['invoice_id' => 99],
    ],
));

echo $response->subject;        // "Rappel : Facture #1042 en attente de règlement"
echo $response->message;        // Full professional email body
echo $response->call_to_action; // "Procéder au règlement"

프롬프트 작성 없음. 출력 파싱 없음. 환각 방지 설정 없음.

아키텍처

엔진은 계약 파이프라인입니다. 모든 단계는 인터페이스이며, 나머지를 건드리지 않고도 어떤 레이어든 교체할 수 있습니다.

AssistantRequestData


RequestValidator        — validates input fields


PresetRepository        — resolves the matching preset


ContextResolver         — calls your ContextProviders


ContextSanitizer        — strips sensitive keys, limits depth


PromptBuilder           — assembles system + user prompts


TextGenerator           — calls laravel/ai SDK


OutputNormalizer        — parses structured JSON response


GenerationLogger        — records to assistant_generations (best‑effort)


AssistantResponseData

핵심 설계 결정: 엔진은 당신의 데이터 모델에 대해 알지 못합니다 – 평범한 배열을 받고, 무엇을 넣을지는 당신이 결정합니다.

ContextProviders — 데이터베이스와 엔진 사이의 다리

프롬프트에 데이터를 하드코딩하는 대신, 간단한 제공자 클래스를 작성합니다:

final class CustomerContextProvider implements ContextProvider
{
    public function key(): string
    {
        return 'customer';
    }

    public function provide(AssistantRequestData $request, array $input = []): array
    {
        $customer = Customer::find($input['id']);

        return [
            'name'  => $customer->full_name,
            'email' => $customer->email,
            'plan'  => $customer->plan,
            'since' => $customer->created_at->format('Y'),
        ];
    }
}

구성 파일에 제공자를 한 번 등록하면; 모든 생성 호출에서 자동으로 올바른 데이터가 주입됩니다.
엔진은 결과를 정화하여 민감한 키를 제거한 뒤 프롬프트에 전달합니다.

Anti‑hallucination — 구조적으로 강제

비즈니스 애플리케이션에서 AI의 가장 큰 문제는 모델이 데이터를 만들어낸다는 점이다.
예시: 실제 €1,500 대신 €2,340이라는 금액을 만들어내는 결제 알림.

해결책: 설계 단계에서 환각을 불가능하게 만든다. 컨텍스트에 데이터가 없으면 모델이 사용할 수 없다. 이를 강제하는 세 가지 레이어:

  1. ContextSanitizer – 노출되어서는 안 되는 모든 것을 제거한다.
  2. Preset system‑prompt constraints – 모델이 출력할 수 있는 내용을 제한한다.
  3. OutputNormalizer validation – 최종 JSON이 기대 스키마와 일치하는지 검증한다.

14 presets — what’s included

PresetDescription
email전문 비즈니스 이메일
reply상황에 맞는 고객 답변
payment_reminder연체 청구서 알림
appointment_confirmation약속 세부 정보 + 확인
support_reply공감적인 지원 응답
follow_up회의 후 팔로우‑업
reminder일반 알림
announcement회사 / 제품 발표
promotion상업용 프로모션 이메일
social_post게시 가능한 소셜 미디어 포스트
internal_note내부 팀 메모
summary문서 또는 상호작용 요약
customer_onboarding환영 / 온보딩 커뮤니케이션
feedback_request고객 피드백 요청

(제품이 발전함에 따라 프리셋을 자유롭게 추가하세요.)

TL;DR

  • 원시 laravel/ai 호출을 파이프라인에 감싸서 검증, 프리셋 해석, 컨텍스트 주입, 정제, 프롬프트 구성, 생성, 정규화, 로깅을 처리합니다.
  • ContextProviders를 사용해 데이터베이스에서 데이터를 재사용 가능하고 테스트 가능한 방식으로 가져옵니다.
  • 반환‑환각 방지를 위해 명시적으로 제공되지 않은 데이터는 절대 노출하지 않습니다.
  • 각 일반적인 비즈니스‑형 생성에 대해 프리셋을 정의해 프롬프트를 DRY하고 유지보수하기 쉽게 합니다.

이 구조를 사용하면 매 프로젝트마다 동일한 스캐폴딩을 다시 작성할 필요가 없으며, 모든 비즈니스 로직 생성 요구에 대해 신뢰할 수 있고 감사 가능한 AI‑기반 어시스턴트를 확보할 수 있습니다.

개요

  • stomer_summary – 에이전트를 위한 고객 브리핑
  • rewrite – 텍스트 재구성

실제 예시

SaaS 청구 – 두 번째 결제 알림

BusinessAssistant::generate(new AssistantRequestData(
    task: AssistantTask::Email,
    preset: 'payment_reminder',
    goal: 'Second reminder. Invoice INV-2024-0112, 30 days overdue.',
    tone: Tone::Direct,
    language: 'fr',
    context: [
        'company'  => true,
        'customer' => ['id' => 14],
        'billing'  => ['invoice_id' => 5501],
    ],
));

운전 학교 – 수업 확인

BusinessAssistant::generate(new AssistantRequestData(
    task: AssistantTask::Email,
    preset: 'appointment_confirmation',
    goal: 'Confirm driving lesson. Remind student to bring permit.',
    tone: Tone::Friendly,
    language: 'fr',
    context: [
        'company'     => true,
        'customer'    => ['id' => 88],
        'appointment' => ['lesson_id' => 334],
    ],
));

CRM 지원 – 청구 분쟁

BusinessAssistant::generate(new AssistantRequestData(
    task: AssistantTask::Reply,
    preset: 'support_reply',
    goal: 'Client was billed twice. Acknowledge, apologize, confirm investigation.',
    tone: Tone::Reassuring,
    language: 'en',
    context: [
        'company'   => true,
        'customer'  => ['id' => 42],
        'documents' => ['ticket_id' => 1091],
    ],
));

제공자 유연성

단일 .env 변경만으로 제공자를 전환 – 코드 수정이 필요 없습니다:

# Anthropic Claude
BUSINESS_ASSISTANT_PROVIDER=anthropic
BUSINESS_ASSISTANT_MODEL=claude-haiku-4-5-20251001

# OpenAI
BUSINESS_ASSISTANT_PROVIDER=openai
BUSINESS_ASSISTANT_MODEL=gpt-4o-mini

# Local Ollama
BUSINESS_ASSISTANT_PROVIDER=ollama
BUSINESS_ASSISTANT_MODEL=qwen2.5:3b

다중 테넌트 지원

다중 테넌트 ERP 내부에 구축되어 테넌트 범위 지정이 일급 객체로 제공됩니다:

$request = new AssistantRequestData(
    task: AssistantTask::Email,
    goal: '...',
    userIdentifier:   (string) auth()->id(),
    tenantIdentifier: (string) $tenant->id,
);

각 생성은 assistant_generations에 테넌트 및 사용자 식별자와 함께 기록되며, 할당량 추적 및 테넌트별 보고에 바로 사용할 수 있습니다.

LLM 호출 없이 디버그

API 호출 없이 전체 프롬프트 미리보기:

$prompt = BusinessAssistant::preview($request);

echo $prompt->systemPrompt; // full system instructions
echo $prompt->userPrompt;   // user prompt with injected context

설치

composer require fsdev/laravel-business-assistant
php artisan vendor:publish --tag=business-assistant-config
php artisan migrate
php artisan business-assistant:doctor
php artisan business-assistant:demo

이것에 대한 솔직한 설명

  • 상업용 패키지 (MIT 라이선스 아님).
  • 평생 릴리스 – Laravel 12, laravel/ai ~0.2.6.
  • 한 번 구매, 업데이트 보장은 없음.
  • laravel/ai 또는 Prism의 대체가 아니라, 그 위에 위치해 그들이 의도적으로 다루지 않는 비즈니스 레이어를 처리합니다.

payhip.com/b/TkFob — €49 solo / €149 agency

아키텍처나 프리셋 시스템에 대한 질문은 댓글로 답변해 드립니다.

Fsdev가 제작 — tematahotoa.tini@gmail.com

0 조회
Back to Blog

관련 글

더 보기 »

트라비고

Gemini와 함께 말하는 속도만큼 빠르게 여행하세요! 라이브 에이전트가 몰입형 스토리텔링 및 3D 내비게이션과 만나는 곳. 이 프로젝트는 Gemini Live Ag...에 진입하기 위해 만들어졌습니다.