Skip to content

Q&A質問投稿時にピヨルドが自動回答する機能を追加#9863

Merged
komagata merged 6 commits intomainfrom
feature/pjord-auto-answer-qa
Mar 30, 2026
Merged

Q&A質問投稿時にピヨルドが自動回答する機能を追加#9863
komagata merged 6 commits intomainfrom
feature/pjord-auto-answer-qa

Conversation

@komagata
Copy link
Copy Markdown
Member

@komagata komagata commented Mar 30, 2026

概要

Q&Aに質問が投稿されたら、AIピヨルド(@pjord)が自動的に回答(Answer)を投稿するようにしました。
同時に、既存のAI解答機能(ai_answer カラム)を削除しました。

変更内容

削除: 既存のAI解答機能

  • AiAnswerCreator, AiAnswerCreateJob, Ai::AnswerGenerator を削除
  • questions.ai_answer カラム削除(マイグレーション追加)
  • ビュー(_ai_answer.html.slim)、API(jbuilder)から表示を削除

追加: ピヨルド自動回答

  • PjordQuestionAnswerer: question.create 通知のサブスクライバ
  • PjordQuestionAnswerJob: Pjord.respond で回答生成 → Answer として投稿
    • 質問の title + description を送信
    • practice 情報をコンテキストに含む
    • エラー時3回リトライ
  • 既存のメンション応答機能(PjordRespondJob)は変更なし

テスト

  • PjordQuestionAnswerJobTest: 回答作成、質問なし、pjordなし、応答blank、context確認
  • PjordQuestionAnswererTest: ジョブenqueue確認

Summary by CodeRabbit

  • 新機能

    • システムユーザ名義で質問に回答を作成する新しい自動回答ジョブを追加しました。
  • 削除

    • 既存のOpenAIベース自動生成クラスと関連の背景ジョブ、メンター専用のAI表示テンプレート、関連VCRカセットと旧テストを削除しました。
  • 変更

    • 通知購読を新しいサービスへ切替、LLMプロバイダ設定のデフォルトを更新しました。
  • テスト

    • 新ジョブ/サービス用のテストを追加し、テスト用モック応答を拡充しました。
  • その他

    • 質問テーブルからai_answerカラムを削除するマイグレーションを追加しました。

- 既存のAI解答機能(ai_answer)を完全に削除
  - AiAnswerCreator, AiAnswerCreateJob, Ai::AnswerGenerator を削除
  - ai_answer カラム削除マイグレーション追加
  - show.html.slim, API jbuilder からai_answer表示を削除
- Q&A質問作成時にピヨルドが自動回答する機能を追加
  - PjordQuestionAnswerer: question.create の通知でジョブをenqueue
  - PjordQuestionAnswerJob: Pjord.respondで回答生成しAnswerとして投稿
  - メンション不要で自動的に回答
  - 既存のメンション応答機能(PjordRespondJob)は変更なし
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 21c9125f-5fa2-471d-a5fb-86e4af987576

📥 Commits

Reviewing files that changed from the base of the PR and between 0cb7ee1 and fb9e1fa.

📒 Files selected for processing (1)
  • app/jobs/pjord_question_answer_job.rb
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/jobs/pjord_question_answer_job.rb

📝 Walkthrough

Walkthrough

OpenAIベースの生成(Ai::AnswerGenerator、AiAnswerCreateJob、関連ビュー/テスト/VCR)を削除し、PJORDベースの自動応答(PjordQuestionAnswerJob、PjordQuestionAnswerer)へ移行。questionsテーブルからai_answerカラムを削除しました。

Changes

