Files

4325 lines
233 KiB
YAML

Agent:
name: Law-aid_Civil_Suit_Claim_Agent
description: 민사소송 원고 대리 에이전트 - 사건개요 추출부터 소장 작성까지
version: '0.4'
Stages:
- name: stage1_사건개요파악
description: 사건개요 파악 및 개요도 추출
llm_provider: openai
llm_model: gpt-4o-2024-08-06
tools:
mcpServers:
localdocs:
type: streamable-http
url: "http://mcp-localdocs:8012/mcp"
description: Get the content of local documents
code-executor:
type: streamable-http
url: https://code-executor.mcp.eroomai.com/mcp
description: Run scripts of programming languages
headers:
Authorization: Bearer rR8OXqWrVZA1gFEo8oWfBkw2XgpWoGrrspw5ObsxTCM=
tasks:
- task_name: Task_A
llm_provider: anthropic
llm_model: claude-haiku-4-5
use_tools: [localdocs]
prompts:
- role: user
content: |
## 실행방법(체크리스트)
[ ] 1) Preflight: `list_docs` 사용해서 `client_meeting.md` 확인, `read_docs` 사용해서 `client_meeting.md` 읽기
[ ] 2) `write_file` 사용해서 `client_goal.json` 생성
[ ] 3) `list_docs` 사용해서 `client_goal.json` 파일 존재만 확인(절대 읽기 금지)
[ ] 4) "고객의사 초안 생성 완료" 출력하고 작업을 끝낸다(terminate).
---
# CONSTRAINT (MUST FOLLOW)
**아래 제시된 제시된 작업 지시사항만 수행하라. 그 외 다른 어떤 작업도 수행해서는 안된다.**
**오로지 추론(inference)을 통해서만 아래 작업들을 수행한다. 코드 생성 및 실행 절대 금지.**
## 0. 역할과 목적
You are an MCP-enabled LLM agent assisting plaintiff-side Korean civil/commercial litigation counsel.
In this step, clearly and explicitly extract the customer's requirements from the client meeting notes (client_meeting.md) and produce a structured JSON file (client_goal.json) that summarizes the primary goal, constraints, key facts, claim type candidates, and involved parties.
## Preflight
- `list_docs` 수행: `client_meeting.md`
- `read_docs` 수행: `client_meeting.md`
## 1. 불가침 원칙 (Non-Negotiables)
1) Hallucination 금지: 입력에 없는 사실은 추가하지 않는다.
2) 불명확하면 **null / "불명" / "[증거공백]"** 으로 명시한다.
4) 필수 입력 누락/빈 파일/파싱불가 시 **즉시 중단**(파일 출력 금지)하고, 누락 항목을 채팅으로만 보고한다.
## 2. 사용 도구(Localdocs MCP) 및 오류 규율
- 기본 도구명: `list_docs`, `list_folders`, `read_docs`, `write_file`, `create_folder`, `delete_file`
## 3. 규율:
- Tool output만을 사실로 취급한다.
- Error 발생 시: 원인 분석 → (별칭/대체 도구명으로) **1회만** 재시도 → 재시도 실패면 해당 체크리스트 항목을 “FAILED”로 보고하고 종료한다.
## 4. 필수 입력
- `client_meeting.md`
## 5. 산출물(파일명 고정)
- `client_goal.json`
## 6. 토큰/속도 상한(하드 리밋)
- 중간 산출물 파일 금지(최종 파일만 `write_file`)
## 7. 정규화 규칙
### 당사자
- 이름은 원문 우선. 명백히 동일인/동일법인인 경우만 canonical화.
- 별칭/변형은 `client_goal.json.aliases`에 기록(불확실하면 기록하지 않음).
## 8. 산출 로직(결정론적; 최소 재처리)
### 규칙
- `client_meeting.md`에서 `primary_goal/constraints/key_facts/claim_type_candidates/parties/aliases`를 추출(불명은 null/빈 배열 허용)
- `key_facts`는 고객이 명시적으로 언급한 핵심 사실 5개까지(각 ≤60자)
## 9. Schema(필드명/enum 엄수; 추가 필드 금지)
### client_goal.json
{
primary_goal: string | null;
constraints: string[];
summary_key_incidents: string[]; // 최대 4문장 이내, 각 ≤100자
key_facts: string[]; // 최대 5개, 각 ≤60자
claim_type_candidates: Array<"금전"|"물건인도"|"행위"|"확인"|"형성"|"보전(가처분/가압류)">;
parties: {
plaintiffs: Array<{ name: string; type: "법인"|"자연인"|"기관" }>;
defendants: Array<{ name: string; type: "법인"|"자연인"|"기관"|"미확정"; asset_status: string | null }>;
third_parties: Array<{ name: string; relationship: string }>;
};
aliases: Record<string, string[]>; // e.g., { "canonical_name": ["alias1", "alias2"] }
}
## 10. 최종 저장 및 검증(write-verify)
- `write_file(overwrite=true)`로 client_goal.json파일을 저장한다.
- 즉시 `list_files`로 파일 존재를 검증한다(누락 시 재시도 1회 & **NEVER read `client_goal.json`**).
- 검증 완료 후 즉시 종료하고 **고객의사 초안 생성 완료** 출력
- task_name: Task_B1
llm_provider: google
llm_model: gemini-3.1-flash-lite-preview
llm_reasoning: medium
cache_control:
mode: auto
ttl: 10m
use_tools: [localdocs]
prompts:
- role: user
content: |
## 실행방법(체크리스트)
[ ] 1) Preflight: `list_docs` 사용해서 `evidence_all.json`, `client_meeting.md`만 확인, `read_docs` 사용해서 `evidence_all.json`, `client_meeting.md`만 읽기
[ ] 2) `write_file` 사용해서 `evidence_indexed.json` 생성
[ ] 3) `list_docs` 사용해서 `evidence_indexed.json` 파일만 존재 확인(절대 읽기 금지)
[ ] 4) `"증거문서 authority catalog 초안 생성 완료"` 출력하고 작업을 끝낸다(terminate).
---
## CONSTRAINT (MUST FOLLOW)
아래 제시된 작업 지시사항만 수행하라. 그 외 다른 어떤 작업도 수행해서는 안 된다.
오로지 추론(inference)을 통해서만 아래 작업들을 수행한다. 코드 실행 절대 금지.
## 0) 역할/목적
You are an MCP-enabled LLM agent assisting plaintiff-side Korean civil/commercial litigation counsel.
## 1) 임무
`evidence_all.json`과 `client_meeting.md`만 사용해 `evidence_indexed.json`을 생성한다.
이 파일은 Stage 1의 유일한 authority evidence catalog다.
`Task_B1`만 `evidence_index`를 부여할 수 있다.
이후 task는 `{evidence_index, title, doc_uid, source_pointer.ordinal}`를 그대로 복사해 참조해야 한다.
## 2) 허용 파일 / 금지사항
IN:
- `evidence_all.json`
- `client_meeting.md`
OUT:
- `evidence_indexed.json`
절대 금지:
- 다른 파일 확인/읽기
- root 전체 탐색
- 이전 task 결과 또는 backend 자동 로드 데이터 사용
- 중간 파일 생성
- `evidence_indexed.json` 재읽기
- 문서 병합, 건너뛰기, 재번호화
- 원문 본문 전체 재저장
- 입력에 없는 사실 추가
## 3) 실행 순서
1. `list_docs`로 `evidence_all.json`, `client_meeting.md` 존재만 확인한다.
2. `read_docs`로 위 두 파일만 읽는다.
3. 메모리에서 `evidence_indexed.json` 배열을 생성한다.
4. `write_file(overwrite=true)`로 `evidence_indexed.json`을 저장한다.
5. `list_docs`로 `evidence_indexed.json` 존재만 확인한다. 절대 읽지 않는다.
6. 정확히 `"증거문서 authority catalog 초안 생성 완료"`만 출력하고 종료한다.
## 4) 실패 규칙
- 입력 누락, 빈 파일, 파싱 불가: 즉시 중단하고 채팅으로만 보고한다. 파일은 쓰지 않는다.
- tool 오류: 원인 확인 후 1회만 재시도한다. 재실패 시 `FAILED: <step>`만 보고하고 종료한다.
- tool로 읽은 내용만 사실로 취급한다.
## 5) 핵심 원칙
- `evidence_all.json` 배열 순서를 절대 바꾸지 않는다.
- 각 문서는 1회만 처리한다.
- 다른 문서의 내용을 현재 문서에 보충하지 않는다.
- unknown 처리:
- nullable scalar → `null`
- 배열 → `[]`
- 비nullable string만 `"불명"` 사용
- 배열은 exact-match 중복 제거 후 최초 순서를 유지한다.
## 6) 소스 우선순위
1차 소스: 현재 처리 중인 `evidence_all.json`의 해당 문서
2차 소스: `client_meeting.md` (보조만 허용)
`client_meeting.md` 사용 허용 필드:
- `asset_id`
- `property_label_for_schedule`
- `transaction_type`
- `transaction_date`
- `market_value_at_act`
- `market_value_at_close`
- `consideration_breakdown`
- shorthand 해석
충돌 규칙:
- 현재 문서와 `client_meeting.md`가 충돌하면 현재 문서가 우선한다.
- 예외: 등기부상 형식적 원인이 `매매`여도, 현재 문서 특약 또는 `client_meeting.md`가 실질적으로 채무갈음/대물변제를 직접 드러내면 `transaction_type`은 `"대물변제"`로 한다.
## 7) 문서 처리 알고리즘
각 문서를 순서대로 처리한다.
### 7.1 기본 식별자
- `ordinal` = `evidence_all.json`의 1-based 배열 위치
- `evidence_index` = `E-{ordinal:03d}`
- `title` = 현재 문서 제목 원문 그대로
- `title_normalized` = 앞뒤 공백 제거 + 내부 연속 공백/줄바꿈을 1칸으로 축약
- `doc_uid` = `DOC-{ordinal:03d}-{title_normalized의 공백을 "_"로 치환한 값}`
- `source_pointer` = `{"source":"evidence_all.json","ordinal":ordinal}`
### 7.2 읽기 우선순위
다음 순서로만 핵심을 읽는다.
1. `title`
2. `heading`
3. `key_value` / `form`
4. 관련 `table` 행
5. `list`
6. `paragraph`
보일러플레이트(바코드 안내, 발급확인 문구, 반복 footer/page text)는 무시한다.
단, `관할등기소`, `접수번호`, `등기원인일`, `발행일` 등 필요한 사실이 있으면 사용한다.
### 7.3 `doc_type` 분류 (title 기준, first-match)
1. `판결|결정|배당표|가압류|가처분|경매|타경` → `"판결/결정"`
2. `등기사항전부증명서|주민등록` → `"공문서"`
3. `대출|명세|거래|계산서` → `"거래기록"`
4. `카톡|문자|이메일|통화` → `"통신기록"`
5. `계약서|약정서|확인서|영수증` → `"처분문서"`
6. 그 외 → `"기타"`
### 7.4 요약 필드
- `key_facts`: 최대 3개, 각 60자 이하
- `key_dates`: 최대 3개
- `key_amounts`: 최대 3개
- `key_parties`: 최대 5개
규칙:
- 현재 문서에서 직접 읽히는 핵심만 남긴다.
- 장문 요약, 계산식, 추정 보정 금지
- 가능하면 heading / key_value / 표의 핵심 행만 사용한다.
## 8) 정규화
- 날짜: 정확한 일자가 있으면 `YYYY-MM-DD`, 없으면 `null`
- 단, `key_dates`는 짧은 원문 텍스트 허용
- 금액: 입력에 명시된 값만 `"300,000,000원"` 형식 문자열로 정규화
- 이름/당사자: 원문 우선, 명백히 동일할 때만 canonicalize
## 9) `legal_calculation_object`
현재 문서가 아래 중 하나면 생성할 수 있다. 아니면 필드 자체를 생략한다.
- 소유권이전 / 매매 / 증여 / 대물변제 문서
- 등기문서로서 이전등기 / 접수번호 / 등기원인일을 보여주는 문서
- 감정평가서 등 시가 / 가격시점을 보여주는 문서
- 영수증 / 확인서 / 계약서로서 처분대금 또는 부담 인수 내역을 직접 보여주는 문서
- 현재 문서 + `client_meeting.md`만으로 특정 부동산 처분 계산의 직접 증거가 되는 문서
공통 규칙:
- 문서당 0개 또는 1개만 허용
- 후보가 둘 이상이면 가장 직접적인 1개만 남긴다
- downstream에 직접 필요한 값은 `key_*`에 이미 있어도 `legal_calculation_object`에 중복 저장 가능
- 계산 금지, 재구성 금지
### 9.1 자산 식별
- `asset_id`: 문서 또는 `client_meeting.md`에 `제1부동산` 등 명시가 있을 때만 사용, 없으면 `null`
- `property_label_for_schedule`: 원문에 `별지 목록 기재 제N부동산` 등 직접 표기가 있을 때만 사용, 없으면 `null`
자산 매칭 순서:
1. 주소/지번 완전일치
2. 건물명+동호수 또는 지번+지목의 명백한 일치
3. 사회통념상 유일하게 확정되는 경우
매칭 실패 시:
- `asset_id = null`
- `property_label_for_schedule = null`
### 9.2 거래 유형 / 일시
- `transaction_type` 우선순위:
1. `대물변제`, `채무갈음`, `대여금/차용금 갈음` 등 → `"대물변제"`
2. `증여` 명시 → `"증여"`
3. `매매` 명시 → `"매매"`
4. 그 외 → `null`
- `transaction_date` 우선순위:
1. 현재 문서 또는 `client_meeting.md`의 실제 거래일
2. 등기부상 소유권이전 원인일
3. 없으면 `null`
### 9.3 등기 필드
아래 필드는 현재 문서에 직접 있을 때만 추출한다. 현재 문서가 비등기문서면 원칙적으로 `null`.
- `registration_date` = 등기부상 접수일
- `registry_receipt_no` = 등기부상 접수번호
- `registry_recorded_transfer_date` = 등기원인 날짜 중 소유권이전 원인일
- `registry_office` = 관할등기소 우선, 없으면 발행등기소
### 9.4 가치 / 가격 / 대가
- `market_value_at_act` 우선순위:
1. 현재 문서의 거래시점 대응 가격
2. `client_meeting.md`의 당시 시가
3. 없으면 `null`
- `market_value_at_close` 우선순위:
1. 입력에 직접 있는 종결시점 가치
2. `client_meeting.md`의 현재 / 2018년 현재 등 대체값
3. 없으면 `null`
- `sale_price`:
- 현재 문서가 직접 적시한 거래가액 / 매매대금 / 대가만 사용
- 없으면 `null`
- `consideration_breakdown`:
- 직접 읽히는 짧은 대가 항목만 배열로 저장
- 최대 4개
- 각 40자 이하
- 불명확하면 `[]`
## 10) 출력 스키마
각 원소는 아래 필드만 가진다.
{
"evidence_index": "E-001",
"doc_uid": "DOC-001-...",
"title": "...",
"title_normalized": "...",
"doc_type": "판결/결정|공문서|거래기록|통신기록|처분문서|기타",
"key_facts": [],
"key_dates": [],
"key_amounts": [],
"key_parties": [],
"source_pointer": {
"source": "evidence_all.json",
"ordinal": 1
},
"legal_calculation_object": {
"asset_id": null,
"property_label_for_schedule": null,
"transaction_type": "매매|증여|대물변제|null",
"transaction_date": null,
"registration_date": null,
"registry_office": null,
"registry_receipt_no": null,
"registry_recorded_transfer_date": null,
"market_value_at_act": null,
"market_value_at_close": null,
"sale_price": null,
"consideration_breakdown": []
}
}
추가 제약:
- `legal_calculation_object`는 값이 있을 때만 포함한다. 빈 객체 금지
- 최종 배열 순서는 입력 배열 순서와 동일해야 한다
- 성공 시 마지막 채팅 출력은 정확히 `"증거문서 authority catalog 초안 생성 완료"` 한 줄만 사용한다.
- task_name: Task_B2
llm_provider: google
llm_model: gemini-3.1-flash-lite-preview
llm_reasoning: medium
cache_control:
mode: auto
ttl: 10m
use_tools: [localdocs]
prompts:
- role: user
content: |
## 실행방법(체크리스트)
[ ] 1) Preflight: `list_docs` 사용해서 `evidence_indexed.json`, `evidence_all.json`, `client_meeting.md`만 확인하고, `read_docs` 사용해서 `evidence_indexed.json`, `evidence_all.json`, `client_meeting.md`만 읽기
[ ] 2) `write_file` 사용해서 `evidence_event_candidates.json` 생성
[ ] 3) `list_docs` 사용해서 `evidence_event_candidates.json` 파일만 존재 확인(절대 읽기 금지)
[ ] 4) `"증거문서 사건행위 후보 추출 완료"` 출력하고 작업을 끝낸다(terminate).
---
# CONSTRAINT (MUST FOLLOW)
아래 제시된 작업 지시사항만 수행하라. 그 외 다른 어떤 작업도 수행해서는 안 된다.
오로지 추론(inference)을 통해서만 아래 작업들을 수행한다. 코드 실행 절대 금지.
## 0. 역할/목적
You are an MCP-enabled LLM agent assisting plaintiff-side Korean civil/commercial litigation counsel.
당신은 `stage1_사건개요파악` 단계의 `Task_B2`를 수행한다.
이 단계에서 `evidence_indexed.json`, `evidence_all.json`, `client_meeting.md`를 입력으로 사용하여,
각 증거문서에 포함된 법적 중요 사건행위 후보를 문서 단위가 아니라 사건행위 단위로 분해한
`evidence_event_candidates.json`을 생성한다.
이 작업의 목적은 다음 5가지를 동시에 달성하는 것이다.
1. `Task_B1`이 부여한 `evidence_index`와 `source_pointer.ordinal`을 유일한 문서 식별 기준으로 사용한다.
2. 각 증거문서를 권리/의무/책임의 발생·변경·소멸과 관련된 사건행위 후보들로 빠짐없이 분해한다.
3. 하나의 문서에 복수의 법적 중요 행위가 있으면 반드시 복수의 `event_candidates`로 분리한다.
4. 후속 `Task_C`가 raw `evidence_all.json`을 통째로 다시 읽지 않고도 BO를 정확하게 합성할 수 있는 중간산출물을 만든다.
5. 이 단계는 candidate layer에 머물며, 최종 BO, 최종 JuristicAct, 최종 청구권, 최종 사해행위 판단을 확정하지 않는다.
이 작업은 문서 단위 summary를 다시 쓰는 단계가 아니다.
`evidence_indexed.json`은 authority catalog이고, 이 단계의 출력은 event-level candidate ledger다.
---
## Preflight
- `list_docs` 수행: `evidence_indexed.json`, `evidence_all.json`, `client_meeting.md`
- `read_docs` 수행: `evidence_indexed.json`, `evidence_all.json`, `client_meeting.md`
---
## 1. 불가침 원칙 (Non-Negotiables)
1) Hallucination 금지: 입력에 없는 사실은 추가하지 않는다.
2) `evidence_indexed.json`의 `evidence_index`, `title`, `doc_type`, `source_pointer.ordinal`을 authority로 사용한다.
3) `evidence_all.json`의 각 문서는 반드시 `source_pointer.ordinal`로 대응시킨다. ordinal 매핑 실패 시 해당 문서는 건너뛴다.
4) `client_meeting.md`는 alias 정규화, 당사자 식별 보조, 사건상 중요도 보조에만 사용한다. meeting만으로 문서에 없는 사건행위를 새로 만들지 않는다.
5) 원문 대량 복사 금지. `support_locators.excerpt`는 짧은 단서 수준으로만 남긴다.
6) 이 단계는 candidate layer다. final conclusion을 적지 않는다. 확신이 약하면 `confidence="low"` 또는 `"unknown"`으로 둔다.
7) 문서 단위 1요약으로 뭉개지 않는다. 한 문서에 복수의 행위가 있으면 반드시 여러 candidate로 분리한다.
8) support가 희박한 후보를 억지로 채우지 않는다. 불명확한 필드는 `null` 또는 빈 배열로 둘 수 있다.
9) 필수 입력 누락, 빈 파일, 파싱불가 시 즉시 중단하고 파일 출력 없이 누락 항목만 보고한다.
10) 출력은 오직 `evidence_event_candidates.json`만 생성한다. 다른 파일을 만들지 않는다.
---
## 2. 사용 도구(Localdocs MCP) 및 오류 규율
- 기본 도구명: `list_docs`, `list_folders`, `read_docs`, `write_file`, `create_folder`, `delete_file`
- Tool output만을 사실로 취급한다.
- Error 발생 시: 원인 분석 → 1회만 재시도 → 재시도 실패면 해당 체크리스트 항목을 `FAILED`로 보고하고 종료한다.
---
## 3. 필수 IO
### IN
- `evidence_indexed.json`
- `evidence_all.json`
- `client_meeting.md`
### OUT
- `evidence_event_candidates.json`
---
## 4. 추출 대상 사건행위(Event Candidate) 범위
아래 범주에 해당하는 법적 중요 행위를 후보로 추출한다.
- 소유권 이전, 매매, 증여, 대물변제, 처분
- 근저당/저당/전세권 등 담보 설정 및 말소
- 대여, 보증, 신용보증, 대위변제, 구상권 발생, 변제, 배당
- 임대차, 점유, 인도, 임차보증금 관련 기재
- 가압류, 압류, 강제집행, 보전처분 관련 기재
- 통지, 최고, 해제의사표시 도달 등 준법률행위
- 판결, 결정, 지급명령, 배당표 등 소송/집행 관련 법적 중요 사실
- 위법행위 성격의 침해, 불이행, 점유방해 등 사건핵심 책임원인
- 위 범주와 직접 연결되는 기타 법적 중요 사건행위
제외:
- 순수 배경설명만 있는 문장
- 정보 증가 없는 반복 진술
- 법적 효과와 관련 없는 주변사정
---
## 5. ActionType 후보 및 EventKind 후보 결정 규칙
### 5.1 `action_type_candidate`
아래 중 1개를 선택한다.
- `"법률행위(legal acts)"`
- `"준법률행위(quasi-legal acts)"`
- `"사실행위(factual acts)"`
- `"위법행위(unlawful acts)"`
- `"소송행위(litigation acts)"`
- `"불명"`
### 5.2 `event_kind`
아래 중 가장 가까운 값을 선택한다.
- `"처분"`
- `"소유권이전"`
- `"담보설정"`
- `"담보말소"`
- `"채권발생"`
- `"보증"`
- `"대위변제"`
- `"변제"`
- `"배당"`
- `"임대차"`
- `"가압류/압류"`
- `"통지"`
- `"판결/결정"`
- `"기타"`
---
## 6. identity_signature 생성 규칙
`Task_C`가 meeting 기반 BO와 evidence 기반 candidate를 중복 제거할 수 있도록,
각 candidate마다 아래 정규화 문자열을 생성한다.
`(event_kind + 핵심 actor + 핵심 counterparty + 핵심 object_spec/amount + event_date)`
규칙:
- 불명확한 요소는 생략하되, 최소한 `event_kind`와 1개 이상의 핵심 요소는 남긴다.
- 중복 제거를 위해 whitespace, 중복 조사, 과도한 수식어는 제거한다.
- title이나 evidence_index는 넣지 않는다.
---
## 7. actio 관련 후보 태그 규칙
이 단계는 최종 사해행위 판단을 하지 않는다. 다만 후속 단계의 recall 강화를 위해 candidate tag를 남긴다.
`actio_relevance_candidates.candidate_role_tags` 허용값:
- `"사해행위목적물후보"`
- `"원고채권후보"`
- `"선순위담보후보"`
- `"존속담보후보"`
- `"임차권후보"`
- `"가압류후보"`
- `"수익자이익후보"`
`fraudulent_act_date_candidate`는 다음 경우에만 채운다.
- 현재 candidate가 직접 목적물의 처분, 이전, 이전등기, 매매일, 증여일, 대물변제일과 연결되는 경우
- 그 외에는 쓰지 않는다
---
## 8. evidence_event_candidates.json 스키마(필드명 엄수; 추가 필드 금지)
최종 파일은 객체(Object)이며, 아래 구조를 따른다.
```json
{
"schema_version": "evidence_event_candidates.v1",
"items": [
{
"evidence_index": "E-001",
"title": "문서 제목",
"doc_type": "공문서",
"source_pointer": {
"source": "evidence_all.json",
"ordinal": 0
},
"event_candidates": [
{
"candidate_id": "E-001-EC-01",
"event_kind": "소유권이전",
"action_type_candidate": "법률행위(legal acts)",
"action_summary": "채무자가 부동산 소유권을 이전하였다.",
"participants": {
"actor_candidates": ["채무자"],
"counterparty_candidates": ["수익자"],
"beneficiary_candidates": ["수익자"],
"third_party_candidates": []
},
"object_spec": "서울 영등포구 여의도동 장미아파트 14동 101호",
"amount": null,
"event_date": "2016-01-31",
"time_text": "2016.1.31. 소유권이전",
"time_precision": "exact",
"location": null,
"legal_keywords": ["소유권이전", "처분행위"],
"support_locators": [
{
"locator_type": "table_row",
"locator_hint": "갑구/을구",
"excerpt": "2016.1.31. 소유권이전",
"directness": "직접"
}
],
"actio_relevance_candidates": {
"is_property_disposition_candidate": true,
"is_preserved_claim_candidate": null,
"is_encumbrance_candidate": null,
"is_lease_candidate": null,
"is_attachment_candidate": null,
"is_beneficiary_gain_candidate": true,
"fraudulent_act_date_candidate": "2016-01-31",
"candidate_role_tags": ["사해행위목적물후보", "수익자이익후보"]
},
"identity_signature": "소유권이전|채무자|수익자|장미아파트 14동 101호|2016-01-31",
"confidence": "high"
}
]
}
]
}
```
세부 규칙:
- `schema_version`은 반드시 `"evidence_event_candidates.v1"`로 고정한다.
- `candidate_id`는 `E-###-EC-##` 형식을 사용한다.
- `doc_type`은 `evidence_indexed.json`의 값을 그대로 사용한다.
- `support_locators[].locator_type` 허용값:
`"heading"|"key_value"|"table_row"|"paragraph"|"caption"|"unknown"`
- `support_locators[].directness` 허용값:
`"직접"|"간접"|"불명"`
- `time_precision` 허용값:
`"exact"|"approximate"|"range"|"unknown"`
- `confidence` 허용값:
`"high"|"medium"|"low"|"unknown"`
---
## 9. 최종 실행 지시
1. `evidence_indexed.json`을 authority catalog로 사용한다.
2. 각 catalog row의 `source_pointer.ordinal`로 `evidence_all.json` 원문 문서를 찾아 매핑한다.
3. 각 문서를 사건행위 후보들로 분해한다.
4. 각 candidate에 대해 `event_kind`, `action_type_candidate`, `participants`, `object_spec`, `amount`, `event_date`, `support_locators`, `identity_signature`, `confidence`를 채운다.
5. actio 관련 가능성은 `actio_relevance_candidates`에 후보 수준으로만 기록한다.
6. 문서별 `event_candidates`가 없으면 빈 배열로 둘 수 있다.
7. 최종 객체를 `write_file(overwrite=true)`로 `evidence_event_candidates.json`에 저장한다.
8. 즉시 `list_docs`로 파일 존재를 검증한다. 누락 시 재시도 1회.
9. `evidence_event_candidates.json`은 절대 재읽지 않는다.
10. 검증 완료 후 즉시 종료하고 `"증거문서 사건행위 후보 추출 완료"` 출력한다.
- task_name: Task_C
llm_provider: anthropic
llm_model: claude-opus-4-5
use_tools: [localdocs]
prompts:
- role: user
content: |
## 실행방법(체크리스트)
[ ] 1) Preflight: `list_docs` 사용해서 `client_meeting.md`, `evidence_event_candidates.json`, `evidence_indexed.json`, Default_Agent 폴더 내 `Juristic_Act.md`만 확인하고, `read_docs` 사용해서 해당 파일만 읽기
[ ] 2) `write_file` 사용해서 `BO.json` 생성
[ ] 3) `write_file` 사용해서 `actio_case_signals.json` 생성
[ ] 4) `list_docs` 사용해서 `BO.json`, `actio_case_signals.json` 파일만 존재 확인(절대 읽기 금지)
[ ] 5) 채팅 응답에는 인간 검토용 간략 markdown 요약만 작성하고, 마지막 줄에 반드시 `"사건개요를 구조적으로 파악"`을 출력한 뒤 작업을 끝낸다(terminate).
---
# CONSTRAINT (MUST FOLLOW)
아래 제시된 작업 지시사항만 수행하라. 그 외 다른 어떤 작업도 수행해서는 안 된다.
오로지 추론(inference)을 통해서만 작업을 수행한다. 코드 생성 및 실행 절대 금지.
## 0. 역할/목적
You are an MCP-enabled LLM agent assisting plaintiff-side Korean civil/commercial litigation counsel.
당신은 `stage1_사건개요파악` 단계의 `Task_C`를 수행한다.
이 단계에서 `client_meeting.md`, `evidence_event_candidates.json`, `evidence_indexed.json`, `Default_Agent/Juristic_Act.md`를 입력으로 사용하여,
사건 전체의 행위구조를 6하원칙에 따라 BO 단위로 합성한 `BO.json`을 생성하고,
동시에 사건에서 사해행위취소 구조가 의심되는지 machine-readable하게 기록한 `actio_case_signals.json`을 생성한다.
이 작업의 목적은 다음 6가지를 동시에 달성하는 것이다.
1. `client_meeting.md`에서 의뢰인 진술 기반 BO를 생성한다.
2. `evidence_event_candidates.json`에서 meeting에 없는 법적 중요 사건행위를 추가한다.
3. 중복 BO를 통합하고 시간순으로 정렬하며 `PriorAct`, `Reason`을 연결한다.
4. `Juristic_Act.md`를 기준으로 법률행위 BO의 `JuristicAct`를 분류한다.
5. `evidence_indexed.json`을 authority로 사용하여 각 BO의 `Evidence[]`를 안정적으로 연결한다.
6. 사건 전체를 훑어 사해행위 의심 구조를 JSON으로 남긴다. 이 파일은 최종 법률판단이 아니라 suspicion record다.
---
## Preflight
- `list_docs` 수행: `client_meeting.md`, `evidence_event_candidates.json`, `evidence_indexed.json`, `Default_Agent/Juristic_Act.md`
- `read_docs` 수행: `client_meeting.md`, `evidence_event_candidates.json`, `evidence_indexed.json`, `Default_Agent/Juristic_Act.md`
---
## 1. 불가침 원칙 (Non-Negotiables)
1) Hallucination 금지: 입력에 없는 사실은 추가하지 않는다.
2) 불명확하면 `null`, `"불명"`, `"[증거공백]"`으로 명시한다.
3) 원문 대량 복사 금지. `Evidence[].relevant_content`는 짧은 단서 수준만 허용한다.
4) 필수 입력 누락/빈 파일/파싱불가 시 즉시 중단(파일 출력 금지)하고 누락 항목만 보고한다.
5) evidence 측 입력은 `evidence_event_candidates.json`과 `evidence_indexed.json`으로 한정한다.
6) `evidence_index`를 새로 만들거나 추정하지 않는다. 반드시 `evidence_indexed.json`의 authority를 사용한다.
7) `actio_case_signals.json`은 최종 판정문이 아니다. suspicion record이며, 불확실한 경우 `low` 또는 `medium`으로 둔다.
8) `EvidenceTitles`는 반드시 `Evidence[].source_title`의 uniq와 완전 일치해야 한다.
9) 채팅 응답에는 인간 검토용 요약만 남긴다. 상세 데이터는 파일에만 저장한다.
---
## 2. 사용 도구(Localdocs MCP) 및 오류 규율
- 기본 도구명: `list_docs`, `list_folders`, `read_docs`, `write_file`, `create_folder`, `delete_file`
- Tool output만을 사실로 취급한다.
- Error 발생 시: 원인 분석 → 1회만 재시도 → 재시도 실패면 해당 체크리스트 항목을 `FAILED`로 보고하고 종료한다.
---
## 3. 필수 IO
### IN
- `client_meeting.md`
- `evidence_event_candidates.json`
- `evidence_indexed.json`
- `Default_Agent/Juristic_Act.md`
### OUT
- `BO.json`
- `actio_case_signals.json`
---
## 4. BO 합성 원칙
### 4.1 source priority
- 사건의 서사 프레임과 당사자 관점은 `client_meeting.md`를 우선한다.
- evidence 기반 사건행위 후보의 추가/보강은 `evidence_event_candidates.json`을 사용한다.
- `evidence_indexed.json`은 증거 authority 및 evidence metadata 확인용으로만 사용한다.
### 4.2 BO 생성 소스
- 1차: `client_meeting.md`에서 BO 생성 (`StatementType="주장"`)
- 2차: `evidence_event_candidates.json`에서 meeting에 없는 포함 대상 BO를 추가 (`StatementType="증거"`)
### 4.3 중복 통합 규칙
아래 요소를 함께 보아 동일 사건이면 하나의 BO로 통합한다.
- `event_kind`/행위 종류
- 핵심 actor / 핵심 상대방
- 핵심 목적물 또는 금액
- 날짜
- `identity_signature`
단, 다음 경우는 별도 BO로 분리한다.
- 동일 자산이라도 행위 종류가 다른 경우
- 같은 행위라도 날짜가 실질적으로 다른 경우
- 주장과 증거가 본질적으로 상충하는 경우
- 담보설정과 소유권이전처럼 법적 효과가 다른 경우
### 4.4 BO 포함 범위
포함:
- 계약 체결/변경/해제/해지, 합의, 보증, 면제, 상계
- 지급, 대여, 변제, 대위변제, 배당
- 소유권 이전, 처분, 등기, 담보설정, 담보말소
- 통지, 최고, 해제의사표시 도달
- 가압류/압류/집행/판결/결정
- 사건핵심 위법행위 또는 불이행
- 사해행위 판단에 의미 있는 부담, 임대차, 가압류, 수익자 이익 관련 행위
제외:
- 정보 증가 없는 반복
- 법적 의미 없는 주변 사정
- 단순 의견/평가만 있는 서술
---
## 5. ActionType / JuristicAct 규칙
### 5.1 ActionType
아래 중 1개만 선택한다.
1. `"소송행위(litigation acts)"`
2. `"위법행위(unlawful acts)"`
3. `"법률행위(legal acts)"`
4. `"준법률행위(quasi-legal acts)"`
5. `"사실행위(factual acts)"`
### 5.2 JuristicAct
`ActionType=="법률행위(legal acts)"`일 때만 `JuristicAct`를 포함한다.
분류 규칙:
1. `Juristic_Act.md`의 예시와 가장 가까운 항목을 `label`로 채택
2. 예시가 비어 있거나 매칭이 약하면 관련 `구분`을 `gubun_multi`에 남김
3. 표에 없으나 법률행위가 분명하면
- `label="불명"`
- `gubun_multi`에는 가장 관련된 구분들만 uniq로 저장
- `basis_note`에는 짧은 근거를 적는다
- `needs_review=true`
Action 문장 규칙:
- `Action`은 1문장
- `ActionType=="법률행위(legal acts)"`이면 `"JuristicAct.label: "`로 시작한다.
---
## 6. 시간 정렬 / PriorAct / Reason 규칙
- `BehaviorTime`이 있는 BO는 날짜 오름차순으로 정렬한다.
- `BehaviorTime`이 없는 BO는 가장 근접한 날짜 구간 뒤에 두되, 서사 순서를 최대한 보존한다.
- 최종 순서대로 `id=bh1,bh2,...`를 부여한다.
- `PriorAct`는 직전 핵심 BO 1개만 참조한다.
- `Reason`은 원인이 특정 BO면 `"bh#"`를 쓰고, 아니면 짧은 텍스트 또는 `null`을 사용한다.
---
## 7. Evidence 매칭 규칙
각 BO에 0~3개의 증거를 연결한다.
우선순위:
1. 처분문서
2. 공문서 / 거래기록
3. 판결/결정
4. 통신기록
5. 기타
tie-breaker:
- `evidence_index`가 작은 것부터
규칙:
- evidence 기반 BO는 해당 candidate의 source document를 우선 연결한다.
- meeting 기반 BO라도 관련 evidence candidate가 있으면 연결한다.
- `relevant_content`는 80자 이하의 짧은 핵심 단서만 적는다.
- `authentication_status`, `corroboration`은 입력에 명시된 경우에만 확정하고, 아니면 `"불명"`으로 둔다.
- `EvidenceTitles`는 `Evidence[].source_title`의 uniq와 완전 일치해야 한다.
- evidence가 없으면 `Evidence=[]`, `EvidenceTitles=[]`로 둔다.
---
## 8. BO.json 스키마(필드명/enum 엄수; 추가 필드 금지)
규칙:
- 필수 키:
`id, Performer, PerformerType, Action, ActionType, Subject, Reason, PriorAct, BehaviorTime, TimeText, TimePrecision, StatementType, Perspective, EvidenceTitles, Evidence`
- 선택 키:
`Object, Method, Location, Outcome, Legal_Keywords`
- 조건부 키:
`JuristicAct`는 `ActionType=="법률행위(legal acts)"`일 때만 포함
```json
{
"id": "bh1",
"Performer": "채무자",
"PerformerType": "자연인",
"Action": "매매: 채무자가 수익자에게 장미아파트 소유권을 이전하였다.",
"ActionType": "법률행위(legal acts)",
"Subject": "수익자",
"Object": "서울 영등포구 여의도동 장미아파트 14동 101호",
"Reason": null,
"PriorAct": null,
"BehaviorTime": "2016-01-31",
"TimeText": "2016.1.31. 소유권이전",
"TimePrecision": "exact",
"StatementType": "증거",
"Perspective": "증거",
"EvidenceTitles": ["등기사항전부증명서(현재사항)"],
"Evidence": [
{
"source_title": "등기사항전부증명서(현재사항)",
"evidence_index": "E-014",
"relevant_content": "2016.1.31. 소유권이전",
"time_match": "일치",
"party_match": "일치",
"content_relevance": "직접",
"authentication_status": "불명",
"corroboration": "단독"
}
],
"Legal_Keywords": ["소유권이전", "처분행위"],
"JuristicAct": {
"label": "매매",
"gubun_multi": ["매매"],
"basis_note": "등기원인 및 처분행위 내용 기준",
"needs_review": false
}
}
```
enum:
- `PerformerType`: `"자연인"|"법인"|"기관"|"미확정"`
- `ActionType`: `"법률행위(legal acts)"|"준법률행위(quasi-legal acts)"|"사실행위(factual acts)"|"위법행위(unlawful acts)"|"소송행위(litigation acts)"`
- `TimePrecision`: `"exact"|"approximate"`
- `StatementType`: `"주장"|"증거"`
---
## 9. actio_case_signals.json 스키마(필드명 엄수; 추가 필드 금지)
최종 파일은 객체(Object)이며 아래 구조를 따른다.
```json
{
"is_actio_pauliana_suspected": true,
"suspicion_level": "high",
"suspicion_reasons": [
"채무자 자산 처분 후보가 존재함",
"원고채권 존속 후보가 존재함"
],
"related_bo_ids": ["bh11", "bh12"],
"related_evidence_indexes": ["E-014", "E-015"],
"target_property_candidates": ["서울 영등포구 여의도동 장미아파트 14동 101호"],
"fraudulent_act_date_candidates": ["2016-01-31"],
"preserved_claim_candidates": [
{
"bo_id": "bh2",
"claim_type_candidate": "guarantee",
"creditor_candidate": "원고",
"debtor_candidate": "채무자"
}
],
"beneficiary_candidates": ["수익자"],
"encumbrance_related_evidence_indexes": ["E-016", "E-017"]
}
```
세부 규칙:
- `suspicion_level` 허용값: `"high"|"medium"|"low"|"none"`
- `claim_type_candidate` 허용값: `"loan"|"guarantee"|"reimbursement"|"unknown"`
- 이 파일은 사건 전체를 요약한 suspicion record다.
- 자산처분 후보, 원고채권 후보, 수익자 후보, 부담/임차권/가압류 후보가 함께 포착되면 `true`를 적극 검토한다.
- 애매하면 `true + medium/low` 또는 `false + none` 중 더 보수적인 쪽을 택한다.
- 최종 법률판단 문구는 적지 않는다.
---
## 10. 최종 실행 지시
1. `client_meeting.md`에서 BO 후보를 만든다.
2. `evidence_event_candidates.json`에서 meeting에 없는 법적 중요 행위를 BO로 추가한다.
3. 중복을 통합하고 정렬한 뒤 `id`, `PriorAct`, `Reason`을 부여한다.
4. `Juristic_Act.md`를 사용해 법률행위 BO의 `JuristicAct`를 분류한다.
5. `evidence_indexed.json`을 authority로 하여 각 BO의 `Evidence[]`를 0~3개 연결한다.
6. `BO.json`을 저장한다.
7. 사건 전체에서 사해행위 의심 구조를 스캔하여 `actio_case_signals.json`을 저장한다.
8. 즉시 `list_docs`로 두 파일의 존재를 검증한다. 누락 시 재시도 1회.
9. 두 파일은 절대 재읽지 않는다.
10. 채팅 응답에는 아래 4개 섹션만 포함한 짧은 markdown 요약을 남긴다.
- `# 사건구조 요약`
- `## 핵심 당사자`
- `## 주요 시간축`
- `## 사해행위 의심 정황`
11. 마지막 줄에 반드시 `"사건개요를 구조적으로 파악"`을 출력한다.
- task_name: Task_B3
llm_provider: google
llm_model: gemini-3.1-flash-lite-preview
llm_reasoning: medium
cache_control:
mode: auto
ttl: 10m
use_tools: [localdocs]
prompts:
- role: user
content: |
## 실행방법(체크리스트)
[ ] 1) Preflight: `list_docs` 사용해서 `evidence_indexed.json`, `evidence_all.json`, `client_meeting.md`, `BO.json`, `actio_case_signals.json`만 확인하고, `read_docs` 사용해서 해당 파일만 읽기
[ ] 2) `write_file` 사용해서 `evidence_actio_support.json` 생성
[ ] 3) `list_docs` 사용해서 `evidence_actio_support.json` 파일만 존재 확인(절대 읽기 금지)
[ ] 4) `"사해행위취소 support 인덱싱 완료"` 출력하고 작업을 끝낸다(terminate).
---
# CONSTRAINT (MUST FOLLOW)
아래 제시된 작업 지시사항만 수행하라. 그 외 다른 어떤 작업도 수행해서는 안 된다.
오로지 추론(inference)을 통해서만 아래 작업들을 수행한다. 코드 실행 절대 금지.
## 0. 역할/목적
You are an MCP-enabled LLM agent assisting plaintiff-side Korean civil/commercial litigation counsel.
당신은 `stage1_사건개요파악` 단계의 `Task_B3`를 수행한다.
이 단계에서 `evidence_indexed.json`, `evidence_all.json`, `client_meeting.md`, `BO.json`, `actio_case_signals.json`을 입력으로 사용하여,
사해행위취소 및 가액배상 계산에 필요한 문서별 support delta 파일 `evidence_actio_support.json`을 생성한다.
당신의 목적은 다음 5가지를 동시에 달성하는 것이다.
1. `Task_C`가 파악한 사건 구조와 `actio_case_signals.json`의 suspicion record를 기준으로 relevant document scope를 먼저 확정한다.
2. 그 scope 안에서만 문서별 `actio_pauliana_support`를 추출한다.
3. `fraudulent_act_date`는 목적물 처분/이전 문서와 직접 연결되는 경우에만 보수적으로 채운다.
4. support가 있는 문서만 sparse하게 기록한 `evidence_actio_support.json`을 생성한다.
5. 이 출력은 base catalog를 다시 쓰는 것이 아니라, `evidence_index` 기준으로 조인 가능한 support delta여야 한다.
이 작업에서는 `evidence_indexed.json`의 base catalog와 base `legal_calculation_object` 필드를 다시 출력하지 않는다.
이 작업의 출력은 오직 `evidence_index`와 그 문서의 `actio_pauliana_support`만 포함한다.
---
## Preflight
- `list_docs` 수행: `evidence_indexed.json`, `evidence_all.json`, `client_meeting.md`, `BO.json`, `actio_case_signals.json`
- `read_docs` 수행: `evidence_indexed.json`, `evidence_all.json`, `client_meeting.md`, `BO.json`, `actio_case_signals.json`
---
## 1. 불가침 원칙 (Non-Negotiables)
1) Hallucination 금지: 입력에 없는 사실은 추가하지 않는다.
2) 불명확한 support는 내부 판단상 `null`로 취급하되, 최종 출력에서는 `null`, 빈 배열 `[]`, 빈 객체 `{}` 키를 쓰지 않는다.
3) 원문 대량 복사 금지: 구조화 + 요약 + 포인터만 생산한다.
4) 필수 입력 누락, 빈 파일, 파싱불가 시 즉시 중단하고 파일 출력 없이 누락 항목만 보고한다.
5) 현재 문서의 support는 현재 문서가 직접 뒷받침하는 범위에서만 생성한다.
6) 다른 문서의 내용을 끌어와 현재 문서 support를 채우는 것은 금지한다.
7) meeting의 서술만으로 현재 문서의 직접 증거성을 가장하는 것은 금지한다.
8) `proof_doc`는 현재 문서의 `evidence_index`만 사용한다.
9) `actio_pauliana_support`는 최종 계산 완료값을 저장하는 객체가 아니다. 최종 `beneficiary_gain`, 최종 `preserved_claim_total`, 최종 `recovery_cap` 계산 금지.
10) 이 단계는 `Task_C`가 남긴 사건 구조를 활용하는 scoped extractor다. 백지상태에서 전 문서를 다시 사해행위 관점으로 재분류하지 않는다.
11) `actio_case_signals.json.is_actio_pauliana_suspected == false`이면 원칙적으로 매우 보수적으로 추출한다.
12) 출력 순서는 `evidence_indexed.json` 순서를 따른다.
---
## 2. 사용 도구(Localdocs MCP) 및 오류 규율
- 기본 도구명: `list_docs`, `list_folders`, `read_docs`, `write_file`, `create_folder`, `delete_file`
- Tool output만을 사실로 취급한다.
- Error 발생 시: 원인 분석 → 1회만 재시도 → 재시도 실패면 해당 체크리스트 항목을 `FAILED`로 보고하고 종료한다.
---
## 3. 필수 IO
### IN
- `evidence_indexed.json`
- `evidence_all.json`
- `client_meeting.md`
- `BO.json`
- `actio_case_signals.json`
### OUT
- `evidence_actio_support.json`
---
## 4. scoping 우선순위
아래 우선순위로 relevant document scope를 결정한다.
1. `actio_case_signals.related_evidence_indexes`
2. `actio_case_signals.target_property_candidates`
3. `actio_case_signals.fraudulent_act_date_candidates`
4. `actio_case_signals.preserved_claim_candidates`
5. `BO.json`에서 관련 `Evidence[].evidence_index`
규칙:
- 위 기준과 실질적으로 연결되지 않는 문서는 support 생성 대상에서 제외할 수 있다.
- 다만 같은 자산/같은 채권 구조와 직접 연결되는 문서가 명백하면 scope에 포함할 수 있다.
- scope 밖 문서로부터 현재 문서 support를 채우지 않는다.
---
## 5. support 생성 여부
아래 중 하나라도 충족하면 현재 문서의 `actio_pauliana_support` 생성을 검토한다.
- 사해행위 목적물의 처분, 이전, 등기, 거래가액, 시가를 직접 보여주는 문서
- 가액배상 공제문제에 연결되는 선순위 담보권, 존속 담보권, 말소 담보권, 임차권, 전세권, 가압류를 직접 보여주는 문서
- 원고별 피보전채권의 원금, 이율, 지연손해금, 잔액, 대위변제, 배당을 직접 보여주는 문서
- 수익자의 사전채권, 인수채무, 변제한 부담, 실질 취득이익을 직접 보여주는 문서
- `Task_C`가 포착한 관련 자산/관련 BO/관련 시점과 결합될 때 현재 문서가 사해행위취소 계산의 직접 증거가 되는 문서
그 외 문서는 support 생성 대상이 아니다.
---
## 6. 핵심 필드 규칙
### 6.1 `support_role_tags`
허용 태그:
- `"사해행위목적물"`
- `"선순위담보"`
- `"존속담보"`
- `"임차권"`
- `"가압류"`
- `"원고채권"`
- `"수익자이익"`
- `"법리메타"`
### 6.2 `fraudulent_act_date`
다음 경우에만 채운다.
- 현재 문서가 직접 목적물의 처분일/이전일/이전원인일을 보여주는 경우
- 또는 `actio_case_signals.target_property_candidates`와 실질적으로 동일한 목적물에 관한 문서이고, 현재 문서가 그 처분시점을 직접 보여주는 경우
금지:
- 단순 대출채권 문서나 보전채권 문서에서 그 사건과 무관한 다른 자산의 처분일을 끌어와 채우는 것
- 같은 claim packet 안에 있다는 이유만으로 날짜를 올리는 것
### 6.3 `plaintiff_claim_snapshots`
- 보전채권 구조를 직접 보여주는 문서에만 채운다.
- `claim_type` 허용값: `"loan"|"guarantee"|"reimbursement"`
- 담보부분/무담보부분은 문서가 직접 또는 고도의 명백성으로 보여줄 때만 채운다.
### 6.4 부담/임차권/가압류 관련 support
- 같은 목적물 또는 같은 자산 cluster에 직접 연결되는 문서에만 채운다.
- 임대차, 가압류, 담보권은 support role과 공제성 후보를 분리한다.
- 최종 공제 여부 결론은 쓰지 않는다. candidate만 허용한다.
### 6.5 `legal_meta_flags`
- 결론이 아니라 legal candidate flag만 저장한다.
- `restoration_mode_candidate`는 강한 후보가 있을 때만 채운다.
- `confidence_score`는 `"high"|"medium"|"low"|"unknown"` 중 하나를 사용한다.
---
## 7. evidence_actio_support.json 스키마(필드명/enum 엄수; 추가 필드 금지)
최종 파일은 배열(Array)이다.
```json
[
{
"evidence_index": "E-014",
"actio_pauliana_support": {
"support_role_tags": ["사해행위목적물"],
"fraudulent_act_date": "2016-01-31",
"property_value_at_close_of_arguments_support": {
"candidate_amount": "560000000원",
"candidate_date": "2024-03-01",
"basis_type": "현재시점대체",
"reason_text": "시가 후보 문서",
"confidence": "medium",
"proof_doc": "E-014"
},
"encumbrances_at_fraudulent_act": [
{
"kind": "근저당",
"holder": "근저당권자",
"rank": 1,
"registered_max_amount": "300000000원",
"actual_secured_debt_at_act": "250000000원",
"supported_date": "2016-01-31",
"deductible_in_value_compensation": true,
"deductibility_reason": "선순위담보 candidate",
"proof_doc": "E-014",
"proof_strength": "direct"
}
],
"encumbrances_at_close_of_arguments": [
{
"kind": "근저당",
"holder": "근저당권자",
"rank": 1,
"actual_outstanding_at_close": "0원",
"released_date": "2017-02-01",
"released_by_beneficiary": true,
"proof_doc": "E-014",
"proof_strength": "direct"
}
],
"lease_deposit_deductibility_support": {
"tenant_name": "임차인",
"lease_deposit_amount": "100000000원",
"has_possession": true,
"has_resident_registration": true,
"has_fixed_date": true,
"senior_security_exists": true,
"deductible_candidate": true,
"deductibility_reason": "대항력/확정일자 candidate",
"proof_doc": "E-014",
"proof_strength": "indirect"
},
"non_deductible_attachment_claims": [
{
"holder": "가압류권자",
"kind": "가압류",
"claim_amount": "50000000원",
"attachment_non_deductible_flag": true,
"reason": "가압류채권 비공제 candidate",
"proof_doc": "E-014",
"proof_strength": "direct"
}
],
"beneficiary_gain_support": {
"beneficiary_name": "수익자",
"nominal_transfer_value": "400000000원",
"beneficiary_preexisting_claim_amount": "100000000원",
"assumed_debt_amount": "50000000원",
"released_encumbrance_amount": "250000000원",
"beneficiary_gain_estimate_candidate": "실질 취득이익 후보",
"estimation_basis": "매매가액/인수채무/부담 해소 단서",
"proof_doc": "E-014",
"confidence": "medium"
},
"plaintiff_claim_snapshots": [
{
"plaintiff_name": "원고",
"claim_type": "guarantee",
"principal_existing_at_fraudulent_act": "200000000원",
"secured_portion_at_fraudulent_act": "0원",
"unsecured_portion_at_fraudulent_act": "200000000원",
"contractual_interest_rate": "연 5%",
"default_interest_rate": "연 12%",
"interest_start_date": "2015-01-01",
"amount_as_of_close_of_arguments": "240000000원",
"proof_status": "direct",
"proof_doc": "E-014"
}
],
"legal_meta_flags": {
"is_actio_pauliana_relevant_candidate": true,
"restoration_mode_candidate": "가액배상",
"is_value_compensation_exception_triggered_candidate": true,
"lease_deposit_deductible_candidate": true,
"attachment_claim_deductible_candidate": false,
"remaining_joint_collateral_value_estimate_candidate": "잔여 공동담보가치 후보",
"beneficiary_gain_estimate_candidate": "수익자 이익 후보",
"confidence_score": "medium"
}
}
}
]
```
추가 규칙:
- 최종 배열이 비어 있을 수 있다. 이 경우 `[]`를 저장한다.
- 레코드에는 `evidence_index`와 `actio_pauliana_support` 외 다른 최상위 키를 넣지 않는다.
- base catalog 필드(`title`, `doc_type`, `key_*`, `source_pointer`, base `legal_calculation_object`)를 반복 저장하지 않는다.
- pruning 이후 `actio_pauliana_support`가 비면 해당 문서는 출력하지 않는다.
---
## 8. 최종 저장 및 검증(write-verify)
1. `evidence_indexed.json`의 순서를 기준으로 각 문서를 검토한다.
2. `actio_case_signals.json`과 `BO.json`을 기준으로 relevant scope를 먼저 정한다.
3. scope 안의 문서에 대해서만 문서-직접형 sparse support를 추출한다.
4. `write_file(overwrite=true)`로 `evidence_actio_support.json` 파일을 저장한다.
5. 즉시 `list_docs`로 파일 존재를 검증한다. 누락 시 재시도 1회.
6. `evidence_actio_support.json`은 절대 재읽지 않는다.
7. 검증 완료 후 즉시 종료하고 `"사해행위취소 support 인덱싱 완료"` 출력한다.
- task_name: Task_D1
llm_provider: google
llm_model: gemini-3.1-flash-lite-preview
llm_reasoning: medium
cache_control:
mode: auto
ttl: 10m
use_tools: [localdocs]
prompts:
- role: user
content: |
## 실행방법(체크리스트)
[ ] 1) Preflight: `list_docs` 사용해서 `BO.json`, `evidence_indexed.json`만 확인하고, `read_docs` 사용해서 `BO.json`, `evidence_indexed.json`만 읽기
[ ] 2) `write_file` 사용해서 `Fact_Ledger_base.json` 생성
[ ] 3) `list_docs` 사용해서 `Fact_Ledger_base.json` 파일만 존재 확인(절대 읽기 금지)
[ ] 4) `"Fact Ledger base 생성 완료"` 출력하고 작업을 끝낸다(terminate).
---
# CONSTRAINT (MUST FOLLOW)
아래 제시된 작업 지시사항만 수행하라. 그 외 다른 어떤 작업도 수행해서는 안 된다.
오로지 추론(inference)을 통해서만 아래 작업들을 수행한다. 코드 실행 절대 금지.
## 0. 역할/목적
You are an MCP-enabled LLM agent assisting plaintiff-side Korean civil/commercial litigation counsel.
당신은 `stage1_사건개요파악` 단계의 `Task_D1`를 수행한다.
이 단계에서 `BO.json`과 `evidence_indexed.json`을 읽고, 각 BO에 대응하는 base fact ledger 항목 1개를 생성하여 `Fact_Ledger_base.json`을 만든다.
이 작업의 목적은 다음 4가지를 동시에 달성하는 것이다.
1. `1 BO = 1 Fact` 원칙으로 사건행위 단위 사실원장을 생성한다.
2. `evidence_indexed.json`에 이미 구조화된 base `legal_calculation_object`만 linked evidence 기준으로 최소 병합한다.
3. 후속 단계가 가볍게 조인할 수 있도록 `actio_pauliana_support`는 완전히 배제한 base ledger만 만든다.
4. `BO.json`의 각 `Evidence[]`가 `evidence_indexed.json` authority와 정합적인지 검증하여, stale evidence index나 title drift가 있는 경우 오염된 사실원장 생성을 차단한다.
이 작업은 `BO.json`과 `evidence_indexed.json`만 사용한다.
다른 파일을 읽거나 참조하지 않는다.
---
## 1. Preflight
- `list_docs` 수행: `BO.json`, `evidence_indexed.json`
- `read_docs` 수행: `BO.json`, `evidence_indexed.json`
---
## 2. 입력 계약(Input Contract)
### 2.1 `BO.json`
`BO.json`은 배열이다. 각 원소는 아래 구조를 가진다.
```ts
{
id: string; // 예: "bh1"
Performer: string | null;
PerformerType: string | null;
Action: string | null;
ActionType: string | null; // 예: "법률행위(legal acts)"
Subject: string | null;
Reason: string | null;
PriorAct: string | null;
BehaviorTime: string | null; // 가능하면 YYYY-MM-DD
TimeText: string | null;
TimePrecision: string | null;
StatementType: string | null;
Perspective: string | null;
EvidenceTitles: string[];
Evidence: Array<{
source_title: string | null;
evidence_index: string | null; // 예: "E-011"
relevant_content: string | null;
time_match: string | null;
party_match: string | null;
content_relevance: "직접"|"간접"|"불명"|"반대"|string|null;
authentication_status: string | null;
corroboration: string | null;
}>;
Legal_Keywords: string[];
JuristicAct: {
label: string | null;
gubun_multi: string[] | null;
basis_note: string | null;
needs_review: boolean | null;
} | null;
Object?: string | null;
Method?: string | null;
Location?: string | null;
Outcome?: string | null;
}
```
### 2.2 `evidence_indexed.json`
`evidence_indexed.json`은 배열이다. 각 원소는 아래 구조를 가진다.
```ts
{
evidence_index: string; // 예: "E-001"
title: string;
doc_type: "판결/결정"|"공문서"|"거래기록"|"통신기록"|"처분문서"|"기타";
key_facts: string[];
key_dates: string[];
key_amounts: string[];
key_parties: string[];
source_pointer: {
source: string;
ordinal: number;
};
legal_calculation_object?: {
asset_id: string | null;
property_label_for_schedule: string | null;
transaction_type: "매매"|"증여"|"대물변제"|null;
transaction_date: string | null;
registration_date: string | null;
registry_office: string | null;
registry_receipt_no: string | null;
registry_recorded_transfer_date: string | null;
market_value_at_act: string | null;
market_value_at_close: string | null;
sale_price: string | null;
consideration_breakdown: string[];
claim_id: string | null;
claim_label_for_schedule: string | null;
claim_event_type: "대여"|"연대보증"|"신용보증"|"보증채무"|"대위변제"|"구상권발생"|"변제"|"배당"|"채권양도"|"채무인수"|"상계"|"면제"|"경개"|"담보제공"|null;
claim_nature_tags: Array<"상사채권"|"민사채권"|"특정물채권"|"종류채권"|"금전채권"|"이자채권"|"선택채권"|"법정채권"|"불완전채권">;
claim_creditor: string | null;
claim_debtor: string | null;
claim_guarantor: string | null;
claim_security_provider: string | null;
claim_arising_date: string | null;
claim_maturity_date: string | null;
claim_default_or_acceleration_date: string | null;
claim_performance_date: string | null;
claim_instrument_date: string | null;
claim_instrument_identifier: string | null;
claim_instrument_issuer: string | null;
claim_principal_amount_at_act: string | null;
claim_total_amount_at_act: string | null;
claim_outstanding_amount_at_close: string | null;
claim_actual_performance_amount: string | null;
claim_guarantee_limit_amount: string | null;
claim_secured_cap_amount: string | null;
claim_interest_rate: string | null;
claim_default_rate: string | null;
claim_component_breakdown: string[];
claim_status_tags: Array<"발생"|"만기도래"|"기한의이익상실"|"연체"|"대위변제완료"|"구상권발생"|"일부회수"|"잔존"|"소멸"|"불명">;
};
}
```
주의:
- 입력 evidence 문서에 `actio_pauliana_support`가 있더라도 이 작업에서는 완전히 무시한다.
- 이 작업에서 생성하는 출력에는 `actio_pauliana_support`를 절대 포함하지 않는다.
---
## 3. 불가침 원칙 (Non-Negotiables)
1) Hallucination 금지: 입력에 없는 사실은 추가하지 않는다.
2) `1 BO = 1 Fact`를 엄수한다.
3) `BO.json` 배열 순서를 최종 출력 순서로 유지한다.
4) `BO.Evidence[].evidence_index`는 반드시 `evidence_indexed.json`에서 매칭되어야 한다. 매칭 실패는 스킵할 수 있으나, 같은 Evidence item에서 `source_title`과 catalog `title`이 normalization 후에도 불일치하면 입력 오염으로 보고 즉시 중단한다.
5) `legal_calculation_object`는 linked evidence의 `legal_calculation_object` 내부 값에서만 병합한다.
6) `key_facts`, `key_dates`, `key_amounts`, `key_parties`만을 근거로 계산필드를 새로 만들지 않는다.
7) BO 본문만으로 `legal_calculation_object`를 새로 창작하지 않는다.
8) linked evidence가 없는 BO에는 `legal_calculation_object`를 생성하지 않는다.
9) linked evidence 밖의 다른 증거를 끌어와 병합하지 않는다.
10) 스칼라 충돌 시 우선순위가 높은 값 1개만 채택한다.
11) 배열은 exact-match 기준으로 중복 제거한다.
12) 불명확하면 `null`, `[]`, `["증거공백"]`으로 처리한다.
13) 금액 합산, 차감, 재계산 금지.
14) `Fact_Ledger_base.json`에는 아래 정의된 필드만 허용한다.
---
## 4. 사용 도구(Localdocs MCP) 및 오류 규율
- 기본 도구명: `list_docs`, `list_folders`, `read_docs`, `write_file`, `create_folder`, `delete_file`
## 5. 규율
- Tool output만을 사실로 취급한다.
- Error 발생 시: 원인 분석 -> (별칭/대체 도구명으로) 1회만 재시도 -> 재시도 실패면 해당 체크리스트 항목을 `FAILED`로 보고하고 종료한다.
---
## 6. 필수 IO
### IN
- `BO.json`
- `evidence_indexed.json`
### OUT
- `Fact_Ledger_base.json`
---
## 7. 토큰/속도 상한(하드 리밋)
- `parties` <=5
- `evidence_refs` <=6
- `object_spec`는 가능한 경우 40자 내외의 짧은 명사구
- `action`은 1문장
- `legal_calculation_object.consideration_breakdown` <=4 (각 <=40자)
- `legal_calculation_object.claim_component_breakdown` <=5 (각 <=40자)
- `legal_calculation_object.claim_nature_tags` <=4
- `legal_calculation_object.claim_status_tags` <=4
- 중간 산출물 파일 금지(최종 파일만 `write_file`)
- 출력에 문서 원문 장문 재인용 금지
---
## 8. 소스 우선순위
### 8.1 top-level Fact 필드
- `BO.json`이 1차 소스다.
- linked evidence는 date, amount, parties 보조와 base `legal_calculation_object` 병합에만 사용한다.
### 8.2 충돌 해결
- BO의 명시 내용과 linked evidence가 충돌하면, top-level Fact 필드는 BO를 우선한다.
- `legal_calculation_object` 내부 필드는 linked evidence의 계산객체를 우선한다.
- linked evidence끼리 충돌하면 아래 정의한 필드별 우선순위를 따른다.
---
## 9. Fact 생성 규칙
### 9.1 `1 BO = 1 Fact`
- `BO.json`의 각 원소마다 `Fact_Ledger_base.json`의 원소 1개를 생성한다.
- `fact_id`는 `"F-001"`, `"F-002"` 식의 3자리 0패딩을 사용한다.
### 9.2 linked evidence lookup
- `evidence_indexed.json`을 `evidence_index` 기준 해시맵으로 만든다.
- 각 BO의 `Evidence[]`를 순회하면서 `evidence_index`가 일치하는 evidence만 linked evidence로 채택한다.
- `evidence_index`가 `null`이거나 매칭 실패면 그 항목은 스킵한다.
- 단, `BO.Evidence[]`의 어떤 항목에서 `source_title`이 존재하고 매칭된 catalog `title`도 존재할 때,
whitespace-normalization, 괄호/전각공백 정규화, 연속공백 축약을 적용한 뒤에도 title이 불일치하면
이는 evidence authority drift로 간주한다.
- evidence authority drift가 1건이라도 발견되면 `Fact_Ledger_base.json`을 쓰지 말고 즉시 중단한다.
### 9.3 linked evidence 정렬 우선순위
같은 BO의 linked evidence들을 아래 기준으로 정렬한다.
1. `BO.Evidence[].content_relevance`
- `"직접"` > `"간접"` > `"불명"` > `"반대"`
2. 대응 evidence의 `doc_type`
- `"처분문서"` > `"공문서"` > `"거래기록"` > `"판결/결정"` > `"통신기록"` > `"기타"`
3. `evidence_index` 오름차순
이 정렬순서를 전체 병합의 기본 우선순위로 사용한다.
### 9.4 top-level 필드 매핑
#### 9.4.1 `source_bo_id`
- `BO.id`
#### 9.4.2 `type`
- `BO.ActionType`
#### 9.4.3 `date`
- 1순위: `BO.BehaviorTime`
- 2순위: linked evidence의 base `legal_calculation_object`에서 아래 우선순위
1. 부동산 처분, 증여, 대물변제, 소유권이전 관련 BO -> `transaction_date`
2. 등기, 공시, 말소 관련 BO -> `registration_date`
3. 대여, 보증, 담보제공, 구상권발생 관련 BO -> `claim_arising_date`
4. 변제, 대위변제, 배당 관련 BO -> `claim_performance_date`
5. 만기, 연체, 기한의이익상실 관련 BO -> `claim_default_or_acceleration_date`
6. 그 외 -> `claim_instrument_date`
- 그래도 없으면 `null`
#### 9.4.4 `parties`
아래 순서로 수집하고 중복 제거한다.
1. `BO.Performer`
2. `BO.Subject`가 사람 또는 법인 이름 그 자체이거나, `"...의 ... 채무"`처럼 특정 채권관계의 당사자를 식별하게 해 주는 표현일 때 그 직접 당사자
3. `BO.Action`에 명시된 직접 상대방 이름
4. linked evidence의 base `legal_calculation_object`에서 현재 BO와 직접 관련된 당사자
- `claim_creditor`
- `claim_debtor`
- `claim_guarantor`
- `claim_security_provider`
제거 규칙:
- 주소, 부동산 표기, 금액 문자열, 순수 채무명칭은 `parties`에 넣지 않는다.
- 동일인/동일법인이 명백하면 1개만 남긴다.
- 최종 배열은 최대 5개다.
#### 9.4.5 `object_spec`
아래 우선순위로 짧은 명사구 1개를 만든다.
1. `BO.Subject`가 목적물, 채권, 담보, 배당금, 대출, 보증, 부동산, 근저당 피담보채무 등 객체를 직접 가리키면 `BO.Subject`를 축약 사용
2. `BO.Subject`가 당사자명 위주라 객체성이 부족하면 `BO.Action`에서 객체 부분만 짧게 추출
3. linked evidence의 base `legal_calculation_object`가 존재하고, 그 객체가 더 직접적이면 아래 값 중 하나를 사용 가능
- 부동산형: 부동산 표기
- 채권형: 채권명, 담보채무명, 배당금, 구상채권 등
- 불명확하면 `null`
#### 9.4.6 `amount`
`amount`는 해당 Fact의 대표 금액이다.
우선순위:
1. `BO.Action` 또는 `BO.Subject`에 현재 행위와 직접 연결된 금액이 명시되어 있으면 그 값
2. 위 금액이 없을 때만 linked evidence의 base `legal_calculation_object`에서 아래 우선순위
1. 부동산 처분행위 -> `sale_price`
2. 대여, 대출원금 형성 -> `claim_principal_amount_at_act`
3. 보증한도 설정 -> `claim_guarantee_limit_amount`
4. 담보최고액 설정 -> `claim_secured_cap_amount`
5. 변제, 대위변제, 배당 -> `claim_actual_performance_amount`
6. 잔존채권 또는 구상채권 파악 -> `claim_outstanding_amount_at_close`
7. 행위시점 총채권액이 직접 쟁점일 때만 -> `claim_total_amount_at_act`
- 여러 금액이 경쟁하면 행위와 가장 직접 대응하는 1개만 쓴다.
- 불명확하면 `null`
- 금지:
- 여러 금액을 합산, 차감해 새 금액 만들기
- `sale_price` 또는 `claim_total_amount_at_act`를 기계적으로 복사하기
#### 9.4.7 `action`
- `BO.Action`을 바탕으로 1문장으로 정리한다.
- 입력 표현을 유지하되 군더더기만 줄인다.
- 새 사실 추가 금지.
#### 9.4.8 `evidence_refs`
- `BO.Evidence[]`를 순서대로 보면서 매칭된 evidence에 대해
- ``evidence_index + " (" + evidence.title + ")"`` 형식으로 기록한다.
- 매칭된 evidence가 없으면 `["증거공백"]`
- exact-match 중복 제거
#### 9.4.9 `credibility`
아래 규칙으로 하나만 선택한다.
- `high`
- linked evidence 중 `content_relevance=="직접"`가 1개 이상이고,
- 그 evidence의 `doc_type`이 `공문서`, `처분문서`, `거래기록` 중 하나이거나,
- BO에 연결된 복수 증거가 서로 보강한다.
- `medium`
- linked evidence는 있으나 직접성, 문서성, 보강 정도가 혼재한다.
- `low`
- linked evidence가 없거나,
- `간접`, `불명`, `반대` 위주이거나,
- `["증거공백"]` 상태다.
---
## 10. base `legal_calculation_object` 생성 규칙
### 10.1 생성 여부
아래를 모두 충족할 때만 생성한다.
1. 해당 BO의 linked evidence 중 적어도 1개가 non-empty base `legal_calculation_object`를 가진다.
2. 해당 BO가 아래 중 적어도 하나와 실질적 관련이 있다.
- 부동산 처분, 이전, 증여, 대물변제, 등기, 말소, 시가, 대가 구성
- 채권의 발생, 대여, 보증, 담보제공, 구상권발생, 변제, 대위변제, 배당, 채권양도, 채무인수, 상계, 면제, 경개
3. 병합 결과가 빈 객체가 아니다.
그 외에는 `legal_calculation_object`를 생성하지 않는다.
### 10.2 사용 가능한 데이터 원천
- base `legal_calculation_object`의 각 필드는 linked evidence의 `legal_calculation_object` 내부 값에서만 가져온다.
- linked evidence 밖의 다른 증거를 섞지 않는다.
- `key_facts`, `key_dates`, `key_amounts`, `key_parties`만으로 새 계산값을 만들지 않는다.
- BO의 `Action`, `Subject`만으로 새 계산필드를 만들지 않는다.
### 10.3 빈 객체 방지
병합 후 아래가 모두 비어 있으면 `legal_calculation_object`를 생성하지 않는다.
- 스칼라 필드가 모두 `null`
- `consideration_breakdown`가 빈 배열
- `claim_nature_tags`가 빈 배열
- `claim_component_breakdown`가 빈 배열
- `claim_status_tags`가 빈 배열
---
## 11. base `legal_calculation_object` 병합 규칙
### 11.1 A. 일반 부동산 거래 필드
아래 필드는 기본 우선순위가 가장 높은 linked evidence부터 보아 `null`이 아닌 첫 값을 채택한다.
- `asset_id`
- `property_label_for_schedule`
- `transaction_type`
- `transaction_date`
### 11.2 B. 등기 전용 필드
아래 필드는 등기문서 성격이 가장 강한 linked evidence를 우선 사용한다.
- `registration_date`
- `registry_office`
- `registry_receipt_no`
- `registry_recorded_transfer_date`
등기문서 우선순위:
1. `title`에 `등기사항전부증명서`가 포함된 문서
2. 그 외 `doc_type=="공문서"`이면서 해당 필드가 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
### 11.3 C. 시가, 가액 필드
아래 필드는 가격평가 성격이 가장 직접적인 linked evidence를 우선 사용한다.
- `market_value_at_act`
- `market_value_at_close`
가격평가 문서 우선순위:
1. `title`에 `감정평가`, `감정서`, `평가서`가 포함된 문서
2. 그 외 해당 필드가 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
### 11.4 D. 처분대가 필드
`sale_price`는 아래 우선순위로 1개만 채택한다.
1. `transaction_type`가 존재하고 `sale_price`가 non-null인 문서
2. `title`에 `계약서`, `약정서`, `등기사항전부증명서`, `영수증`, `확인서`가 포함되고 `sale_price`가 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
### 11.5 E. 부동산 대가 구성 배열
- `consideration_breakdown`는 기본 우선순위 순서대로 배열을 순차 추가한다.
- exact-match 중복 제거
- 최대 4개까지만 유지
- 장문 병합 금지
- 추론으로 새 항목 생성 금지
### 11.6 F. 채권 관계 스칼라 필드
아래 필드는 기본 우선순위가 가장 높은 linked evidence부터 보아 `null`이 아닌 첫 값을 채택한다.
- `claim_id`
- `claim_label_for_schedule`
- `claim_event_type`
- `claim_creditor`
- `claim_debtor`
- `claim_guarantor`
- `claim_security_provider`
### 11.7 G. 채권 성질 태그
- `claim_nature_tags`는 기본 우선순위 순서대로 배열을 합친다.
- exact-match 중복 제거
- 최대 4개까지만 유지
- 새 태그 추론 금지
### 11.8 H. 채권 날짜 필드
#### 11.8.1 `claim_arising_date`
우선순위:
1. `title`에 `약정서`, `계약서`, `보증서`, `대출`, `확인서`가 포함되고 값이 non-null인 문서
2. 그 외 `doc_type=="처분문서"` 또는 `doc_type=="거래기록"`이면서 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
#### 11.8.2 `claim_maturity_date`
우선순위:
1. `title`에 `약정서`, `계약서`, `대출`, `보증서`가 포함되고 값이 non-null인 문서
2. 그 외 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
#### 11.8.3 `claim_default_or_acceleration_date`
우선순위:
1. `title`에 `최고`, `기한의이익`, `연체`, `부도`, `내용증명`, `확인서`가 포함되고 값이 non-null인 문서
2. `doc_type=="거래기록"` 또는 `doc_type=="처분문서"`이면서 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
#### 11.8.4 `claim_performance_date`
우선순위:
1. `title`에 `영수증`, `배당표`, `대위변제`, `상환`, `거래내역`, `입금`, `출금`이 포함되고 값이 non-null인 문서
2. `doc_type=="거래기록"` 또는 `doc_type=="판결/결정"`이면서 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
#### 11.8.5 `claim_instrument_date`
우선순위:
1. `claim_instrument_identifier`가 함께 존재하는 문서
2. `claim_instrument_issuer`가 함께 존재하는 문서
3. 그 외 값이 non-null인 문서
4. 그래도 없으면 기본 우선순위 fallback
### 11.9 I. 채권 문서 식별 필드
아래 필드는 문서 식별성이 강한 evidence를 우선한다.
- `claim_instrument_identifier`
- `claim_instrument_issuer`
우선순위:
1. `title`에 `약정서`, `계약서`, `보증서`, `배당표`, `판결`, `결정`, `확인서`, `증서`가 포함된 문서
2. 그 외 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
### 11.10 J. 채권 금액 필드
#### 11.10.1 `claim_principal_amount_at_act`
우선순위:
1. `title`에 `약정서`, `계약서`, `대출`, `차용`, `보증서`가 포함되고 값이 non-null인 문서
2. `doc_type=="처분문서"` 또는 `doc_type=="거래기록"`이면서 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
#### 11.10.2 `claim_total_amount_at_act`
우선순위:
1. `title`에 `잔액`, `명세`, `확인서`, `대위변제`, `배당표`가 포함되고 값이 non-null인 문서
2. 그 외 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
#### 11.10.3 `claim_outstanding_amount_at_close`
우선순위:
1. `title`에 `잔액`, `채무잔액`, `미회수`, `구상금`, `확인서`, `배당표`가 포함되고 값이 non-null인 문서
2. `doc_type=="거래기록"` 또는 `doc_type=="처분문서"`이면서 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
#### 11.10.4 `claim_actual_performance_amount`
우선순위:
1. `title`에 `영수증`, `배당표`, `대위변제`, `상환`, `거래내역`, `입금`, `출금`이 포함되고 값이 non-null인 문서
2. `doc_type=="거래기록"` 또는 `doc_type=="판결/결정"`이면서 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
#### 11.10.5 `claim_guarantee_limit_amount`
우선순위:
1. `title`에 `보증`, `보증서`, `약정서`, `계약서`가 포함되고 값이 non-null인 문서
2. 그 외 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
#### 11.10.6 `claim_secured_cap_amount`
우선순위:
1. `title`에 `등기사항전부증명서`, `근저당`, `담보`, `설정계약`, `배당표`가 포함되고 값이 non-null인 문서
2. `doc_type=="공문서"`이면서 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
### 11.11 K. 금리 필드
- `claim_interest_rate`
- `claim_default_rate`
우선순위:
1. `title`에 `약정서`, `계약서`, `보증서`, `대출`이 포함되고 값이 non-null인 문서
2. 그 외 값이 non-null인 문서
3. 그래도 없으면 기본 우선순위 fallback
### 11.12 L. 채권 구성요소 배열
- `claim_component_breakdown`는 기본 우선순위 순서대로 배열을 합친다.
- exact-match 중복 제거
- 최대 5개까지만 유지
- 장문 병합 금지
- 추론으로 새 요소 생성 금지
### 11.13 M. 채권 상태 태그
- `claim_status_tags`는 기본 우선순위 순서대로 배열을 합친다.
- exact-match 중복 제거
- 최대 4개까지만 유지
- 새 상태 추론 금지
---
## 12. 충돌 처리
### 12.1 scalar field
- 서로 다른 linked evidence가 같은 scalar field에 서로 다른 값을 제시하면, 해당 필드 우선순위에서 앞선 값 1개만 채택한다.
- 판단 불가하면 `null`.
### 12.2 array field
- 배열은 exact-match 중복 제거 후 앞선 값부터 유지한다.
- 상한을 넘으면 앞에서부터 자른다.
---
## 13. 값 형식
- 날짜는 가능한 경우 `"YYYY-MM-DD"`
- 금액은 가능한 경우 `"300,000,000원"` 같은 문자열
- 비율은 예: `"월 1%"`, `"연 12%"`
- 불명값은 `null`
- 불명 배열은 `[]`
---
## 14. 최종 Schema(필드명/enum 엄수; 추가 필드 금지)
### Fact_Ledger_base.json (Array)
```ts
{
fact_id: string; // "F-001"
source_bo_id: string; // "bh#"
type: string | null;
date: string | null;
parties: string[];
object_spec: string | null;
amount: string | null;
action: string | null;
evidence_refs: string[]; // ["E-001 (title)"] 또는 ["증거공백"]
credibility: "high"|"medium"|"low";
legal_calculation_object?: {
asset_id: string | null;
property_label_for_schedule: string | null;
transaction_type: "매매"|"증여"|"대물변제"|null;
transaction_date: string | null;
registration_date: string | null;
registry_office: string | null;
registry_receipt_no: string | null;
registry_recorded_transfer_date: string | null;
market_value_at_act: string | null;
market_value_at_close: string | null;
sale_price: string | null;
consideration_breakdown: string[];
claim_id: string | null;
claim_label_for_schedule: string | null;
claim_event_type: "대여"|"연대보증"|"신용보증"|"보증채무"|"대위변제"|"구상권발생"|"변제"|"배당"|"채권양도"|"채무인수"|"상계"|"면제"|"경개"|"담보제공"|null;
claim_nature_tags: Array<"상사채권"|"민사채권"|"특정물채권"|"종류채권"|"금전채권"|"이자채권"|"선택채권"|"법정채권"|"불완전채권">;
claim_creditor: string | null;
claim_debtor: string | null;
claim_guarantor: string | null;
claim_security_provider: string | null;
claim_arising_date: string | null;
claim_maturity_date: string | null;
claim_default_or_acceleration_date: string | null;
claim_performance_date: string | null;
claim_instrument_date: string | null;
claim_instrument_identifier: string | null;
claim_instrument_issuer: string | null;
claim_principal_amount_at_act: string | null;
claim_total_amount_at_act: string | null;
claim_outstanding_amount_at_close: string | null;
claim_actual_performance_amount: string | null;
claim_guarantee_limit_amount: string | null;
claim_secured_cap_amount: string | null;
claim_interest_rate: string | null;
claim_default_rate: string | null;
claim_component_breakdown: string[];
claim_status_tags: Array<"발생"|"만기도래"|"기한의이익상실"|"연체"|"대위변제완료"|"구상권발생"|"일부회수"|"잔존"|"소멸"|"불명">;
};
}
```
추가 규칙:
- `legal_calculation_object`는 값이 있을 때만 포함한다.
- `actio_pauliana_support`는 절대 포함하지 않는다.
- 정의되지 않은 추가 필드 금지.
---
## 15. 최종 실행 지시
1. `BO.json`과 `evidence_indexed.json`을 읽는다.
2. `BO.json` 배열 순서대로 `1 BO = 1 Fact` 원칙에 따라 `Fact_Ledger_base.json` 배열을 만든다.
3. 각 BO의 linked evidence만 사용하여 base `legal_calculation_object`를 선택적으로 병합한다.
4. `write_file(overwrite=true)`로 `Fact_Ledger_base.json`을 저장한다.
5. 즉시 `list_docs`로 파일 존재를 검증한다. 누락 시 재시도 1회.
6. `Fact_Ledger_base.json`은 절대 재읽지 않는다.
- task_name: Task_D2
llm_provider: google
llm_model: gemini-3.1-flash-lite-preview
llm_reasoning: medium
cache_control:
mode: auto
ttl: 10m
use_tools: [localdocs]
prompts:
- role: user
content: |
## 실행방법(체크리스트)
[ ] 1) Preflight: `list_docs` 사용해서 `BO.json`, `evidence_actio_support.json`, `actio_case_signals.json`만 확인하고, `read_docs` 사용해서 `BO.json`, `evidence_actio_support.json`, `actio_case_signals.json`만 읽기
[ ] 2) `write_file` 사용해서 `fact_actio_support.json` 생성
[ ] 3) `list_docs` 사용해서 `fact_actio_support.json` 파일만 존재 확인(절대 읽기 금지)
[ ] 4) `"BO별 actio support 정규화 완료"` 출력하고 작업을 끝낸다(terminate).
---
# CONSTRAINT (MUST FOLLOW)
아래 제시된 작업 지시사항만 수행하라. 그 외 다른 어떤 작업도 수행해서는 안 된다.
오로지 추론(inference)을 통해서만 아래 작업들을 수행한다. 코드 실행 절대 금지.
## 0. 역할/목적
You are an MCP-enabled LLM agent assisting plaintiff-side Korean civil/commercial litigation counsel.
당신은 `stage1_사건개요파악` 단계의 `Task_D2`를 수행한다.
이 단계에서 `BO.json`, `evidence_actio_support.json`, `actio_case_signals.json`을 입력으로 사용하여,
각 BO에 연결된 sparse support 중 사건 전체에서 사해행위취소와 실질적으로 관련된 support만
BO 단위 normalized support object로 병합한 `fact_actio_support.json`을 생성한다.
이 작업의 목적은 다음 6가지를 동시에 달성하는 것이다.
1. 각 BO의 `Evidence[].evidence_index`를 기준으로 연결된 support 문서를 식별한다.
2. `actio_case_signals.json`을 사건 전체의 scope gate로 사용하여, 사건과 무관한 support를 BO 수준에서 제거한다.
3. 문서별 sparse `actio_pauliana_support`를 BO 단위로 최소 병합하되, support field가 허용되는 BO 범위를 엄격히 제한한다.
4. `fraudulent_act_date` 같은 핵심 필드가 다른 자산·다른 사건군 support에서 오염되지 않도록 차단한다.
5. 후속 단계가 `source_bo_id` 기준으로 base fact ledger에 조인할 수 있는 경량 support delta를 생성한다.
6. 계산, 확정판단, 총액 산정 없이 오직 scope-aware normalization만 수행한다.
이 작업은 `BO.json`, `evidence_actio_support.json`, `actio_case_signals.json`만 사용한다.
다른 파일을 읽거나 참조하지 않는다.
---
## 1. Preflight
- `list_docs` 수행: `BO.json`, `evidence_actio_support.json`, `actio_case_signals.json`
- `read_docs` 수행: `BO.json`, `evidence_actio_support.json`, `actio_case_signals.json`
---
## 2. 입력 계약(Input Contract)
### 2.1 `BO.json`
`BO.json`은 배열이다. 각 원소는 아래 구조를 가진다.
```ts
{
id: string; // 예: "bh1"
Performer: string | null;
PerformerType: string | null;
Action: string | null;
ActionType: string | null;
Subject: string | null;
Reason: string | null;
PriorAct: string | null;
BehaviorTime: string | null;
TimeText: string | null;
TimePrecision: string | null;
StatementType: string | null;
Perspective: string | null;
EvidenceTitles: string[];
Evidence: Array<{
source_title: string | null;
evidence_index: string | null; // 예: "E-011"
relevant_content: string | null;
time_match: string | null;
party_match: string | null;
content_relevance: "직접"|"간접"|"불명"|"반대"|string|null;
authentication_status: string | null;
corroboration: string | null;
}>;
Legal_Keywords?: string[];
JuristicAct?: {
label: string | null;
gubun_multi: string[] | null;
basis_note: string | null;
needs_review: boolean | null;
} | null;
Object?: string | null;
Method?: string | null;
Location?: string | null;
Outcome?: string | null;
}
```
### 2.2 `evidence_actio_support.json`
`evidence_actio_support.json`은 배열이다. support가 있는 문서만 sparse하게 들어 있다.
```ts
{
evidence_index: string; // 예: "E-001"
actio_pauliana_support: {
support_role_tags?: Array<"사해행위목적물"|"선순위담보"|"존속담보"|"임차권"|"가압류"|"원고채권"|"수익자이익"|"법리메타">;
fraudulent_act_date?: string;
property_value_at_close_of_arguments_support?: {
candidate_amount?: string;
candidate_date?: string;
basis_type?: "사실심변론종결직접"|"현재시점대체"|"감정기준일"|"거래시점동일"|"불명";
reason_text?: string;
confidence?: "high"|"medium"|"low"|"unknown";
proof_doc?: string;
};
encumbrances_at_fraudulent_act?: Array<{
kind?: "근저당"|"저당"|"가압류"|"전세권"|"임차보증금반환채권";
holder?: string;
rank?: number;
registered_max_amount?: string;
actual_secured_debt_at_act?: string;
supported_date?: string;
deductible_in_value_compensation?: boolean;
deductibility_reason?: string;
proof_doc?: string;
proof_strength?: "direct"|"indirect"|"meeting_only"|"unknown";
}>;
encumbrances_at_close_of_arguments?: Array<{
kind?: "근저당"|"저당"|"가압류"|"전세권"|"임차보증금반환채권";
holder?: string;
rank?: number;
actual_outstanding_at_close?: string;
released_date?: string;
released_by_beneficiary?: boolean;
proof_doc?: string;
proof_strength?: "direct"|"indirect"|"meeting_only"|"unknown";
}>;
lease_deposit_deductibility_support?: {
tenant_name?: string;
lease_deposit_amount?: string;
has_possession?: boolean;
has_resident_registration?: boolean;
has_fixed_date?: boolean;
senior_security_exists?: boolean;
deductible_candidate?: boolean;
deductibility_reason?: string;
proof_doc?: string;
proof_strength?: "direct"|"indirect"|"meeting_only"|"unknown";
};
non_deductible_attachment_claims?: Array<{
holder?: string;
kind?: "가압류"|"압류";
claim_amount?: string;
attachment_non_deductible_flag?: boolean;
reason?: string;
proof_doc?: string;
proof_strength?: "direct"|"indirect"|"meeting_only"|"unknown";
}>;
beneficiary_gain_support?: {
beneficiary_name?: string;
nominal_transfer_value?: string;
beneficiary_preexisting_claim_amount?: string;
assumed_debt_amount?: string;
released_encumbrance_amount?: string;
beneficiary_gain_estimate_candidate?: string;
estimation_basis?: string;
proof_doc?: string;
confidence?: "high"|"medium"|"low"|"unknown";
};
plaintiff_claim_snapshots?: Array<{
plaintiff_name?: string;
claim_type?: "loan"|"guarantee"|"reimbursement";
principal_existing_at_fraudulent_act?: string;
secured_portion_at_fraudulent_act?: string;
unsecured_portion_at_fraudulent_act?: string;
contractual_interest_rate?: string;
default_interest_rate?: string;
interest_start_date?: string;
amount_as_of_close_of_arguments?: string;
proof_status?: "direct"|"indirect"|"meeting_only"|"unknown";
proof_doc?: string;
}>;
legal_meta_flags?: {
is_actio_pauliana_relevant_candidate?: boolean;
restoration_mode_candidate?: "원물반환"|"말소등기"|"가액배상";
is_value_compensation_exception_triggered_candidate?: boolean;
lease_deposit_deductible_candidate?: boolean;
attachment_claim_deductible_candidate?: boolean;
remaining_joint_collateral_value_estimate_candidate?: string;
beneficiary_gain_estimate_candidate?: string;
confidence_score?: "high"|"medium"|"low"|"unknown";
};
};
}
```
### 2.3 `actio_case_signals.json`
`actio_case_signals.json`은 객체(Object)이며, 사건 전체 수준에서 사해행위취소 구조가 의심되는 범위를 지정하는 scope gate다.
```ts
{
is_actio_pauliana_suspected: boolean;
suspicion_level: "high"|"medium"|"low"|"none";
suspicion_reasons?: string[];
related_bo_ids?: string[];
related_evidence_indexes?: string[];
target_property_candidates?: string[];
fraudulent_act_date_candidates?: string[];
preserved_claim_candidates?: Array<{
bo_id?: string;
claim_type_candidate?: "loan"|"guarantee"|"reimbursement"|"unknown";
creditor_candidate?: string;
debtor_candidate?: string;
}>;
beneficiary_candidates?: string[];
encumbrance_related_evidence_indexes?: string[];
}
```
주의:
- 입력 파일은 sparse delta이므로 support가 없는 문서는 아예 존재하지 않을 수 있다.
- `proof_doc`는 입력 support record의 값을 유지해야 한다. `source_bo_id`로 바꾸지 않는다.
- `actio_case_signals.json`은 최종 판결문이 아니라 scope gate다. 이 파일에 없는 필드를 상상해서 추가하지 않는다.
---
## 3. 불가침 원칙 (Non-Negotiables)
1) Hallucination 금지: 입력에 없는 사실은 추가하지 않는다.
2) `1 BO = 0 또는 1 support record`를 엄수한다.
3) `BO.json` 배열 순서를 최종 출력 기준 순서로 사용한다. 다만 support가 남지 않는 BO는 출력하지 않는다.
4) linked evidence 밖의 다른 support 문서를 끌어오지 않는다.
5) `actio_case_signals.json`은 case-level scope gate다. 이 gate를 통과하지 못한 support field는 모두 제거한다.
6) `BO.Action`, `BO.Subject`, `Legal_Keywords`만으로 새로운 support field를 창작하지 않는다.
7) 입력 support를 기반으로 병합만 수행하며 계산, 합산, 차감, 확정판단은 하지 않는다.
8) `common_collateral_value`, 최종 `beneficiary_gain`, 최종 `preserved_claim_total`, 최종 `recovery_cap` 같은 결론값 생성 금지.
9) 스칼라 충돌 시 우선순위가 높은 값 1개만 채택한다.
10) 배열은 identity key 기준 최소 병합 또는 exact-match 중복 제거만 허용한다.
11) 불명확한 값은 내부 판단상 `null` 취급하되, 최종 출력에서는 `null`, `[]`, `{}`를 제거한다.
12) `proof_doc`는 입력 support record의 값만 유지한다. 다른 값으로 변경 금지.
13) `fraudulent_act_date`는 `사해행위목적물` 범주의 BO에만 허용한다. 원고채권/보증/대위변제 BO에는 절대 올리지 않는다.
14) `plaintiff_claim_snapshots`는 `원고채권` 범주의 BO에만 허용한다. 처분 BO나 담보 BO에 절대 올리지 않는다.
15) `encumbrances_*`, `lease_deposit_deductibility_support`, `non_deductible_attachment_claims`는 target property cluster와 직접 연결되는 BO에만 허용한다.
16) `beneficiary_gain_support`는 처분/수익자 관련 BO에만 허용한다.
17) `legal_meta_flags`는 같은 BO에서 하나 이상의 실질 support field가 살아남은 경우에만 보조적으로 유지할 수 있다. 메타 플래그만 단독으로 확장하지 않는다.
18) 최종 출력은 아래 정의한 필드만 허용한다.
---
## 4. 사용 도구(Localdocs MCP) 및 오류 규율
- 기본 도구명: `list_docs`, `list_folders`, `read_docs`, `write_file`, `create_folder`, `delete_file`
- Tool output만을 사실로 취급한다.
- Error 발생 시: 원인 분석 -> (별칭/대체 도구명으로) 1회만 재시도 -> 재시도 실패면 해당 체크리스트 항목을 `FAILED`로 보고하고 종료한다.
---
## 5. 필수 IO
### IN
- `BO.json`
- `evidence_actio_support.json`
- `actio_case_signals.json`
### OUT
- `fact_actio_support.json`
---
## 6. 토큰/속도 상한(하드 리밋)
- support가 남는 BO만 출력
- BO별 출력 레코드는 최대 1개
- `support_role_tags` <=8
- `encumbrances_at_fraudulent_act` <=4
- `encumbrances_at_close_of_arguments` <=4
- `non_deductible_attachment_claims` <=4
- `plaintiff_claim_snapshots` <=4
- `reason_text`, `deductibility_reason`, `reason`, `estimation_basis`는 각 <=50자
- 중간 산출물 파일 금지(최종 파일만 `write_file`)
- 최종 출력에서 `null`, `[]`, `{}` 제거
---
## 7. 사건 수준 scope gate
### 7.1 global hard gate
- `actio_case_signals.is_actio_pauliana_suspected == false` 이고 `suspicion_level == "none"`이면 원칙적으로 `[]`를 저장한다.
- 단, 입력 support가 존재하더라도 사건 수준 gate가 닫혀 있으면 확장하지 않는다.
### 7.2 case scope anchors
아래 값들은 BO relevance를 판단하는 앵커다.
- `related_bo_ids`
- `related_evidence_indexes`
- `target_property_candidates`
- `fraudulent_act_date_candidates`
- `preserved_claim_candidates`
- `beneficiary_candidates`
- `encumbrance_related_evidence_indexes`
`actio_case_signals.json`에 값이 있으면 그것을 우선한다.
값이 비어 있으면 linked support와 BO 의미를 보조적으로 사용하되, 항상 보수적으로 판단한다.
---
## 8. BO별 linked support 매핑
### 8.1 support lookup
- `evidence_actio_support.json`을 `evidence_index` 기준 해시맵으로 만든다.
### 8.2 BO별 linked support
- 각 BO의 `Evidence[]`를 순회한다.
- `Evidence[].evidence_index`와 일치하는 `evidence_actio_support.json` 원소만 해당 BO의 linked support로 채택한다.
- `evidence_index`가 `null`이거나 매칭 실패면 그 항목은 스킵한다.
- linked support가 0개면 그 BO는 출력하지 않는다.
### 8.3 linked support 정렬 우선순위
같은 BO에 연결된 linked support 레코드들을 아래 기준으로 정렬한다.
1. 해당 `evidence_index`가 `actio_case_signals.related_evidence_indexes`에 있으면 최우선
2. encumbrance/lease/attachment 관련 필드 병합 시, 해당 `evidence_index`가 `actio_case_signals.encumbrance_related_evidence_indexes`에 있으면 최우선
3. `BO.Evidence[].content_relevance`
- `"직접"` > `"간접"` > `"불명"` > `"반대"`
4. linked support 내부 원소의 `proof_strength` 또는 `proof_status`
- `"direct"` > `"indirect"` > `"meeting_only"` > `"unknown"`
5. `evidence_index` 오름차순
이 정렬순서를 전체 병합의 기본 우선순위로 사용한다.
---
## 9. BO scope bucket 판정 (actio_case_signals 우선)
각 BO는 아래 bucket 중 0개 이상에 속할 수 있다.
### 9.1 `property_bucket`
다음 중 하나 이상이면 포함:
- `BO.id`가 `related_bo_ids`에 포함
- linked evidence 중 `related_evidence_indexes`와 교집합 존재
- linked support role에 `사해행위목적물` 존재
- `BO.Action`, `BO.Object`, `BO.Legal_Keywords`, `BO.Evidence[].relevant_content`에
처분/매매/증여/대물변제/소유권이전 및 `target_property_candidates`가 함께 드러남
### 9.2 `preserved_claim_bucket`
다음 중 하나 이상이면 포함:
- `BO.id`가 `preserved_claim_candidates[].bo_id`와 일치
- linked support role에 `원고채권` 존재
- `BO.Action`, `JuristicAct.label`, `Legal_Keywords`에 대여/보증/신용보증/대위변제/구상/잔존채권이 직접 나타남
### 9.3 `encumbrance_bucket`
다음 중 하나 이상이면 포함:
- linked evidence 중 `encumbrance_related_evidence_indexes`와 교집합 존재
- linked support role에 `선순위담보` 또는 `존속담보` 존재
- `BO.Action`, `BO.Legal_Keywords`에 근저당/저당/전세권/담보설정/담보말소가 직접 나타남
- 그리고 가능하면 target property cluster와 연결되어야 한다.
### 9.4 `lease_bucket`
다음 중 하나 이상이면 포함:
- linked support role에 `임차권` 존재
- `BO.Action`, `Legal_Keywords`에 임대차/임차보증금/점유/확정일자/전입이 직접 나타남
- 그리고 가능하면 target property cluster와 연결되어야 한다.
### 9.5 `attachment_bucket`
다음 중 하나 이상이면 포함:
- linked support role에 `가압류` 존재
- `BO.Action`, `Legal_Keywords`에 가압류/압류/보전처분이 직접 나타남
- 그리고 가능하면 target property cluster와 연결되어야 한다.
### 9.6 `beneficiary_bucket`
다음 중 하나 이상이면 포함:
- linked support role에 `수익자이익` 존재
- `BO.Subject` 또는 `BO.Action`에 `beneficiary_candidates`가 직접 나타남
- `property_bucket`에 속하면서 수익자/전득자와 직접 관련된 처분 BO임
### 9.7 conservative tie-break
- 두 bucket 사이가 애매하면 더 좁은 범위만 남긴다.
- `target_property_candidates`와 연결 여부가 불명하면 property/encumbrance/lease/attachment/beneficiary 관련 필드는 버린다.
---
## 10. relevance 필터 (support field별 허용 범위)
linked support 전체를 BO에 그대로 붙이지 말고, 아래 필드별 허용 범위에 따라 잘라낸다.
### 10.1 `support_role_tags`
- surviving support field를 실제로 뒷받침한 role tag만 남긴다.
- 단순 union 금지. 결과 필드와 무관한 tag는 제거한다.
### 10.2 `fraudulent_act_date`
허용 조건:
- `property_bucket == true`
- 해당 linked support에 `support_role_tags` 중 `사해행위목적물`이 존재
- 값이 있으면 우선 `actio_case_signals.fraudulent_act_date_candidates`와 일치하는 값만 허용
충돌 규칙:
- surviving candidate가 1개면 채택
- 2개 이상이면 `actio_case_signals.fraudulent_act_date_candidates`와 일치하는 값만 남긴다
- 그래도 복수이거나 일치값이 없으면 `fraudulent_act_date`는 아예 제거한다
### 10.3 `property_value_at_close_of_arguments_support`
허용 조건:
- `property_bucket == true`
- linked support role에 `사해행위목적물`이 존재하거나, 처분 목적물 자체의 가치와 직접 연결됨
### 10.4 `encumbrances_at_fraudulent_act`
허용 조건:
- `encumbrance_bucket == true` 또는 (`property_bucket == true` 이고 동일 target property cluster 관련 부담)
- role tag가 `선순위담보` 또는 `존속담보`이거나, `attachment_bucket`에서 `kind=="가압류"`가 직접 확인됨
금지 조건:
- `preserved_claim_bucket`만 있는 BO에는 올리지 않는다
### 10.5 `encumbrances_at_close_of_arguments`
허용 조건은 10.4와 동일하다.
### 10.6 `lease_deposit_deductibility_support`
허용 조건:
- `lease_bucket == true`
- target property cluster와 직접 연결됨
금지 조건:
- 원고채권 BO, 보증 BO, 대위변제 BO에 올리지 않는다
### 10.7 `non_deductible_attachment_claims`
허용 조건:
- `attachment_bucket == true`
- target property cluster와 직접 연결됨
### 10.8 `beneficiary_gain_support`
허용 조건:
- `beneficiary_bucket == true` 또는 (`property_bucket == true` 이고 처분 상대방/수익자 관련성이 직접 드러남)
- `beneficiary_candidates`가 주어진 경우 가능한 한 그 후보와 부합해야 한다
### 10.9 `plaintiff_claim_snapshots`
허용 조건:
- `preserved_claim_bucket == true`
- `preserved_claim_candidates`가 있는 경우 가능한 한 `bo_id`, `claim_type_candidate`, `creditor_candidate`, `debtor_candidate`와 부합하는 것 우선
금지 조건:
- `property_bucket`, `encumbrance_bucket`, `lease_bucket`, `attachment_bucket`만 있는 BO에는 올리지 않는다
### 10.10 `legal_meta_flags`
허용 조건:
- 동일 BO에서 위 10.2~10.9 중 적어도 하나의 실질 support field가 살아남았을 때만 보조적으로 유지 가능
- 또는 linked support role에 `법리메타`가 있고, BO가 case scope gate를 통과한 경우에만 보조적으로 유지 가능
---
## 11. field-level selection / merge 규칙
아래 규칙은 10장의 relevance 필터를 통과한 support들에만 적용한다.
### 11.1 `support_role_tags`
- surviving field를 실제로 뒷받침한 role tag만 uniq로 병합한다.
### 11.2 `property_value_at_close_of_arguments_support`
아래 우선순위로 가장 직접적인 1개를 선택한다.
1. `basis_type=="사실심변론종결직접"`
2. `basis_type=="현재시점대체"`
3. `basis_type=="감정기준일"`
4. `basis_type=="거래시점동일"`
5. 그 외 기본 우선순위 fallback
선택된 객체에서 null 필드는 더 낮은 우선순위 객체의 동일 필드로 보충할 수 있다.
단, `candidate_amount` 자체가 경쟁하면 더 높은 우선순위 값을 유지한다.
### 11.3 `encumbrances_at_fraudulent_act`
identity key:
- `kind + holder + rank`
병합 규칙:
- 같은 identity key면 높은 우선순위 원소를 base로 사용한다.
- lower-priority 원소는 base의 null 필드만 보충한다.
- `registered_max_amount`와 `actual_secured_debt_at_act`를 새로 계산하지 않는다.
- `deductible_in_value_compensation`이 경쟁하면 high-priority 값을 유지한다.
- `attachment_bucket`만 있는 BO에서는 `kind=="가압류"` 항목만 유지할 수 있다.
### 11.4 `encumbrances_at_close_of_arguments`
identity key:
- `kind + holder + rank`
병합 규칙:
- 같은 identity key면 높은 우선순위 원소를 base로 사용한다.
- `actual_outstanding_at_close`, `released_date`, `released_by_beneficiary`는 null 보충만 허용한다.
- `attachment_bucket`만 있는 BO에서는 `kind=="가압류"` 항목만 유지할 수 있다.
### 11.5 `lease_deposit_deductibility_support`
아래 우선순위로 가장 직접적인 1개를 선택한다.
1. 임대차계약, 주민등록, 인도, 확정일자 직접 문서에서 유래한 record
2. 등기, 처분, 승계문서에서 임대보증금 승계가 직접 보이는 record
3. 그 외 기본 우선순위 fallback
선택된 객체의 null 필드는 더 낮은 우선순위 객체로 보충할 수 있다.
단, `deductible_candidate`가 경쟁하면 더 직접적인 문서 값을 유지한다.
### 11.6 `non_deductible_attachment_claims`
identity key:
- `holder + kind + claim_amount`
병합 규칙:
- exact-match 중복 제거
- 같은 identity key에서 `attachment_non_deductible_flag`는 `true`가 하나라도 있으면 `true` 유지
- `reason`은 high-priority 값을 유지하고 null만 보충
### 11.7 `beneficiary_gain_support`
아래 우선순위로 primary object 1개를 선택한다.
1. 사해행위 목적물의 계약, 처분, 등기에서 유래한 record
2. 수익자가 실제 변제한 부담을 보여주는 영수증, 확인서 성격의 record
3. 그 외 기본 우선순위 fallback
선택된 객체의 null 필드는 lower-priority 동일 사실 객체로 보충할 수 있다.
단, `beneficiary_gain_estimate_candidate`는 계산하지 않고 입력값이 있을 때만 채운다.
### 11.8 `plaintiff_claim_snapshots`
identity key:
- `plaintiff_name + claim_type`
병합 규칙:
- 같은 identity key면 높은 우선순위 원소를 base로 사용한다.
- `principal_existing_at_fraudulent_act`, `secured_portion_at_fraudulent_act`, `unsecured_portion_at_fraudulent_act`, `contractual_interest_rate`, `default_interest_rate`, `interest_start_date`, `amount_as_of_close_of_arguments`는 null 보충만 허용한다.
- `proof_status`는 더 강한 값 우선
- `"direct"` > `"indirect"` > `"meeting_only"` > `"unknown"`
### 11.9 `legal_meta_flags`
스칼라 필드이므로 per-field first-non-null 규칙을 쓴다.
다만 아래 예외를 적용한다.
- `confidence_score`는 `"high"` > `"medium"` > `"low"` > `"unknown"` 우선
- `attachment_claim_deductible_candidate`는 명백한 가압류, 압류 support가 있으면 `false`를 우선 유지할 수 있다
- 실질 support field가 하나도 살아남지 않았으면 `legal_meta_flags` 전체를 버린다
---
## 12. pruning 규칙
### 12.1 필드 pruning
- 스칼라 키가 `null`이면 제거
- 배열 키가 `[]`이면 제거
- 객체 키의 내부가 모두 제거되면 그 객체 키도 제거
### 12.2 레코드 pruning
- pruning 이후 `actio_pauliana_support`가 비어 있으면 해당 BO 레코드는 출력하지 않는다.
### 12.3 proof_doc 유지
- surviving support item의 `proof_doc`는 입력값을 그대로 유지한다.
- `proof_doc`가 없으면 그 키는 제거한다.
---
## 13. fact_actio_support.json 스키마(필드명/enum 엄수; 추가 필드 금지)
최종 파일은 배열(Array)이다.
각 원소는 아래 구조를 따른다.
```ts
{
source_bo_id: string; // "bh1"
actio_pauliana_support: {
support_role_tags?: Array<"사해행위목적물"|"선순위담보"|"존속담보"|"임차권"|"가압류"|"원고채권"|"수익자이익"|"법리메타">;
fraudulent_act_date?: string;
property_value_at_close_of_arguments_support?: {
candidate_amount?: string;
candidate_date?: string;
basis_type?: "사실심변론종결직접"|"현재시점대체"|"감정기준일"|"거래시점동일"|"불명";
reason_text?: string;
confidence?: "high"|"medium"|"low"|"unknown";
proof_doc?: string;
};
encumbrances_at_fraudulent_act?: Array<{
kind?: "근저당"|"저당"|"가압류"|"전세권"|"임차보증금반환채권";
holder?: string;
rank?: number;
registered_max_amount?: string;
actual_secured_debt_at_act?: string;
supported_date?: string;
deductible_in_value_compensation?: boolean;
deductibility_reason?: string;
proof_doc?: string;
proof_strength?: "direct"|"indirect"|"meeting_only"|"unknown";
}>;
encumbrances_at_close_of_arguments?: Array<{
kind?: "근저당"|"저당"|"가압류"|"전세권"|"임차보증금반환채권";
holder?: string;
rank?: number;
actual_outstanding_at_close?: string;
released_date?: string;
released_by_beneficiary?: boolean;
proof_doc?: string;
proof_strength?: "direct"|"indirect"|"meeting_only"|"unknown";
}>;
lease_deposit_deductibility_support?: {
tenant_name?: string;
lease_deposit_amount?: string;
has_possession?: boolean;
has_resident_registration?: boolean;
has_fixed_date?: boolean;
senior_security_exists?: boolean;
deductible_candidate?: boolean;
deductibility_reason?: string;
proof_doc?: string;
proof_strength?: "direct"|"indirect"|"meeting_only"|"unknown";
};
non_deductible_attachment_claims?: Array<{
holder?: string;
kind?: "가압류"|"압류";
claim_amount?: string;
attachment_non_deductible_flag?: boolean;
reason?: string;
proof_doc?: string;
proof_strength?: "direct"|"indirect"|"meeting_only"|"unknown";
}>;
beneficiary_gain_support?: {
beneficiary_name?: string;
nominal_transfer_value?: string;
beneficiary_preexisting_claim_amount?: string;
assumed_debt_amount?: string;
released_encumbrance_amount?: string;
beneficiary_gain_estimate_candidate?: string;
estimation_basis?: string;
proof_doc?: string;
confidence?: "high"|"medium"|"low"|"unknown";
};
plaintiff_claim_snapshots?: Array<{
plaintiff_name?: string;
claim_type?: "loan"|"guarantee"|"reimbursement";
principal_existing_at_fraudulent_act?: string;
secured_portion_at_fraudulent_act?: string;
unsecured_portion_at_fraudulent_act?: string;
contractual_interest_rate?: string;
default_interest_rate?: string;
interest_start_date?: string;
amount_as_of_close_of_arguments?: string;
proof_status?: "direct"|"indirect"|"meeting_only"|"unknown";
proof_doc?: string;
}>;
legal_meta_flags?: {
is_actio_pauliana_relevant_candidate?: boolean;
restoration_mode_candidate?: "원물반환"|"말소등기"|"가액배상";
is_value_compensation_exception_triggered_candidate?: boolean;
lease_deposit_deductible_candidate?: boolean;
attachment_claim_deductible_candidate?: boolean;
remaining_joint_collateral_value_estimate_candidate?: string;
beneficiary_gain_estimate_candidate?: string;
confidence_score?: "high"|"medium"|"low"|"unknown";
};
};
}
```
추가 규칙:
- 최종 배열이 비어 있을 수 있다. 이 경우 `[]`를 저장한다.
- 레코드에는 `source_bo_id`와 `actio_pauliana_support` 외 다른 최상위 키를 넣지 않는다.
- `evidence_index`, `title`, `doc_type`, `key_*`, base `legal_calculation_object`, case-level signals를 반복 저장하지 않는다.
---
## 14. 최종 실행 지시
1. `BO.json`, `evidence_actio_support.json`, `actio_case_signals.json`을 읽는다.
2. 사건 수준 gate가 닫혀 있으면 원칙적으로 `[]`를 저장한다.
3. gate가 열려 있으면 `BO.json` 배열 순서대로 각 BO의 linked support를 식별한다.
4. 각 BO에 대해 case scope anchors를 우선 적용하여 scope bucket을 판정한다.
5. field별 relevance 필터를 적용하여 허용되는 support만 남긴다.
6. field-level merge 규칙에 따라 BO 단위 normalized `actio_pauliana_support`를 만든다.
7. pruning 이후 support가 남는 BO만 `source_bo_id` 기준 sparse output 배열에 포함한다.
8. `write_file(overwrite=true)`로 `fact_actio_support.json`을 저장한다.
9. 즉시 `list_docs`로 파일 존재를 검증한다. 누락 시 재시도 1회.
10. `fact_actio_support.json`은 절대 재읽지 않는다.
task_procedure:
IN:
nexts: ["Task_A", "Task_B1"]
wait_until: []
Task_A:
nexts: ["OUT"]
wait_until: ["IN"]
Task_B1:
nexts: ["Task_B2"]
wait_until: ["IN"]
Task_B2:
nexts: ["Task_C"]
wait_until: ["Task_B1"]
Task_C:
nexts: ["Task_B3", "Task_D1"]
wait_until: ["Task_B1", "Task_B2"]
Task_B3:
nexts: ["Task_D2"]
wait_until: ["Task_B1", "Task_C"]
Task_D1:
nexts: ["OUT"]
wait_until: ["Task_B1", "Task_C"]
Task_D2:
nexts: ["OUT"]
wait_until: ["Task_B3", "Task_C"]
OUT:
nexts: []
wait_until: ["Task_D1", "Task_D2"]
prevs: [ ]
nexts: [stage_2_청구권개요서작성]
- name: stage2_청구개요서작성
description: 청구권 개요서 작성
llm_provider: openai
llm_model: gpt-4o-2024-08-06
tools:
mcpServers:
localdocs:
type: streamable-http
url: "http://mcp-localdocs:8012/mcp"
description: Get the content of local documents
code-executor:
type: streamable-http
url: https://code-executor.mcp.eroomai.com/mcp
description: Run scripts of programming languages
headers:
Authorization: Bearer rR8OXqWrVZA1gFEo8oWfBkw2XgpWoGrrspw5ObsxTCM=
tasks:
- task_name: Task_A
llm_provider: anthropic
llm_model: claude-opus-4-5
prompts:
- role: user
content: |
## 실행방법(체크리스트)
[ ] 1) Preflight: `list_docs`사용해서 BO.json, Fact_Ledger.json, client_goal.json, Default_Agent/적격피고자제외조건.md, evidence_contradictions.json(존재할 때만), Default_Agent/사해행위취소청구_청구고려사항.md 존재 확인, `read_docs`사용해서 BO.json, Fact_Ledger.json, client_goal.json, 적격피고자제외조건.md, evidence_contradictions.json(존재할 때만), 사해행위취소청구_청구고려사항.md 읽기
[ ] 2) `write_file`사용해서 `stage2_1_claims_evaluated.json` 생성
[ ] 3) `list_docs` 사용해서 `stage2_1_claims_evaluated.json` 확인
[ ] 4) "청구권 및 당사자 식별 완료" 출력하고 작업을 종료(terminate)
---
## 0. Role / Output
- 당신은 **대한민국 민사·상사 소송 전문 변호사이자 MCP 에이전트**다. 입력 파일을 근거로 원고가 청구할 수 있는 청구권을 도출하고, 당사자(원고/피고)를 확정한다.
- Output: stage2_1_claims_evaluated.json
## 1. 불가침 원칙(Non-negotiables)
1) Hallucination 금지: 입력에 없는 사실은 추가하지 않는다.
2) 불명확하면 **null / "불명" / "[증거공백]"** 으로 명시한다
3) 입력 원문 **대량 혹은 장문 복사 금지**
4) 필수 입력 누락/빈 파일/파싱불가 시 **즉시 중단**(파일 출력 금지)하고, 누락 항목을 채팅으로만 보고한다.
5) **중간 결과(JSON 덤프, DB 결과 덤프)를 채팅에 출력하지 말 것**
## 2. 에러 처리 정책 (운영 규칙)
- `read_docs` 반환이 `"Error:"`로 시작하면 **동일 doc_name으로 1회만 재시도**.
- 재시도 실패 시: 해당 입력은 **스킵하고 진행**, 대신 `VALIDATION WARNING`에 기록.
- 무한 재시도 금지.
## 3. 불명확성 처리(결정론)
기산점/금액/당사자 귀속/증거 연결이 불명확하면:
- 해당 평가요소는 **Medium**으로 둔다.
- 다음 형식으로 1줄 경고를 남긴다:
`VALIDATION WARNING: <요소명> 산정 불가 - <사유 1줄>`
## 4. 입력 파일
### 4.1 시작 시 반드시 읽는 파일
- `BO.json`
- `client_goal.json`
- `Fact_Ledger.json`
- `Default_Agent/적격피고자제외조건.md`
- `Default_Agent/사해행위취소청구_청구고려사항.md`
### 4.2 존재하면 읽는 파일
- `evidence_contradictions.json`
- 존재하면 읽고, **해당 모순이 핵심 사실(금액/일자/당사자)과 직접 충돌**하는 청구의 승소가능성 상한을 **Medium**으로 제한하고 WARNING에 기록.
## 5. 사건 개요 + 청구권 후보 도출 + 스코어링 + 당사자
### 5.1 사건 개요
- `원고 목표`: client_goal.json의 "primary_goal" 원문
- `사건 핵심 요약`: client_goal.json의 "summary_key_incidents" 원문
- `핵심 사실`: client_goal.json의 "key_facts" 원문
### 5.1 청구권 후보 생성(상한 12개)
- BO의 `Legal_Keywords`와 `ActionType`을 활용해 **쟁점 클러스터**를 만든 뒤, 클러스터별로 청구권 후보를 만든다.
- **쟁점 클러스터** 구성 시 청구권 후보가 "사해행위취소" 청구에 해당할 경우, `사해행위취소청구_청구고려사항.md` 파일이 제시하는 기준을 참조하여 청구권 후보를 세부적으로 도출한다.
- 후보 총량은 **최대 12개**.
- 하드 프리필터:
- 각 후보 청구는 최소 1개 이상의 근거를 반드시 가진다: `fact_id` 또는 `evidence_index` 중 하나 이상.
- 근거가 0이면 후보에서 제외.
각 청구권은 내부적으로 다음 필드를 가진다(채팅 출력 금지):
- `claim_id` (C-001, C-002 …)
- `claim_title`
- `relief_summary` (청구취지 1줄) // 특히 사해행위취소청구 후보의 경우 `사해행위취소청구_청구고려사항.md`의 기준을 반드시 적용하여 청구취지 작성: "취소 범위 특정", "제척기간 식별", "저당권이 설정된 부동산과 가액배상 법리" 기준은 매우 엄격하게 적용
- `cause_summary` (청구원인 2문장 이내) // "relief_summary"와 연계하여 청구원인 작성
- `legal_basis_hint` (예: contract/loan, guarantee, reimbursement, unjust enrichment, tort, actio pauliana 등)
- `source_bo_ids` (≤6)
- `source_fact_ids` (≤6)
- `key_evidence_indexes` (≤6)
### 5.2 등급 평가(High/Medium/Low) 및 청구권 순서
평가축 4개(각 1~3점, 총 12점). 불명확하면 Medium + WARNING.
1) 승소가능성
- Fact_Ledger의 credibility + BO.Evidence의 직접성(직접/간접) 중심.
- **evidence_contradictions.json**에서 금액/일자/당사자 모순이 “핵심”이면 승소가능성 상한을 **Medium**으로 제한 + WARNING.
2) 집행가능성
- 자산 단서 2개 이상: High / 1개: Medium / 없음: Low
- **무자력만으로 자동 제외 금지**. 대신 `EXECUTION_RISK: HIGH` 태깅.
3) 경제성(일반성 강화: 금액/비금전 중요도/보전 필요성)
- 금전청구: ≥1억 High / 5천만~1억 Medium / <5천만 Low
- 비금전/형성/금지·보전 관련:
- High: 권리 중요도·긴급성 높거나 보전 필요성이 높음
- Medium: 불명확(기본값)
- Low: 중요도 낮고 대체수단 존재
4) 시효 긴급도
- 잔여 6개월 이내 High / 1년 이내 Medium / 1년 초과 Low
- 기산점 불명확: Medium + WARNING
5) 청구권(claim_id) 배열 순서:
- 시간 순서: 식별한 청구권(claim_id)에 대응되는 Fact_Ledger의 fact_id와 date을 우선하여 시간 순서상 앞서는 청구권 우선 배치
- 동일 날짜 청구권은 합산점수 높은 순으로 배치
- 절대로 합산점수 순서대로 claim_id를 배치하지 않는다.
- 합산 점수가 5점 이상인 청구권과 5점 미만인 청구권 구별
- 5점 이상은 "청구권"으로 5점 미만은 "기타청구권"으로 분리
### 5.3 원고/피고 결정
* 원고:
- `client_goal.json`의 parties.plaintiffs 기반.
- 권리귀속이 불명확하면 “후보”로 표시하고 사유 1줄.
* 피고:
1) BO의 Performer/Subject/Object 및 client_goal constraints에서 피고 pool 구성
2) `적격피고자제외조건.md`를 적용:
- 제외조건 해당: 제외 또는 “대체 필요”
- 무자력만으로 자동 제외 금지(집행리스크 태깅)
3) client_goal.json의 `constraints`를 적용하여 `소제기`의 실익이 없는 피고를 필터링하여 제외
4) 청구권별로 피고 매핑
## 6. JSON Schema for `stage2_1_claims_evaluated.json`
### 6.1 Schema
{
"title": "Stage 2.1 Claims Evaluated Output",
"description": "Task A: 청구권 후보 도출, 평가 및 당사자 확정 결과",
"type": "object",
"properties": {
"parties": {
"type": "object",
"description": "당사자(원고/피고) 확정 및 적격성/리스크 평가 (전체 풀)",
"properties": {
"plaintiffs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"status": { "type": "string", "enum": ["확정", "후보", "불명"] },
"reason": { "type": ["string", "null"], "description": "권리귀속 불명확 등으로 '후보'인 경우 사유 1줄" }
},
"required": ["name", "status"]
}
},
"defendants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"status": { "type": "string", "enum": ["적격", "제외", "대체 필요", "불명"] },
"execution_risk": { "type": ["string", "null"], "enum": ["HIGH", "MEDIUM", "LOW", null], "description": "무자력인 경우 자동 제외 대신 'HIGH' 태깅" },
"reason": { "type": ["string", "null"], "description": "상태 결정 및 집행리스크 사유 1줄" }
},
"required": ["name", "status"]
}
}
},
"required": ["plaintiffs", "defendants"]
},
"claims": {
"type": "array",
"description": "총점 5점 이상인 '청구권' 목록 (합산점수 내림차순 정렬)",
"maxItems": 12,
"items": { "$ref": "#/definitions/Claim" }
},
"other_claims": {
"type": "array",
"description": "총점 5점 미만인 '기타청구권' 목록 (합산점수 내림차순 정렬)",
"maxItems": 12,
"items": { "$ref": "#/definitions/Claim" }
},
"validation_warnings": {
"type": "array",
"description": "불명확성, 증거공백, 모순점 충돌에 따른 전역 경고 메시지 목록 ('VALIDATION WARNING: ...' 형식)",
"items": { "type": "string", "pattern": "^VALIDATION WARNING:" }
}
},
"required": ["parties", "claims", "other_claims", "validation_warnings"],
"definitions": {
"Claim": {
"type": "object",
"properties": {
"claim_id": { "type": "string", "description": "예: C-001" },
"claim_title": { "type": "string", "description": "청구권 명칭" },
"relief_summary": { "type": "string", "description": "청구취지 1줄" },
"cause_summary": { "type": "string", "description": "청구원인 2문장 이내" },
"legal_basis_hint": { "type": "string", "description": "예: contract/loan, guarantee, reimbursement, unjust enrichment, tort, actio pauliana 등" },
"source_bo_ids": { "type": "array", "items": { "type": "string" }, "maxItems": 6 },
"source_fact_ids": { "type": "array", "items": { "type": "string" }, "maxItems": 6 },
"key_evidence_indexes": { "type": "array", "items": { "type": "string" }, "maxItems": 6 },
"mapped_parties": {
"type": "object",
"description": "해당 청구권에 매핑된 당사자 이름 목록",
"properties": {
"plaintiffs": { "type": "array", "items": { "type": "string" } },
"defendants": { "type": "array", "items": { "type": "string" } }
},
"required": ["plaintiffs", "defendants"]
},
"evaluation": {
"type": "object",
"description": "등급 평가 (각 1~3점, 총 12점 만점)",
"properties": {
"win_probability": { "$ref": "#/definitions/EvaluationMetric" },
"execution_feasibility": { "$ref": "#/definitions/EvaluationMetric" },
"economic_value": { "$ref": "#/definitions/EvaluationMetric" },
"statute_of_limitations_urgency": { "$ref": "#/definitions/EvaluationMetric" },
"total_score": { "type": "integer", "minimum": 1, "maximum": 12 }
},
"required": ["win_probability", "execution_feasibility", "economic_value", "statute_of_limitations_urgency", "total_score"]
}
},
"required": [
"claim_id", "claim_title", "relief_summary", "legal_basis_hint",
"source_bo_ids", "source_fact_ids", "key_evidence_indexes",
"mapped_parties", "evaluation"
]
},
"EvaluationMetric": {
"type": "object",
"properties": {
"grade": { "type": "string", "enum": ["High", "Medium", "Low", "불명", "[증거공백]"] },
"score": { "type": "integer", "minimum": 1, "maximum": 3 },
"reason": { "type": ["string", "null"], "description": "점수 산정 사유 1줄" }
},
"required": ["grade", "score", "reason"]
}
}
}
### 6.2 Example (JSON Mock-up)
{
"overall_claim_content": {
"primary_goal": [
],
"summary_key_incidents": [
]
}
"parties": {
"plaintiffs": [
{
"name": "우방캐피탈 주식회사",
"status": "확정",
"reason": null
}
],
"defendants": [
{
"name": "주식회사 개성금속",
"status": "적격",
"execution_risk": "HIGH",
"reason": "현재 폐업 상태로 집행 가능 재산이 없으나 무자력 자동 제외 금지 원칙 적용"
},
{
"name": "김수경",
"status": "적격",
"execution_risk": "MEDIUM",
"reason": "연대보증인, 본인 명의 재산 없으나 부모로부터 상속 가능성 존재"
}
]
},
"claims": [
{
"claim_id": "C-001",
"claim_title": "대여금 반환 청구",
"relief_summary": "피고 개성금속은 원고 우방캐피탈에게 10억 원 및 지연손해금을 지급하라.",
"cause_summary": "...", // '청구원인'에 대응하는 청구원인 2문장 이내 작성
"legal_basis_hint": "contract/loan",
"source_bo_ids": ["bh1", "bh8"],
"source_fact_ids": ["F-001", "F-008"],
"key_evidence_indexes": ["E-012", "E-008"],
"mapped_parties": {
"plaintiffs": ["우방캐피탈 주식회사"],
"defendants": ["주식회사 개성금속"]
},
"evaluation": {
"win_probability": { "grade": "High", "score": 3, "reason": "대출약정서 및 부도 사실 등 직접 증거 존재" },
"execution_feasibility": { "grade": "Low", "score": 1, "reason": "개성금속 자산 단서 없음" },
"economic_value": { "grade": "High", "score": 3, "reason": "금전청구 1억 원 이상" },
"statute_of_limitations_urgency": { "grade": "High", "score": 3, "reason": "상사채권 소멸시효(5년) 완성 임박" },
"total_score": 10
}
},
{
"claim_id": "C-002",
"claim_title": "연대보증채무 이행 청구",
"relief_summary": "피고 김수경은 피고 개성금속과 연대하여 10억 원을 지급하라.",
"cause_summary": "...", // '청구원인'에 대응하는 청구원인 2문장 이내 작성
"legal_basis_hint": "guarantee",
"source_bo_ids": ["bh2"],
"source_fact_ids": ["F-002"],
"key_evidence_indexes": ["E-012"],
"mapped_parties": {
"plaintiffs": ["우방캐피탈 주식회사"],
"defendants": ["김수경"]
},
"evaluation": {
"win_probability": { "grade": "Medium", "score": 2, "reason": "모순점(C-001) 발견 - 근저당 설정일자 불일치로 승소가능성 상한 제한" },
"execution_feasibility": { "grade": "Medium", "score": 2, "reason": "상속을 통한 자산 취득 가능성 단서 존재" },
"economic_value": { "grade": "High", "score": 3, "reason": "금전청구 1억 원 이상" },
"statute_of_limitations_urgency": { "grade": "High", "score": 3, "reason": "상사채권 소멸시효 완성 임박" },
"total_score": 10
}
}
],
"other_claims": [
{
"claim_id": "C-003",
"claim_title": "부당이득 반환 청구",
"relief_summary": "피고 유재석은 원고에게 4억 원을 지급하라.",
"cause_summary": "...", // '청구원인'에 대응하는 청구원인 2문장 이내 작성
"legal_basis_hint": "unjust enrichment",
"source_bo_ids": ["bh11"],
"source_fact_ids": ["F-011"],
"key_evidence_indexes": ["E-014"],
"mapped_parties": {
"plaintiffs": ["우방캐피탈 주식회사"],
"defendants": ["유재석"]
},
"evaluation": {
"win_probability": { "grade": "Medium", "score": 2, "reason": "대물변제로 인한 부당이득 성립 불명확" },
"execution_feasibility": { "grade": "Low", "score": 1, "reason": "자산 보유 여부 단서 없음" },
"economic_value": { "grade": "Low", "score": 1, "reason": "핵심 청구 대비 우선순위 낮음" },
"statute_of_limitations_urgency": { "grade": "Low", "score": 1, "reason": "시효 1년 초과 잔여" },
"total_score": 5
}
}
],
"validation_warnings": [
"VALIDATION WARNING: F-006 금액 산정 불가 - 증거공백으로 인해 이자 변제액 확인 불가",
"VALIDATION WARNING: C-002 승소가능성 산정 상한 제한 - evidence_contradictions.json의 핵심 날짜(근저당 설정일) 모순과 충돌함"
]
}
- task_name: Task_B
llm_provider: anthropic
llm_model: claude-opus-4-5
prompts:
- role: user
content: |
## 실행방법(체크리스트)
[ ] 1) Preflight: `list_docs`사용해서 stage2_1_claims_evaluated.json, Default_Agent 폴더 내 case_kinds.md 존재 확인, `read_docs`사용해서 stage2_1_claims_evaluated.json, case_kinds.md 읽기
[ ] 2) `write_file`사용해서 `stage2_2_claims_taxonomy.json` 생성
[ ] 3) `list_doc` 사용해서 `stage2_2_claims_taxonomy.json` 확인
[ ] 4) "청구권별 사건 종류 식별 완료" 출력하고 작업을 종료(terminate)
---
## 0. Role / Output
- 당신은 **대한민국 민사·상사 소송 전문 변호사이자 MCP 에이전트**다. 입력 파일을 근거로 원고의 청구권 특징을 파악하여 사건 종류를 식별한다.
- Output: stage2_2_claims_taxonomy.json
## 1. 불가침 원칙(Non-negotiables)
1) Hallucination 금지: 입력에 없는 사실은 추가하지 않는다.
2) 불명확하면 **null / "불명" / "[증거공백]"** 으로 명시한다
3) 입력 원문 **대량 혹은 장문 복사 금지**
4) 필수 입력 누락/빈 파일/파싱불가 시 **즉시 중단**(파일 출력 금지)하고, 누락 항목을 채팅으로만 보고한다.
5) **중간 결과(JSON 덤프, DB 결과 덤프)를 채팅에 출력하지 말 것**
## 2. 에러 처리 정책 (운영 규칙)
- `read_docs` 반환이 `"Error:"`로 시작하면 **동일 doc_name으로 1회만 재시도**.
- 재시도 실패 시: 해당 입력은 **스킵하고 진행**, 대신 `VALIDATION WARNING`에 기록.
## 3. 입력 파일
- `stage2_1_claims_evaluated.json`
- `Default_Agent/case_kinds.md`
## 4. 청구권별 사건 종류 식별
### 4.1 식별 방법
1. 평가된 청구권들의 키워드를 분석해 청구권을 분류한다:
- case_kinds.md 파일이 제시하는 소송대분류(이행/형성/확인/가사)를 1차 확정한다.
- 소송대분류에 해당하는 "분쟁 유형"을 2차 확정한다.
- 분쟁 유형에 해당하는 "사건 종류"의 항목이 존재하면 그것을 선택
- "사건 종류"가 비어 있으면(empty) "분쟁 유형" 내용을 선택
2. 확정된 대분류에 해당하는 분쟁유형과 사건종류를 **조사나 띄어쓰기 변형 없이 100% 동일한 문자열(Exact-match)**로 복사하여 라벨링한다.
- "사건 종류" 항목이 존재하면 그것을 우선 선택, 만일 "사건 종류" 항목이 존재하지 않으면(empty) "분쟁 유형" 내용을 선택
### 4.2 청구(claim)별 exact-match
각 claim에 대해:
- (소송대분류, 분쟁유형, 사건종류)를 case_kinds.md에서 **exact-match**로 복사.
- 확신이 없으면 back-off:
- 사건종류 불명확 → 사건종류 null, 분쟁유형까지만
- 분쟁유형도 불명확 → 소송대분류까지만
## 5. JSON Schema for `stage2_2_claims_taxonomy.json`
### 5.1 Schema
{
"title": "Stage 2.2 Claims Taxonomy Output",
"description": "Task B: 청구권별 사건 종류 Exact-Match 식별 및 분류 결과",
"type": "object",
"properties": {
"claims_taxonomy": {
"type": "array",
"description": "Task A에서 식별된 각 청구권(claim_id)에 대한 분류표(case_kinds.md) 매핑 결과",
"items": {
"type": "object",
"properties": {
"claim_id": {
"type": "string",
"description": "Task A에서 부여된 청구권 식별자 (예: C-001)"
},
"macro_category": {
"type": ["string", "null"],
"enum": ["이행의 소", "확인의 소", "형성의 소", "가사소송", "가족관계등록", "행정소송", null],
"description": "1차 확정: 소송 대분류. 매핑할 수 없는 경우 null."
},
"dispute_type": {
"type": ["string", "null"],
"description": "2차 확정: 분쟁 유형. 표에서 빈칸이거나(예: 형성의 소), 확신이 없어 Back-off 한 경우 null."
},
"case_kind": {
"type": ["string", "null"],
"description": "3차 확정: 사건 종류. 표에서 빈칸이면 프롬프트 지시에 따라 '분쟁 유형'의 문자열을 동일하게 복사. 불명확하여 Back-off 한 경우 null."
},
"is_backoff_applied": {
"type": "boolean",
"description": "확신이 부족하여 하위 분류(사건종류 또는 분쟁유형)를 포기하고 상위 분류만 확정하는 Back-off 전략을 사용했는지 여부"
},
"reason": {
"type": "string",
"description": "분류를 선택한 근거, 빈칸 대체 사유, 또는 Back-off 적용 사유 (1~2줄)"
}
},
"required": [
"claim_id",
"macro_category",
"dispute_type",
"case_kind",
"is_backoff_applied",
"reason"
]
}
},
"validation_warnings": {
"type": "array",
"description": "분류 실패, 빈칸 대체, Back-off 발생 등 파이프라인 진행 중 기록할 전역 경고 메시지 목록",
"items": {
"type": "string",
"pattern": "^VALIDATION WARNING:"
}
}
},
"required": [
"claims_taxonomy",
"validation_warnings"
]
}
### 5.2 Example (JSON Mock-up)
{
"claims_taxonomy": [
{
"claim_id": "C-001",
"macro_category": "이행의 소",
"dispute_type": "금전의 지급을 구하는 소",
"case_kind": "대여금 청구",
"is_backoff_applied": false,
"reason": "금전 반환 목적의 소비대차이므로 '대여금 청구'와 100% exact-match 매핑함."
},
{
"claim_id": "C-002",
"macro_category": "이행의 소",
"dispute_type": "종류물(대체물)의 지급 또는 인도를 구하는 소",
"case_kind": "종류물(대체물)의 지급 또는 인도를 구하는 소",
"is_backoff_applied": false,
"reason": "표에서 '사건 종류' 항목이 비어있으므로(empty) 프롬프트 지시사항 4.1에 따라 '분쟁 유형' 텍스트를 사건 종류에 동일하게 복사함."
},
{
"claim_id": "C-003",
"macro_category": "형성의 소",
"dispute_type": null,
"case_kind": "사해행위취소 청구",
"is_backoff_applied": false,
"reason": "표에서 '형성의 소'는 '분쟁 유형' 항목이 비어있으므로 null 처리하고, 사건 종류는 exact-match로 매칭함."
},
{
"claim_id": "C-004",
"macro_category": "확인의 소",
"dispute_type": "물권에 관한 확인을 구하는 소",
"case_kind": null,
"is_backoff_applied": true,
"reason": "물권 확인 청구이나 표에 제시된 세부 사건종류(도메인/입목 등)와 정확히 일치하지 않아 분쟁유형까지만 확정하고 사건종류는 null로 Back-off함."
}
],
"validation_warnings": [
"VALIDATION WARNING: C-002 사건 종류가 비어 있어 분쟁 유형 내용을 복사하여 대체함.",
"VALIDATION WARNING: C-004 사건종류 확정 불가 - 정확히 일치하는 사건종류가 없어 분쟁유형까지만 매핑하는 Back-off 전략 수행함."
]
}
- task_name: Task_C
llm_provider: anthropic
llm_model: claude-haiku-4-5
prompts:
- role: user
content: |
## 실행방법(체크리스트)
[ ] 1) Preflight: `list_doc`사용해서 stage2_2_claims_taxonomy.json, Default_Agent 폴더 내 객관적병합요건기준.md 존재 확인, `read_docs`사용해서 stage2_2_claims_taxonomy.json, 객관적병합요건기준.md 읽기
[ ] 2) `write_file`사용해서 `stage2_3_consolidation_strategy.json` 생성
[ ] 3) `list_doc` 사용해서 `stage2_3_consolidation_strategy.json` 확인
[ ] 4) "청구권별 사건 종류 식별 완료" 출력하고 작업을 종료(terminate)
---
## 0. Role / Output
- 당신은 **대한민국 민사·상사 소송 전문 변호사이자 MCP 에이전트**다. 입력 파일을 근거로 원고의 청구권들을 병합청구할지 단독청구할지 선택한다.
- Output: stage2_3_consolidation_strategy.json
## 1. 불가침 원칙(Non-negotiables)
1) Hallucination 금지: 입력에 없는 사실은 추가하지 않는다.
2) 불명확하면 **null / "불명" / "[증거공백]"** 으로 명시한다
3) 입력 원문 **대량 혹은 장문 복사 금지**
4) 필수 입력 누락/빈 파일/파싱불가 시 **즉시 중단**(파일 출력 금지)하고, 누락 항목을 채팅으로만 보고한다.
5) **중간 결과(JSON 덤프, DB 결과 덤프)를 채팅에 출력하지 말 것**
## 2. 에러 처리 정책 (운영 규칙)
- `read_docs` 반환이 `"Error:"`로 시작하면 **동일 doc_name으로 1회만 재시도**.
- 재시도 실패 시: 해당 입력은 **스킵하고 진행**, 대신 `VALIDATION WARNING`에 기록.
## 3. 입력 파일
- stage2_2_claims_taxonomy.json
- 객관적병합요건기준.md
## 4. 청구권 병합 기준 식별
- 청구권(claim_id)이 1개만 존재할 경우 단독 청구로 결정
- 청구권이 2개 이상일 경우,
- 기본적으로는 모든 청구권들을 병합(단순병합)하는 것을 최우선 전략으로 취한다.
- 추가적으로 객관적병합요건기준.md가 제시하는 기준에 근거하여
- 청구권 간의 양립 불가성(택일), 주위/예비 관계, 동일 피고 및 사실관계 공유 여부를 분석하고,
- 단순병합 / 선택적 병합 / 예비적 병합 / 분리로 그룹핑(Grouping)을 설계하고, 논리적 근거(Rationale)를 2~3문장으로 작성한다.
## 5. JSON Schema for `stage2_3_consolidation_strategy.json`
### 5.1 Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Stage 2.3 Consolidation Strategy Output",
"description": "Task C: 청구권 병합청구/단독청구/분리 방식 및 그룹핑 설계 결과",
"type": "object",
"properties": {
"consolidation_groups": {
"type": "array",
"description": "하나의 소장(소송) 단위로 묶여 제출될 청구권 그룹의 목록. 병합할 수 없어 '분리'되는 청구권은 별도의 그룹 객체로 파생됨.",
"items": {
"type": "object",
"properties": {
"group_id": {
"type": "string",
"description": "소송 그룹 식별자 (예: G-01, G-02)"
},
"structure_type": {
"type": "string",
"enum": ["단독", "단순병합", "선택적 병합", "예비적 병합", "분리"],
"description": "전체 청구권이 1개뿐이면 '단독', 2개 이상의 청구가 묶이면 병합 종류 명시. 무관한 청구를 별소 제기하는 경우 '분리' 선택."
},
"analysis": {
"type": ["object", "null"],
"description": "구조 결정을 위한 법리적 사전 분석(Chain of Thought). 전체 청구권이 1개라서 '단독'인 경우는 null 처리.",
"properties": {
"is_same_defendant": { "type": "boolean", "description": "동일 피고에 대한 청구인가?" },
"share_core_facts": { "type": "boolean", "description": "동일한 핵심 사실관계나 거래를 공유하는가?" },
"is_incompatible": { "type": "boolean", "description": "청구권 간에 논리적으로 양립할 수 없는(택일적) 관계인가?" },
"is_primary_alternative": { "type": "boolean", "description": "순서대로 판단을 구하는 주위적/예비적 관계가 존재하는가?" }
},
"required": ["is_same_defendant", "share_core_facts", "is_incompatible", "is_primary_alternative"]
},
"claims": {
"type": "array",
"description": "해당 소송 그룹에 포함된 청구권(claim_id) 목록 및 역할",
"items": {
"type": "object",
"properties": {
"claim_id": {
"type": "string",
"description": "Task A에서 식별된 청구권 ID (예: C-001)"
},
"role": {
"type": "string",
"enum": ["단독 청구", "대등한 청구", "주위적 청구", "예비적 청구"],
"description": "병합 구조 내 해당 청구권의 지위 (단순/선택적 병합은 '대등한 청구', 예비적 병합은 '주위적/예비적', 단독/분리는 '단독 청구')"
}
},
"required": ["claim_id", "role"]
},
"minItems": 1
},
"rationale": {
"type": "string",
"description": "객관적병합요건기준.md 파일과 analysis 결과에 근거하여 해당 그룹핑을 선택한 논리적 근거 (반드시 2~3문장으로 압축하여 작성)"
}
},
"required": ["group_id", "structure_type", "analysis", "claims", "rationale"]
},
"minItems": 1
},
"validation_warnings": {
"type": "array",
"description": "에러 처리 정책에 따른 파일 읽기 실패, 기준 모호 등 파이프라인 진행 중 발생한 전역 경고 메시지",
"items": {
"type": "string",
"pattern": "^VALIDATION WARNING:"
}
}
},
"required": ["consolidation_groups", "validation_warnings"]
}
### 5.2 Example (JSON Mock-up)
{
"consolidation_groups": [
{
"group_id": "G-01",
"structure_type": "단순병합",
"analysis": {
"is_same_defendant": true,
"share_core_facts": true,
"is_incompatible": false,
"is_primary_alternative": false
},
"claims": [
{ "claim_id": "C-001", "role": "대등한 청구" },
{ "claim_id": "C-002", "role": "대등한 청구" }
],
"rationale": "C-001(대여금 원금 청구)과 C-002(지연손해금 청구)는 동일 피고에 대한 동일한 소비대차 거래 사실관계를 원인으로 합니다. 두 청구권은 논리적으로 상호 배척되지 않고 양립이 가능하므로, 소송경제를 도모하기 위해 하나의 소장으로 단순병합하여 제기하는 것이 타당합니다."
},
{
"group_id": "G-02",
"structure_type": "예비적 병합",
"analysis": {
"is_same_defendant": true,
"share_core_facts": true,
"is_incompatible": true,
"is_primary_alternative": true
},
"claims": [
{ "claim_id": "C-003", "role": "주위적 청구" },
{ "claim_id": "C-004", "role": "예비적 청구" }
],
"rationale": "C-003(사해행위취소 및 원물반환)과 C-004(가액배상)는 동일 피고를 상대로 하나 그 반환 방법이 논리적으로 양립할 수 없는 관계입니다. 따라서 원고의 주된 목적인 원물반환을 주위적 청구로 구하고, 그것이 불가능하여 기각될 경우를 대비해 가액배상을 예비적 청구로 병합합니다."
},
{
"group_id": "G-03",
"structure_type": "분리",
"analysis": {
"is_same_defendant": false,
"share_core_facts": false,
"is_incompatible": false,
"is_primary_alternative": false
},
"claims": [
{ "claim_id": "C-005", "role": "단독 청구" }
],
"rationale": "C-005(별건 손해배상 청구)는 앞선 청구권들과 피고가 상이하며 기초가 되는 사실관계 및 쟁점의 공통성이 현저히 결여되어 있습니다. 무리하게 병합할 경우 관할 위반 및 절차 지연의 우려가 크므로 기존 사건에서 분리하여 별개의 단독 소송으로 제기합니다."
}
],
"validation_warnings": [
"VALIDATION WARNING: 객관적병합요건기준.md의 선택적 병합 기준이 모호하여, 양립 가능성이 의심되는 부분은 안전하게 '분리' 판정 기준으로 우회 적용함."
]
}
- task_name: Task_D
mcp: code-executor
tool_name: run_code
parameters:
language: python
requirements: |
httpx
network: "agent-network"
timeout: 120
code: |
#!/usr/bin/env python3
import json
import sys
import os
import httpx
from datetime import datetime
from typing import Any
# ═══════════════════════════════════════════════
# 0. MCP localdocs 헬퍼
# ═══════════════════════════════════════════════
LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp"
HEADERS = {
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream",
}
def _log(msg):
print(msg, file=sys.stderr)
def parse_sse(text):
for line in text.strip().split("\n"):
if line.startswith("data: "):
return json.loads(line[6:])
try:
return json.loads(text)
except Exception:
return None
def call_tool(c, name, arguments, msg_id=10):
r = c.post(LOCALDOCS_URL, json={
"jsonrpc": "2.0", "id": msg_id,
"method": "tools/call",
"params": {"name": name, "arguments": arguments}
}, headers=HEADERS)
result = parse_sse(r.text)
if result and "result" in result:
return result
_log(f"Tool {name} error: {json.dumps(result)[:300]}")
return result
def mcp_read_json(c, doc_name, msg_id=10):
"""read_docs로 JSON 파일 읽기 → dict/list 반환"""
result = call_tool(c, "read_docs", {"doc_names": [doc_name]}, msg_id)
if not result or "result" not in result:
return None
raw = result["result"]["content"][0]["text"]
if not raw or not raw.strip():
return None
try:
wrapper = json.loads(raw)
except json.JSONDecodeError:
return None
if isinstance(wrapper, dict) and "results" in wrapper:
for item in wrapper["results"]:
if "error" in item:
_log(f" read_docs({doc_name}): {item['error']}")
return None
content = item.get("content")
if content is None:
return None
if isinstance(content, (dict, list)):
return content
try:
return json.loads(content)
except (json.JSONDecodeError, TypeError):
_log(f" read_docs({doc_name}): JSON parse failed")
return None
return None
def mcp_write(c, path, content, msg_id=20):
"""write_file로 localdocs에 파일 저장"""
result = call_tool(c, "write_file", {
"path": path,
"content": content,
"overwrite": True,
}, msg_id)
if result and "result" in result:
_log(f" write_file({path}): OK")
return True
_log(f" write_file({path}): FAILED")
return False
def mcp_list_docs(c, doc_names, msg_id=30):
"""list_docs로 파일 존재 확인"""
result = call_tool(c, "list_docs", {"doc_names": doc_names}, msg_id)
return result
# ═══════════════════════════════════════════════
# 1. 등급 변환 유틸리티
# ═══════════════════════════════════════════════
def grade_to_korean(grade):
"""High/Medium/Low → 높음/중간/낮음"""
return {"High": "높음", "Medium": "중간", "Low": "낮음"}.get(grade, grade or "불명")
def grade_to_urgency(grade):
"""시효 평가: High→임박, Medium→여유, Low→도과"""
return {"High": "임박", "Medium": "여유", "Low": "도과"}.get(grade, grade or "불명")
def safe_str(val):
if val is None:
return ""
return str(val)
def list_to_str(lst):
"""리스트를 괄호 제거, comma separated string"""
if not lst:
return ""
return ", ".join(str(item) for item in lst)
# ═══════════════════════════════════════════════
# 2. 마크다운 섹션 생성 함수
# ═══════════════════════════════════════════════
def build_section1(claims_eval):
"""## 1. 사건 개요"""
overall = claims_eval.get("overall_claim_content", {})
primary_goal = overall.get("primary_goal", "(목표 미기재)")
incidents = overall.get("summary_key_incidents", [])
lines = []
lines.append("## 1. 사건 개요")
lines.append(f"* 원고 목표: {primary_goal}")
lines.append("* 핵심 내용:")
if isinstance(incidents, list):
for inc in incidents:
lines.append(f" - {inc}")
else:
lines.append(f" - {safe_str(incidents)}")
lines.append("")
return "\n".join(lines)
def build_section2(claims_list):
"""## 2. 청구권 요약 (가능성 높은 것만 = claims 5점 이상)"""
lines = []
lines.append("## 2. 청구권 요약 (가능성 높은 것만 선택)")
lines.append("")
lines.append("| 순위 | 청구 ID | 청구권 | 승소 가능성 | 집행 가능성 | 경제성 | 시효 평가 | 총점 |")
lines.append("|:---:|:---:|------|:---:|:---:|------|:---:|:---:|")
for rank, cl in enumerate(claims_list, 1):
ev = cl.get("evaluation", {})
win = grade_to_korean(ev.get("win_probability", {}).get("grade", ""))
exe = grade_to_korean(ev.get("execution_feasibility", {}).get("grade", ""))
econ_g = grade_to_korean(ev.get("economic_value", {}).get("grade", ""))
econ_r = ev.get("economic_value", {}).get("reason", "")
econ = f"{econ_g}, {econ_r}" if econ_r else econ_g
urg = grade_to_urgency(ev.get("statute_of_limitations_urgency", {}).get("grade", ""))
total = ev.get("total_score", "?")
lines.append(
f"| {rank} | {cl['claim_id']} | {cl['claim_title']} "
f"| {win} | {exe} | {econ} | {urg} | {total} |"
)
lines.append("")
return "\n".join(lines)
def build_section3(all_claims, parties_data):
"""## 3. 청구권 분석 — claim_id별 분석 표 stack"""
lines = []
lines.append("## 3. 청구권 분석")
lines.append("")
for idx, cl in enumerate(all_claims, 1):
cid = cl.get("claim_id", "?")
title = cl.get("claim_title", "?")
relief = cl.get("relief_summary", "")
cause = cl.get("cause_summary", "")
fact_ids = list_to_str(cl.get("source_fact_ids", []))
bo_ids = list_to_str(cl.get("source_bo_ids", []))
ev_ids = list_to_str(cl.get("key_evidence_indexes", []))
# 리스크/점검내용: mapped_parties에 속하는 원고/피고의 reason 수집
risk_items = []
mapped = cl.get("mapped_parties", {})
mapped_pl = mapped.get("plaintiffs", [])
mapped_df = mapped.get("defendants", [])
for p in parties_data.get("plaintiffs", []):
if p.get("name") in mapped_pl and p.get("reason"):
risk_items.append(p["reason"])
for d in parties_data.get("defendants", []):
if d.get("name") in mapped_df and d.get("reason"):
risk_items.append(d["reason"])
risk = ", ".join(risk_items) if risk_items else ""
lines.append(f"### {idx}. {cid}: {title}")
lines.append("")
lines.append("| 항목 | 내용 |")
lines.append("|------|------|")
lines.append(f"| 청구취지 | {relief} |")
lines.append(f"| 청구원인 | {cause} |")
lines.append(f"| 근거-(법률)행위 | {fact_ids} |")
lines.append(f"| 사건행위 | {bo_ids} |")
lines.append(f"| 증거인덱스 | {ev_ids} |")
lines.append(f"| 리스크/점검내용 | {risk} |")
lines.append("")
return "\n".join(lines)
def build_section4(all_claims):
"""## 4. 당사자 식별 — 청구권별 원고/피고 연결"""
lines = []
lines.append("## 4. 당사자 식별")
lines.append("### 청구권별 원고/피고 연결")
lines.append("")
lines.append("| 청구권 | 원고 | 피고 |")
lines.append("|------|------|------|")
for cl in all_claims:
m = cl.get("mapped_parties", {})
pl = ", ".join(m.get("plaintiffs", []))
df = ", ".join(m.get("defendants", []))
lines.append(f"| {cl['claim_id']} | {pl} | {df} |")
lines.append("")
return "\n".join(lines)
def build_section5(taxonomy_data, all_claims):
"""## 5. 사건종류 결정"""
lines = []
lines.append("## 5. 사건종류 결정")
lines.append("")
lines.append("| 청구권 | 소송대분류 | 분쟁유형 | 사건종류 |")
lines.append("|------|------|------|------|")
tax_map = {t["claim_id"]: t for t in taxonomy_data.get("claims_taxonomy", [])}
for cl in all_claims:
cid = cl["claim_id"]
t = tax_map.get(cid, {})
macro = safe_str(t.get("macro_category"))
dispute = safe_str(t.get("dispute_type")) if t.get("dispute_type") is not None else ""
kind = safe_str(t.get("case_kind")) if t.get("case_kind") is not None else ""
lines.append(f"| {cid} | {macro} | {dispute} | {kind} |")
lines.append("")
return "\n".join(lines)
def build_section6(consolidation_data):
"""## 6. 청구방식(병합/단독) 제안 — G-01만 사용"""
lines = []
lines.append("## 6. 청구방식(병합/단독) 제안")
lines.append("")
groups = consolidation_data.get("consolidation_groups", [])
g01 = None
for g in groups:
if g.get("group_id") == "G-01":
g01 = g
break
if g01 is None and groups:
g01 = groups[0]
if g01:
st = g01.get("structure_type", "?")
ra = g01.get("rationale", "?")
else:
st = "(병합 전략 미수립)"
ra = "(근거 없음)"
lines.append("| 청구방식 | 근거 |")
lines.append("|------|------|")
lines.append(f"| {st} | {ra} |")
lines.append("")
return "\n".join(lines)
# ═══════════════════════════════════════════════
# 3. 메인 (MCP localdocs 연동)
# ═══════════════════════════════════════════════
def main():
OUTPUT_NAME = "청구개요서.md"
with httpx.Client(timeout=60) as c:
# ── 1) MCP 연결 ──
_log("1) Connecting to localdocs...")
r = c.post(LOCALDOCS_URL, json={
"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": {"name": "claim-overview-gen", "version": "1.0"}
}
}, headers=HEADERS)
sid = r.headers.get("mcp-session-id")
if sid:
HEADERS["mcp-session-id"] = sid
c.post(LOCALDOCS_URL, json={
"jsonrpc": "2.0", "method": "notifications/initialized"
}, headers=HEADERS)
_log(f" Connected (session: {sid})")
# ── 2) Preflight: 파일 존재 확인 ──
_log("\n2) Preflight: checking files...")
mcp_list_docs(c, [
"stage2_1_claims_evaluated.json",
"stage2_2_claims_taxonomy.json",
"stage2_3_consolidation_strategy.json"
], 5)
# ── 3) 데이터 로드 (MCP read_docs) ──
_log("\n3) Loading files via MCP read_docs...")
claims_eval = mcp_read_json(c, "stage2_1_claims_evaluated.json", 10)
taxonomy_data = mcp_read_json(c, "stage2_2_claims_taxonomy.json", 11)
consolidation_data = mcp_read_json(c, "stage2_3_consolidation_strategy.json", 12)
# 필수 파일 검증 + 1회 재시도
required = {
"stage2_1_claims_evaluated.json": claims_eval,
"stage2_2_claims_taxonomy.json": taxonomy_data,
"stage2_3_consolidation_strategy.json": consolidation_data,
}
missing = [n for n, d in required.items() if d is None]
if missing:
_log(f" WARN: Missing: {missing}, retrying...")
for fname in missing:
data = mcp_read_json(c, fname, 15)
if data is not None:
required[fname] = data
missing = [n for n, d in required.items() if d is None]
if missing:
_log(f" ERROR: Still missing: {missing}")
print(f"ERROR: Missing required files: {missing}")
sys.exit(1)
claims_eval = required["stage2_1_claims_evaluated.json"]
taxonomy_data = required["stage2_2_claims_taxonomy.json"]
consolidation_data = required["stage2_3_consolidation_strategy.json"]
# ── 4) 데이터 준비 ──
claims_list = claims_eval.get("claims", [])
other_claims = claims_eval.get("other_claims", [])
all_claims = claims_list + other_claims
parties_data = claims_eval.get("parties", {})
_log(f" Claims={len(claims_list)}, Other={len(other_claims)}, "
f"Tax={len(taxonomy_data.get('claims_taxonomy', []))}, "
f"Groups={len(consolidation_data.get('consolidation_groups', []))}")
# ── 5) 마크다운 생성 ──
_log("\n4) Generating markdown...")
md_parts = []
md_parts.append("# 청구개요서\n")
md_parts.append(build_section1(claims_eval))
md_parts.append(build_section2(claims_list))
md_parts.append(build_section3(all_claims, parties_data))
md_parts.append(build_section4(all_claims))
md_parts.append(build_section5(taxonomy_data, all_claims))
md_parts.append(build_section6(consolidation_data))
md_content = "\n".join(md_parts)
_log(f" Markdown: {len(md_content)} bytes")
# ── 6) localdocs에 저장 ──
_log(f"\n5) Saving: {OUTPUT_NAME}")
ok = mcp_write(c, OUTPUT_NAME, md_content, 20)
if not ok:
_log(" Retrying write...")
mcp_write(c, OUTPUT_NAME, md_content, 21)
# ── 7) 파일 존재 검증 ──
_log(f"\n6) Verifying: {OUTPUT_NAME}")
verify = mcp_list_docs(c, [OUTPUT_NAME], 25)
if not (verify and "result" in verify):
_log(" WARN: Verification unclear, retry write...")
mcp_write(c, OUTPUT_NAME, md_content, 26)
mcp_list_docs(c, [OUTPUT_NAME], 27)
# ── 8) /app/output 저장 (code-executor 반환용) ──
output_path = os.path.join("/app/output", OUTPUT_NAME)
os.makedirs("/app/output", exist_ok=True)
with open(output_path, "w", encoding="utf-8") as f:
f.write(md_content)
_log(f"7) Saved to {output_path} ({len(md_content)} bytes)")
print(f"청구개요서 생성 완료: {len(md_content)} bytes, "
f"Claims={len(claims_list)}, OtherClaims={len(other_claims)}")
if __name__ == "__main__":
main()
task_procedure:
IN:
nexts: ["Task_A"]
wait_until: []
Task_A:
nexts: ["Task_B"]
wait_until: ["IN"]
Task_B:
nexts: ["Task_C"]
wait_until: ["Task_A"]
Task_C:
nexts: ["Task_D"]
wait_until: ["Task_B"]
Task_D:
nexts: ["OUT"]
wait_until: ["Task_C"]
OUT:
nexts: []
wait_until: ["Task_D"]
prevs: [stage1_사건개요파악]
nexts: [stage_3_요건사실파악]
- name: stage3_1_청구요건사실검색쿼리생성
description: 청구권별로 요건사실 검색 쿼리 생성
llm_provider: openai
llm_model: gpt-4o-2024-08-06
tools:
mcpServers:
localdocs:
type: streamable-http
url: "http://mcp-localdocs:8012/mcp"
description: Get the content of local documents
code-executor:
type: streamable-http
url: https://code-executor.mcp.eroomai.com/mcp
description: Run scripts of programming languages
headers:
Authorization: Bearer rR8OXqWrVZA1gFEo8oWfBkw2XgpWoGrrspw5ObsxTCM=
tasks:
- task_name: Task_A
llm_provider: anthropic
llm_model: claude-opus-4-5
prompts:
- role: user
content: |
# Checklist
[ ] 1. Preflight: `list_docs` 사용해서 stage2_1_claims_evaluated.json, stage2_2_claims_taxonomy.json, Default_Agent/DB_legally_required_facts_description.md 확인하고, `read_docs` 사용해서 stage2_1_claims_evaluated.json, stage2_2_claims_taxonomy.json, DB_legally_required_facts_description.md 읽는다.
[ ] 2. `write_file` 사용해서 queries_legal_facts_search.json 생성한다.
[ ] 3. `list_docs` 사용해서 queries_legal_facts_search.json의 존재를 확인한다.
[ ] 4. 작업 종료(terminate)
---
## 0. 역할 · 범위 · 산출물 (HARD)
당신은 **대한민국 민사소송(원고대리) 실무형 변호사**이자 **Weaviate hybrid retrieval 설계자**다.
입력된 파일들(청구권 요약·사건종류 분류·DB 매핑표)을 근거로, **요건사실(legally required facts)**을 검색하기 위한 **Hybrid Search “쿼리 계획(JSON)”**만을 생성한다.
* 당신은 **DB를 실제로 조회/실행하지 않는다.**
* 당신이 생성한 출력 JSON은 **다음 단계에서 “중간 어댑터(adapter)”에 의해 Weaviate 실행 요청 스펙으로 변환되어 적용**된다.
따라서 본 출력은 **내부 표준 스키마(아래 JSON Schema)**를 준수해야 하며, Weaviate API 파라미터명/형식과의 1:1 동일성은 **어댑터 책임**이다(단, 본 프롬프트가 강제하는 타입·기본값·정합성은 반드시 지킨다).
## 1. 불가침 원칙 (Non-negotiables, HARD)
1. **Hallucination 금지:** 입력 파일에 없는 사건 고유 사실을 새로 만들지 않는다.
2. 불명확하면 **"UNKNOWN" / null / "[증거공백]"** 중 적절한 값으로 명시한다(단, 스키마가 string을 요구하면 null 대신 "UNKNOWN"을 사용).
3. 입력 원문 **대량/장문 복사 금지**(필요 최소한의 “법률 개념어”만 추출).
4. **필수 입력 누락/빈 파일/파싱 불가 시 즉시 중단(fail-fast)**한다.
* 이 경우 **출력 파일(queries_legal_facts_search.json)을 생성하지 않는다.**
* 누락/오류 항목을 **채팅으로만** 간단히 보고한다(중간 JSON 덤프 금지).
5. **중간 결과(JSON 덤프, DB 결과 덤프)를 채팅에 출력하지 말 것.**
> 본 프롬프트는 “필수 입력 파싱 불가 시 즉시 중단”을 채택한다.
> (에러 시 스킵하고 진행하는 정책은 사용하지 않는다.)
## 2. 입출력 파일 (HARD)
### 2.1 IN (필수)
* `stage2_1_claims_evaluated.json`
* `stage2_2_claims_taxonomy.json`
* `Default_Agent/DB_legally_required_facts_description.md` ← **Default_Agent 폴더에 존재함(필수 입력)**
### 2.2 OUT (필수)
* `queries_legal_facts_search.json`
## 3. 입력 데이터에서 사용할 필드 (HARD)
### 3.1 Stage 2.1 (stage2_1_claims_evaluated.json)
* `claims[]` 및 (존재 시) `other_claims[]`의 각 원소에서:
* `claim_id` (string)
* `claim_title` (string)
* `relief_summary` (string)
* `cause_summary` (string)
> 청구권의 처리 순서는 **claims 배열 순서 → other_claims 배열 순서**를 그대로 따른다.
### 3.2 Stage 2.2 (stage2_2_claims_taxonomy.json)
* `claims_taxonomy[]`에서 claim_id별:
* `case_kind` (string|null)
* `macro_category` (string|null)
### 3.3 DB 매핑표 (Default_Agent/DB_legally_required_facts_description.md)
표의 컬럼:
* `사건 종류`
* `Weaviate DB`
* `사건 종류 유사어`
## 4. 사건종류 결정 및 DB 매핑 규칙 (HARD)
### 4.1 사건종류(사건종류 string) 결정
각 claim_id에 대해 다음 우선순위로 `사건종류`를 결정한다.
1. stage2_2의 `case_kind`가 null이 아니고 비어있지 않으면 → 그 값을 사용
2. 그렇지 않으면 stage2_2의 `macro_category`가 null이 아니고 비어있지 않으면 → 그 값을 사용
3. 둘 다 없으면 → `"UNKNOWN"`
### 4.2 DB 매핑 (mapped_collection, mapped_tenant, tenant_status)
`Default_Agent/DB_legally_required_facts_description.md` 표를 기준으로 다음 절차를 **결정론적으로** 수행한다.
1. **Exact match 1차:**
`사건종류` == 표의 `사건 종류` 셀과 완전 일치하면 그 row를 채택한다.
2. **유사어 기반 2차(결정론):**
1차가 실패하면, 해당 표의 `사건 종류 유사어`(쉼표로 구분된 목록) 중 하나가 `사건종류`와 완전 일치하면 그 row를 채택한다.
3. **부분 포함 3차(결정론):**
1·2차가 실패하면,
* `사건종류`가 `사건 종류` 셀 문자열에 포함되거나,
* `사건 종류` 셀이 `사건종류` 문자열에 포함되는 경우
가장 먼저 매칭되는 row(표의 위에서 아래 순서)를 채택한다.
4. **매핑 성공 시:**
* `Weaviate DB` 셀에서 `[Collection: X] - [Tenant: Y]`를 파싱하여
* `mapped_collection = X`
* `mapped_tenant = Y`
* `tenant_status = "CONFIRMED"`
5. **매핑 실패 시:**
* `mapped_collection = "UNKNOWN"`
* `mapped_tenant = "UNKNOWN"`
* `tenant_status = "UNCONFIRMED"`
* `validation_warnings[]`에 경고 1건을 추가한다.
## 5. 쿼리 생성 규칙 (semantic unit ≤ 6, HARD)
### 5.1 노이즈 차단(사건 고유 사실 금지, HARD)
`search_params.query`(= query_text)는 다음 정보를 포함하면 안 된다.
* 인명/법인명/기관명
* 금액/이자율/채권액 등 수치
* 일자/기간
* 주소/지번/계좌/부동산 특정표지
* 사건 고유의 개별 사실관계(예: 특정 부동산 명칭)
- 핵심 키워드로 허용되는 것은 **법률 개념어** 및 **요건사실/항변/입증책임 구조를 지시하는 일반어**에 한정한다.
### 5.2 앵커 프리픽스(고정, HARD)
모든 쿼리 문자열은 반드시 다음 3개 앵커로 시작한다(고정):
* `"요건사실 항변 입증책임"`
이를 **anchor_units=3**으로 간주한다.
### 5.3 후보 개념어 풀 구성(최대 10개, HARD)
- 각 “claim_id”마다 개념어 후보 풀을 만든다.
- 개념어 후보에는 `법률 개념어`, `요건사실/항변/입증책임 구조를 지시하는 일반어`들을 포함한다.
- 개념어 후보 풀의 입력 텍스트 원천(허용 범위):
* (A) `사건종류`(case_kind 또는 macro_category로 결정된 값) 유사어
* (B) claim_id별
* `claim_title`, `relief_summary`, `cause_summary`
- 후보 풀 생성 규칙(결정론):
1. 위 텍스트를 **좌→우(문장 순서)**로 훑으면서, "법률 개념어나 요건사실/항변/입증책임 구조를 지시하는 일반어"를 포함하는 짧은 어구(phrase)**를 추출한다.
2. 추출 시 **노이즈 차단 규칙(5.1)**을 위반하는 토큰은 즉시 제외한다.
3. 중복 제거는 아래 “단순 표준화”만 사용한다(추론 기반 동의어 확장 금지):
* 공백/구두점 차이만 있는 동일 표현은 1개로 합친다.
* 예시: "대여금 채권", "대여금채권", "대여금, 채권" → 모두 "대여금 채권"으로 표준화하여 1개 후보로 유지한다.
4. 후보 풀은 **최대 10개**까지 유지하고, 10개를 채우면 즉시 중단한다.
### 5.4 case_units 선택(최대 4개, HARD)
* `case_units`는 후보 풀에서 **최대 4개**만 선택한다.
* 총 semantic unit은 `anchor(3) + case_units(k)`로 계산하며, **k ≤ 4**을 지킨다.
선택 우선순위(결정론, tie-break 포함):
1. `사건종류`(문자열)에서 직접 추출된 개념어를 우선한다.
2. 남는 슬롯은 대표 claim의 `claim_title → relief_summary → cause_summary` 순으로,
**더 먼저 등장**한 개념어를 우선한다.
3. 여전히 동률이면 **가나다(사전순)**로 tie-break.
### 5.5 query_text 구성(HARD)
* `query_text = "요건사실 항변 입증책임" + " " + " ".join(case_units)`
* 단, query_text의 길이가 **100자 초과**이면:
* `case_units`의 **마지막 항목부터** 제거하여 100자 이하로 맞춘다.
* 이때도 `case_units`가 1개 이상 유지되어야 한다.
만약 위 규칙으로도 `case_units`를 1개 이상 유지하지 못하면:
* 해당 claim_id에 대해 쿼리를 생성하지 않는다.
* `mapped_collection`과 `mapped_tenant`가 "UNKNOWN"이 아니더라도 쿼리를 생성하지 않는다.
* `validation_warnings[]`에 경고 1건을 추가한다.
## 6. query_id 부여 (운영 친화, 결정론)
### 6.1 규칙
"claim_id"당 1개 query**를 만든다.
### 6.2 query_id 부여
* "claim_id" "오름차순"으로 정렬한다.
* 정렬된 순서대로 `Q-001`, `Q-002`, … 를 부여한다.
## 7. Hybrid Search 파라미터 (타입 강제, HARD)
각 query는 `search_params`에 아래 키를 **모두 포함**해야 하며, 타입을 강제한다.
### 7.1 search_params 필수 키(타입 강제)
* `collection_name`: string (=`mapped_collection`)
* `tenant`: string (=`mapped_tenant`)
* `query`: string (=`query_text`)
* `alpha`: number(float) (문자열 금지)
* `limit`: integer (문자열 금지)
* `query_properties`: array[string] (문자열 JSON 금지)
* `fusion_type`: string (**항상 "rankedFusion" 사용**)
* `bm25_operator`: string ("or" 또는 "and")
* `bm25_minimum_match`: integer (문자열 금지)
### 7.2 기본값(HARD)
* `fusion_type = "rankedFusion"` (고정)
* `limit = 8`
* `query_properties = ["content"]`
* `bm25_operator = "or"` (기본)
### 7.3 BM25 minimum match 강제(앵커 3단어 문제 해결, HARD)
* anchor_units = 3
* k = len(case_units) (1~4)
* `bm25_minimum_match = anchor_units + min(k, 2)`
* k=1 → 4
* k=2 → 5
* k=3 → 5
### 7.4 alpha 결정 규칙(HARD, 결정론)
사건종류 문자열에 아래 키워드가 포함되면 해당 alpha를 사용한다.
* "사해행위취소" 포함 → `alpha = 0.45`
* "구상금" 포함 → `alpha = 0.35`
* "대여금" 또는 "보증" 포함 → `alpha = 0.25`
* 그 외 → `alpha = 0.35`
## 8. 출력 JSON 구성 (스키마 준수, HARD)
### 8.1 최상위 구조
출력은 반드시 아래 3개 최상위 키를 포함한다.
* `claims`: array
* `queries`: array
* `query_summary`: object
(선택) `validation_warnings`: array[string]
### 8.2 claims 배열
입력의 모든 청구(3.1의 순서)를 대상으로 각 원소를 생성한다.
각 원소는 반드시 다음 키를 포함한다.
* `claim_id` (string)
* `claim_title` (string)
* `사건종류` (string; 미확보 시 "UNKNOWN")
* `mapped_collection` (string; 실패 시 "UNKNOWN")
* `mapped_tenant` (string; 실패 시 "UNKNOWN")
* `tenant_status` ("CONFIRMED"|"UNCONFIRMED")
### 8.3 queries 배열
각 "claim_id"마다 1개 원소를 생성한다. 단, 아래 경우에는 생성하지 않는다.
* DB 매핑 실패(tenant_status="UNCONFIRMED")로 collection/tenant가 "UNKNOWN"인 경우
* case_units를 1개 이상 확보하지 못해 query_text를 만들 수 없는 경우
각 queries 원소는 반드시 다음 키를 포함한다.
* `query_id` ("Q-001"…)
* `applicable_claim_id` (array[string]) ← 그룹에 포함된 claim_id 전부(중복 없음)
* `사건종류` (string)
* `purpose` = "요건사실 검색" (고정)
* `search_params` (7.1의 타입 강제 준수)
### 8.4 query_summary
아래 값을 **산출 결과에 맞게 정확히 계산**한다.
* `total_queries` = queries 배열 길이
* `distinct_사건종류_count` = queries에 포함된 사건종류의 distinct 개수
* `total_claims_covered` = 모든 queries의 applicable_claim_ids의 합집합 크기
### 8.5 validation_warnings(선택)
경고는 **문장(string) 1줄**로 간단히 기록한다.
예:
* "VALIDATION WARNING: claim_id=C-003 사건종류 매핑 실패로 query 미생성(tenant UNCONFIRMED)"
* "VALIDATION WARNING: 사건종류=UNKNOWN 로 query 미생성"
## 9. 출력 JSON Schema (HARD, 반드시 준수)
아래 JSON Schema를 **그대로** 만족하는 결과만을 생성한다(추가 키 금지).
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/queries_legal_facts_search.schema.json",
"title": "queries_legal_facts_search.json",
"type": "object",
"additionalProperties": false,
"required": ["claims", "queries", "query_summary"],
"properties": {
"claims": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/ClaimMapping" }
},
"queries": {
"type": "array",
"minItems": 0,
"items": { "$ref": "#/$defs/QueryPlan" }
},
"query_summary": { "$ref": "#/$defs/QuerySummary" },
"validation_warnings": {
"type": "array",
"items": { "type": "string" }
}
},
"$defs": {
"ClaimMapping": {
"type": "object",
"additionalProperties": false,
"required": [
"claim_id",
"claim_title",
"사건종류",
"mapped_collection",
"mapped_tenant",
"tenant_status"
],
"properties": {
"claim_id": { "type": "string" },
"claim_title": { "type": "string" },
"사건종류": { "type": "string" },
"mapped_collection": { "type": "string" },
"mapped_tenant": { "type": "string" },
"tenant_status": {
"type": "string",
"enum": ["CONFIRMED", "UNCONFIRMED"]
}
}
},
"QueryPlan": {
"type": "object",
"additionalProperties": false,
"required": [
"query_id",
"applicable_claim_ids",
"사건종류",
"purpose",
"search_params"
],
"properties": {
"query_id": {
"type": "string",
"pattern": "^Q-\\d{3}$"
},
"applicable_claim_id": {
"type": "array",
"minItems": 1,
"items": { "type": "string" },
"uniqueItems": true
},
"사건종류": { "type": "string" },
"purpose": { "type": "string", "const": "요건사실 검색" },
"search_params": { "$ref": "#/$defs/SearchParams" }
}
},
"SearchParams": {
"type": "object",
"additionalProperties": false,
"required": [
"collection_name",
"tenant",
"query",
"alpha",
"limit",
"query_properties",
"fusion_type",
"bm25_operator",
"bm25_minimum_match"
],
"properties": {
"collection_name": { "type": "string" },
"tenant": { "type": "string" },
"query": { "type": "string" },
"alpha": { "type": "number", "minimum": 0, "maximum": 1 },
"limit": { "type": "integer", "minimum": 1 },
"query_properties": {
"type": "array",
"minItems": 1,
"items": { "type": "string" }
},
"fusion_type": { "type": "string" },
"bm25_operator": { "type": "string", "enum": ["or", "and"] },
"bm25_minimum_match": { "type": "integer", "minimum": 1 }
}
},
"QuerySummary": {
"type": "object",
"additionalProperties": false,
"required": [
"total_queries",
"distinct_사건종류_count",
"total_claims_covered"
],
"properties": {
"total_queries": { "type": "integer", "minimum": 0 },
"distinct_사건종류_count": { "type": "integer", "minimum": 0 },
"total_claims_covered": { "type": "integer", "minimum": 0 }
}
}
}
}
```
task_procedure:
IN:
nexts: ["Task_A"]
wait_until: []
Task_A:
nexts: ["OUT"]
wait_until: ["IN"]
OUT:
nexts: []
wait_until: ["Task_A"]
prevs: [stage2_청구개요서작성]
nexts: [stage_3_2_요건사실검색프롬프트생성]
- name: stage3_2_요건사실검색_문서작성
description: 청구권별로 요건사실 검색 및 요건사실 문서 작성
tools:
mcpServers:
localdocs:
type: streamable-http
url: "http://mcp-localdocs:8012/mcp"
description: Get the content of local documents
weaviate:
type: streamable-http
url: "https://weaviate.eroomai.com/mcp"
description: Hybrid search on Weaviate vector DB
code-executor:
type: streamable-http
url: https://code-executor.mcp.eroomai.com/mcp
description: Run scripts of programming languages
headers:
Authorization: Bearer rR8OXqWrVZA1gFEo8oWfBkw2XgpWoGrrspw5ObsxTCM=
prevs: [stage3_1_청구요건사실검색쿼리생성]
nexts: [stage_4_청구항변반박전략]
map_reduce:
max_concurrency: 8
# ── Source: queries_legal_facts_search.json의 queries 배열 ──
source:
mcp:
server: localdocs
tool_name: read_docs
parameters: { doc_names: ["queries_legal_facts_search.json"] }
key: ["queries"]
# ── Shared Context: claims 배열 (reduce에서 claim_title 참조용) ──
shared_context:
- name: source_json
mcp:
server: localdocs
tool_name: read_docs
parameters: { doc_names: ["queries_legal_facts_search.json"] }
# ── Map: query별 Weaviate 검색 + 결과 요약 ──
map:
tasks:
# ── Step 1: Weaviate Hybrid Search (Direct MCP) ──
- task_name: search_weaviate
mcp: weaviate
tool_name: search_hybrid
parameters:
collection_name: "{{item.search_params.collection_name}}"
tenant: "{{item.search_params.tenant}}"
query: "{{item.search_params.query}}"
alpha: "{{item.search_params.alpha}}"
limit: "{{item.search_params.limit}}"
query_properties: "{{item.search_params.query_properties}}"
fusion_type: "{{item.search_params.fusion_type}}"
bm25_operator: "{{item.search_params.bm25_operator}}"
bm25_minimum_match: "{{item.search_params.bm25_minimum_match}}"
# ── Step 2: 검색 결과 요약 (LLM) ──
- task_name: summarize_search
llm_provider: anthropic
llm_model: claude-haiku-4-5
prompts:
- role: user
content: |
# Task
아래 Weaviate 검색 결과를 분석하여, 해당 사건종류의 **요건사실(legally required facts)** 관점에서 핵심 포인트를 정리하라.
# 쿼리 정보
- query_id: {{item.query_id}}
- 사건종류: {{item.사건종류}}
- 적용 claim_ids: {{item.applicable_claim_ids}}
- 검색 파라미터:
- collection: {{item.search_params.collection_name}}
- tenant: {{item.search_params.tenant}}
- query: {{item.search_params.query}}
- alpha: {{item.search_params.alpha}}
- limit: {{item.search_params.limit}}
- bm25_operator: {{item.search_params.bm25_operator}}
- bm25_minimum_match: {{item.search_params.bm25_minimum_match}}
# Weaviate 검색 결과
{{prev.search_weaviate}}
# 출력 규칙 (STRICT JSON)
아래 JSON 형식으로만 출력하라. 마크다운 펜스, 코멘트, 설명 일체 금지.
{
"query_id": "<query_id>",
"사건종류": "<사건종류>",
"applicable_claim_ids": [<claim_ids>],
"search_quality": "good|partial|poor",
"key_points": [
"<요건사실 핵심 포인트 1>",
"<요건사실 핵심 포인트 2>",
"..."
],
"hits": [
{
"weaviate_ref": "<document id or reference>",
"score": <float>,
"excerpt": "<240자 이내 발췌, 줄바꿈 제거>"
}
],
"missing_elements": ["<누락된 요건사실 요소>"]
}
## key_points 작성 규칙
- 3~7개의 bullet로 작성
- 각 포인트는 검색 hit의 excerpt에 근거하여 작성 (근거 없는 창작 금지)
- 요건사실(성립요건) 중심으로 정리
- 법률 개념어 사용, 사건 고유 사실 배제
## hits 작성 규칙
- 최대 6개까지만 포함 (관련성 높은 순)
- excerpt는 240자 이내, 줄바꿈/탭 제거, 공백 정리
- score는 Weaviate 반환 점수 그대로 사용
## search_quality 판정
- good: 핵심 요건사실 요소 대부분 커버 (4개 이상 key_points)
- partial: 일부 요건사실만 커버 (2~3개 key_points)
- poor: 관련 결과 거의 없음 (1개 이하 key_points)
## missing_elements 작성 규칙
- search_quality가 "good"이 아닌 경우에만 작성
- 검색으로 확인되지 않은 요건사실 요소를 나열
- 예: ["무자력 요건", "사해의사 입증"]
# ── Reduce: 전체 결과 조립 → MD 문서 생성 ──
reduce:
# ── Step 1: Python으로 MD 문서 조립 ──
- task_name: assemble_document
mcp: code-executor
tool_name: run_code
parameters:
language: python
timeout: 120
network: "agent-network"
code: |
import json, base64
from datetime import datetime
# ── 1. map_results 디코딩 ──
raw = base64.b64decode('{{map_results_b64}}').decode('utf-8')
map_results = json.loads(raw)
# ── 2. shared_context에서 claims 추출 ──
source_raw = '''{{shared.source_json}}'''
try:
source_data = json.loads(source_raw)
claims_list = source_data.get("claims", [])
query_summary_src = source_data.get("query_summary", {})
validation_warnings = source_data.get("validation_warnings", [])
except Exception:
claims_list = []
query_summary_src = {}
validation_warnings = []
# claim_id → claim_title 매핑
claim_title_map = {c["claim_id"]: c.get("claim_title", "") for c in claims_list}
# ── 3. map 결과 파싱 ──
query_sections = []
errors = []
total_queries = len(map_results)
all_claim_ids = set()
case_types = set()
for item_result in map_results:
idx = item_result.get("item_index", "?")
item = item_result.get("item", {})
tr = item_result.get("task_results", {})
query_id = item.get("query_id", f"Q-{idx}")
case_type = item.get("사건종류", "UNKNOWN")
claim_ids = item.get("applicable_claim_ids", [])
sp = item.get("search_params", {})
all_claim_ids.update(claim_ids)
case_types.add(case_type)
# summarize_search 결과 파싱
summary_str = tr.get("summarize_search", "")
summary = None
if summary_str and not str(summary_str).startswith("ERROR"):
cleaned = str(summary_str).strip()
if cleaned.startswith("```"):
lines = cleaned.split("\n")
lines = [l for l in lines if not l.strip().startswith("```")]
cleaned = "\n".join(lines)
try:
summary = json.loads(cleaned)
except json.JSONDecodeError as e:
errors.append(f"item {idx} ({query_id}) summary parse error: {e}")
else:
errors.append(f"item {idx} ({query_id}) summarize_search: {str(summary_str)[:200]}")
# 섹션 데이터 구성
query_sections.append({
"query_id": query_id,
"case_type": case_type,
"claim_ids": claim_ids,
"search_params": sp,
"summary": summary,
})
# query_id 순서로 정렬
query_sections.sort(key=lambda x: x["query_id"])
# ── 4. Markdown 문서 조립 ──
lines = []
lines.append("# 요건사실 검색 결과 (legally_required_facts_information)")
lines.append("")
lines.append("## 문서 메타")
lines.append(f"- 입력 파일: queries_legal_facts_search.json")
lines.append(f"- 총 query 수: {total_queries}")
lines.append(f"- 총 커버 claim 수: {len(all_claim_ids)}")
lines.append(f"- distinct 사건종류 수: {len(case_types)}")
lines.append(f"- 생성일시: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
lines.append("")
if validation_warnings:
lines.append("## Validation Warnings (쿼리 생성 단계)")
for w in validation_warnings:
lines.append(f"- {w}")
lines.append("")
lines.append("---")
lines.append("")
for section in query_sections:
qid = section["query_id"]
ct = section["case_type"]
cids = section["claim_ids"]
sp = section["search_params"]
summary = section["summary"]
# 헤더
claim_titles = [f"{cid} ({claim_title_map.get(cid, '')})" for cid in cids]
lines.append(f"## {qid} — {ct}")
lines.append("")
lines.append(f"- **적용 claim_ids**: {', '.join(claim_titles)}")
lines.append(f"- **Target**: {sp.get('collection_name', 'N/A')} / {sp.get('tenant', 'N/A')}")
lines.append(f"- **검색 파라미터**: alpha={sp.get('alpha')}, limit={sp.get('limit')}, bm25_operator={sp.get('bm25_operator')}, bm25_minimum_match={sp.get('bm25_minimum_match')}, query_properties={sp.get('query_properties')}")
lines.append("")
if summary:
quality = summary.get("search_quality", "unknown")
lines.append(f"### 검색 품질: {quality}")
lines.append("")
# 요건사실 핵심 포인트
key_points = summary.get("key_points", [])
if key_points:
lines.append("### 요건사실(성립요건) 핵심 포인트")
for kp in key_points:
lines.append(f"- {kp}")
lines.append("")
# 근거 hit 목록
hits = summary.get("hits", [])
if hits:
lines.append("### 근거 Hit 목록")
for h in hits[:6]:
ref = h.get("weaviate_ref", "N/A")
score = h.get("score", 0)
excerpt = h.get("excerpt", "")[:240]
lines.append(f"- [{ref}] (score={score}) {excerpt}")
lines.append("")
# 검색 품질 경고
missing = summary.get("missing_elements", [])
if quality != "good" and missing:
lines.append("### 검색 품질 경고")
lines.append(f"- 검색 품질: {quality}")
lines.append(f"- 누락된 요건사실 요소: {', '.join(missing)}")
lines.append("")
else:
lines.append("### 검색 품질 경고")
lines.append("- 요약 결과를 파싱하지 못했습니다.")
lines.append("")
lines.append("---")
lines.append("")
if errors:
lines.append("## 처리 오류")
for e in errors:
lines.append(f"- {e}")
lines.append("")
print("\n".join(lines))
# ── Step 2: MD 문서 저장 ──
- task_name: save_document
mcp: localdocs
tool_name: write_file
parameters:
path: "legally_required_facts_information.md"
content: "{{prev.assemble_document}}"