~/tools / Chạy LLM cục bộ trên máy chủ của bạn / Ollama Stream Reader

Ollama Stream Reader — Ghép luồng Ollama và xem tốc độ cùng lý do dừng

Dán một phản hồi dạng luồng từ Ollama, từ /api/chat, /api/generate hoặc /v1/chat/completions tương thích OpenAI, và công cụ ghép các mảnh lại thành toàn bộ văn bản, thinking và tool calls. Nó cho biết vì sao mô hình dừng, tốc độ sinh và tốc độ xử lý prompt, luồng có bị cắt giữa chừng không, và đưa ra thông điệp assistant để thêm vào yêu cầu tiếp theo. Không có gì rời khỏi trình duyệt của bạn.

Cách lưu một luồng bằng curl

-N tắt bộ đệm của curl, và > stream.txt lưu luồng vào một tệp. Thay mô hình và thông điệp bằng của bạn.

curl -sN http://localhost:11434/api/chat -d '{"model":"qwen2.5:7b","messages":[{"role":"user","content":"Hello"}]}' > stream.txt

curl -sN http://localhost:11434/v1/chat/completions -H "Content-Type: application/json" -d '{"model":"qwen2.5:7b","stream":true,"stream_options":{"include_usage":true},"messages":[{"role":"user","content":"Hello"}]}' > stream.txt

Đã đọc là một luồng /api/chat (mỗi dòng một đối tượng JSON).

Tóm tắt

Mô hình
qwen2.5:7b
Lý do dừng
stop
Số chunk
7
Token đã sinh
7
Tốc độ sinh
10.4 token/giây
Token của prompt
35
Tốc độ prompt (token không nằm trong cache)
64.1 token/giây
Thời gian tải mô hình
4.923 giây
Tổng thời gian
6.310 giây
Từ chunk đầu đến chunk cuối
0.675 giây

Văn bản

Hello, nice to meet.

Thông điệp assistant cho yêu cầu tiếp theo

Thêm thông điệp này vào messages, tiếp theo là kết quả của các tool call, nếu có.

{
  "role": "assistant",
  "content": "Hello, nice to meet."
}

eval_count cũng đếm các token của tool calls, vốn không được truyền dưới dạng văn bản. Trong thử nghiệm của chúng tôi, một tool call đến trong một chunk duy nhất với eval_count 20.

Tài liệu của Ollama nói rằng prompt_eval_duration chỉ tính các token prompt không nằm trong cache. Tốc độ prompt ở đây chỉ chia các token đó. Mọi thời lượng đều tính bằng nano giây; ở dạng OpenAI, timings tính bằng mili giây nguyên.

Ở dạng tương thích OpenAI, số token chỉ được gửi cùng stream_options.include_usage, trong một chunk bổ sung trước data: [DONE]. Nếu luồng bị cắt giữa chừng, chunk đó sẽ không đến.

Các định dạng lấy từ tài liệu về streaming và usage của Ollama cùng mã nguồn của nó (v0.40.15), được kiểm tra với các luồng thật từ Ollama 0.40.11 vào 2026-11-07. Ollama: Streaming

Mọi việc đều được làm trong trình duyệt của bạn. Công cụ không kết nối tới đâu cả.