Cohort / File(s) Summary
削除されたOpenAI実装
app/models/ai/answer_generator.rb, app/jobs/ai_answer_create_job.rb, test/models/ai/answer_generator_test.rb, test/jobs/ai_answer_create_job_test.rb, test/cassettes/ai/answer_generator.yml, test/cassettes/question/ai_answer_create_job.yml
OpenAIクライアントを用いる生成クラス・ジョブ・関連テストとVCRカセットを完全に削除。
新規PJORDジョブ・サービスとテスト
app/jobs/pjord_question_answer_job.rb, app/models/pjord_question_answerer.rb, test/jobs/pjord_question_answer_job_test.rb, test/models/pjord_question_answerer_test.rb
PJORDを呼び出すJobとサービスを追加/リネームし、対応する単体テストを追加。
ビュー/API出力の削除
app/views/questions/_ai_answer.html.slim, app/views/questions/show.html.slim, app/views/api/questions/show.json.jbuilder
AI回答表示パーシャルを削除、詳細ビューからのレンダリング削除、JSON出力からai_answer除外。
DB移行とフィクスチャ更新
db/migrate/20260330072542_remove_ai_answer_from_questions.rb, db/fixtures/questions.yml
questions.ai_answerカラムを削除するマイグレーションを追加し、fixtureから該当フィールドを削除。
初期化と通知購読の変更
config/initializers/active_support_notifications.rb
ActiveSupport通知の購読先をAiAnswerCreatorPjordQuestionAnswererへ切替。question.update購読を削除。
テストサポートの修正
test/supports/mock_helper.rb
OpenAIモックレスポンスの構造を拡張(choices/message/usage等を追加)。
RubyLLM初期化のプロバイダ変更
config/initializers/ruby_llm.rb
RubyLLM設定でOpenAIキーからAnthropicキーへ設定先を変更(openai_api_keyanthropic_api_key)。
Pjord設定変更
app/models/pjord.rb
PJORDのデフォルトLLM名の環境変数フォールバックを変更(gpt-5-miniclaude-sonnet-4-6)。

シーケンス図

sequenceDiagram
    participant User as 質問作成者
    participant Notifier as ActiveSupport\n通知
    participant Answerer as PjordQuestionAnswerer
    participant Job as PjordQuestionAnswerJob
    participant Pjord as Pjord(LLMクライアント)
    participant DB as DB / Answer

    User->>Notifier: question.create イベント発行
    Notifier->>Answerer: call(event,..., question: question)
    Answerer->>Job: PjordQuestionAnswerJob.perform_later(question_id: id)
    Job->>Job: Question.find(id)
    Job->>Pjord: Pjord.user / Pjord.respond(message, context)
    Pjord-->>Job: 回答テキスト
    Job->>DB: Answer.create!(user: pjord, question: question, description: response)
    DB-->>Job: 作成完了
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested labels

新機能

Suggested reviewers

  • okuramasafumi
  • ryufuta

ウサギの詩

🐰 古い答えは風に乗せ、
PJORDの葉で新しく歌う、
質問は跳ねて、応答は届き、
小さな記録に未来を植え、
ぴょんと進むコードの庭で 🌱

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PRのタイトルは、既存のAI解答機能の削除とピヨルドによる自動回答機能の追加を扱っており、変更内容の中核を明確に反映しています。
Description check ✅ Passed PRの説明は、テンプレートの必須セクション「概要」を完全に満たし、削除内容、追加内容、テスト内容を詳しく記載しています。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/pjord-auto-answer-qa

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 30, 2026

🚀 Review App

URL: https://bootcamp-pr-9863-fvlfu45apq-an.a.run.app

Basic認証: fjord / (ステージングと同じ)
PR更新時に自動で再デプロイされます。

- RuboCop: unused argument message → _message に修正
- テスト: retry_on を削除(inline adapter で enqueue_at が NotImplementedError)
- テスト: Ai::AnswerGenerator テスト・VCRカセットの削除漏れ修正
- テスト: PjordQuestionAnswererTest を ActiveJob::TestCase に変更
- mock_helper: OpenAI レスポンスに RubyLLM 互換フィールド追加
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
test/jobs/pjord_question_answer_job_test.rb (1)

46-46: API エラー時のテストケース追加を検討してください。

Pjord.respond が API エラー時に例外を発生させる場合のテストがあると、リトライ動作の検証に役立ちます。

📝 テストケースの例
test 'raises exception when Pjord.respond fails' do
  question = questions(:question1)

  Pjord.stub(:respond, ->(*) { raise StandardError, 'API error' }) do
    assert_raises(StandardError) do
      PjordQuestionAnswerJob.perform_now(question_id: question.id)
    end
  end
