디바이스 내의 문서 컬렉션에 관한 질문에 답변하는 RAG(검색 증강 생성) 애플리케이션을 제작하세요. RAG는 포함 기반 검색을 채팅 모델과 결합하여 모델의 학습 지식에만 의존하지 않고 사용자 고유의 데이터에 대한 답변을 기반으로 합니다.
이 자습서에서는 다음 방법을 알아봅니다.
- 프로젝트 설정 및 Foundry 로컬 SDK 설치
- 텍스트 문서의 기술 자료 만들기
- 문서에 대한 임베딩 생성
- 유사성을 사용하여 관련 문서 검색
- 검색된 컨텍스트에 기반한 답변 생성
- 리소스 정리
필수 구성 요소
- RAM이 8GB 이상인 Windows, macOS 또는 Linux 컴퓨터
- Python 3.11 이상이 설치되었습니다.
패키지 설치
Windows 개발하거나 배송하는 경우 Windows 탭을 선택합니다. Windows 패키지는 Windows ML 런타임과 통합되어 광범위한 하드웨어 가속으로 동일한 API 노출 영역을 제공합니다.
기술 자료 만들기
RAG 애플리케이션에는 검색할 문서 컬렉션이 필요합니다. 기술 자료 역할을 하는 텍스트 문자열 목록을 정의합니다. 프로덕션 애플리케이션에서 목록은 파일, 데이터베이스 레코드 또는 기타 텍스트 원본의 단락일 수 있습니다.
호출 main.py 된 파일을 만들고 다음 코드를 추가합니다.
import math
from foundry_local_sdk import Configuration, FoundryLocalManager
# Knowledge base — each string represents a document
documents = [
"Foundry Local runs AI models directly on your device without cloud connectivity.",
"The Foundry Local SDK supports Python, C#, JavaScript, and Rust.",
"Embedding models convert text into numerical vectors for similarity search.",
"Foundry Local uses ONNX Runtime for efficient model inference on CPUs and GPUs.",
"The model catalog provides pre-optimized models that you can download and run locally.",
"Retrieval-augmented generation grounds model responses in your own data.",
"Vector similarity search finds documents that are semantically close to a query.",
"Chat completions generate natural language responses from a prompt and context.",
]
문서 임베딩 생성
SDK를 초기화하고, 포함 모델을 로드하고, 각 문서를 숫자 벡터로 변환합니다. 이러한 벡터는 각 문서의 의미 체계 의미를 나타내며 유사성 검색을 사용하도록 설정합니다.
다음 코드를 main.py에 추가합니다.
def main():
# Initialize the SDK
config = Configuration(app_name="foundry_local_rag")
FoundryLocalManager.initialize(config)
manager = FoundryLocalManager.instance
# Load the embedding model
embedding_model = manager.catalog.get_model("qwen3-embedding-0.6b")
embedding_model.download(
lambda p: print(f"\rDownloading embedding model: {p:.1f}%", end="", flush=True)
)
print()
embedding_model.load()
embedding_client = embedding_model.get_embedding_client()
# Embed all documents in a single batch call
response = embedding_client.generate_embeddings(documents)
doc_embeddings = [item.embedding for item in response.data]
print(f"Indexed {len(doc_embeddings)} documents.")
이 메서드는 generate_embeddings 문자열 목록을 허용하고 입력당 하나의 벡터를 반환합니다. 각 벡터는 텍스트의 의미 체계적 의미를 캡처하므로 유사한 문서는 포함 공간에서 서로 가까운 벡터를 생성합니다.
관련 문서 검색
쿼리와 관련된 문서를 찾으려면 코사인 유사성을 사용하여 쿼리의 포함을 모든 문서 포함과 비교합니다. 코사인 유사성은 크기에 관계없이 두 벡터가 방향에 얼마나 가까운지 측정합니다. 1.0에 가까운 값은 높은 유사성을 나타냅니다.
다음 도우미 함수를 main() 위의 main.py에 추가합니다.
def cosine_similarity(a, b):
"""Compute cosine similarity between two vectors."""
dot = sum(x * y for x, y in zip(a, b))
norm_a = math.sqrt(sum(x * x for x in a))
norm_b = math.sqrt(sum(x * x for x in b))
return dot / (norm_a * norm_b) if norm_a and norm_b else 0.0
def find_relevant(query_embedding, doc_embeddings, top_k=2):
"""Return the indices and scores of the top-k most similar documents."""
scores = []
for i, doc_emb in enumerate(doc_embeddings):
score = cosine_similarity(query_embedding, doc_emb)
scores.append((i, score))
scores.sort(key=lambda x: x[1], reverse=True)
return scores[:top_k]
함수는 find_relevant 모든 문서의 순위를 유사성으로 지정하고 상위 일치 항목을 반환합니다. 이 방법은 작은 컬렉션에 적합합니다. 더 큰 데이터 세트의 경우 전용 벡터 데이터베이스를 고려합니다.
근거 있는 답변 생성
채팅 모델을 로드하고 시스템 프롬프트에서 검색된 문서를 사용자의 질문과 결합합니다. 채팅 모델은 제공된 컨텍스트를 사용하여 문서에 기반한 답변을 생성합니다.
포함 섹션 다음에 다음 코드를 main() 함수에 추가합니다.
# Load the chat model
chat_model = manager.catalog.get_model("qwen2.5-0.5b")
chat_model.download(
lambda p: print(f"\rDownloading chat model: {p:.1f}%", end="", flush=True)
)
print()
chat_model.load()
chat_client = chat_model.get_chat_client()
print("\nModels loaded. Ready for questions.")
print("\nThe knowledge base contains information about:")
print(" - Foundry Local features and architecture")
print(" - Supported programming languages")
print(" - Embedding models and vector search")
print(" - ONNX Runtime inference")
print(" - The model catalog")
print(" - RAG and chat completions")
print("\nExample questions:")
print(' "What programming languages does the SDK support?"')
print(' "How does Foundry Local run models?"')
print(' "What is retrieval-augmented generation?"')
print('\nType "quit" to exit.\n')
# Interactive query loop
while True:
query = input("Question: ").strip()
if not query or query.lower() == "quit":
break
# Embed the query
query_response = embedding_client.generate_embedding(query)
query_embedding = query_response.data[0].embedding
# Retrieve the most relevant documents
results = find_relevant(query_embedding, doc_embeddings, top_k=2)
context = "\n".join(f"- {documents[i]}" for i, _ in results)
# Build the prompt with retrieved context
messages = [
{
"role": "system",
"content": (
"Answer the user's question using only the provided context. "
"If the context doesn't contain enough information, say so.\n\n"
f"Context:\n{context}"
),
},
{"role": "user", "content": query},
]
# Stream the response
print("Answer: ", end="", flush=True)
for chunk in chat_client.complete_streaming_chat(messages):
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
print("\n")
# Clean up
embedding_model.unload()
chat_model.unload()
print("Models unloaded. Done!")
if __name__ == "__main__":
main()
시스템 프롬프트는 검색된 컨텍스트만 사용하여 응답하도록 모델에 지시합니다. 시스템 프롬프트는 응답이 문서에 기반을 두도록 유지하여 잘못된 답변을 줄입니다. 스트리밍 출력은 각 토큰이 생성될 때 출력되므로 응답이 대화형으로 느껴집니다.
전체 코드
다음은 모든 단계를 결합하는 전체 애플리케이션입니다.
import math
from foundry_local_sdk import Configuration, FoundryLocalManager
# Knowledge base
documents = [
"Foundry Local runs AI models directly on your device without cloud connectivity.",
"The Foundry Local SDK supports Python, C#, JavaScript, and Rust.",
"Embedding models convert text into numerical vectors for similarity search.",
"Foundry Local uses ONNX Runtime for efficient model inference on CPUs and GPUs.",
"The model catalog provides pre-optimized models that you can download and run locally.",
"Retrieval-augmented generation grounds model responses in your own data.",
"Vector similarity search finds documents that are semantically close to a query.",
"Chat completions generate natural language responses from a prompt and context.",
]
def cosine_similarity(a, b):
"""Compute cosine similarity between two vectors."""
dot = sum(x * y for x, y in zip(a, b))
norm_a = math.sqrt(sum(x * x for x in a))
norm_b = math.sqrt(sum(x * x for x in b))
return dot / (norm_a * norm_b) if norm_a and norm_b else 0.0
def find_relevant(query_embedding, doc_embeddings, top_k=2):
"""Return the indices and scores of the top-k most similar documents."""
scores = []
for i, doc_emb in enumerate(doc_embeddings):
score = cosine_similarity(query_embedding, doc_emb)
scores.append((i, score))
scores.sort(key=lambda x: x[1], reverse=True)
return scores[:top_k]
def main():
# Initialize the SDK
config = Configuration(app_name="foundry_local_rag")
FoundryLocalManager.initialize(config)
manager = FoundryLocalManager.instance
# Load the embedding model
embedding_model = manager.catalog.get_model("qwen3-embedding-0.6b")
embedding_model.download(
lambda p: print(f"\rDownloading embedding model: {p:.1f}%", end="", flush=True)
)
print()
embedding_model.load()
embedding_client = embedding_model.get_embedding_client()
# Embed all documents
response = embedding_client.generate_embeddings(documents)
doc_embeddings = [item.embedding for item in response.data]
print(f"Indexed {len(doc_embeddings)} documents.")
# Load the chat model
chat_model = manager.catalog.get_model("qwen2.5-0.5b")
chat_model.download(
lambda p: print(f"\rDownloading chat model: {p:.1f}%", end="", flush=True)
)
print()
chat_model.load()
chat_client = chat_model.get_chat_client()
print("\nModels loaded. Ready for questions.")
print("\nThe knowledge base contains information about:")
print(" - Foundry Local features and architecture")
print(" - Supported programming languages")
print(" - Embedding models and vector search")
print(" - ONNX Runtime inference")
print(" - The model catalog")
print(" - RAG and chat completions")
print("\nExample questions:")
print(' "What programming languages does the SDK support?"')
print(' "How does Foundry Local run models?"')
print(' "What is retrieval-augmented generation?"')
print('\nType "quit" to exit.\n')
# Interactive query loop
while True:
query = input("Question: ").strip()
if not query or query.lower() == "quit":
break
# Embed the query
query_response = embedding_client.generate_embedding(query)
query_embedding = query_response.data[0].embedding
# Retrieve the most relevant documents
results = find_relevant(query_embedding, doc_embeddings, top_k=2)
context = "\n".join(f"- {documents[i]}" for i, _ in results)
# Build the prompt with retrieved context
messages = [
{
"role": "system",
"content": (
"Answer the user's question using only the provided context. "
"If the context doesn't contain enough information, say so.\n\n"
f"Context:\n{context}"
),
},
{"role": "user", "content": query},
]
# Stream the response
print("Answer: ", end="", flush=True)
for chunk in chat_client.complete_streaming_chat(messages):
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
print("\n")
# Clean up
embedding_model.unload()
chat_model.unload()
print("Models unloaded. Done!")
if __name__ == "__main__":
main()
애플리케이션을 실행합니다.
python main.py
다음과 유사한 출력이 표시됩니다.
Downloading embedding model: 100.0%
Indexed 8 documents.
Downloading chat model: 100.0%
Models loaded. Ready for questions.
The knowledge base contains information about:
- Foundry Local features and architecture
- Supported programming languages
- Embedding models and vector search
- ONNX Runtime inference
- The model catalog
- RAG and chat completions
Example questions:
"What programming languages does the SDK support?"
"How does Foundry Local run models?"
"What is retrieval-augmented generation?"
Type "quit" to exit.
Question: What programming languages does the SDK support?
Answer: The Foundry Local SDK supports Python, C#, JavaScript, and Rust.
Question: quit
Models unloaded. Done!
리소스 정리
모델 가중치는 모델을 언로드한 후 로컬 캐시에 유지됩니다. 다음에 애플리케이션을 실행할 때 다운로드 단계를 건너뛰고 모델이 더 빠르게 로드됩니다. 디스크 공간을 회수하려는 경우가 아니면 추가 정리가 필요하지 않습니다.