Skip to content

[Phase 4] feat(ai): 분석 결과 기반 답변 생성 - #46

Merged
HOKAGO-MEMORIES merged 3 commits into
developfrom
phase/4-grounded-analysis-answer
Jun 23, 2026
Merged

[Phase 4] feat(ai): 분석 결과 기반 답변 생성#46
HOKAGO-MEMORIES merged 3 commits into
developfrom
phase/4-grounded-analysis-answer

Conversation

@HOKAGO-MEMORIES

@HOKAGO-MEMORIES HOKAGO-MEMORIES commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

변경 내용

  • F-4 시세/안전 분석 답변이 analysisCards와 Spring tool_results의 구체 수치를 근거로 생성되도록 구현했습니다.
  • live LLM 사용 시 분석 카드와 tool result JSON을 포함하는 전용 프롬프트를 추가했습니다.
  • 결정적 fallback 단위 테스트와 chat API 통합 테스트를 보강했습니다.

closes #45

테스트

  • cd backend-ai && .\.venv\Scripts\python.exe -m pytest tests
  • git diff --cached --check

Summary by CodeRabbit

Release Notes

  • New Features

    • PRICE_ANALYSIS 및 SAFETY_ANALYSIS에 대해 제공된 분석 카드와 도구 결과를 근거로 하는 한국어 응답 생성이 강화되었습니다(최근 실거래 수/지역 평균 보증금, 안전 점수·반경 내 CCTV/벨/조명/경찰 지표 등 포함).
  • Bug Fixes

    • 라이브 생성이 실패해도 기존 하드코딩된 대체 문구 대신 로컬 생성 결과로 안정적으로 이어지도록 개선되었습니다.
  • Tests

    • 가격/안전 응답 포맷과 숫자 포함 여부, 데이터 부족 및 0값 케이스에 대한 검증을 업데이트 및 추가했습니다.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fe9e0bb9-6764-4af8-966f-0b1a0c9e6a32

📥 Commits

Reviewing files that changed from the base of the PR and between 5e8843b and 31dfd69.

📒 Files selected for processing (2)
  • backend-ai/app/services/analysis_answer_service.py
  • backend-ai/tests/test_analysis_answer_generation.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend-ai/app/services/analysis_answer_service.py

📝 Walkthrough

Walkthrough

Adds grounded answer generation for PRICE_ANALYSIS and SAFETY_ANALYSIS intents. A new ANALYSIS_ANSWER_SYSTEM_PROMPT constant and build_analysis_answer_prompt function are introduced in the prompts module. LLMClient.generate_answer now attempts a live LLM call via _generate_live_analysis_answer, falling back to new deterministic local generators in AnalysisAnswerService that extract Korean fact sentences from structured tool results.

Changes

Grounded Analysis Answer Generation

Layer / File(s) Summary
Analysis answer prompt contract
backend-ai/app/rag/prompts.py, backend-ai/app/clients/llm_client.py
Adds ANALYSIS_ANSWER_SYSTEM_PROMPT constant that instructs the assistant to answer in Korean using only provided analysis cards and tool results, avoiding eligibility conclusions and legal advice. Introduces build_analysis_answer_prompt(question, analysis_cards, tool_results_json) that concatenates the system prompt, user question, serialized cards, tool results JSON, and a 2–4 sentence instruction. llm_client.py imports the new builder.
Live LLM call and generate_answer routing
backend-ai/app/clients/llm_client.py
generate_answer routes PRICE_ANALYSIS and SAFETY_ANALYSIS through _generate_live_analysis_answer; that method validates state/config, builds the prompt, calls /chat/completions with JSON-serialized tool results, and returns None on HTTP/parse errors so the caller falls through to local generators.
Deterministic local price/safety answer generators
backend-ai/app/services/analysis_answer_service.py, backend-ai/app/services/__init__.py
Introduces AnalysisAnswerService with generate_price_answer and generate_safety_answer methods that extract priceAnalysis/safetySummary and transaction/metric data from agent state, format Korean fact sentences with won amounts and counts, and return "insufficient data" fallback when no facts exist. Includes private helpers (_tool_result, _analysis_card, _combined_metrics, _items, _format_won) for safe dict/list extraction and localized number formatting.
Unit and integration tests
backend-ai/tests/test_analysis_answer_generation.py, backend-ai/tests/test_agent_chat.py
New test module covers price answer generation from nested tool_results.priceAnalysis, safety answer generation with metric preference, empty-data fallback, and assertions on formatted output and absence of "HUG"/"확정" strings. Existing agent chat tests gain richer mock payloads with nested transaction structures and safety metrics, extended assertions for transaction count ("최근 실거래 2건"), regional average deposit/rent, safety score ("안전 점수 78점"), and CCTV count ("CCTV 8개").

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant generate_answer as LLMClient.generate_answer
  participant _generate_live_analysis_answer
  participant builder as build_analysis_answer_prompt
  participant chat_completions as /chat/completions
  participant fallback as AnalysisAnswerService

  rect rgba(70, 130, 180, 0.5)
    note over generate_answer,_generate_live_analysis_answer: Live LLM path (PRICE/SAFETY_ANALYSIS)
    generate_answer->>_generate_live_analysis_answer: AgentState
    _generate_live_analysis_answer->>builder: question, analysis_cards, tool_results_json
    builder-->>_generate_live_analysis_answer: formatted prompt string
    _generate_live_analysis_answer->>chat_completions: POST with prompt
    chat_completions-->>_generate_live_analysis_answer: completion text
    _generate_live_analysis_answer-->>generate_answer: answer string
  end

  rect rgba(180, 100, 70, 0.5)
    note over generate_answer: Fallback path (None returned or error)
    _generate_live_analysis_answer-->>generate_answer: None
    alt PRICE_ANALYSIS
      generate_answer->>fallback: generate_price_answer(state)
    else SAFETY_ANALYSIS
      generate_answer->>fallback: generate_safety_answer(state)
    end
    fallback-->>generate_answer: Korean fact sentence(s) or insufficient data message
  end

  generate_answer-->>Client: final answer
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ssafy-salman/salmanhae#31: Modifies LLMClient.generate_answer routing for PRICE_ANALYSIS and SAFETY_ANALYSIS with intent-specific behavior and fixed template responses, directly overlapping with this PR's answer generation architecture.
  • ssafy-salman/salmanhae#37: Adds and propagates analysis_cards and selectedPropertyId through agent and chat state, which this PR consumes inside build_analysis_answer_prompt and the new price/safety answer generation logic.
  • ssafy-salman/salmanhae#43: Modifies backend-ai/tests/test_agent_chat.py to exercise PRICE_ANALYSIS/SAFETY_ANALYSIS paths and expected answer content, directly connected to the test payload and assertion updates in this PR.