Ollama truyền câu trả lời theo luồng theo mặc định. /api/chat và /api/generate gửi mỗi dòng một đối tượng JSON (newline-delimited JSON): mỗi dòng mang vài ký tự của câu trả lời, và dòng cuối có done đặt là true cùng số token và thời lượng, tính bằng nano giây. /v1/chat/completions tương thích OpenAI chỉ truyền luồng khi stream là true. Khi đó nó gửi server-sent events: các dòng bắt đầu bằng data:, một chunk riêng có delta rỗng và finish_reason, và data: [DONE] ở cuối. Số token chỉ có khi yêu cầu đặt stream_options.include_usage, trong một chunk bổ sung có danh sách choices rỗng, và Ollama thêm một đối tượng timings vào đó. Công cụ này đọc cả ba dạng, kể cả đầu ra của curl -i kèm tiêu đề HTTP, ghép content, thinking (reasoning ở dạng OpenAI) và tool calls, rồi liệt kê điều gì không ổn: luồng không có dòng done hoặc không có [DONE] đã bị cắt giữa chừng; một dòng {"error": …} nghĩa là Ollama dừng vì lỗi sau khi đã bắt đầu gửi; done_reason là length nghĩa là đã đạt num_predict hoặc max_tokens. Tốc độ sinh là eval_count chia cho eval_duration. Với prompt, tài liệu của Ollama nói rằng prompt_eval_duration chỉ tính các token prompt không nằm trong cache, nên công cụ chỉ chia các token đó cho thời lượng này. Ở dạng OpenAI, timings.prompt_per_second của chính Ollama chia tất cả token prompt, kể cả token từ cache, cho thời lượng đó. Trong thử nghiệm của chúng tôi, nó báo 360.8 token/giây cho một prompt có 34 trên 35 token đến từ cache và token còn lại mất 97 ms. Tool calls ở dạng OpenAI có thể đến thành nhiều mảnh dùng chung một index, nên công cụ ghép arguments theo index; bản thân Ollama gửi mỗi tool call nguyên vẹn trong một chunk. Chúng tôi đã kiểm tra công cụ với các luồng thật từ Ollama 0.40.11 với qwen2.5:7b, và kiểm tra các định dạng với tài liệu và mã nguồn của Ollama.

Phần xử lý chính của công cụ này là mã nguồn mở theo giấy phép MIT. github.com/seigo5555/toolkitbox-lib/src/ollama-stream.ts

Trường hợp dùng

  • Xem toàn bộ câu trả lời của một yêu cầu dạng luồng bạn gửi bằng curl, thay vì đọc từng mảnh một dòng
  • Tìm hiểu xem luồng bị cắt giữa chừng, dừng ở giới hạn length hay kết thúc bằng lỗi
  • Đo tốc độ sinh và tốc độ xử lý prompt của một mô hình trên máy hoặc máy chủ của bạn

Cách dùng

  1. Chạy yêu cầu của bạn với curl -sN và lưu đầu ra vào một tệp, hoặc sao chép những gì chương trình của bạn nhận được.
  2. Dán toàn bộ luồng. Công cụ nhận ra nó đến từ /api/chat, /api/generate hay /v1/chat/completions.
  3. Đọc các vấn đề và các con số, rồi sao chép văn bản hoặc thông điệp assistant cho yêu cầu tiếp theo.

Hỏi đáp

Vì sao luồng /v1/chat/completions của tôi không có số token?

Ở dạng tương thích OpenAI, các số đếm chỉ được gửi khi yêu cầu đặt stream_options là {"include_usage": true}. Chúng nằm trong một chunk bổ sung có danh sách choices rỗng, ngay trước data: [DONE]. /api/chat và /api/generate luôn đặt chúng ở dòng cuối.

Vì sao tốc độ prompt khác nhiều so với timings.prompt_per_second?

Tài liệu của Ollama nói rằng prompt_eval_duration chỉ tính các token prompt không nằm trong cache. timings.prompt_per_second của Ollama chia tất cả token prompt, kể cả token từ cache, cho thời lượng đó, nên trông nhanh hơn nhiều khi cache được dùng. Công cụ chỉ chia các token không nằm trong cache.

Mô hình đã gọi một công cụ nhưng eval_count là 20. Những token đó ở đâu?

Văn bản của một tool call không được truyền dưới dạng content; Ollama chuyển nó thành tool_calls và gửi trong một chunk. Các token vẫn được sinh ra và được đếm. Trong thử nghiệm của chúng tôi, một tool call có một đối số đến trong một chunk duy nhất với eval_count 20.

Vì sao /api/chat báo stop khi mô hình đã gọi một công cụ?

/api/chat giữ done_reason là stop. Endpoint tương thích OpenAI đổi stop thành tool_calls trong finish_reason khi một tool call đã được gửi.

Tôi nên làm gì với thông điệp assistant?

Tài liệu của Ollama lưu ý rằng bạn cần ghép các mảnh đã truyền để giữ lịch sử hội thoại, điều này quan trọng nhất với tool calls. Thêm thông điệp vào messages, thêm kết quả của mỗi tool call dưới dạng một thông điệp có role là tool, rồi gửi yêu cầu tiếp theo.

Dữ liệu của tôi có được gửi đi đâu không?

Không. Luồng được đọc trong trình duyệt của bạn. Các lệnh curl chỉ là văn bản để bạn tự chạy.

Bài viết liên quan

Liên quan