~/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,同一份 schema 也會寫進 system 訊息,這正是 Ollama 結構化輸出頁面的建議。不選用時,則是要求模型在每個句子後面用方括號寫出編號。truncate 選項很重要,因為 Ollama 會在沒有任何錯誤的情況下截斷過長的提示詞:在 Ollama 的原始碼(0.40.11)中,它保留開頭的幾個 token 與提示詞的結尾,並丟棄中間的部分;在我們用 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 個 token。回應中的 prompt_eval_count 欄位會顯示提示詞用了多少個 token。

JSON 選項能保證引用正確嗎?

不能。format 欄位會讓回答遵循 JSON Schema,但模型仍可能引用錯誤的來源,或引用來源中沒有的內容。請用 RAG Citation Checker 檢查回答。

可以搭配相容 OpenAI 的 API 使用嗎?

本工具寫出的是原生的 /api/chat 請求。Ollama 的文件指出,結構化輸出也能透過相容 OpenAI 的 API 以 response_format 使用,但請求的形式不同。

我的資料會被傳送到任何地方嗎?

不會。請求是在你的瀏覽器中建立的。本工具從不連線到 Ollama。

相關文章

相關工具