Poem

🐇 Hopping through the data fields so wide,
Tool results and cards now serve as guide,
No "HUG" conclusions, no legal advice—
Just Korean facts, precise and nice,
When data runs dry, a fallback we find,
Grounded answers for every inquiring mind! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title '[Phase 4] feat(ai): 분석 결과 기반 답변 생성' clearly describes the main change: implementing analysis-result-based answer generation for Phase 4, which aligns with the primary objective of grounding answers in analysis data.
Description check ✅ Passed The PR description covers key changes: grounding analysis answers in concrete data, adding a dedicated LLM prompt, and enhancing test coverage. It references the linked issue #45 and confirms test execution with checkboxes, fulfilling the template requirements.
Linked Issues check ✅ Passed The PR implementation fully addresses issue #45 requirements: price analysis references transaction data and regional statistics, safety analysis references scores and facility counts, responses exclude HUG/legal advice, and deterministic fallback tests are added.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #45 objectives: new LLM client method for live analysis, prompt builder for analysis cards/tool results, analysis answer service for fallback generation, and comprehensive test coverage for price/safety analysis scenarios.

✏️ 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 phase/4-grounded-analysis-answer

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend-ai/app/clients/llm_client.py`:
- Around line 240-380: The functions generate_price_analysis_answer,
generate_safety_analysis_answer, and their helper functions _tool_result,
_analysis_card, _combined_metrics, _items, and _format_won contain business
domain logic for fact extraction and response formatting that should not reside
in the LLMClient class which is a transport/client layer concern. Create a new
Service-layer module (e.g., AnalysisService or PriceAnalysisService) and move
all these functions there, then import and call them from LLMClient to maintain
separation of concerns between transport logic and business logic.
- Around line 341-358: The _analysis_card function returns the first matching
card from the analysis_cards list, but since newer cards are appended at the end
of the list, this returns stale data in multi-turn sessions. Change
_analysis_card to return the last matching analysis card instead of the first by
iterating through the list in reverse order or using a different approach to
find the most recent card. Additionally, in the _combined_metrics function, the
current merge order {**result_metrics, **card_metrics} causes older card metrics
to override current tool_result metrics. Reverse the merge order to
{**card_metrics, **result_metrics} so that result_metrics take precedence and
stale card values do not overwrite current facts.
- Around line 186-191: The json.dumps call with tool_results is currently
outside the try block that follows, which means serialization errors will not be
caught by the fallback handler. Move the json.dumps(tool_results,
ensure_ascii=False) call inside the try block along with the
build_analysis_answer_prompt function call so that any serialization errors are
properly handled by the existing fallback mechanism that returns None.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1bd6c58c-bb00-40eb-a853-fe3ad9d6c874

📥 Commits

Reviewing files that changed from the base of the PR and between 9a4f934 and f60581f.

📒 Files selected for processing (4)
  • backend-ai/app/clients/llm_client.py
  • backend-ai/app/rag/prompts.py
  • backend-ai/tests/test_agent_chat.py
  • backend-ai/tests/test_analysis_answer_generation.py

Comment thread backend-ai/app/clients/llm_client.py Outdated
Comment thread backend-ai/app/clients/llm_client.py Outdated
Comment thread backend-ai/app/clients/llm_client.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend-ai/app/services/analysis_answer_service.py`:
- Around line 18-20: The code uses the `or` operator for numeric field fallbacks
(e.g., `metrics.get("comparableTransactionCount") or len(transactions)`), which
treats valid `0` values as falsy and incorrectly replaces them with default
values. Replace all instances of this pattern with explicit `is None` checks
instead. Specifically, change the comparable_count assignment on line 18 and the
similar numeric field checks in lines 69-74 to use `if metrics.get("fieldName")
is not None` pattern to preserve `0` as a valid metric value.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e02f0a78-0c0e-4d5a-b619-aedb0061b400

📥 Commits

Reviewing files that changed from the base of the PR and between f60581f and 5e8843b.

📒 Files selected for processing (4)
  • backend-ai/app/clients/llm_client.py
  • backend-ai/app/services/__init__.py
  • backend-ai/app/services/analysis_answer_service.py
  • backend-ai/tests/test_analysis_answer_generation.py
✅ Files skipped from review due to trivial changes (1)
  • backend-ai/app/services/init.py

Comment thread backend-ai/app/services/analysis_answer_service.py Outdated
@HOKAGO-MEMORIES
HOKAGO-MEMORIES merged commit 7255f39 into develop Jun 23, 2026
1 check passed
@HOKAGO-MEMORIES
HOKAGO-MEMORIES deleted the phase/4-grounded-analysis-answer branch June 25, 2026 07:57
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.

[Phase 4] Grounded Analysis Answer

1 participant