~/tools / 임베딩과 검색(RAG) / RAG Prompt Builder

RAG Prompt Builder — 번호 붙인 출처만으로 답하고 인용하는 Ollama /api/chat 요청 만들기

검색 결과와 질문으로 Ollama /api/chat 요청을 만듭니다. 모델에게 번호를 붙인 출처만으로 답하고 그것을 인용하라고 요청합니다. 선택하면 각 인용에 출처 번호와 인용문을 담은 JSON을 요구하고, temperature를 0으로 설정하며, "truncate": false를 보내 맞지 않는 프롬프트가 잘리는 대신 실패하게 합니다. 입력한 내용은 브라우저 밖으로 나가지 않습니다.

예시의 출처는 json_agg가 돌려주는 모양으로 직접 쓴 것입니다.

출처 3개, 객체의 JSON 배열로 읽었습니다.

curl 명령

이 컴퓨터의 Ollama(localhost:11434)로 요청을 보냅니다.

curl http://localhost:11434/api/chat -d '{
  "model": "qwen2.5:7b",
  "messages": [
    {
      "role": "system",
      "content": "Answer the question using only the numbered sources in the user message. Cite the number of each source you use. If the sources do not contain the answer, say that you do not know and cite nothing. Answer in the same language as the question. For each citation, give the source number and copy a short quote from that source exactly as it is written. Return JSON that matches this schema: {\"type\":\"object\",\"properties\":{\"answer\":{\"type\":\"string\"},\"citations\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"integer\"},\"quote\":{\"type\":\"string\"}},\"required\":[\"source\",\"quote\"]}}},\"required\":[\"answer\",\"citations\"]}"
    },
    {
      "role": "user",
      "content": "Sources:\n\n[1] (id: 12)\nWith HNSW, hnsw.ef_search is 40 by default, so a query returns at most 40 rows unless you raise it.\n\n[2] (id: 7)\nSET LOCAL inside a transaction changes hnsw.ef_search for a single query.\n\n[3] (id: 3)\npgvector supports PostgreSQL 13 and later.\n\nQuestion: Why does my HNSW search return only 40 rows with LIMIT 100?"
    }
  ],
  "stream": false,
  "format": {
    "type": "object",
    "properties": {
      "answer": {
        "type": "string"
      },
      "citations": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "source": {
              "type": "integer"
            },
            "quote": {
              "type": "string"
            }
          },
          "required": [
            "source",
            "quote"
          ]
        }
      }
    },
    "required": [
      "answer",
      "citations"
    ]
  },
  "options": {
    "temperature": 0
  },
  "truncate": false
}'

요청 본문(JSON)

{
  "model": "qwen2.5:7b",
  "messages": [
    {
      "role": "system",
      "content": "Answer the question using only the numbered sources in the user message. Cite the number of each source you use. If the sources do not contain the answer, say that you do not know and cite nothing. Answer in the same language as the question. For each citation, give the source number and copy a short quote from that source exactly as it is written. Return JSON that matches this schema: {\"type\":\"object\",\"properties\":{\"answer\":{\"type\":\"string\"},\"citations\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"integer\"},\"quote\":{\"type\":\"string\"}},\"required\":[\"source\",\"quote\"]}}},\"required\":[\"answer\",\"citations\"]}"
    },
    {
      "role": "user",
      "content": "Sources:\n\n[1] (id: 12)\nWith HNSW, hnsw.ef_search is 40 by default, so a query returns at most 40 rows unless you raise it.\n\n[2] (id: 7)\nSET LOCAL inside a transaction changes hnsw.ef_search for a single query.\n\n[3] (id: 3)\npgvector supports PostgreSQL 13 and later.\n\nQuestion: Why does my HNSW search return only 40 rows with LIMIT 100?"
    }
  ],
  "stream": false,
  "format": {
    "type": "object",
    "properties": {
      "answer": {
        "type": "string"
      },
      "citations": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "source": {
              "type": "integer"
            },
            "quote": {
              "type": "string"
            }
          },
          "required": [
            "source",
            "quote"
          ]
        }
      }
    },
    "required": [
      "answer",
      "citations"
    ]
  },
  "options": {
    "temperature": 0
  },
  "truncate": false
}

응답에서 답변은 message.content에 있습니다. prompt_eval_count가 예상보다 훨씬 낮다면 프롬프트가 잘렸을 수 있습니다.

요청 필드는 Ollama의 API 레퍼런스와 소스 코드에서 2026-11-05에 Ollama 0.40.11로 확인했습니다. Ollama API: /api/chat

모든 처리는 브라우저 안에서 이루어집니다. 도구는 요청을 작성할 뿐 어디에도 연결하지 않습니다.

