Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,18 @@ Ollama가 없어도 업로드, 인덱싱과 검색은 동작합니다. 검색

`start`는 BGE-M3 준비 완료까지 최대 15분 기다리며, 실패하면 서비스별 복구 명령을 출력합니다.

검색만 시연할 때는 명시적 로컬 범위를 사용합니다. `.env`의 local·Local Filesystem 설정과 실제
Compose 대상 DB를 확인하고 PostgreSQL·Valkey·BGE-M3를 준비하며 Ollama는 호출하지 않습니다.

```bash
./scripts/local-services.sh start --scope search
./scripts/local-services.sh status --scope search
```

`--scope rag`는 위 서비스와 사전 기동된 Native Ollama·모델을 확인합니다. 이 명시적 범위에서는
Homebrew 서비스나 Ollama 앱을 자동 기동하지 않습니다. 인자 없는 기존 명령의 동작은 유지됩니다.
설정 파일은 실행하지 않고 `KEY=VALUE` 형식만 읽으며, 원격 프로필·저장소·모호한 설정은 자동 수정하지 않고 거부합니다.

Apple Silicon에서는 `ollama ps`의 `PROCESSOR`가 GPU인지 확인합니다. 설치와 성능 관련 상세 내용은
[백엔드 실행 문서](backend/README.md#ollama-rag-llm-서버)를 참고하세요.

Expand Down
44 changes: 44 additions & 0 deletions docs/design/local-demo-readiness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 로컬 시연 설정과 준비 점검

## 목적

명시적 search/rag 범위는 앱이 사용할 설정과 실제 로컬 Compose 자원을 대조한다.
기존 인자 없는 `local-services.sh start/status`의 Native Ollama 기동·필수 판정은 유지한다.

## 설정 해석

- 예제의 `KEY=VALUE` 형식만 허용한다. `.env`를 source/eval하지 않는다.
- 환경변수는 파일 값보다 우선한다. 셸 치환·모호한 따옴표·이스케이프·중복 키는 거부한다.
- local 프로필·Local Filesystem·IPv4 로컬 DB/Valkey만 지원한다. DB/Valkey의 localhost는 자식 환경에서도 127.0.0.1로 확정한다.
- 원본 경로는 저장소 `data` 또는 `.demo/storage` 아래로 제한한다. 원격 OpenSQL·GCS·Spring/JVM 설정 우회는 이 경로에서 거부한다.
- 프런트엔드 파일과 프로세스 환경에서 읽은 API·WS 주소도 로컬 API 8080과 맞는지 검사한다.
- 개인 환경 파일은 읽기만 하며 자동 수정하지 않는다. 지원되지 않는 파일 문법은 명확히 거부한다.

## Docker·DB 대조

Docker CLI의 DOCKER_CONTEXT 우선순위에 맞춰 실제 endpoint를 확인한다. Unix 소켓만 허용하고 이후 자식 명령은
그 endpoint에 고정해 전역 context 변경의 영향을 줄인다. [공식 CLI 환경변수 문서](https://docs.docker.com/reference/cli/docker/).

```text
설정 해석 → 로컬 endpoint 확인
→ up 전에 기존 컨테이너의 서비스·작업 디렉터리 확인
→ --no-recreate로 대상 서비스 기동
→ 컨테이너 ID·포트·DB 초기 설정 대조
→ 앱이 사용할 호스트 TCP 포트 연결 확인
→ 같은 DB 계정·SSL·search_path로 SELECT만 실행
→ pgvector 0.8.1 사용 가능 여부 확인
```

단순 localhost 판정만으로 SSH 터널과 실제 로컬 DB를 구분했다고 주장하지 않는다.
DB 점검은 Compose 식별·바인딩·연결·인증·vector를 함께 확인한다. pg_trgm은 Flyway 마이그레이션 뒤 준비되는 항목이다.

## 서비스 범위

| 호출 | Compose | Ollama |
| --- | --- | --- |
| 인자 없는 기존 start/status | 기존 Valkey·BGE-M3 계약 | 기존 Native 기동·필수 판정 유지 |
| `start/status --scope search` | PostgreSQL·Valkey·BGE-M3 | 기동·API 호출·필수 판정 모두 생략 |
| `start/status --scope rag` | 같은 3개 서비스 | 사전 기동된 Native API·모델 재사용. 서비스 관리자·앱을 자동 기동하지 않음 |

명시적 rag의 Native 서버가 준비되지 않았으면 Compose 기동 전에 실패한다.
실제 생성 모델 답변 시험은 준비 상태 확인과 별개다.
46 changes: 46 additions & 0 deletions docs/test-results/demo-readiness-20261018/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# 로컬 설정·준비 범위 계약 검증

## 목적

앱과 점검 도구가 서로 다른 설정을 사용하는 문제를 줄이고, 명시적 search 범위가 Ollama를 요구하지 않도록 한다.
로컬 주소뿐 아니라 실제 Compose 자원을 대조하는 계약도 검사한다.

## 테스트 환경

- 로컬 Python 3.9.6·Bash, 구현 `004768c`, 최종 시험 `c2a924a`.
- API·Docker 명령은 제어 대체물이다. 실제 Docker 기동·DB 접속·모델 호출은 0건이다.
- 원본 환경 파일이나 기존 실행 자원은 수정하지 않았다.

## 테스트 방법

```bash
python3 -m unittest discover -s scripts/demo -p 'test_*.py' -v
bash -n scripts/local-services.sh
```

1. 임시 환경 파일에서 치환·중복·인용 오류와 원격 DB·저장소·설정 우회를 검사한다.
2. 컨테이너 출처·포트 불일치·호스트 포트 단절·DB 인증/확장 결과를 제어한다.
3. 실행 파일 대체물로 실제 Bash start/status 분기를 실행해 search의 Ollama 호출 0과 기존 계약을 확인한다.
4. 이전 시연 판정 회귀도 함께 실행한다.

## 관측 결과

| 실행 ID | 목적 | 결과 | 근거 |
| --- | --- | --- | --- |
| demo-readiness-contract-01 | 초기 설정·범위 계약 | 51/51 통과 | [로그](demo-readiness-contract-01.log) |
| demo-readiness-shell-syntax-01 | Bash 문법 | 종료 코드 0 | [로그](demo-readiness-shell-syntax-01.log) |
| demo-readiness-contract-02 | context 우선순위·호스트 포트 보완 | 53/53 통과 | [로그](demo-readiness-contract-02.log) |
| demo-readiness-contract-03 | 기존 명령·Native 사전 준비 보완 | 55/55 통과 | [로그](demo-readiness-contract-03.log) |

search 제어 기동은 PostgreSQL·Valkey·BGE-M3만 요청했고 Ollama 주소 호출은 0건이었다.
Native 미준비 rag는 Compose up 이전에 중단했다. 이는 실제 모델·독립 환경의 E2E 결과가 아니다.

## 검증 과정에서 확인된 사항

- 기존 Bash는 자체적으로 `.env`를 읽지 않았고 Ollama를 항상 필수로 다뤘다.
- Docker CLI는 DOCKER_CONTEXT가 DOCKER_HOST보다 우선한다. 해당 순서와 이후 endpoint 고정을 별도 검사했다.
- 다른 작업공간의 컨테이너를 up 뒤에만 검사하면 늦을 수 있어, up 전 출처 확인과 --no-recreate를 적용했다.
- 컨테이너 내부 SQL만 성공하면 앱이 접속하는 호스트 포트 단절을 놓칠 수 있어 TCP 연결도 검사한다.
- PostgreSQL SELECT는 같은 사용자·비밀번호·SSL·search_path를 사용하고 비밀번호는 명령 인자로 넣지 않는다.
- 엄격한 설정 부분집합이므로 모호한 기존 파일은 자동 수정하지 않고 거부한다.
- 실제 서비스·원격 OpenSQL·Native Ollama·새 환경 시연은 이 회차에서 실행하지 않았다. 임시 제어 파일은 각 시험 종료 시 정리했다.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
실행 ID: demo-readiness-contract-01
목적: 로컬 설정·컨테이너 식별·검색 범위 회귀
환경: Python 3.9.6, 제어 명령·API 응답, 실제 Docker 변경 없음
합격 기준: 51개 검사 통과, search의 Ollama 호출 0건
명령: python3 -m unittest discover -s scripts/demo -p test_*.py -v
시작 시각: 2026-10-18T14:32:40+09:00
test_changed_target_refuses_replay_before_upload (test_demo_verdict.DemoVerdictTest) ... ok
test_citation_outside_search_candidates_fails (test_demo_verdict.DemoVerdictTest) ... ok
test_different_previous_document_id_fails (test_demo_verdict.DemoVerdictTest) ... ok
test_download_hash_mismatch_fails (test_demo_verdict.DemoVerdictTest) ... ok
test_empty_search_results_fail (test_demo_verdict.DemoVerdictTest) ... ok
test_event_redaction_happens_before_storage (test_demo_verdict.DemoVerdictTest) ... ok
test_existing_report_is_never_overwritten (test_demo_verdict.DemoVerdictTest) ... ok
test_full_search_scope_passes_without_rag_polling (test_demo_verdict.DemoVerdictTest) ... ok
test_invalid_candidate_is_not_a_valid_search (test_demo_verdict.DemoVerdictTest) ... ok
test_missing_expected_document_fails (test_demo_verdict.DemoVerdictTest) ... ok
test_missing_query_id_is_not_a_valid_search (test_demo_verdict.DemoVerdictTest) ... ok
test_previous_document_ids_are_compared (test_demo_verdict.DemoVerdictTest) ... ok
test_rag_failed_is_not_success (test_demo_verdict.DemoVerdictTest) ... ok
test_rag_incomplete_is_not_success (test_demo_verdict.DemoVerdictTest) ... ok
test_rag_scope_requires_success_and_valid_citations (test_demo_verdict.DemoVerdictTest) ... ok
test_search_http_error_fails_after_all_documents_indexed (test_demo_verdict.DemoVerdictTest) ... ok
test_table_text_missing_fails (test_demo_verdict.DemoVerdictTest) ... ok
test_unrun_required_stages_cannot_pass (test_demo_verdict.DemoVerdictTest) ... ok
test_http_error_preserves_code_for_signup_decision (test_demo_verdict.HttpBoundaryTest) ... ok
test_oversized_original_is_rejected (test_demo_verdict.HttpBoundaryTest) ... ok
test_ambiguous_quotes_and_inline_comments_are_refused (test_local_config.LocalConfigTest) ... ok
test_container_from_another_checkout_is_refused (test_local_config.LocalConfigTest) ... ok
test_database_check_is_readonly_and_password_is_not_an_argument (test_local_config.LocalConfigTest) ... ok
test_defaults_match_local_quickstart (test_local_config.LocalConfigTest) ... ok
test_disabled_worker_is_refused (test_local_config.LocalConfigTest) ... ok
test_duplicate_key_is_refused (test_local_config.LocalConfigTest) ... ok
test_environment_overrides_file (test_local_config.LocalConfigTest) ... ok
test_foreign_container_is_refused_before_up (test_local_config.LocalConfigTest) ... ok
test_frontend_remote_or_different_api_is_refused (test_local_config.LocalConfigTest) ... ok
test_loopback_port_without_matching_container_is_refused (test_local_config.LocalConfigTest) ... ok
test_remote_database_and_model_are_refused (test_local_config.LocalConfigTest) ... ok
test_remote_docker_endpoint_is_refused (test_local_config.LocalConfigTest) ... ok
test_remote_profile_and_storage_are_refused (test_local_config.LocalConfigTest) ... ok
test_shell_substitution_is_refused_without_execution (test_local_config.LocalConfigTest) ... ok
test_spring_or_jvm_bypass_is_refused (test_local_config.LocalConfigTest) ... ok
test_storage_outside_checkout_is_refused (test_local_config.LocalConfigTest) ... ok
test_url_credentials_query_and_non_http_are_refused (test_local_config.LocalConfigTest) ... ok
test_wrong_extension_version_fails (test_local_config.LocalConfigTest) ... ok
test_search_start_never_calls_ollama (test_local_config.ServiceScopeTest) ... ok
test_claimed_or_retrying_job_is_processing (test_seed_demo_corpus.ClassifyTest) ... ok
test_failed_job (test_seed_demo_corpus.ClassifyTest) ... ok
test_indexed_document (test_seed_demo_corpus.ClassifyTest) ... ok
test_new_upload_waits_for_worker (test_seed_demo_corpus.ClassifyTest) ... ok
test_embedding_hint_first_when_server_not_ready (test_seed_demo_corpus.DiagnoseTest) ... ok
test_embedding_hint_for_failed_documents (test_seed_demo_corpus.DiagnoseTest) ... ok
test_no_hint_when_everything_indexed (test_seed_demo_corpus.DiagnoseTest) ... ok
test_slow_hint_when_worker_and_server_are_up (test_seed_demo_corpus.DiagnoseTest) ... ok
test_worker_hint_when_no_job_started (test_seed_demo_corpus.DiagnoseTest) ... ok
test_different_bytes_produce_different_keys (test_seed_demo_corpus.IdempotencyKeyTest) ... ok
test_same_bytes_produce_same_valid_key (test_seed_demo_corpus.IdempotencyKeyTest) ... ok
test_committed_corpus_is_complete (test_seed_demo_corpus.ManifestTest) ... ok

----------------------------------------------------------------------
Ran 51 tests in 1.219s

OK
종료 시각: 2026-10-18T14:32:42+09:00
종료 코드: 0
소요 시간: 1.315초
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
실행 ID: demo-readiness-contract-02
목적: Docker context 우선순위·실제 호스트 포트·DB 설정 정합성 최종 회귀
환경: Python 3.9.6, 제어 명령·API 응답
합격 기준: 53개 검사 통과, 실제 Docker 변경·모델 호출 0건
명령: python3 -m unittest discover -s scripts/demo -p test_*.py -v
시작 시각: 2026-10-18T14:36:38+09:00
test_changed_target_refuses_replay_before_upload (test_demo_verdict.DemoVerdictTest) ... ok
test_citation_outside_search_candidates_fails (test_demo_verdict.DemoVerdictTest) ... ok
test_different_previous_document_id_fails (test_demo_verdict.DemoVerdictTest) ... ok
test_download_hash_mismatch_fails (test_demo_verdict.DemoVerdictTest) ... ok
test_empty_search_results_fail (test_demo_verdict.DemoVerdictTest) ... ok
test_event_redaction_happens_before_storage (test_demo_verdict.DemoVerdictTest) ... ok
test_existing_report_is_never_overwritten (test_demo_verdict.DemoVerdictTest) ... ok
test_full_search_scope_passes_without_rag_polling (test_demo_verdict.DemoVerdictTest) ... ok
test_invalid_candidate_is_not_a_valid_search (test_demo_verdict.DemoVerdictTest) ... ok
test_missing_expected_document_fails (test_demo_verdict.DemoVerdictTest) ... ok
test_missing_query_id_is_not_a_valid_search (test_demo_verdict.DemoVerdictTest) ... ok
test_previous_document_ids_are_compared (test_demo_verdict.DemoVerdictTest) ... ok
test_rag_failed_is_not_success (test_demo_verdict.DemoVerdictTest) ... ok
test_rag_incomplete_is_not_success (test_demo_verdict.DemoVerdictTest) ... ok
test_rag_scope_requires_success_and_valid_citations (test_demo_verdict.DemoVerdictTest) ... ok
test_search_http_error_fails_after_all_documents_indexed (test_demo_verdict.DemoVerdictTest) ... ok
test_table_text_missing_fails (test_demo_verdict.DemoVerdictTest) ... ok
test_unrun_required_stages_cannot_pass (test_demo_verdict.DemoVerdictTest) ... ok
test_http_error_preserves_code_for_signup_decision (test_demo_verdict.HttpBoundaryTest) ... ok
test_oversized_original_is_rejected (test_demo_verdict.HttpBoundaryTest) ... ok
test_ambiguous_quotes_and_inline_comments_are_refused (test_local_config.LocalConfigTest) ... ok
test_container_from_another_checkout_is_refused (test_local_config.LocalConfigTest) ... ok
test_database_check_is_readonly_and_password_is_not_an_argument (test_local_config.LocalConfigTest) ... ok
test_defaults_match_local_quickstart (test_local_config.LocalConfigTest) ... ok
test_disabled_worker_is_refused (test_local_config.LocalConfigTest) ... ok
test_docker_context_takes_precedence_over_host (test_local_config.LocalConfigTest) ... ok
test_duplicate_key_is_refused (test_local_config.LocalConfigTest) ... ok
test_environment_overrides_file (test_local_config.LocalConfigTest) ... ok
test_foreign_container_is_refused_before_up (test_local_config.LocalConfigTest) ... ok
test_frontend_remote_or_different_api_is_refused (test_local_config.LocalConfigTest) ... ok
test_loopback_port_without_matching_container_is_refused (test_local_config.LocalConfigTest) ... ok
test_missing_host_port_is_not_ready_even_if_container_runs (test_local_config.LocalConfigTest) ... ok
test_remote_database_and_model_are_refused (test_local_config.LocalConfigTest) ... ok
test_remote_docker_endpoint_is_refused (test_local_config.LocalConfigTest) ... ok
test_remote_profile_and_storage_are_refused (test_local_config.LocalConfigTest) ... ok
test_shell_substitution_is_refused_without_execution (test_local_config.LocalConfigTest) ... ok
test_spring_or_jvm_bypass_is_refused (test_local_config.LocalConfigTest) ... ok
test_storage_outside_checkout_is_refused (test_local_config.LocalConfigTest) ... ok
test_url_credentials_query_and_non_http_are_refused (test_local_config.LocalConfigTest) ... ok
test_wrong_extension_version_fails (test_local_config.LocalConfigTest) ... ok
test_search_start_never_calls_ollama (test_local_config.ServiceScopeTest) ... ok
test_claimed_or_retrying_job_is_processing (test_seed_demo_corpus.ClassifyTest) ... ok
test_failed_job (test_seed_demo_corpus.ClassifyTest) ... ok
test_indexed_document (test_seed_demo_corpus.ClassifyTest) ... ok
test_new_upload_waits_for_worker (test_seed_demo_corpus.ClassifyTest) ... ok
test_embedding_hint_first_when_server_not_ready (test_seed_demo_corpus.DiagnoseTest) ... ok
test_embedding_hint_for_failed_documents (test_seed_demo_corpus.DiagnoseTest) ... ok
test_no_hint_when_everything_indexed (test_seed_demo_corpus.DiagnoseTest) ... ok
test_slow_hint_when_worker_and_server_are_up (test_seed_demo_corpus.DiagnoseTest) ... ok
test_worker_hint_when_no_job_started (test_seed_demo_corpus.DiagnoseTest) ... ok
test_different_bytes_produce_different_keys (test_seed_demo_corpus.IdempotencyKeyTest) ... ok
test_same_bytes_produce_same_valid_key (test_seed_demo_corpus.IdempotencyKeyTest) ... ok
test_committed_corpus_is_complete (test_seed_demo_corpus.ManifestTest) ... ok

----------------------------------------------------------------------
Ran 53 tests in 0.771s

OK
종료 시각: 2026-10-18T14:36:39+09:00
종료 코드: 0
소요 시간: 0.875초
Loading
Loading