Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

レコメンド機能の作成(フェーズ3) #23

Merged
merged 11 commits into from
Aug 29, 2024
4 changes: 4 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,7 @@ AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"

PRTIMES_API_TOKEN=""

OPENAI_API_TOKEN=""
37 changes: 37 additions & 0 deletions backend/_ide_helper_models.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
* @property string $keyword
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\PressRelease> $pressReleases
* @property-read int|null $press_releases_count
* @method static \Illuminate\Database\Eloquent\Builder|Keyword newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Keyword newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Keyword query()
Expand All @@ -37,15 +39,23 @@ class Keyword extends \Eloquent {}
* @property int $id
* @property string $company_id
* @property string $release_id
* @property string $title
* @property string $summary
* @property string $release_created_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Keyword> $keywords
* @property-read int|null $keywords_count
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease query()
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereCompanyId($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereReleaseCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereReleaseId($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereSummary($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereTitle($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereUpdatedAt($value)
*/
class PressRelease extends \Eloquent {}
Expand All @@ -66,6 +76,8 @@ class PressRelease extends \Eloquent {}
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
* @property-read int|null $notifications_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ViewHistory> $view_histories
* @property-read int|null $view_histories_count
* @method static \Database\Factories\UserFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
Expand All @@ -83,3 +95,28 @@ class PressRelease extends \Eloquent {}
class User extends \Eloquent {}
}

namespace App\Models{
/**
*
*
* @property int $id
* @property int $score
* @property int $user_id
* @property int $keyword_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\Keyword $keyword
* @property-read \App\Models\User $user
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory query()
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereKeywordId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereScore($value)
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereUserId($value)
*/
class ViewHistory extends \Eloquent {}
}

59 changes: 59 additions & 0 deletions backend/app/Http/Controllers/Api/PressRelease/ViewController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace App\Http\Controllers\Api\PressRelease;

use App\Exceptions\HttpJsonResponseException;
use App\Http\Controllers\Controller;
use App\Models\Keyword;
use App\Models\User;
use App\UseCases\User\FindByIdAction as UserFindByIdAction;
use App\UseCases\PressRelease\FindByIdsAction;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class ViewController extends Controller
{
/**
* @param Request $request
* @return void
* @throws Exception
*/
public function __invoke(Request $request)
{
/* @var User $user */
$user = UserFindByIdAction::run(Auth::id());
if ($user === null) // @todo まともにExceptionする
throw new Exception("test");

$company_id = $request->get("company_id");
$release_id = $request->get("release_id");

$press_release = FindByIdsAction::run(company_id: $company_id, release_id: $release_id);

if (!$press_release)
throw new HttpJsonResponseException(
400,
"notfound",
"PressRelease Not Found",
compact("company_id", "release_id")
);

/* @var Keyword $keyword */
foreach ($press_release->keywords() as $keyword) {


$user->view_histories()->create([

]);




}
$press_release->keywords();


$press_release->keywords();
}
}
123 changes: 123 additions & 0 deletions backend/app/Jobs/PressReleaseAnalyze.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

namespace App\Jobs;

use App\Libs\Bedrock;
use App\Libs\OpenAI;
use App\UseCases\Keyword\UpdateOrCreateAction;
use App\UseCases\PressRelease\CreateAction as PressReleaseCreateAction;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use GuzzleHttp\Client as GuzzleClient;

class PressReleaseAnalyze implements ShouldQueue
{
use Queueable;

const EXACT_MATCH_WEIGHT = 2;

/**
* @param int $company_id
* @param int $release_id
*/
public function __construct(
private readonly int $company_id,
private readonly int $release_id
)
{}

/**
* @return void
* @throws GuzzleException
* @throws Exception
*/
public function handle(): void
{
$headers = [
"Authorization" => "Bearer " . config("services.prtimes.token")
];

$guzzle = new GuzzleClient([
"base_uri" => "https://hackathon.stg-prtimes.net/"
]);

$openai = new OpenAI(config("services.openai.token"));

$bedrock = new Bedrock();

$response = $guzzle->get("/api/companies/112/releases/1078", ["headers" => $headers]);

if ($response->getStatusCode() !== 200)
throw new Exception("PR TIMES API Error");

/* @var array{
* body: string,
* company_id: int,
* company_name: string,
* created_at: string,
* lead_paragraph: string,
* like: int,
* main_category_id: int,
* main_category_name: string,
* main_image: string,
* main_image_fastly: string,
* release_id: int,
* release_type: string,
* sub_category_id: string,
* sub_category_name: string,
* subtitle: string,
* title: string,
* url: string
* } $data
*/
$data = json_decode($response->getBody()->getContents(), true);

$body = strip_tags($data["body"]);

$summary = $openai->answer(
question: $body,
background: "あなたは、プレスリリースの内容を元にして、若者に興味を持ってもらえる文章を生成するエージェントです。\n
以下の条件を満たしてください。\n
1. 生成した文章は100文字以内\n
2. 絵文字は用いない\n
3. 内容に基づく。感想を含めない。"
);

$press_release = PressReleaseCreateAction::run(
company_id: $this->company_id,
release_id: $this->release_id,
title: $data["title"],
summary: $summary,
release_created_at: $data["created_at"]
);

$keywords_string = $bedrock->answer(
question: "以下の文章から重要なキーワードを抜き出せ \n
指示 \n
名詞や固有名詞のみ出力すること \n
年月日は重要ではないので出力しないこと \n
以下のようなJSONの配列形式で出力すること \n
[\"キーワード\", \"キーワード\", ...] \n
JSONデータ以外一切出力しないこと \n\n" . $body
);
$keywords = json_decode($keywords_string) ?? [];

foreach ($keywords as $keyword) {

$keyword = UpdateOrCreateAction::run($keyword);

$press_release->keywords()
->syncWithPivotValues(
$keyword->id,
[
"weight" => self::EXACT_MATCH_WEIGHT
],
false
);
}

// @todo さらに形態素解析をかまして、低い重みでキーワードを登録する。
}
}
2 changes: 1 addition & 1 deletion backend/app/Libs/Bedrock.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Bedrock

public function __construct(
private readonly string $model_id = "anthropic.claude-3-haiku-20240307-v1:0",
private readonly string $anthropic_version = "anthropic.claude-3-haiku-20240307-v1:0",
private readonly string $anthropic_version = "bedrock-2023-05-31",
private readonly int $max_token = 1000,
array $client_configs = [
"region" => "us-east-1",
Expand Down
6 changes: 3 additions & 3 deletions backend/app/Models/Keyword.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ class Keyword extends Model
use HasFactory;

protected $fillable = [
"keyword",
"weight"
"keyword"
];

/**
* @return BelongsToMany
*/
public function pressReleases(): BelongsToMany
{
return $this->belongsToMany(PressRelease::class);
return $this->belongsToMany(PressRelease::class)
->withPivot("weight");
}
}
3 changes: 2 additions & 1 deletion backend/app/Models/PressRelease.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class PressRelease extends Model
*/
public function keywords(): BelongsToMany
{
return $this->belongsToMany(Keyword::class);
return $this->belongsToMany(Keyword::class)
->withPivot("weight");
}
}
10 changes: 9 additions & 1 deletion backend/app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

Expand All @@ -24,4 +24,12 @@ class User extends Authenticatable
'age',
'prefecture'
];

/**
* @return HasMany
*/
public function view_histories(): HasMany
{
return $this->hasMany(ViewHistory::class);
}
}
33 changes: 33 additions & 0 deletions backend/app/Models/ViewHistory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class ViewHistory extends Model
{
use HasFactory;

protected $fillable = [
"score",
"user_id", "keyword_id"
];

/**
* @return BelongsTo
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}

/**
* @return BelongsTo
*/
public function keyword(): BelongsTo
{
return $this->belongsTo(Keyword::class);
}
}
11 changes: 7 additions & 4 deletions backend/app/UseCases/Keyword/CreateOrFirstAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@

class CreateOrFirstAction
{
/**
* @param string $keyword
* @return Keyword
*/
public static function run(
string $keyword,
int $weight
string $keyword
): Keyword {
// @todo barryvdh/laravel-ide-helperにより強制的に型付け
/* @var Keyword */
return Keyword::createOrFirst(
compact("keyword", "weight"),
compact("keyword", "weight")
["keyword" => $keyword],
["keyword" => $keyword]
);
}
}
Loading