end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/jobs/pjord_question_answer_job_test.rb` at line 46, Add a test that
simulates an API error from Pjord.respond and asserts the job raises so retry
behavior can be validated: stub Pjord.respond to raise (e.g. StandardError 'API
error') and call PjordQuestionAnswerJob.perform_now with a fixture question
(e.g. questions(:question1)), then use assert_raises to confirm the exception is
propagated; reference Pjord.respond and PjordQuestionAnswerJob.perform_now to
locate where to add this test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/jobs/pjord_question_answer_job.rb`:
- Around line 3-5: The job PjordQuestionAnswerJob is missing the described
3-attempt retry behavior; add a retry_on for StandardError with attempts: 3 (and
optionally a wait/backoff) to ensure Pjord.respond failures are retried three
times; keep the existing discard_on ActiveJob::DeserializationError intact and
place the retry_on declaration inside the PjordQuestionAnswerJob class so
retries apply to perform invocations that call Pjord.respond.
- Around line 24-28: In build_context replace the conditional assignment that
uses respond_to?(:where_mention) with a direct call to question.where_mention;
remove the respond_to? check and set context[:location] = question.where_mention
(the Question model includes the Mentioner concern so where_mention is always
available).

---

Nitpick comments:
In `@test/jobs/pjord_question_answer_job_test.rb`:
- Line 46: Add a test that simulates an API error from Pjord.respond and asserts
the job raises so retry behavior can be validated: stub Pjord.respond to raise
(e.g. StandardError 'API error') and call PjordQuestionAnswerJob.perform_now
with a fixture question (e.g. questions(:question1)), then use assert_raises to
confirm the exception is propagated; reference Pjord.respond and
PjordQuestionAnswerJob.perform_now to locate where to add this test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c978cf33-ba73-4c57-9ff9-e29ec88b7bee

📥 Commits

Reviewing files that changed from the base of the PR and between 66e3218 and 2b8fe71.

📒 Files selected for processing (17)
  • app/jobs/ai_answer_create_job.rb
  • app/jobs/pjord_question_answer_job.rb
  • app/models/ai/answer_generator.rb
  • app/models/pjord_question_answerer.rb
  • app/views/api/questions/show.json.jbuilder
  • app/views/questions/_ai_answer.html.slim
  • app/views/questions/show.html.slim
  • config/initializers/active_support_notifications.rb
  • db/fixtures/questions.yml
  • db/migrate/20260330072542_remove_ai_answer_from_questions.rb
  • test/cassettes/ai/answer_generator.yml
  • test/cassettes/question/ai_answer_create_job.yml
  • test/jobs/ai_answer_create_job_test.rb
  • test/jobs/pjord_question_answer_job_test.rb
  • test/models/ai/answer_generator_test.rb
  • test/models/pjord_question_answerer_test.rb
  • test/supports/mock_helper.rb
💤 Files with no reviewable changes (9)
  • app/views/questions/_ai_answer.html.slim
  • app/views/questions/show.html.slim
  • test/cassettes/question/ai_answer_create_job.yml
  • test/cassettes/ai/answer_generator.yml
  • test/jobs/ai_answer_create_job_test.rb
  • test/models/ai/answer_generator_test.rb
  • app/jobs/ai_answer_create_job.rb
  • app/models/ai/answer_generator.rb
  • db/fixtures/questions.yml

- PjordQuestionAnswerJob: Pjord.respond の例外を rescue して return
  - CIではAPIキーが未設定のため RubyLLM::ConfigurationError が発生
  - WebMockスタブはRubyLLMのAPIキー検証より後のため効かない
- テスト: lambda の keyword argument 名を message: に戻す
  - _message: だと呼び出し側の message: とマッチしない
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
app/jobs/pjord_question_answer_job.rb (1)

5-5: ⚠️ Potential issue | 🟠 Major

リトライ要件が未達で、例外握りつぶしにより再実行も発生しません。

Line 5 に retry_on がなく、Line 19-21 で StandardErrorreturn しているため、PR記載の「最大3回リトライ」が機能しません。Pjord.respond 失敗時はジョブ失敗として再試行させてください。

修正案
 class PjordQuestionAnswerJob < ApplicationJob
   queue_as :default
   discard_on ActiveJob::DeserializationError
+  retry_on StandardError, wait: :polynomially_longer, attempts: 3
 
   def perform(question_id:)
@@
-    begin
-      response = Pjord.respond(message: message, context: context)
-    rescue StandardError => e
-      Rails.logger.error("[PjordQuestionAnswerJob] #{e.class}: #{e.message}")
-      return
-    end
+    response = Pjord.respond(message: message, context: context)
#!/bin/bash
# retry設定と例外握りつぶしの有無を確認(read-only)
rg -n 'retry_on|discard_on|rescue StandardError|Pjord.respond' app/jobs/pjord_question_answer_job.rb
nl -ba app/jobs/pjord_question_answer_job.rb | sed -n '1,80p'