검색 증강 생성(RAG)에서는 먼저 문서를 검색한 다음, 가장 가까운 구절을 질문과 함께 모델에 줍니다. 이 도구는 그 요청을 Ollama의 /api/chat용으로 작성합니다. 검색 결과는 문자열의 JSON 배열, 객체의 JSON 배열(예를 들어 PostgreSQL의 json_agg 출력이며, 텍스트는 content, text, chunk, document 또는 page_content에서 읽고 id, title, url은 출처의 제목에 덧붙습니다), 또는 --- 줄이나 빈 줄로 구분한 일반 텍스트로 붙여 넣습니다. 도구는 출처에 [1]부터 번호를 붙이고, 규칙은 system 메시지에, 출처와 이어서 질문은 하나의 user 메시지에 넣습니다. JSON 옵션을 쓰면 format 필드에 답변과 인용 목록(출처 번호와 인용문)을 담은 JSON Schema가 들어가고, Ollama의 구조화된 출력 페이지가 권하는 대로 같은 스키마가 system 메시지에도 적힙니다. 쓰지 않으면 모델에게 각 문장 뒤에 번호를 대괄호로 적으라고 요청합니다. Ollama는 긴 프롬프트를 오류 없이 자르기 때문에 truncate 옵션이 중요합니다. Ollama의 소스 코드(0.40.11)에서는 처음 몇 개의 토큰과 프롬프트의 끝을 남기고 그 사이를 버리며, num_ctx 1024로 한 우리의 테스트에서는 이 때문에 system 메시지와 첫 번째 출처들이 사라져 답이 바뀌었습니다. truncate 필드는 Ollama의 소스 코드에는 있지만 /api/chat 페이지에는 없으며, "truncate": false를 주면 같은 요청이 대신 HTTP 400을 돌려주었습니다. 이 도구의 요청을 두 모드 모두에서 qwen2.5:7b로 Ollama 0.40.11에 보냈습니다.

이 도구의 핵심 로직은 MIT 라이선스의 오픈 소스로 공개되어 있습니다. github.com/seigo5555/toolkitbox-lib/src/rag-prompt.ts

이럴 때 사용

  • 내 매뉴얼이나 메모에 대한 질문에 로컬 모델로, 각 문장의 출처를 붙여 답하기
  • 애플리케이션 코드를 쓰기 전에 실제 검색 결과 몇 개로 프롬프트와 인용 형식을 시험하기
  • 검색 결과가 모델의 컨텍스트 길이에 아직 들어가는지 알아보기

사용법

  1. 질문을 입력하고 검색 결과를 붙여 넣으세요. 예를 들어 pgvector SQL Builder의 쿼리가 돌려준 행입니다.
  2. 모델을 고르고, 필요하면 num_ctx와 JSON 인용, temperature 0, truncate 옵션을 고르세요.
  3. curl 명령이나 JSON 본문을 복사해 Ollama에 보내세요. 답변과 같은 출처를 RAG Citation Checker에 붙여 넣으세요.

FAQ

데이터베이스 id 대신 출처에 번호를 붙이는 이유는 무엇인가요?

짧은 번호는 긴 id보다 모델이 옮겨 적기 쉽고, 보낸 목록과 대조해 확인할 수 있습니다. 각 객체의 id, title, url은 번호 옆에 그대로 표시되므로 번호를 원래 행으로 되돌릴 수 있습니다.

질문이 출처 뒤에 오는 이유는 무엇인가요?

프롬프트가 컨텍스트 길이보다 길면 Ollama는 프롬프트의 끝을 남기고 그 앞의 텍스트를 버리므로, 끝에 있는 질문이 남는 부분입니다. 그래도 "truncate": false를 보내는 편이 더 안전합니다. 오류가 나므로 보내는 행을 줄이거나 num_ctx를 올릴 수 있습니다.

출처를 몇 개까지 보낼 수 있나요?

도구는 최대 50개까지 받습니다. 몇 개가 들어가는지는 출처의 길이와 컨텍스트 길이에 달려 있습니다. Ollama 문서는 VRAM이 24 GiB 미만이면 기본값을 4k 토큰이라고 합니다. 응답의 prompt_eval_count 필드는 프롬프트가 쓴 토큰 수를 보여 줍니다.

JSON 옵션을 쓰면 인용이 정확하다고 보장되나요?

아니요. format 필드는 답변이 JSON Schema를 따르게 하지만, 모델은 여전히 엉뚱한 출처를 인용하거나 출처에 없는 내용을 인용할 수 있습니다. 답변은 RAG Citation Checker로 확인하세요.

OpenAI 호환 API에서도 쓸 수 있나요?

이 도구는 네이티브 /api/chat 요청을 작성합니다. Ollama 문서에 따르면 구조화된 출력은 OpenAI 호환 API에서도 response_format으로 동작하지만, 요청의 모양이 다릅니다.

내 데이터가 어딘가로 전송되나요?

아니요. 요청은 브라우저 안에서 만들어집니다. 도구는 Ollama에 연결하지 않습니다.

관련 글

관련 도구