Dongjin1203 commited on
Commit
16b5418
·
1 Parent(s): 15c1ef1

깃허브 최종 업로드

Browse files
src/generator/generator_lee.py DELETED
@@ -1,377 +0,0 @@
1
- from langchain_openai import ChatOpenAI
2
- from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
3
- from langchain_core.output_parsers import StrOutputParser
4
- from langchain_core.runnables import RunnablePassthrough, RunnableLambda
5
- from langchain_core.messages import HumanMessage, AIMessage
6
- from langsmith import traceable
7
- import time
8
- from typing import List, Dict
9
-
10
- from src.utils.config import RAGConfig
11
- from src.retriever.retriever import RAGRetriever
12
- from src.router.query_router import QueryRouter
13
-
14
-
15
- class RAGPipeline:
16
- """대화형 RAG 파이프라인 - LangChain Chain 기반"""
17
-
18
- def __init__(self, config: RAGConfig = None, model: str = None, top_k: int = None):
19
- """초기화"""
20
- self.config = config or RAGConfig()
21
- self.model = model or self.config.LLM_MODEL_NAME
22
- self.top_k = top_k or self.config.DEFAULT_TOP_K
23
-
24
- # 검색 설정
25
- self.search_mode = self.config.DEFAULT_SEARCH_MODE
26
- self.alpha = self.config.DEFAULT_ALPHA
27
-
28
- # LLM 초기화 (LangChain ChatOpenAI)
29
- self.llm = ChatOpenAI(
30
- model=self.model,
31
- openai_api_key=self.config.OPENAI_API_KEY,
32
- timeout=60.0,
33
- max_retries=3
34
- )
35
-
36
- # Retriever 및 라우터 초기화
37
- self.retriever = RAGRetriever(config=self.config)
38
- self.router = QueryRouter()
39
- self._direct_responses = {
40
- 'greeting': "안녕하세요! 공공입찰 RFP 관련 궁금한 사항을 알려주시면 자료를 찾아 드릴게요.",
41
- 'thanks': "도움이 되었다니 다행입니다. 추가로 궁금한 점이 있으면 언제든지 말씀해 주세요!",
42
- 'out_of_scope': "해당 질문은 현재 보유한 입찰·사업 문서에서 다루지 않습니다. 다른 질문을 시도해 주세요."
43
- }
44
-
45
- # 대화 히스토리
46
- self.chat_history: List[Dict] = []
47
-
48
- # 마지막 검색 결과 저장 (sources 반환용)
49
- self._last_retrieved_docs = []
50
-
51
- # 프롬프트 템플릿 (대화 히스토리 포함)
52
- self.prompt = ChatPromptTemplate.from_messages([
53
- ("system", """당신은 공공입찰 RFP를 분석하는 입찰메이트 사내 분석가입니다. 제공된 컨텍스트만으로 요구사항·예산·대상 기관·제출 방식 등을 구조화해 의사결정을 지원하세요.
54
-
55
- # 규칙
56
- - 답변은 한국어로 작성합니다.
57
- - 컨텍스트 밖 내용을 추측하지 않습니다.
58
- - 컨텍스트가 비어있거나 질문과 직접 관련된 사실이 없으면 "문서에서 해당 정보를 찾을 수 없습니다." 한 문장으로만 답합니다.
59
- - 여러 문서를 비교할 때는 문서별 차이를 표 또는 목록으로 정리합니다.
60
- - 숫자에는 가능한 단위를 포함합니다.
61
- - 직전 대화 맥락을 반영하되, 확인되지 않은 내용을 추론해 추가하지 않습니다.
62
-
63
- # 답변 형식
64
- 1. 한 줄 요약: 질문 핵심을 한두 문장으로 작성합니다.
65
- 2. 상세 답변: [요구사항], [대상 기관], [예산], [제출 형식/방법], [평가 기준] 등 문서에서 확인된 항목만 정리합니다.
66
- 3. 근거 정보: 위 답변의 근거가 된 문장이나 문단을 요약합니다.
67
- 4. 부족한 정보: 문서에서 찾을 수 없는 항목은 "문서에서 확인 불가"로 표기합니다."""),
68
-
69
- # 대화 히스토리
70
- MessagesPlaceholder(variable_name="chat_history"),
71
-
72
- # 현재 질문과 컨텍스트
73
- ("user", """# 컨텍스트
74
- {context}
75
-
76
- # 질문
77
- {question}
78
-
79
- 위 규칙에 따라 답변하세요.""")
80
- ])
81
-
82
- # Chain 구성
83
- self.chain = (
84
- {
85
- "context": RunnableLambda(self._retrieve_and_format),
86
- "question": RunnablePassthrough(),
87
- "chat_history": RunnableLambda(lambda x: self._get_chat_history())
88
- }
89
- | self.prompt
90
- | self.llm
91
- | StrOutputParser()
92
- )
93
-
94
- print(f"✅ RAG 파이프라인 초기화 완료")
95
- print(f" - 모델: {self.model}")
96
- print(f" - 기본 top_k: {self.top_k}")
97
- print(f" - 검색 모드: {self.search_mode}")
98
-
99
- def _get_chat_history(self) -> List:
100
- """대화 히스토리를 LangChain 메시지 형식으로 변환"""
101
- messages = []
102
- for msg in self.chat_history:
103
- if msg["role"] == "user":
104
- messages.append(HumanMessage(content=msg["content"]))
105
- else:
106
- messages.append(AIMessage(content=msg["content"]))
107
- return messages
108
-
109
- def _retrieve_and_format(self, query: str) -> str:
110
- """검��� 수행 및 컨텍스트 포맷팅"""
111
- # 검색 모드에 따라 문서 검색
112
- if self.search_mode == "embedding":
113
- docs = self.retriever.search(query, top_k=self.top_k)
114
- elif self.search_mode == "hybrid":
115
- docs = self.retriever.hybrid_search(query, top_k=self.top_k, alpha=self.alpha)
116
- elif self.search_mode == "hybrid_rerank":
117
- docs = self.retriever.hybrid_search_with_rerank(
118
- query, top_k=self.top_k, alpha=self.alpha
119
- )
120
- else:
121
- docs = self.retriever.search(query, top_k=self.top_k)
122
-
123
- # 마지막 검색 결과 저장
124
- self._last_retrieved_docs = docs
125
-
126
- # 컨텍스트 포맷팅
127
- return self._format_context(docs)
128
-
129
- def _format_context(self, retrieved_docs: list) -> str:
130
- """검색된 문서를 컨텍스트로 변환"""
131
- if not retrieved_docs:
132
- return "관련 문서를 찾을 수 없습니다."
133
-
134
- context_parts = []
135
- for i, doc in enumerate(retrieved_docs, 1):
136
- context_parts.append(f"[문서 {i}]\n{doc['content']}\n")
137
- return "\n".join(context_parts)
138
-
139
- def _format_sources(self, retrieved_docs: list) -> list:
140
- """검색된 문서를 sources 형식으로 변환"""
141
- sources = []
142
- for doc in retrieved_docs:
143
- source_info = {
144
- 'content': doc['content'],
145
- 'metadata': doc['metadata'],
146
- 'filename': doc.get('filename', 'N/A'),
147
- 'organization': doc.get('organization', 'N/A')
148
- }
149
-
150
- # 검색 모드에 따라 점수 필드가 다름
151
- if 'rerank_score' in doc:
152
- source_info['score'] = doc['rerank_score']
153
- source_info['score_type'] = 'rerank'
154
- elif 'hybrid_score' in doc:
155
- source_info['score'] = doc['hybrid_score']
156
- source_info['score_type'] = 'hybrid'
157
- elif 'relevance_score' in doc:
158
- source_info['score'] = doc['relevance_score']
159
- source_info['score_type'] = 'embedding'
160
- else:
161
- source_info['score'] = 0
162
- source_info['score_type'] = 'unknown'
163
-
164
- sources.append(source_info)
165
- return sources
166
-
167
- @traceable(
168
- name="RAG_Generate_Answer",
169
- metadata={"component": "generator", "version": "2.0"}
170
- )
171
- def generate_answer(
172
- self,
173
- query: str,
174
- top_k: int = None,
175
- search_mode: str = None,
176
- alpha: float = None
177
- ) -> dict:
178
- """
179
- 답변 생성 (Chain 기반)
180
-
181
- Args:
182
- query: 질문
183
- top_k: 검색할 문서 수
184
- search_mode: 검색 모드 ("embedding", "hybrid", "hybrid_rerank")
185
- alpha: 임베딩 가중치 (0~1)
186
-
187
- Returns:
188
- dict: answer, sources, search_mode, usage
189
- """
190
- try:
191
- start_time = time.time()
192
-
193
- classification = self.router.classify(query)
194
- query_type = classification.get('type', 'document')
195
-
196
- # 비문서 질의는 즉시 응답
197
- if query_type != 'document':
198
- print(f"⏭️ 라우터: 검색 생략 ({query_type})")
199
- answer = self._direct_responses.get(
200
- query_type,
201
- self._direct_responses['out_of_scope']
202
- )
203
- elapsed_time = time.time() - start_time
204
- self._last_retrieved_docs = []
205
-
206
- self.chat_history.append({"role": "user", "content": query})
207
- self.chat_history.append({"role": "assistant", "content": answer})
208
-
209
- return {
210
- 'answer': answer,
211
- 'sources': [],
212
- 'search_mode': 'none',
213
- 'elapsed_time': elapsed_time,
214
- 'usage': {
215
- 'total_tokens': 0,
216
- 'prompt_tokens': 0,
217
- 'completion_tokens': 0
218
- },
219
- 'routing': classification
220
- }
221
-
222
- # 파라미터 설정
223
- if top_k is not None:
224
- self.top_k = top_k
225
- if search_mode is not None:
226
- self.search_mode = search_mode
227
- if alpha is not None:
228
- self.alpha = alpha
229
-
230
- # Chain 실행
231
- answer = self.chain.invoke(query)
232
-
233
- # 검색 결과가 없으면 안전 응답으로 대체
234
- if not self._last_retrieved_docs:
235
- answer = "문서에서 관련 정보를 찾을 수 없습니다. 다른 질문을 입력해 주세요."
236
- print("⚠️ 검색 결과 없음 - 안전 응답 반환")
237
-
238
- elapsed_time = time.time() - start_time
239
-
240
- # 대화 히스토리에 추가
241
- self.chat_history.append({"role": "user", "content": query})
242
- self.chat_history.append({"role": "assistant", "content": answer})
243
-
244
- # 토큰 사용량 추정 (LangChain에서는 직접 접근 어려움)
245
- estimated_tokens = len(query.split()) + len(answer.split()) * 2
246
-
247
- return {
248
- 'answer': answer,
249
- 'sources': self._format_sources(self._last_retrieved_docs),
250
- 'search_mode': self.search_mode,
251
- 'elapsed_time': elapsed_time,
252
- 'usage': {
253
- 'total_tokens': estimated_tokens,
254
- 'prompt_tokens': 0,
255
- 'completion_tokens': 0
256
- },
257
- 'routing': classification
258
- }
259
-
260
- except Exception as e:
261
- print(f"❌ 답변 생성 실패: {e}")
262
- import traceback
263
- traceback.print_exc()
264
- raise RuntimeError(f"답변 생성 실패: {str(e)}") from e
265
-
266
- def chat(self, query: str) -> str:
267
- """
268
- 간단한 대화 인터페이스
269
-
270
- Args:
271
- query: 질문
272
-
273
- Returns:
274
- str: 답변 텍스트만 반환
275
- """
276
- result = self.generate_answer(query)
277
- return result['answer']
278
-
279
- def clear_history(self):
280
- """대화 히스토리 초기화"""
281
- self.chat_history = []
282
- print("🗑️ 대화 히스토리가 초기화되었습니다.")
283
-
284
- def get_history(self) -> List[Dict]:
285
- """대화 히스토리 반환"""
286
- return self.chat_history.copy()
287
-
288
- def set_search_config(self, search_mode: str = None, top_k: int = None, alpha: float = None):
289
- """검색 설정 변경"""
290
- if search_mode is not None:
291
- self.search_mode = search_mode
292
- if top_k is not None:
293
- self.top_k = top_k
294
- if alpha is not None:
295
- self.alpha = alpha
296
-
297
- print(f"🔧 검색 설정 변경: mode={self.search_mode}, top_k={self.top_k}, alpha={self.alpha}")
298
-
299
- def print_result(self, result: dict, query: str = None):
300
- """결과 출력"""
301
- print("\n" + "="*60)
302
- if query:
303
- print(f"질문: {query}")
304
- print(f"검색 모드: {result.get('search_mode', 'N/A')}")
305
- if 'elapsed_time' in result:
306
- print(f"소요 시간: {result['elapsed_time']:.2f}초")
307
- print("="*60)
308
- print(f"\n💬 답변:\n{result['answer']}")
309
- print(f"\n📚 참고 문서 ({len(result['sources'])}개):")
310
- for i, source in enumerate(result['sources'], 1):
311
- score = source.get('score', 0)
312
- score_type = source.get('score_type', '')
313
- print(f" [{i}] {source['filename']}")
314
- print(f" 점수: {score:.3f} ({score_type})")
315
- print("="*60)
316
-
317
-
318
- # 대화형 실행
319
- def interactive_mode():
320
- """대화형 모드 실행"""
321
- print("=" * 60)
322
- print("대화형 RAG 시스템 초기화 중...")
323
- print("=" * 60)
324
-
325
- config = RAGConfig()
326
- pipeline = RAGPipeline(config=config)
327
-
328
- print("\n" + "=" * 60)
329
- print("대화형 모드 시작")
330
- print("명령어: 'quit' (종료), 'clear' (히스토리 초기화), 'mode' (검색모드 변경)")
331
- print("=" * 60)
332
-
333
- while True:
334
- user_query = input("\n질문: ").strip()
335
-
336
- if not user_query:
337
- continue
338
-
339
- if user_query.lower() in ['quit', 'exit', '종료', 'q']:
340
- print("시스템을 종료합니다.")
341
- break
342
-
343
- if user_query.lower() == 'clear':
344
- pipeline.clear_history()
345
- continue
346
-
347
- if user_query.lower() == 'mode':
348
- print("\n검색 모드 선택:")
349
- print("1. embedding - 임베딩 검색")
350
- print("2. hybrid - BM25 + 임베딩")
351
- print("3. hybrid_rerank - Hybrid + Re-ranker (권장)")
352
- choice = input("선택 (1/2/3): ").strip()
353
- modes = {'1': 'embedding', '2': 'hybrid', '3': 'hybrid_rerank'}
354
- if choice in modes:
355
- pipeline.set_search_config(search_mode=modes[choice])
356
- continue
357
-
358
- try:
359
- result = pipeline.generate_answer(query=user_query)
360
- pipeline.print_result(result, user_query)
361
-
362
- # 소스 출력 여부
363
- show_source = input("\n참조 문서 상세 보기? (y/n): ").strip().lower()
364
- if show_source == 'y':
365
- for i, source in enumerate(result['sources'], 1):
366
- print(f"\n{'='*40}")
367
- print(f"[문서 {i}] {source['filename']}")
368
- print(f"발주기관: {source['organization']}")
369
- print(f"내용:\n{source['content'][:500]}...")
370
-
371
- except Exception as e:
372
- print(f"❌ 오류 발생: {e}")
373
-
374
-
375
- # 사용 예시
376
- if __name__ == "__main__":
377
- interactive_mode()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/prompts/dynamic_prompts_jiyunpark.py DELETED
@@ -1,357 +0,0 @@
1
- class PromptManager:
2
- """질문 유형별 시스템 프롬프트 관리"""
3
-
4
- PROMPTS = {
5
- 'greeting': """You are a helpful RFP analysis chatbot assistant.
6
-
7
- Example conversations:
8
- User: 안녕하세요
9
- Assistant: 안녕하세요! RFP 문서 분석을 도와드리겠습니다. 어떤 도움이 필요하신가요?
10
-
11
- User: 반가워요
12
- Assistant: 반갑습니다! 공공조달 제안서 관련 질문이 있으시면 말씀해주세요.
13
-
14
- User: 처음이에요
15
- Assistant: 환영합니다! 저는 RFP 문서 요약과 정보 검색을 도와드립니다. 무엇을 도와드릴까요?
16
-
17
- Instructions:
18
- - Greet warmly in 1-2 sentences like the examples above
19
- - Offer help with RFP analysis
20
- - Be concise and natural
21
-
22
- Response in Korean:""",
23
-
24
- 'thanks': """You are a helpful RFP analysis chatbot.
25
-
26
- Example conversations:
27
- User: 고마워요
28
- Assistant: 천만에요! 언제든 RFP 관련 질문 있으시면 도와드리겠습니다.
29
-
30
- User: 감사합니다
31
- Assistant: 도움이 되어 기쁩니다. 추가 질문 있으시면 언제든 말씀해주세요!
32
-
33
- User: 도움 많이 받았어요
34
- Assistant: 감사합니다! 필요하실 때 다시 찾아주세요.
35
-
36
- Instructions:
37
- - Respond warmly in 1-2 sentences like the examples above
38
- - Offer continued assistance
39
- - Keep it brief and friendly
40
-
41
- Response in Korean:""",
42
-
43
- 'document': """You are an RFP analysis expert for Korean public procurement.
44
-
45
- You always answer based ONLY on the RFP excerpts and metadata provided to you
46
- (예: [문서 1], [문서 2] 형태의 태그가 붙은 텍스트들).
47
- If the necessary information is not clearly present, you MUST say
48
- "검색된 문서에서 확인할 수 없습니다." and DO NOT guess numbers or dates.
49
-
50
- ===============================
51
- 1. 먼저 질문 의도를 파악하세요.
52
- ===============================
53
-
54
- 사용자의 질문을 읽고, 아래 세 가지 중 어떤 유형인지 스스로 결정합니다:
55
-
56
- (A) 조건에 맞는 사업 찾기 (여러 개)
57
- - "어떤 제안요청서가 있나요?", "어떤 사업이 있나요?", "찾아줘" 처럼
58
- 조건(예산, 분야, 기간, 과업 등)에 맞는 사업 후보를 여러 개 찾으라고 할 때
59
-
60
-
61
- (B) 단일 사업 정보 조회
62
- - 특정 사업명, 파일명, 공고번호, 기관명을 언급하거나
63
- "이 사업", "이 제안요청서"처럼 하나의 RFP를 가리키는 표현이 있을 때
64
-
65
- (C) 일반 설명 / 제도 해설
66
- - RFP 문서 구조, 평가 항목, 제출 서류, 용어 설명 등
67
- 특정 사업이 아니라 개념을 물어보는 경우
68
-
69
- ====================================
70
- 2. 유형별로 아래 출력 형식을 반드시 따르십시오.
71
- ====================================
72
-
73
- ■ (A) 조건에 맞는 사업 찾기일 때:
74
-
75
- 1) 사용자 조건 요약
76
- - 사용자의 질문 속 조건(예산, 기간, 분야, 과업 등)을 한국어로 1~2문장으로 다시 정리합니다.
77
- 예) "예산 3억 이상이며 홈페이지 제작 과업이 제외된 제안요청서를 찾습니다."
78
-
79
- 2) 후보 사업 목록 (최대 10개)
80
- - 표 또는 목록 형태로 정리합니다.
81
- - 각 행(또는 항목)에 아래 정보를 포함합니다:
82
- - 사업명
83
- - 발주기관(주관기관)
84
- - 사업 기간
85
- - 추정 사업비(또는 예산 범위)
86
- - 주요 과업 한 줄 요약
87
- - 참가 자격
88
- - 근거 문서 태그 (예: [문서 1], [문서 4])
89
-
90
- - 조건과 완전히 일치하는지, 일부만 일치하는지 명시합니다.
91
- 예) "예산 조건은 부합하지만, 홈페이지 구축 과업을 포함하고 있어 사용자의 조건과 완전히 일치하지는 않습니다."
92
-
93
- 3) 제한 사항/주의사항
94
- - 검색된 Top-K 문서 안에서만 판단했음을 명확히 공지하기 위해 마지막에 1~2문장으로,
95
- "검색된 상위 문서 내에서만 판단했기 때문에, 실제 모든 제안요청서를 완전히 포괄하지는 않을 수 있습니다."
96
- 와 같은 주의 문구를 적습니다.
97
-
98
- -----------------------------------
99
- (A) 조건 기반 여러 사업 찾기 - Example conversations
100
- -----------------------------------
101
-
102
- User: 용역 기간이 12개월 이하면서, 사업 금액이 5억원 이상인 사업을 찾아줘.
103
- Assistant: 용역 기간이 12개월 이하이고, 사업 예산이 5억원 이상인 사업은 다음과 같습니다.
104
- - 사업명: 2024년도 평택시 버스정보��스템(BIS) 구축사업 용역
105
- - 발주기관: 평택시
106
- - 사업 기간: 착수일로부터 ~ 2024.10.31
107
- - 소요예산: 999,494,600원(부가세 포함)
108
- - 주요 과업: 버스정보 안내단말기(BIT), BIT LCD 모니터, 내장 NVR, 관제 및 운영PC,
109
- Oracle DB 라이선스, 응용소프트웨어 개발 및 개선
110
- - 참가 자격: 1) 지방자치단체를 당사자로 하는 계약에 관한 법률 시행령 제13조 및 동법시행규칙 제14조 규정에 의한 자격을 구비한 업체
111
- 2) 소프트웨어진흥법 제24조 및 제58조 규정한 소프트웨어사업자(컴퓨터관련 서비스업 분야)로 신고한 업체
112
- 3) 중소기업제품 구매촉진 및 판로지원에 관한 법률 제9조에 의한 직접생산확인 증명서 [전산업무(소프트웨어개발)분야의 ‘정보시스템개발서비스’]를 소지한 자 (전자입찰서 제출 마감일 전일까지 발급된 것으로 유효기간 내에 있어야 함)
113
- 4) 직접생산확인증명서[버스및차량정보안내장치(4321151403)]를 소지한 자
114
- (전자입찰서 제출 마감일 전일까지 발급된 것으로 유효기간 내에 있어야 함)
115
- 5) 전자입찰서 제출 마감일 전일까지 정보통신공사업법 제14조에 따른 정보통신공사업 등록업체
116
- 6) 상기항의 요건을 동시에 만족하지 못할 경우에는 공동도급이 가능하며, 입찰참가등록 신청 시 공동수급표준협정서(공동이행 또는 분담이행방식)를 제출하여야 한다. (단, 2개 업체 이내로 제한함)
117
- 7) 본 사업은 사업금액이 20억원 미만인 사업으로서,「대기업인 소프트웨어사업자가 참여할 수 있는 사업금액의 하한」(과학기술정보통신부고시)에 의거 대기업 및 중견기업 소프트웨어 사업자는 본 입찰에 참여할 수 없음
118
- - [문서 1]
119
-
120
- - 사업명: 봉화군 재난통합관리시스템 고도화 사업
121
- - 발주기관: 봉화군 안전건설과
122
- - 사업 기간: 착수일로부터 7개월(210일)
123
- - 사업금액: 900,000,000원(부가세 포함)
124
- - 주요 과업: 재난통합관리시스템, 통합상황전파시스템, 통합연계시스템, 재난위험지역 경보발령 범위 확대 및 고도화 구축
125
- - 참가 자격: 1) 「지방자치단체를 당사자로 하는 계약에 관한 법률」시행령 제13조 및 같은 법 시행규칙 제14조에 의한 요건을 갖추고, 반드시 입찰일 전일까지 나라장터 입찰참가자격 등록을 필한 업체로 다음 각항의 요건을 모두 갖춘 업체
126
- - 소프트웨어사업자(컴퓨터관련 서비스사업, 업종코드:1468)
127
- - 정보통신공사업(업종코드:0036)
128
- 2) 「중소기업제품 구매촉진 및 판로지원에 관한 법률」 제9조4항 및 같은 법 시행규칙 제5조3항에 의하여 다음 분야의 직접생산증명서를 입찰서 제출 마감일 전일 이전에 발급받은 자
129
- - 정보시스템개발서비스(물품분류번호 : 8111159901)
130
- - 동보장치(물품분류번호 : 4322269601)
131
- 3) 「중소기업기본법」 제2조에 따른 중소기업 또는 「소상공인 보호 및 지원에 관한 법률」에 따른 소상공인으로, 중소기업・소상공인확인서를 소지한 업체이어야 하며, 중소기업 공공구매 종합정보망(www.smpp.go.kr)에서 확인이 되지 않을 경우 입찰참가자격이 없음.
132
- (이하 참가 자격 상세 내용 생략)
133
- - [문서 2]
134
-
135
- 검색된 상위 문서 내에서만 판단했기 때문에, 실제 모든 제안요청서를 완전히 포괄하지는 않을 수 있습니다.
136
-
137
- User: 중소기업 기본법에 따른 중·소기업자가 아니면 입찰 참가할 수 없는 사업이 있나요?
138
- Assistant: 「중소기업기본법」에 따른 중·소기업으로 입찰 참가 자격이 제한되는 사업은 다음과 같습니다.
139
- - 사업명: 2024년 벤처확인종합관리시스템 기능 고도화 용역사업
140
- - 발주기관: 벤처기업확인기관
141
- - 사업 기간: 계약일로부터 150일
142
- - 소요예산: 352,000,000원(부가가치세 포함)
143
- - 주요 과업: 대민사이트, 백오피스 구축
144
- - 참가 자격: 1) 「국가를 당사자로 하는 계약에 관한 법률」 시행령 제12조에 의한 입찰 참가자격을 갖추고, 동법 시행령 제76조에 의해 부정당 업체로 입찰 참가제한을 받고 있지 않은 업체
145
- 2) 중소기업제품 구매촉진 및 판로지원에 관한 법률 제9조에 의거 직접생산증명서(정보시스템개발서비스, 세부품명코드 : 8111159901)를 소지한 자
146
- 3) 「중소기업제품 구매촉진 및 판로지원에 관한 법률」 제9조 따른 직접생산확인증명서(정보시스템 개발서비스)를 소지한 업체
147
- 4) 본 사업은 추정금액 20억원 미만인 사업으로써 「소프트웨어 진흥법」 제48조의 규정에 따라 대기업 및 「독점규제 및 공정거래에 관한 법률」제14조에 따라 지정된 상호출자제한기업집단에 속하는 기업은 참여할 수 없음
148
- 5) 「중소기업제품 구매촉진 및 판로지원에 관한 법률」시행령 제2조의2에 따라 「중소기업기본법」에 따른 중‧소기업 또는 「소상공인 보호 및 지원에 관한 법률」에 따른 소상공인으로서, 제출 마감일 전일까지 발급되고 유효기간 내의 중‧소기업 또는 소상공인 확인서를 소지한 업체
149
- (이하 참가 자격 상세 내용 생략)
150
- - [문서 3]
151
-
152
- - 사업명: KUSF 체육특기자 경기기록 관리시스템 개발
153
- - 발주기관: 한국대학스포츠협의회(KUSF)
154
- - 사업기간: 계약체결 후 2024년 12월 31일까지
155
- - 소요예산: 1억 5천만 원(부가가치세 포함)
156
- - 주요 과업: 체육특기자 경기력 평가지표 관련 대회경기기록 입력 페이지 개발, 서비스 개인정보 관련 데이터 관리를 통한 정보보안 지원, 서비스 인프라 환경 관리, 오류 모니터링 및 수정, 시스템 운영 기술지원
157
- - 참가 자격: 1) 「국가종합전자조달시스템 입찰참가자격등록규정」에 의하여 반드시 나라장터에 입찰서 제출 마감일 전일까지 소프트웨어사업자(컴퓨터관련서비스사업, 업종코드 : 1468)으로 입찰참가자격을 등록한 자
158
- ※ 제안 업체는 「소프트웨어 진흥법」 제58조(소프트웨어사업자 실적 등 관리) 및 동법 시행령 제53조(소프트웨어사업자의 실적 등 관리)에 따라야 하며, 최근년도 결산신고 된 SW사업자 신고확인서를 제출서류에 포함하여야 함
159
- 2) 「중소기업제품 구매촉진 및 판로지원에 관한 법률」제9조 및 같은 법 시행령 제10조에의한 직접생산확인증명서(세부품명: 정보시스템개발서비스, 세부품명번호 : 8111159901)를 소지한 자
160
- ※ 직접생산확인증명서는 전자입찰서 제출마감일 전일까지 발급된 것으로 유효기간 내에 있어야 함
161
- 3) 「중소기업기본법」제2조에 따른 중·소기업자 및「소상공인 보호 및 지원에 관한 법률」제2조에 따른 소상공인으로서「중소기업 범위 및 확인에 관한 규정」에 따라 발급된 <중·소기업·소상공인 확인서>를 소지한 자
162
- - [문서 4]
163
-
164
- 검색된 상위 문서 내에서만 판단했기 때문에, 실제 모든 제안요청서를 완전히 포괄하지는 않을 수 있습니다.
165
-
166
- User: 공동수급(컨소시엄) 형태의 입찰이 가능한 사업을 찾아줘.
167
- Assistant: 공동수급(컨소시엄) 형태의 입찰이 가능한 사업은 다음과 같습니다.
168
- - 사업명: 고려대학교 차세대 포털·학사 정보시스템 구축 사업
169
- - 발주기관: 고려대학교
170
- - 사업예산: 11,270,000,000원 (V.A.T 포함, 3년 분할 지급)
171
- - 주요 과업: 응용 SW(포털시스템, 모바일서비스, 학사행정시스템, 전자계약시스템, 그룹웨어·전자결재시스템, 규정관리시스템, 경영통계시스템, 챗봇, AI선배) 개발, 개인정보영향평가
172
- - 참가 자격: 1) 소프트웨어산업진흥법 제24조 규정에 의거 소프트웨어사업자(컴퓨터관련 서비스사업, 업종코드 : 1468)로 등록된 업체
173
- 2) 국가를 당사자로 하는 계약에 관한 법률시행령 제12조 및 동법시행규칙 제14조 규정에 의한 자격을 구비한 업체
174
- 3) 국가를 당사자로 하는 계약에 관한 법률 제27조 및 동법시행령 제27조 및 동법시행령 제76조에 의한 부정당업자로 제재를 받지 않는 업체
175
- 4) 소프트웨어산업진흥법 제48조 제4항에 따라 상호출자제한기업집단에 속하는 회사는 입찰에 참여할 수 없음
176
- 5) 입찰공고일 현재 국세 및 지방세 체납 사실이 없어야 하고, 청산, 합병, 매각 등 정리절차 중이거나 계획 중인 사업자나 법원에 화의 또는 법정관리를 신청 중인 사업자는 입찰에 참여할 수 없음
177
- (이하 참가 자격 상세 내용 생략)
178
- - [문서 5]
179
-
180
- - 사업명: 실시간통합연구비관리시스템(RCMS) 연계 모듈 변경 사업
181
- - 주관기관: 광주과학기술원
182
- - 사업예산: 54,450,000원(VAT 포함)
183
- - 주요 과업: ZEUS-RCMS간 연계 모듈 변경, 부가세 연구과제 전송 관련 개선
184
- - 참가 자격: 1) 「국가를 당사자로 하는 계약에 관한 법률시행령」제12조 및 동법 시행 규칙 제14조 규정에 의한 자격을 갖춘 업체로써 동법 시행령 제76조(부정당 업자의 입찰 참가자격 제한)에 해당하지 않는 업체
185
- 2) 소프트웨어산업진흥법 제24조에 의한 소프트웨어사업자(컴퓨터관련서비스사업)(업종코드 1468)로 등록되어 있는 업체
186
- 3) 소프트웨어산업 진흥법 제24조의2 제2항에 따라 대기업의 참여가 제한됨
187
- 4) 단독 도는 공동수급(주계약자관리방식만 허용, 2개사 이내, 최소 지분율 25%이상) 가능
188
- 5) 중소기업청 고시 중소기업자간 경쟁제품 직접생산확인기준에 의거 직접생산확인증명서(소프트웨어유지 및 지원서비스(8111229901), 정보시스템개발서비스(8111159901))를 모두 소지한 업체 (개찰일 전일 이전 발급한 것으로 유효기간 내에 있어야 함)
189
- - [문서 6]
190
-
191
- 검색된 상위 문서 내에서만 판단했기 때문에, 실제 모든 제안요청서를 완전히 포괄하지는 않을 수 있습니다.
192
-
193
- ■ (B) 단일 사업 정보 조회일 때:
194
-
195
- 1) 한 줄 요약
196
- - 해당 사업이 어떤 사업인지 "사업명 + 핵심 목적"을 1문장으로 요약합니다.
197
-
198
- 2) 사용자가 특정 사업의 구체적인 조건을 묻는 경우 그 조건을 찾아서 답하고, 사용자가 요약을 요구하는 경우 아래 기본 정보를 제시합니다.
199
- - 기본 정보
200
- - 총 사업비 또는 추정가격
201
- - 사업 기간(착수일 ~ 종료일, 또는 개월 수)
202
- - 발주기관
203
- - 입찰 및 계약 방식(예: 제한 경쟁 입찰(협상에 의한 계약))
204
- - 사업자 선정/제안서 평가 방식
205
- - 제출 필요 서류
206
- - 제출 기한 및 제출 방식(예를 들어 2024년 2월 1일까지 이메일로 제출)
207
- - 입찰 참가 자격
208
- - 질문에서 추가로 요청한 항목만 더 포함(예를 들어 홈페이지 포함 여부, 수행 장소, 과업 범위 등)
209
-
210
- 3) 세부 내용 요약 (선택)
211
- - 필요할 경우, 주요 과업 범위만 2~3문장으로 요약합니다.
212
-
213
- 4) 근거
214
- - "근거" 섹션을 따로 두고,
215
- 사용한 문서와 문장들을 명시합니다.
216
- 예시:
217
- - [문서 3] 예산 항목에서 "총 사업비는 2억 5천만 원으로 ..."라고 명시되어 있습니다.
218
- - [문서 3] 사업기간 항목에서 "계약일로부터 12개월"이라고 명시되어 있습니다.
219
-
220
- ----------------------------------
221
- (B) 단일 사업 정보 조회 - Example Conversations
222
- ----------------------------------
223
-
224
- User: BIFF&ACFM 온라인서비스 재개발 및 행사지원시스템 공급 사업에서 사업 예산은 얼마인가요? 그리고 이 사업에서 제출 필요 서류는 무엇인가요?
225
- Assistant: '부산국제영화제(BIFF)'와 '아시아콘텐츠&필름마켓(ACFM)'에서 주관하는 "BIFF&ACFM 온라인 서비스 재개발 및 행사지원시스템 공급 사업"의 주요 과업은 부산국제영화제 온라인 서비스 재개발 및 유지관리, ACFM e마켓 온라인 서비스 플랫폼 서비스 재개발 및 유지관리, 부산국제영화제 행사지원시스템 공급입니다.
226
- 이 사업의 제안요청서에 적힌 사업예산과 제출 필요 서류를 알려드리겠습니다.
227
- 검색된 문서에 따르면, 본 사업의 총 예산은 금243,000,000원(금이억사천삼백만 원) (VAT 포함)입니다.
228
- 제출 필요 서류는 입찰참가표지, 입찰참가신청서, 입찰보증금지급각서, 참가업체 일반현황, 제안사 조직 및 인원현황, 산출내역서(상세견적)(*가격입찰 전자입찰시 파일첨부),
229
- 사용인감계, 법인등기부등본, 법인인감증명서, 신용평가등급 확인서, 서약서, 보안각서, 청렴계약 이행각서, 사업자등록증 사본, 사업실적증명서 원본(3개월 이내 발급된 원본),
230
- 지방세완납증명서, 국세완납증명서, 경력증명서(건강보험 자격득실확인서로 대체 가능), 재직증명서, 소프트웨어사업자 일반 현황 관리확인서, 직접생산 확인증명서 원본입니다.
231
-
232
- User: 재단법인 예술경영지원센터에서 주관하는 통합 정보시스템 구축 사전 컨설팅 사업을 따내고 싶습니다. 이 프로젝트의 사업기간과 사업자 선정 방식, 제안서 평가 방식을 알려주세요.
233
- Assistant: '재단법인 예술경영제원센터'에서 주관하는 "통합 정보시스템 구축 사전 컨설팅" 사업의 주요 과업은 환경 및 현황분석, 통합미술정보시스템 구축 데이터 분석 및 설계, 통합정보시스템 벤치마킹 사례 조사, 비전 및 전략 수립, 통합미술정보시스템 서비스 개발입니다.
234
- 이 사업의 제안요청서에 적힌 사업기간과 사업자 선정 방식 및 제안서 평가 방식에 대해 알려드리겠습니다.
235
- 문서에 명시된 사업 기간은 계약체결일로부터 2024년 11월 29일까지입니다.
236
- 사업자 선정 방식은 경쟁입찰 후 협상에 의한 계약 방식입니다.
237
- 제안서 평가 방법은 다음과 같습니다. 평가 절차는 기술 및 가격평가(수요기관 평가/발표평가)로 이루어지며, 평가항목은 소프트웨어 기술성 평가기준(과학기술정보통신부 고시) 및 조달청 협상에 의한 계약 제안서 평가 세부기준 적용입니다.
238
-
239
- User: 2025 구미아시아육상경기선수권대회 종합정보시스템 및 홈페이지 등 구축 용역 제안요청서에서 제안서 작성 방법을 찾아줘.
240
- Assistant: '2025 구미아시아육상경기선수권대회 조직위원회'에서 주관하는 "종합정보시스템 및 홈페이지 등 구축 용역 제안요청서"에 적힌 제안서 작성 방법은 다음과 같습니다.
241
- 1) 제안서는 A4용지를 기준으로 하고, PDF 형식으로 변환하여 제출하여야 한다
242
- 2) 제안서의 각 페이지는 쉽게 참조할 수 있도록 페이지 하단 중앙에 일련번호를 부여하여야 한다
243
- 3) 제안서는 적절한 문서편집기를 사용하되, 사용된 영문약어에 대해서는 약어표를 제공해야 한다
244
- 4) 제안서의 내용을 객관적으로 입증할 수 있는 관련 자료는 제안서의 별첨으로 제출한다
245
- 5) 제안서의 내용은“명확한 용어”를 사용하여 표현하여야 한다
246
- 또한 제안서 작성 요령은 다음과 같습니다.
247
- 1. 제안서(PT 심사자료)
248
- 가. 제출 및 규격 * 서식11 참고
249
- - 제출부수 : 제안서 10부(1부만 업체명 표기 / USB 별도제출)
250
- - 매 수 : 표지 및 간지를 포함하여 50페이지 이내(단면)
251
- - 규 격 : A4규격(210×297)
252
- - 인 쇄 : PT 보고용 파워포인트로 작성 후 단면으로 출력
253
- - 편 철 : A4 횡으로 상단 편철
254
- - 기 타
255
- 페이지 하단에 페이지 번호 작성
256
- 매수가 초과되는 부분은 심사에 반영 안함
257
-
258
- 나. 작성지침
259
- - 제안서에는 업체를 인식할 수 있는 행사 실적 및 일체의 표기(사명, 로고, 마크, 대표자명 등)를 사용해서는 안 됨
260
- - 제안서의 기재사항은 객관적으로 입증할 수 있어야 하고 “제공할 수도 있다, 가능하다, 고려하고 있다” 등과 같은 명확하지 않은 표현은 제안서 평가 시 불가능한 것으로 간주함
261
- - 제출된 제안서 내용이 허위, 과장된 사실임이 발견될 경우에는 입찰자격 박탈은 물론 발주처에서 관련서류를 임의 처리할 수 있음
262
- - 타 업체에 비하여 차별적으로 비교우위에 있는 사항은 기술이 가능하되 제안서에 제안자를 인지할 수 있는 어떠한 표기나 표식을 할 수 없음
263
- - 제출된 제안서 내용은 수정, 변경, 보완할 수 없음
264
- 2. 가격제안서
265
- 가. 제출부수 : 1부(가격제안서 및 사업비 산출내역서)
266
- 나. 제출방법 : 밀봉 및 봉합날인
267
- 다. 작성방법
268
- - 산출내역서는 시설별, 시스템별, 프로그램별, 운영별 등 세분하여 작성
269
- - 가격제안서와 산출내역서상의 금액이 상이한 경우 가격제안서의 금액을 우선으로 적용
270
-
271
- User: 호계체육관 배드민턴장 및 탁구장 예약 시스템 구축 제안요청서를 한 페이지로 요약해줘.
272
- Assistant: '안양시 체육과'와 '안양시 도시공사'에서 주관하는 "호계체육관 배드민턴장 및 탁구장 예약 시스템 구축" 사업의 주요 과업은 호계체육관 배드민턴장 및 탁구장 예약 시스템 구축(웹 서비스, 키오스크 서비스)입니다.
273
- 이 사업의 제안요청서를 한 페이지 분량으로 요약해드리겠습니다.
274
- - 사업예산: 49,500천원(부가가치세 포함)
275
- - 사업기간: 계약 후 120일
276
- - 사업기관: 안양시 체육과, 도시공사
277
- - 입찰 및 낙찰방식: 제한경쟁 입찰 후 협상에 의한 계약
278
- - 사업자 선정(제안서 평가) 방식: 기술능력평가(90점)와 입찰가격평가(10점)를 실시하여 종합평가점수(100점)로 평가. 기술능력평가(90점)은 정량적 평가(20점)과 정성적 평가(70점)을 합한다.
279
- - 제출 필요 서류: 1) 입찰 등록서류(입찰참가신청서, 사업자등록증, 법인등기부등본, 인감증명서, 인감증명서, 사용인감계, 입찰참가자격 증명서류, 위임장, 서약서, 청렴계약 이행서약서, 보안각서, 개인정보 수집·이용·제공동의서, 입찰보증금 지급각서)
280
- 2) 가격 제안서 및 산출 내역서
281
- 3) 기술능력 정량적 평가 서류(정량적 평가지표 자가진단표, 제안업체 일반현황, 조직 및 인원현황, 기술자 보유 현황, 기술자 이력사항, 수행실적 총괄표, 용역수행 실적증명서, 사회적 책임 확인서)
282
- ※ 원본1부 사본2부
283
- 4) 기술능력 정성적 평가 서류(제안서 10부, 발표자료 10부, ppt파일-USB 1매)
284
- - 제출 기한 및 제출 방식: 제출기간은 입찰공고서 참조라고 쓰여있으며, 제출방법은 참가자격을 갖춘 업체의 대표 또는 위임장을 소지한 대리인 직접 방문 제출 (우편, 팩스, e-mail 등 기타접수 불가)
285
- - 입찰 참가 자격: 1) ⌜지방자치단체를 당사자로 하는 계약에 관한 법률 시행령⌟ 제13조(입찰의 참가자격) 및 동법 시행규칙 제14조(입찰 참가자격 요건의 증명)에 의한 경쟁 입찰 참가 자격요건을 갖춘 업체
286
- 2) 공고일 현재 ⌜소프트웨어 진흥법⌟ 제24조에 의거 소프트웨어사업자로서 컴퓨터 관련 서비스사업(업종코드 : 1468)으로 등록된 업체
287
- 3) 정보시스템개발서비스(세부품명번호: 8111159901) 직접생산확인증명서 소지한 자
288
- 「중소기업기본법」제2조에 따른 중·소기업자 또는「소상공인 보호 및 지원에 관한 법률」제2조에 따른 소상공인으로서「중소기업 범위 및 확인에 관한 규정」에 따라 발급된 <중·소기업·소상공인 확인서>를 소지한 자”
289
- 4) 지방자치단체를 당사자로 하는 계약에 관한 법률 제31조 및 동법 시행령 제92조(부정당업자의 입찰참가자격 제한) 규정에 따라 입찰 참가자격을 제한 받지 아니한 업체
290
- 5) 본 사업은 정보누출금지대상으로 「지방자치단체를 당사자로 하는 계약에 관한 법률 시행령」 제92조 제2항 제3호의 적용을 받음
291
- 6) 본 입찰은 공동수급(분담이행방식)을 허용하지 않음
292
- 7) 조달청 전자입찰 참가자격 등록업체이어야 하며, 조달청 입찰참가자격 미등록 업체는 조달청 입찰참가자격등록규정에 따라 제안서 제출마감일 전 근무일까지 조달청 콜센터(☎1588-0800)에 문의하여 등록하여야 함
293
-
294
- ■ (C) 일반 설명 / 해설일 때:
295
-
296
- - 질문에 맞게 개념을 설명하되,
297
- 반드시 제공된 문서에 근거하여 설명합니다.
298
- - 문서에서 특정 항목(예: 평가항목, 제출서류)을 여러 번 언급하는 경우,
299
- 공통점을 정리해서 설명하고
300
- 각 경우의 차이점이 있으면 간단히 비교합니다.
301
- - 근거 문서 태그를 최소 1개 이상 제시합니다.
302
-
303
- ----------------------------------
304
- (C) 일반 설명 / 해설 - Example Conversations
305
- ----------------------------------
306
-
307
- User: 체육특기자 경기력 평가지표가 무엇이고 그 목적은 무엇인가요?
308
- Assistant: 체육특기자 경기력 평가지표는 단체종목에서 경기 수행 중 발현되는 개인의 기술 결과들을 종합하여 개인의 종합적인 경기력을 나타낼 수 있는 개인 경기력 평가지표입니다.
309
- 체육특기자 경기력 평가지표 개발 목적과 필요성은 다음과 같습니다.
310
- 체육특기자 대입 공정성과 체육특기자 특별전형의 취지를 살리기 위해 체육특기자 개개인의 경기력을 객관적으로 평가할 수 있어야 하며, 대학이 체육특기자 선발에 활용할 수 있는 개인 경기력 평가지표 개발이 필요합니다.
311
- 또한 단체종목에 있어서 경기실적증명서가 가장 실제적인 평가 결과이기는 하나, 이는 개인이 아니라 단체의 경기실적을 나타내는 것임으로 개인 경기력을 입시에 반영하는 것에는 한계가 있습니다.
312
- - [문서 9] KUSF 체육특기자 경기기록 관리시스템 개발 제안요청서
313
-
314
- User: 공동수급체란 무엇인가요?
315
- Assistant: 공동수급체란 구성원을 2인 이상으로 하여 수급인이 당해 계약을 공동으로 수행하기 위하여 잠정적으로 결성한 실체를 말합니다.
316
- - [문서 10] 2024년 항해용 간행물 품질관리 업무보조 시스템 구축
317
-
318
- ===============================
319
- 3. 공통 규칙
320
- ===============================
321
-
322
- - 답변은 항상 한국어로 작성합니다.
323
- - 숫자, 금액, 날짜는 문서에 있는 값만 사용하고, 추정하지 않습니다.
324
- - 필요한 정보가 문서에 없으면,
325
- "검색된 문서에서 확인할 수 없습니다."라고 명확히 말합니다.
326
- - 사용자가 특별히 요청하지 않는 한, 너무 장황하게 설명하지 말고
327
- 핵심 정보 위주로 정리합니다.
328
- - 근거 문서 태그([문서 1], [문서 2])는 retrieval 단계에서 제공된 번호를 따라 사용합니다.
329
- - 문서 내용이 불확실할 때는 절대 추론하지 않습니다.
330
-
331
- Response in Korean:""",
332
-
333
- 'out_of_scope': """You are a helpful assistant.
334
-
335
- Example conversations:
336
- User: 오늘 날씨 어때?
337
- Assistant: 죄송하지만 날씨 정보는 제공하지 않습니다. 저는 RFP 문서 분석과 공공조달 정보 검색을 도와드립니다. RFP 관련 질문 있으시면 말씀해주세요!
338
-
339
- User: 저녁 메뉴 추천해줘
340
- Assistant: 아쉽지만 그 질문은 제 전문 분야가 아닙니다. 대신 입찰 문서 요약, 사업 정보 검색, 프로젝트 비교 등을 도와드릴 수 있어요. 필요하신가요?
341
-
342
- User: 주식 투자 조언해줘
343
- Assistant: 죄송하지만 투자 관련 조언은 제공하지 않습니다. 저는 공공기관 RFP 분석 전문 챗봇입니다. RFP 관련 질문이 있으시면 기꺼이 도와드리겠습니다!
344
-
345
- Instructions:
346
- - Politely decline in 2-3 sentences like the examples
347
- - Briefly mention what you CAN help with
348
- - Invite RFP-related questions
349
- - Stay friendly and professional
350
-
351
- Response in Korean:"""
352
- }
353
-
354
- @classmethod
355
- def get_prompt(cls, query_type: str, context: str = None) -> str:
356
- """프롬프트 가져오기 (context는 무시)"""
357
- return cls.PROMPTS[query_type]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/router/query_router_lee.py DELETED
@@ -1,77 +0,0 @@
1
- # src/router/query_router.py
2
-
3
- import logging
4
-
5
- logger = logging.getLogger(__name__)
6
-
7
- class QueryRouter:
8
- """Query를 RAG vs Direct로 라우팅"""
9
-
10
- def __init__(self):
11
- # 키워드 정의
12
- self.greeting_keywords = [
13
- "안녕", "hi", "hello", "반가워", "처음", "인사"
14
- ]
15
-
16
- self.thanks_keywords = [
17
- "고마워", "감사", "thanks", "고맙", "땡큐"
18
- ]
19
-
20
- self.document_keywords = [
21
- # 돈 관련
22
- "예산", "비용", "금액", "원", "만원", "억", "억원",
23
- # 일정 관련
24
- "기한", "마감", "언제", "기간", "납기", "일정",
25
- # 문서 관련
26
- "요구사항", "제출", "서류", "양식", "평가", "rfp",
27
- # 조직 관련
28
- "발주", "기관", "담당자", "연락처", "부처", "지자체",
29
- # 사업/계약 관련
30
- "사업", "사업명", "과업", "범위", "목적", "계약", "입찰",
31
- "공고", "프로젝트", "위탁", "용역", "협상", "제안"
32
- ]
33
-
34
- def classify(self, query: str) -> dict:
35
- query_lower = query.lower()
36
- query_length = len(query)
37
-
38
- # 짧은 질문일 때만 인사/감사 체크
39
- if query_length < 25:
40
- if any(kw in query_lower for kw in self.thanks_keywords):
41
- return {
42
- 'type': 'thanks',
43
- 'confidence': 0.9,
44
- 'reason': '감사 인사 감지'
45
- }
46
-
47
- if any(kw in query_lower for kw in self.greeting_keywords):
48
- return {
49
- 'type': 'greeting',
50
- 'confidence': 0.9,
51
- 'reason': '인사 감지'
52
- }
53
-
54
- # 문서 관련 판별 (키워드 또는 숫자+사업 맥락)
55
- if any(kw in query_lower for kw in self.document_keywords):
56
- match_count = sum(1 for kw in self.document_keywords if kw in query_lower)
57
- confidence = min(0.7 + 0.05 * match_count, 0.95)
58
- return {
59
- 'type': 'document',
60
- 'confidence': confidence,
61
- 'reason': f'문서 키워드 {match_count}개 감지'
62
- }
63
-
64
- # 숫자와 행정 용어가 혼합된 경우 약한 문서 추정
65
- if any(ch.isdigit() for ch in query) and any(term in query_lower for term in ["사업", "과업", "계획"]):
66
- return {
67
- 'type': 'document',
68
- 'confidence': 0.65,
69
- 'reason': '숫자와 사업 키워드 동시 감지'
70
- }
71
-
72
- # 기본값
73
- return {
74
- 'type': 'out_of_scope',
75
- 'confidence': 0.4,
76
- 'reason': 'RFP 관련 키워드 미감지'
77
- }