Also applies to: 17-22

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/jobs/pjord_question_answer_job.rb` at line 5,
現在のジョブ(app/jobs/pjord_question_answer_job.rb)は discard_on
ActiveJob::DeserializationError がある一方で retry_on が定義されておらず、また Pjord.respond
呼び出し失敗時に rescue 節で StandardError を return しているため「最大3回リトライ」が機能していません。fix: ジョブクラスに
retry_on StandardError, attempts: 3(または指定された回数)を追加して再試行を有効化し、Pjord.respond
を呼ぶ箇所(および該当する rescue 節)では例外を握りつぶさず raise するか、失敗時に raise
するように変更してジョブを失敗させ再試行させてください;既存の discard_on ActiveJob::DeserializationError
はそのまま残します。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@app/jobs/pjord_question_answer_job.rb`:
- Line 5: 現在のジョブ(app/jobs/pjord_question_answer_job.rb)は discard_on
ActiveJob::DeserializationError がある一方で retry_on が定義されておらず、また Pjord.respond
呼び出し失敗時に rescue 節で StandardError を return しているため「最大3回リトライ」が機能していません。fix: ジョブクラスに
retry_on StandardError, attempts: 3(または指定された回数)を追加して再試行を有効化し、Pjord.respond
を呼ぶ箇所(および該当する rescue 節)では例外を握りつぶさず raise するか、失敗時に raise
するように変更してジョブを失敗させ再試行させてください;既存の discard_on ActiveJob::DeserializationError
はそのまま残します。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 56349a6c-95c1-48ce-98e4-4852c29b4ba6

📥 Commits

Reviewing files that changed from the base of the PR and between 2b8fe71 and f88bcff.

📒 Files selected for processing (2)
  • app/jobs/pjord_question_answer_job.rb
  • test/jobs/pjord_question_answer_job_test.rb
✅ Files skipped from review due to trivial changes (1)
  • test/jobs/pjord_question_answer_job_test.rb

- build_context: respond_to?(:where_mention) チェックを削除(Question は常に Mentioner を include)
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/jobs/pjord_question_answer_job.rb (1)

7-27: 実装は適切です。

ガードクローズによる早期リターン、エラーハンドリング、回答作成の流れが明確に構成されています。Pjord.respond の例外を rescue StandardError で処理し、静かに終了する設計は意図的なものと理解しています。

(任意)エラーログに question_id を追加すると、デバッグ時に追跡しやすくなります。

♻️ 提案(任意)
     rescue StandardError => e
-      Rails.logger.error("[PjordQuestionAnswerJob] #{e.class}: #{e.message}")
+      Rails.logger.error("[PjordQuestionAnswerJob] question_id=#{question_id} #{e.class}: #{e.message}")
       return
     end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/jobs/pjord_question_answer_job.rb` around lines 7 - 27, The error log in
the rescue block should include the question identifier to aid debugging; update
the rescue in perform to include question_id (and optionally question.title) in
the Rails.logger.error message when rescuing Pjord.respond errors (referencing
perform, Pjord.respond, and Rails.logger.error), so logs read something like
"[PjordQuestionAnswerJob] question_id=... #{e.class}: #{e.message}" while
leaving the existing early returns and Answer.create! behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/jobs/pjord_question_answer_job.rb`:
- Around line 7-27: The error log in the rescue block should include the
question identifier to aid debugging; update the rescue in perform to include
question_id (and optionally question.title) in the Rails.logger.error message
when rescuing Pjord.respond errors (referencing perform, Pjord.respond, and
Rails.logger.error), so logs read something like "[PjordQuestionAnswerJob]
question_id=... #{e.class}: #{e.message}" while leaving the existing early
returns and Answer.create! behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 24057a28-e38d-4ae4-85eb-bdfa47425073

📥 Commits

Reviewing files that changed from the base of the PR and between f88bcff and e2ab8f1.

📒 Files selected for processing (1)
  • app/jobs/pjord_question_answer_job.rb

- RubyLLMの設定をANTHROPIC_API_KEYを使用するように変更
- デフォルトモデルをclaude-sonnet-4-6に変更
AIがQ&Aの構造を理解しやすいように、タイトルと質問内容を
明確に区別したフォーマットに変更
@komagata komagata merged commit d3fee0c into main Mar 30, 2026
4 checks passed
@komagata komagata deleted the feature/pjord-auto-answer-qa branch March 30, 2026 19:30
@github-actions github-actions bot mentioned this pull request Mar 30, 2026
20 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant