在Open-WebUI中提升向量化速度

问题背景

在之前的内容中,我们完成了一则壮举------使用Open-WebUI在一台旧安卓手机上搭建了一个完整的、专业级的AI聊天平台。然而,当前的网站存在一个极其影响用户体验的问题,就是每当用户在对话中开启网页搜索(Web Search)时,即使是一个简单的提问都需要等待20分钟以上才能生成响应。

如果你此时查看Open-WebUI的日志输出,会发现绝大部分时间有卡在了"向量化"步骤上。

kotlin 复制代码
2026-04-03 15:36:27.068 | INFO     | uvicorn.protocols.http.httptools_impl:send:483 - 192.168.1.31:57953 - "GET /_app/version.json HTTP/1.1" 200
Fetching pages:   0%|                                                                | 0/19 [00:00<?, ?it/s]2026-04-03 15:36:39.010 | WARNING  | open_webui.retrieval.web.utils:_fetch:578 - Error fetching https://interfax.com/newsroom/top-stories/116939/ with attempt 1/3: Cannot connect to host interfax.com:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1016)')]. Retrying...
Fetching pages:  68%|#####################################6                 | 13/19 [00:02<00:01,  5.20it/s]2026-04-03 15:36:41.427 | WARNING  | open_webui.retrieval.web.utils:_fetch:578 - Error fetching https://interfax.com/newsroom/top-stories/116939/ with attempt 2/3: Cannot connect to host interfax.com:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1016)')]. Retrying...
2026-04-03 15:36:44.853 | WARNING  | langchain_community.document_loaders.web_base:_fetch_with_rate_limit:271 - Error fetching https://interfax.com/newsroom/top-stories/116939/, skipping due to continue_on_failure=True
Fetching pages: 100%|#######################################################| 19/19 [00:06<00:00,  2.88it/s]
2026-04-03 15:36:47.132 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1373 - Using markdown header text splitter
2026-04-03 15:36:47.735 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1451 - generating embeddings for web-search-18dda4fe25ca52bd0a7c77b5a00c43ba8a35e10e56175199e2c4
Batches:  53%|████████████████████████████████▎                            | 92/174 [00:39<00:33,  2.42it/s]2026-04-03 15:37:27.926 | INFO     | uvicorn.protocols.http.httptools_impl:send:483 - 192.168.1.31:5269 - "GET /_app/version.json HTTP/1.1" 200
Batches: 100%|████████████████████████████████████████████████████████████| 174/174 [01:07<00:00,  2.56it/s]
2026-04-03 15:37:55.925 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1497 - embeddings generated 174 for 174 items
2026-04-03 15:37:55.935 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1509 - adding to collection web-search-18dda4fe25ca52bd0a7c77b5a00c43ba8a35e10e56175199e2c4
2026-04-03 15:37:58.132 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1515 - added 174 items to collection web-search-18dda4fe25ca52bd0a7c77b5a00c43ba8a35e10e56175199e2c4
Batches: 100%|████████████████████████████████████████████████████████████████| 3/3 [00:00<00:00,  8.25it/s]
2026-04-03 15:38:10.232 | INFO     | open_webui.retrieval.utils:query_doc:157 - query_doc:result [['7c906434-7993-46c5-9d0d-77a3b8800f2d', '3d81785b-aed4-4168-8e5f-1065dda477e5', '7240b58c-a7bc-4c5d-8df4-8dae36bd6676']] [[{'start_index': 4496, 'language': 'en', 'source': 'https://www.themoscowtimes.com/tag/Siberia', 'title': 'Articles - The Moscow Times', 'description': 'Independent news from Russia', 'embedding_config': "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}"}, {'language': 'en', 'source': 'https://indianexpress.com/about/siberia/', 'embedding_config': "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}", 'start_index': 772, 'description': 'Siberia News Updates: Get Latest Breaking News, Photos and Video News on Siberia. Also find Siberia latest news headlines, picture gallery, top videos, comments, blog posts and opinion at The Indian Express.', 'title': 'Latest News on Siberia: Get Siberia News Updates along with Photos, Videos and Latest News Headlines | The Indian Express'}, {'title': 'FSB Says It Defused 'Scooter Bomb' Parked Near Moscow Business Center - The Moscow Times', 'embedding_config': "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}", 'description': 'Russia's FSB security service said Friday that it thwarted a Ukrainian plot to assassinate a high-ranking security official in Moscow, which allegedly used "similar tactics" to the December 2024 assassination of the head of the Russian military's chemical weapons unit.', 'language': 'en', 'source': 'https://www.themoscowtimes.com/2026/04/03/fsb-says-it-defused-scooter-bomb-parked-near-moscow-business-center-a92421', 'start_index': 4251}]]
2026-04-03 15:38:10.280 | INFO     | open_webui.retrieval.utils:query_doc:157 - query_doc:result [['c893a516-eaf7-417f-8768-891f42bb35d0', 'cfcf65c9-705b-4d40-83c8-41b2adaeb26d', '21a02d88-4b21-4e42-9cd5-b27d26c4d237']] [[{'description': 'Latest news on Siberia, including wildfires, melting permafrost and other big stories concerning the vast geographical region.', 'start_index': 0, 'title': 'Siberia News | Latest News - NewsNow', 'embedding_config': "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}", 'language': 'No language found.', 'source': 'https://www.newsnow.com/us/World/Asia/Russia/Siberia'}, {'language': 'en', 'source': 'https://indianexpress.com/about/siberia/', 'description': 'Siberia News Updates: Get Latest Breaking News, Photos and Video News on Siberia. Also find Siberia latest news headlines, picture gallery, top videos, comments, blog posts and opinion at The Indian Express.', 'title': 'Latest News on Siberia: Get Siberia News Updates along with Photos, Videos and Latest News Headlines | The Indian Express', 'embedding_config': "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}", 'start_index': 0}, {'start_index': 0, 'language': 'No language found.', 'embedding_config': "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}", 'description': 'Latest news on Siberia, including wildfires, melting permafrost and other big stories concerning the vast geographical region.', 'source': 'https://www.newsnow.co.uk/h/World+News/Asia/Russia/Siberia', 'title': 'Siberia News | Latest News - NewsNow'}]]
2026-04-03 15:38:10.323 | INFO     | open_webui.retrieval.utils:query_doc:157 - query_doc:result [['c893a516-eaf7-417f-8768-891f42bb35d0', '21a02d88-4b21-4e42-9cd5-b27d26c4d237', '94135859-f23a-4345-902b-2b2db7b5202d']] [[{'description': 'Latest news on Siberia, including wildfires, melting permafrost and other big stories concerning the vast geographical region.', 'source': 'https://www.newsnow.com/us/World/Asia/Russia/Siberia', 'title': 'Siberia News | Latest News - NewsNow', 'start_index': 0, 'embedding_config': "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}", 'language': 'No language found.'}, {'source': 'https://www.newsnow.co.uk/h/World+News/Asia/Russia/Siberia', 'title': 'Siberia News | Latest News - NewsNow', 'language': 'No language found.', 'embedding_config': "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}", 'start_index': 0, 'description': 'Latest news on Siberia, including wildfires, melting permafrost and other big stories concerning the vast geographical region.'}, {'embedding_config': "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}", 'description': 'Latest news on Siberia, including wildfires, melting permafrost and other big stories concerning the vast geographical region.', 'source': 'https://www.newsnow.co.uk/h/World+News/Asia/Russia/Siberia', 'title': 'Siberia News | Latest News - NewsNow', 'start_index': 7825, 'language': 'No language found.'}]]

好吧。这是为何?为了解释,我们有必要看一下Open-WebUI的默认设置以及网页搜索功能(本质上是一种检索增强生成)的逻辑过程。

检索增强生成(Retrieval-Augmented Generation,RAG)是一种让AI能够使用实时信息提供服务的方法。简而言之,当用户发送提示词后,系统在一个外部数据库(可以是一系列公司内部文档或搜索得来的网页)中查找可能对AI有帮助的信息,接着将筛出的结果一并传递给AI作为上下文帮助AI响应,例如,回答用户问题。这样,模型无需纯依赖训练内容就可以实时使用最新信息响应。

网页搜索逻辑流程

回到在前文中我们搭建好的网站,以管理员账户登录。开启网页搜索,尝试询问AI一个问题。该过程十分缓慢痛苦,因为向量化提速的问题还未解决,但此处主要的目的是观察Open-WebUI的网页搜索逻辑。

展开搜索过程,注意到如下显示。

我们使用的模型(Ollama上的gemini-3-flash-preview:cloud)与Open-WebUI的协同运作过程大致如下(描述可能与Open-WebUI的实际内部运作方式存在偏差。如有误,欢迎在评论区指出):

  1. 首先,由于网页搜索开启,Open-WebUI(而非模型)将用户发送的提示词截获,无条件地将当前对话的最后6条消息,包括用户当前发送的这条提示词和最多前5条历史对话(如果有5条,即3条模型历史响应和2条历史提示词)打包到下面的提示词模板中发送给任务模型(Task Model)。

    md 复制代码
    ### Task:
    Analyze the chat history to determine the necessity of generating search queries, in the given language. By default, **prioritize generating 1-3 broad and relevant search queries** unless it is absolutely certain that no additional information is required. The aim is to retrieve comprehensive, updated, and valuable information even with minimal uncertainty. If no search is unequivocally needed, return an empty list.
    
    ### Guidelines:
    - Respond **EXCLUSIVELY** with a JSON object. Any form of extra commentary, explanation, or additional text is strictly prohibited.
    - When generating search queries, respond in the format: { "queries": ["query1", "query2"] }, ensuring each query is distinct, concise, and relevant to the topic.
    - If and only if it is entirely certain that no useful results can be retrieved by a search, return: { "queries": [] }.
    - Err on the side of suggesting search queries if there is **any chance** they might provide useful or updated information.
    - Be concise and focused on composing high-quality search queries, avoiding unnecessary elaboration, commentary, or assumptions.
    - Today's date is: {{CURRENT_DATE}}.
    - Always prioritize providing actionable and broad queries that maximize informational coverage.
    
    ### Output:
    Strictly return in JSON format: 
    {
    "queries": ["query1", "query2"]
    }
    
    ### Chat History:
    <chat_history>
    {{MESSAGES:END:6}}
    </chat_history>

    任务模型负责为当前对话生成标题、关键字和追问提示词等信息,且默认与当前对话模型相同(设置中可调)。

    如果愿意,你还可以更改搜索关键字生成提示词的模板来,例如,使模型一次生成更多关键字。留空以使用默认提示词。

  2. 我们在当前设置中指定了每次获取9条搜索结果(Search Result Count)。

    然而,如果观察,会发现Open-WebUI去获取了24个网页。

    这看上去十分令人费解,似乎Open-WebUI完全忽略了我们的设置。这是因为Open-WebUI会尝试为每个关键字都从我们配置的搜索引擎(在我们的环境中,SearXNG是前文中我们为Open-WebUI配置的元搜索引擎)获取9条搜索结果,所以实际获取到的网页数量是

    实际获取网页数 = 关键字数 ⋅ 搜索结果数 − 爬取失败的网页数 实际获取网页数=关键字数\cdot 搜索结果数-爬取失败的网页数 实际获取网页数=关键字数⋅搜索结果数−爬取失败的网页数

    那么它本应获取 3 ( 9 ) = 3(9)= 3(9)=27个网页,其中3条获取失败了。这样,这些数字就情有可原。

  3. 所以,Open-WebUI从SearXNG获取到了27条搜索结果后,其内置的爬虫引擎以默认10协程的并发方式将这些网页访问了一遍,下载完整网页内容。其中24条下载成功。

  4. 接下来,根据默认RAG设置所示,如果要处理的内容包含Markdown格式,例如用户在对话中手动上传了Markdown文件而非PDF,Open-WebUI会先对内容按照标题结构分成"大块",否则将把整个文档当作整体处理。该设置仅针对用户上传的文档等上下文,而非网页搜索结果,所以我们无需多虑。

    Open-WebUI将这些网页内容"砍"成每段1000字符(因为文本分割器,即Text Splitter的模式为Default (Character),即默认------字符)的小段(块),长度由块大小(Chunk Size)设置决定。每下一段开头与上一段末尾重复100字符(Chunk Overlap),以防止一句完整的句子被从中间生硬截断。

    接着,Open-WebUI将每一块(Chunk)提交给一个轻量级本地模型sentence-transformers/all-MiniLM-L6-v2(Embedding Model Engine,嵌入模型设置项),让其将这一段内的文本转换为一个代表这一段总结语义的高维向量(可以想象成一个数组,例如[0.1, 0.8..., 0.9],但可能有,例如,三千多维)。并且,一"块"的最小长度要求为0字符(Chunk Min Size Target)。也就是说,例如到了网页末尾,可以将剩下的不到1000字的内容算作一块,无论剩余多少,哪怕只有1字符。Open-WebUI每次提交给本地模型1块让其进行同时处理(Embedding Batch Size,嵌入批次大小)。

  5. Open-WebUI将所有文本切段,一点点提交给该模型。该模型将每一块的综合语义转为一个高维向量。这些转换完成的高维向量会被存在一个临时向量数据库中。

    等所有网页内容全部转换为向量后,Open-WebUI再次让任务模型使用与网页搜索关键字生成相同的提示词模板生成几个新的关键字(这里生成了3个),接着将关键字也转为向量,与由网页块转成的语义向量们进行对比。

    我们筛选出最多"K"个与关键字们有足够高的余弦相似度的1000字块(例如10个)并将它们的文本加入到给模型响应时使用的上下文中。余弦相似度是一种对比义向量相似度的方法。对于向量 A A A、 B B B,

    余弦相似度 ( A , B ) = A ⋅ B ∥ A ∥ ∥ B ∥ = ∑ i = 1 n A i B i ∑ i = 1 n A i 2 ∑ i = 1 n B i 2 \text{余弦相似度}(A, B) = \frac{A \cdot B}{\|A\| \|B\|} = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}} 余弦相似度(A,B)=∥A∥∥B∥A⋅B=∑i=1nAi2 ∑i=1nBi2 ∑i=1nAiBi

    我们可以使用Top K设置决定最多推送给模型多少块。Open-WebUI会保留最多K块相似度最高的块,作为上下文之后推送给模型。这里我设置为10。默认值可能是3。

    选取这些块后,Open-WebUI将最初的用户问题与所有选中的、相似度最高的块,打包在下面的提示词模板中发送给模型。这是模型真正收到的提示词。

    md 复制代码
    ### Task:
    Respond to the user query using the provided context, incorporating inline citations in the format [id] **only when the <source> tag includes an explicit id attribute** (e.g., <source id="1">).
    
    ### Guidelines:
    - If you don't know the answer, clearly state that.
    - If uncertain, ask the user for clarification.
    - Respond in the same language as the user's query.
    - If the context is unreadable or of poor quality, inform the user and provide the best possible answer.
    - If the answer isn't present in the context but you possess the knowledge, explain this to the user and provide the answer using your own understanding.
    - **Only include inline citations using [id] (e.g., [1], [2]) when the <source> tag includes an id attribute.**
    - Do not cite if the <source> tag does not contain an id attribute.
    - Do not use XML tags in your response.
    - Ensure citations are concise and directly related to the information provided.
    
    ### Example of Citation:
    If the user asks about a specific topic and the information is found in a source with a provided id attribute, the response should include the citation like in the following example:
    * "According to the study, the proposed method increases efficiency by 20% [1]."
    
    ### Output:
    Provide a clear and direct response to the user's query, including inline citations in the format [id] only when the <source> tag with id attribute is present in the context.
    
    <context>
    {{CONTEXT}}
    </context>

    值得注意的是,当模型完成响应,你继续发送下一条提示词时,对话历史记录并非以这种方式再次发给模型,而只是以你在屏幕上看到的那样储存。该模板每次只应用在用户当前发送的提示词上。

  6. 最终,Gemini根据所有被推来的达标"上下文块"和用户提示词这两部分信息生成响应。Open-WebUI的提示词模板还告诉Gemini在响应中包含引用块的网页作为展示给用户的信息来源。

    Open-WebUI会在响应底部显示来源总结。

    如果你点开其中一个网页链接,会发现Open-WebUI列出了此次传递给模型的所有来源于该网页、相关度够高的块,及其余弦相似度。

    如果你点击++内容++ ,Open-WebUI将自动转跳到包含该块的原始网页,并标出对应该块的文本。

值得注意的是,有时单纯对比一个问题的与某段文本的纯语义相似度并不能保证那段文本就是该问题最好的解答,因为解答需要回答问题,所以其语义和问题不一定高度一致。为此,你可以启用Top K Reranker,但我们将不在此讨论该功能。

确定缓慢原因

很好。现在我们理解了Open-WebUI进行网页搜索的完整逻辑过程。现在,就可以直截了当地解释------因为一台手机(我的设备是小米6X)的性能实在太差。将24个网页的全部文本内容以1000字为一块转换为语义向量花费20分钟已经是"很对得起人"了。

既然如此,我们有两种选择。

  • 坚持在本地完成向量化,或
  • 干脆将向量化工作交给云模型处理。

提速方案1:坚持本地(不推荐)

如果一定要在只有手机的情况下坚持在本地完成向量化,你可能需要执行下面的优化。

例如,先停止使用默认的all-MiniLM-L6-v2模型。我们将使用Ollama自己运行一个单独的向量化模型。如果你像我一样拥有另一台手机(华为Mate 8。详见之前的文章),还可以将向量化模型单独放在其上运行。

例如,我们在Ollama平台上筛选最受欢迎的向量化模型。

紧接着,我们选中一个模型。例如nomic-embed-text。

我们在Ollama中下载该模型。

