如何通过 Ollama api 向模型传递正确的 context
结论
RAG 中的 context 无须对应 Ollama API 的同名参数。它就是检索得到的文本,可以和问题一起写入 prompt,也可以通过 /api/chat 的 messages[].content 发送给模型。
Ollama /api/generate 中已经弃用的 context 字段保存的是上一轮生成返回的 token 状态,用来延续对话,与 RAG 检索得到的参考资料含义不同。
LangChain 如何传入 context
下面代码里的 context 是提示词模板变量:
const messages = await promptTemplate.invoke({ question, context });
promptTemplate.invoke() 会把 question 和 context 渲染成普通消息。模板生成的内容可能如下:
请根据以下资料回答问题。
资料:
SQLite is a lightweight embedded database...
问题:
SQLite 适合什么场景?
随后,ChatOllama 会把渲染后的消息发送给 Ollama。模型收到的是消息文本,Ollama API 并没有接收一个专门用于 RAG 的 context 字段。
通过 /api/chat 传入 RAG 上下文
对话模型通常使用 /api/chat。可以用系统消息规定回答方式,再把检索内容和问题放进用户消息:
curl http://localhost:11434/api/chat \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.2:latest",
"messages": [
{
"role": "system",
"content": "请根据用户提供的参考资料回答问题。如果资料不足,请明确说明无法从资料中确定答案,不要自行补充事实。"
},
{
"role": "user",
"content": "参考资料:\n<context>\nSQLite 是一种嵌入式关系型数据库,数据库内容通常保存在单个文件中。它不需要独立的数据库服务器进程。\n</context>\n\n问题:\nSQLite 与传统客户端服务器数据库的主要区别是什么?"
}
],
"options": {
"temperature": 0
},
"stream": false
}'
非流式响应的答案通常在以下位置:
{
"message": {
"role": "assistant",
"content": "..."
}
}
<context> 并非 Ollama 规定的语法,它只是一个分隔标记,方便模型区分参考资料和问题。也可以改用 参考资料开始、Markdown 标题或其他清晰的分隔方式。
通过 /api/generate 传入上下文
如果不需要对话消息结构,可以把所有内容拼接成一个 prompt:
curl http://localhost:11434/api/generate \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.2:latest",
"prompt": "请仅根据以下参考资料回答问题。如果资料不足,请回答无法确定。\n\n<context>\nSQLite 是一种嵌入式关系型数据库,数据库内容通常保存在单个文件中。它不需要独立的数据库服务器进程。\n</context>\n\n问题:SQLite 与传统客户端服务器数据库的主要区别是什么?",
"options": {
"temperature": 0
},
"stream": false
}'
这种写法与 LangChain 渲染提示词后的效果接近,只是需要自行拼接提示词。
RAG 请求的组织方式
一个完整的 RAG 请求通常包含以下步骤:
- 根据用户问题生成 embedding,或执行关键词检索。
- 从 SQLite 或向量库中取回相关片段。
- 将取回的片段合并成参考资料。
- 把参考资料和问题写入同一条请求。
- 调用
/api/chat或/api/generate。
用 JavaScript 直接调用 /api/chat 时,可以这样组织请求:
const retrievedContext = documents
.map((document, index) => `[${index + 1}] ${document.content}`)
.join('\n\n');
const response = await fetch('http://localhost:11434/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'llama3.2:latest',
messages: [
{
role: 'system',
content:
'请仅根据用户提供的参考资料回答问题。资料不足时,请明确说明无法确定。',
},
{
role: 'user',
content: `参考资料:
<context>
${retrievedContext}
</context>
问题:
${question}`,
},
],
options: {
temperature: 0,
},
stream: false,
}),
});
if (!response.ok) {
throw new Error(`Ollama request failed: ${response.status}`);
}
const result = await response.json();
const answer = result.message.content;
使用 JSON.stringify() 构造请求体,可以防止引号、换行符或反斜杠破坏 JSON 格式。
区分三种 context
在这个场景里,context 可能有三种含义:
- RAG context:从 SQLite、向量数据库或其他数据源检索到的文本,需要写入提示词。
- Conversation context:此前的对话消息。调用
/api/chat时,可以继续把这些消息放入messages。 /api/generate的旧context字段:上一轮返回的 token 状态,用来延续生成,不能用于传递 RAG 文档。
变量可以命名为 context、documents 或 retrievedText。Ollama 不会根据变量名判断其用途,模型最终接收到的是请求里的文本和消息。
注意事项
建议用明确的分隔符包裹检索内容,并在提示词中要求模型“仅根据参考资料回答”。这样可以减少模型脱离资料作答的情况,但无法完全消除幻觉。
还要控制检索片段的总长度。问题、系统指令、历史消息和 RAG 文本都会占用模型的上下文窗口。超过可用长度后,部分内容可能被截断,具体行为取决于模型和 Ollama 配置。如果当前 Ollama 版本支持相关选项,可以检查 num_ctx 设置。不要盲目增大该值,否则会增加内存占用和推理成本。
外部文档中的文本还可能包含恶意指令。应用应将检索内容视为不可信数据,用清晰的边界包裹,并要求模型忽略参考资料里的操作指令。权限控制和数据隔离等安全措施不能只靠提示词实现。
备注:内容仅供参考。