shell 复制代码
ollama pull nomic-embed-text:latest

紧接着在Open-WebUI中配置下面的设置并保存。

  • 我们将分块方式换成了以词元(token)而非字符为单位(Token (Tiktoken)),这样能让我们更精确地"按字"分隔,控制一次传递给向量化模型的内容多少。
  • 我们保留Markdown标题文本分隔选项,尽管它与网页搜索结果RAG无关。
  • 我们将块大小设置为500,重叠设置为50。尽管该模型的最大上下文为2048个词元,我们最好将其设置得远低于上限。否则,即使设置为诸如1800之类的数字也可能导致Ollama返回400错误,声称提交的内容长度超过模型处理上限。而这是因为Open-WebUI的Tiktoken使用cl100k_base分词器,而nomic-embed-text使用一个基于BERT的分词器。这导致即使对于同样一段文本,Open-WebUI可能认为长度小于等于1800个词元,但nomic-embed-text却认为词元数大于2048!Ollama和Open-WebUI端400错误示例:
kotlin 复制代码
time=2026-04-05T11:59:12.167Z level=INFO source=server.go:1751 msg="llm embedding error: the input length exceeds the context length"
[GIN] 2026/04/05 - 12:09:14 | 200 |        15m45s |       127.0.0.1 | POST     "/api/embed"
[GIN] 2026/04/05 - 12:20:25 | 200 |        26m57s |       127.0.0.1 | POST     "/api/embed"
[GIN] 2026/04/05 - 12:34:06 | 200 |        40m37s |       127.0.0.1 | POST     "/api/embed
time=2026-04-05T12:40:29.350Z level=INFO source=server.go:1751 msg="llm embedding error: the input length exceeds the context length"
[GIN] 2026/04/05 - 12:40:29 | 200 |         47m0s |       127.0.0.1 | POST     "/api/embed"
time=2026-04-05T12:40:29.391Z level=INFO source=server.go:1751 msg="llm embedding error: the input length exceeds the context length"
time=2026-04-05T12:40:29.412Z level=INFO source=server.go:1751 msg="llm embedding error: the input length exceeds the context length"
[GIN] 2026/04/05 - 12:40:29 | 400 |         47m0s |       127.0.0.1 | POST     "/api/embed"
[GIN] 2026/04/05 - 12:40:39 | 500 | 10.143322965s |       127.0.0.1 | POST     "/api/chat"
[GIN] 2026/04/05 - 12:40:45 | 200 |  5.110776769s |       127.0.0.1 | POST     "/api/embed"
[GIN] 2026/04/05 - 12:40:55 | 500 | 10.044866194s |       127.0.0.1 | POST     "/api/chat"
kotlin 复制代码
2026-04-05 12:40:29.425 | ERROR    | open_webui.routers.retrieval:save_docs_to_vector_db:1518 - Ollama embed error (400): the input length exceeds the context length

Traceback (most recent call last):

  File "/usr/lib/python3.11/threading.py", line 1002, in _bootstrap
    self._bootstrap_inner()
    │    └ <function Thread._bootstrap_inner at 0x7e1a316340>
    └ <WorkerThread(AnyIO worker thread, started 537909850528)>
  File "/usr/lib/python3.11/threading.py", line 1045, in _bootstrap_inner
    self.run()
    │    └ <function WorkerThread.run at 0x7d8dc8ade0>
    └ <WorkerThread(AnyIO worker thread, started 537909850528)>
  File "/root/ow/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 1002, in run
    result = context.run(func, *args)
             │       │   │      └ ()
             │       │   └ functools.partial(<function save_docs_to_vector_db at 0x7db08a19e0>, <starlette.requests.Request object at 0x7d6116d250>, [Do...
             │       └ <method 'run' of '_contextvars.Context' objects>
             └ <_contextvars.Context object at 0x7d62e35200>
> File "/root/ow/lib/python3.11/site-packages/open_webui/routers/retrieval.py", line 1496, in save_docs_to_vector_db
    embeddings = future.result(timeout=embedding_timeout)
                 │      │              └ None
                 │      └ <function Future.result at 0x7e1a00f740>
                 └ <Future at 0x7d6114f110 state=finished raised Exception>
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 456, in result
    return self.__get_result()
           └ None
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
          └ None
  File "/root/ow/lib/python3.11/site-packages/open_webui/retrieval/utils.py", line 833, in async_embedding_function
    batch_results = await asyncio.gather(*tasks)
                          │       │       └ [<coroutine object get_embedding_function.<locals>.async_embedding_function.<locals>.generate_batch_with_semaphore at 0x7d8ba...
                          │       └ <function gather at 0x7e196742c0>
                          └ <module 'asyncio' from '/usr/lib/python3.11/asyncio/__init__.py'>
  File "/root/ow/lib/python3.11/site-packages/open_webui/retrieval/utils.py", line 828, in generate_batch_with_semaphore
    return await embedding_function(batch, prefix=prefix, user=user)
                 │                  │             │            └ UserModel(id='83a245de-83b8-42f3-b42d-f1c278f6d2a4', email='xyzzklk@hysotrmyclouds.com', username=None, role='admin', name='x...
                 │                  │             └ None
                 │                  └ ["Status of Python versions   Contents   Menu   Expand   Light mode   Dark mode   Auto light/dark, in light mode   Auto light...
                 └ <function get_embedding_function.<locals>.<lambda> at 0x7e165802c0>
  File "/root/ow/lib/python3.11/site-packages/open_webui/retrieval/utils.py", line 877, in generate_embeddings
    embeddings = await agenerate_ollama_batch_embeddings(
                       └ <function agenerate_ollama_batch_embeddings at 0x7db08a07c0>
  File "/root/ow/lib/python3.11/site-packages/open_webui/retrieval/utils.py", line 767, in agenerate_ollama_batch_embeddings
    raise Exception(f'Ollama embed error ({r.status}): {error_detail}')
Exception: Ollama embed error (400): the input length exceeds the context length
2026-04-05 12:40:55.303 | ERROR    | open_webui.routers.ollama:send_post_request:150 - Failed to parse error response: 500, message='Attempt to decode JSON with unexpected mimetype: application/x-ndjson', url='http://localhost:11434/api/chat'
  • 我们将块的最小长度要求设置为0,这样我们不放过任何一段文字。
  • 接着我们告诉Open-WebUI要使用一个Ollama API进行向量化转换。我们填写API地址,和目标模型名。
  • 我们将单次提交的批处理块数改为4,为了相比默认的1增加些效率。
  • 我们保留启用异步向量化处理,并将同时向Ollama API提交处理任务的线程数增加为10,为了尽量增加效率,即使手机发烫。

尝试下来,效果并不理想,甚至比原来的默认模型还要缓慢,手机却更烫。至于用户,估计早就放弃逃跑了。

提速方案2:迁移到云API(推荐)

不如将这种简单重复性劳动转移给云API去完成。如果你按照之前的文章完成了网站搭建,你应该已订阅了Mistral AI Studio和Google AI Studio。幸运的是,它们都提供向量化云模型!由于Mistral的词元限制更大方,我将配置Open-WebUI去Mistral完成这一任务。

至于模型选用,我将选择Mistral免费套餐内提供的mistral-embed-2312。该模型相比于codestral-embed更适合处理日常综合文本,而且词元上限也更高。

将你的配置更改为如下。

  • 我们继续按照词元数而非字符数分隔块。
  • 我们遇到Markdown文档时继续先按照标题拆分Markdown文档。
  • 我们将块大小改为1000,块重叠改为100。尽管mistral-embed的上下文上限为8196个词元,但将块设置得过大会导致向量无法精确捕捉块内含义,而使计算出的语义变为块内整段文字的"平均含义";块过小虽然能精确捕捉每一小段文字的具体含义,却会失去上下文,导致语句丧失意义。将块大小设置为500到1000,块重叠设置为块大小的 1 10 \frac{1}{10} 101是一个比较合理的范围。由于我们块大小为1000而远小于上限8196,即使mistral-embed与我们计量词元的方式不同也极不可能导致超出上下文限制。
  • 对于块大小下限,我们设置100。这是为了防止,例如,网页末尾的一段无用页脚被单独作为一段文本处理,浪费计算资源。
  • 接着,我们配置Open-WebUI使用OpenAI兼容API格式,填入Mistral的基础地址和秘钥,并将模型选择设置为mistral-embed-2312。
  • 鉴于Mistral的服务器算力极佳,我们可以将单次同时处理的任务数改为32,减少API的提交次数。如果你的网络很不稳定,可以改为16,减少单次API请求的内容大小。如果你网络极佳,还可以改为64。
  • 我们保留异步提交,但将同时提交的任务数改为1,因为Mistral对API调用频率极其严格,超过1秒1次会返回429错误(请求过于频繁)。
  • 我们最后保留Top K值不变。

现在,保存设置。更改位于前文中提到的~/OW虚拟环境文件夹名/lib/python3.11/.env文件,修改环境变量,将Open-WebUI的日志等级增加到DEBUG。

shell 复制代码
# .env
GLOBAL_LOG_LEVEL="DEBUG" # 添加这行
WEBUI_NAME="你的网站名称"
WEBUI_URL=https://你的完整三级域名
CORS_ALLOW_ORIGIN="https://你的完整三级域名;https://你手机的内网IP:Caddy端口;http://localhost:8080"
WEBUI_SESSION_COOKIE_SECURE=true
WEBUI_AUTH_COOKIE_SECURE=true
WEBUI_SESSION_COOKIE_SAME_SITE=lax
WEBUI_AUTH_COOKIE_SAME_SITE=lax

然后重启Open-WebUI。

shell 复制代码
open-webui serve

现在,开启网页搜索的情况下,询问AI某个问题,例如北京明天的天气,然后查看日志。

kotlin 复制代码
...
2026-04-08 10:20:49.521 | DEBUG    | open_webui.retrieval.web.utils:get_web_loader:703 - Using WEB_LOADER_ENGINE SafeWebBaseLoader for 18 URLs
Fetching pages: 100%|###################################################################| 18/18 [00:02<00:00,  6.14it/s]
2026-04-08 10:20:53.962 | DEBUG    | open_webui.routers.retrieval:save_docs_to_vector_db:1348 - save_docs_to_vector_db: document Beijing, Beijing, China Monthly Weather | AccuWeather, Access Denied, Beijing, Beijing, China Current Weather | AccuWeather, Beijing weather in April 2026 | Beijing 14 day weather, Beijing, People's Republic Of China 10-Day Weather Forecast | Weather Underground, Beijing weather in April 2026 | China: How hot?,
	Beijing Weather. 14 Day Forecast - WorldWeatherOnline.com
, Qianxinzhuang weather --- today, tomorrow & daily weather forecast, National and Local Weather Forecast ,china Meteorological Service for Public, Professional Weather Forecast, METEOFOR: Weather in Beijing for tomorrow, weather forecast for tomorrow Beijing, China, Historical Weather: Beijing, China (april 2026) • PredictWind,
        Beijing Weather in April 2026: What to Do & Weather Tips
    , Beijing, Beijing Municipality, China 14 day weather forecast, Weather in April 2026 in Beijing, Beijing Municipality, China, Beijing - BBC Weather, Beijing Weather in April 2026: Mild Climate, Windy, Weather Beijing in April 2026: Temperature & Climate web-search-96f657f4c8355efeaa07d82df8f2a7ad3515f29b91f004526007
2026-04-08 10:20:53.963 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1373 - Using markdown header text splitter
2026-04-08 10:20:54.201 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1411 - Using token text splitter: cl100k_base
2026-04-08 10:20:54.901 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1451 - generating embeddings for web-search-96f657f4c8355efeaa07d82df8f2a7ad3515f29b91f004526007
2026-04-08 10:20:54.905 | DEBUG    | open_webui.retrieval.utils:async_embedding_function:820 - generate_multiple_async: Processing 2 batches in parallel
2026-04-08 10:20:54.907 | DEBUG    | open_webui.retrieval.utils:agenerate_openai_batch_embeddings:588 - agenerate_openai_batch_embeddings:model mistral-embed-2312 batch size: 32
2026-04-08 10:20:56.875 | DEBUG    | open_webui.retrieval.utils:agenerate_openai_batch_embeddings:588 - agenerate_openai_batch_embeddings:model mistral-embed-2312 batch size: 11
2026-04-08 10:20:58.160 | DEBUG    | open_webui.retrieval.utils:async_embedding_function:847 - generate_multiple_async: Generated 43 embeddings from 2 parallel batches
2026-04-08 10:20:58.163 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1497 - embeddings generated 43 for 43 items
2026-04-08 10:20:58.168 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1509 - adding to collection web-search-96f657f4c8355efeaa07d82df8f2a7ad3515f29b91f004526007
2026-04-08 10:21:00.375 | INFO     | open_webui.routers.retrieval:save_docs_to_vector_db:1515 - added 43 items to collection web-search-96f657f4c8355efeaa07d82df8f2a7ad3515f29b91f004526007
2026-04-08 10:21:00.504 | DEBUG    | open_webui.utils.middleware:process_chat_payload:2465 - tool_ids=None
2026-04-08 10:21:00.506 | DEBUG    | open_webui.utils.middleware:process_chat_payload:2466 - direct_tool_servers=[]
2026-04-08 10:21:00.507 | DEBUG    | open_webui.routers.tasks:generate_queries:470 - generating retrieval queries using model gemini-3-flash-preview:cloud for user

现在,对于18个网页的搜索结果,向量化过程不超过10秒!

我们的问题被完美解决。

结语

Open-WebUI的网页搜索功能设计得有些僵硬。更好的方式其实是让模型调用工具,以智能体的(Agentic)方式自行搜索,而非一成不变地截获用户提示词执行固定流程。

相关推荐
QXWZ_IA14 分钟前
矿山安全监测从感知到预警的全链路算法
人工智能·安全
日日大王学编程36 分钟前
AI Agent 技术发展历程、演进逻辑与技术范式综述
人工智能·python
又过二十38 分钟前
rea-video拆解视频测试和效果展示
ai·aigc
看浪的路人1 小时前
番外1:Eval 工程化——从“跑分”到“防回归”
ai
Yuzhiyuxia1 小时前
【AI Agent面试系列】上下文管理1
人工智能·面试·职场和发展
王中阳Go1 小时前
预算给够了,时间花错了地方:三跳链路给了 27 秒,实际只用掉 9 秒
人工智能·后端·性能优化
方方洛2 小时前
ray教程-25-附录E-端到端实战案例
人工智能·分布式·机器学习
火山引擎开发者社区2 小时前
上万个 Agent 在线不乱跑,云沙箱给 Agent 造了间“独立办公室”
人工智能
神鹰手哥_起飞版2 小时前
一句话生成带品牌 logo 的架构图:方舟 Seed 2.1 负责听懂,代码负责画准
人工智能