1 điểm bởi jhs6312 6 giờ trước | Chưa có bình luận nào. | Chia sẻ qua WhatsApp

Xin chào. Tôi đang xây dựng Spanlens, một nền tảng quan sát mã nguồn mở để xem log các lệnh gọi LLM, theo dõi chi phí và trace của agent ở cùng một nơi.

Lý do tạo ra

Khi dùng LLM cho các dự án phụ cá nhân, có hai điều cứ liên tục làm tôi thấy vướng.

Một là theo dõi chi phí.
Mọi chuyện bắt đầu khi tôi làm một tiện ích mở rộng trình duyệt dựa trên GPT và lần đầu giật mình khi nhận hóa đơn OpenAI vào cuối tháng. Vì còn là sinh viên nên số tiền không quá lớn, nhưng dù thấy tổng chi phí, tôi lại không biết riêng từng tính năng tốn bao nhiêu, hay mỗi model trung bình dùng bao nhiêu token. Thế là tôi cứ phải chèn console.log vào code, xuất CSV, cộng lại trong Excel và nhân với đơn giá theo từng model để ước tính, lặp đi lặp lại như vậy.

Điều còn lại là debug agent.
Đây là điều tôi trực tiếp gặp phải khi gắn tích hợp LangGraph vào Spanlens: khi một trace trộn nhiều lệnh gọi LLM mất 30 giây để chạy xong, tôi phải bung log ra và lần theo bằng tay để biết

  • thời gian bị tiêu tốn ở node nào
  • vì sao cùng một công cụ lại bị gọi hai lần
  • state của LangGraph đã thay đổi ở thời điểm nào

Tôi tạo Spanlens vì muốn giảm bớt hai vấn đề này.

Tính năng chính

  1. Tích hợp chỉ với một dòng baseURL

Chỉ cần đổi baseURL của SDK OpenAI/Anthropic/Gemini thành dạng https://api.spanlens.io/proxy/openai/v1, thì request, response, token và chi phí sẽ được tự động ghi lại. Response được passthrough nguyên trạng, nên streaming, tool calling và JSON mode đều hoạt động y hệt bản gốc.

Với các trường hợp như agent cần cách wrap, tôi đã làm SDK để có thể chèn trace_id, span_id, từ đó ghi lại cả quan hệ cha con.

  1. Trace của agent + LangGraph topology view

Bạn có thể xem trace không chỉ dưới dạng timeline theo thời gian mà còn được chồng trực tiếp lên các node của đồ thị thực tế. Nếu agent được viết bằng LangGraph, bạn sẽ thấy ngay trên một màn hình node nào tốn thời gian, cạnh nào được đi qua thường xuyên nhất.

  1. Phân tích Critical Path tự động

Spanlens tự động đánh dấu chuỗi lệnh gọi chiếm nhiều độ trễ nhất trong trace. Tôi muốn giảm số lần bấm chuột cần thiết để tìm ra câu trả lời cho câu hỏi “trace này vì sao chậm?”.

  1. So sánh thống kê A/B cho prompts

So sánh hai phiên bản của cùng một prompt theo độ trễ, chi phí và lượng token sử dụng. Không chỉ là chênh lệch trung bình đơn thuần, Spanlens còn áp dụng Welch t-test để cho thấy khác biệt có xét tới cả phương sai mẫu. Tôi thêm tính năng này để có thể nói không chỉ là “trung bình có vẻ thấp hơn chút”, mà là “có khác biệt có ý nghĩa”.

  1. Self-hosting

Có thể triển khai lên máy chủ riêng bằng Docker image. Cùng đúng bộ code như bản SaaS đang được đặt nguyên trong repo public. Toàn bộ mã nguồn dùng giấy phép MIT.

Được triển khai như thế nào

Pipeline hiện tại đại khái như sau.

  • Request proxy được nhận bằng Hono, tách header Authorization và metadata X-Spanlens-*, giải mã provider key bằng AES-256-GCM và chỉ dùng trong bộ nhớ ngay trước khi gọi.
  • Với response dạng streaming, dùng body.tee() để trả ngay luồng gốc cho client, còn bản sao sẽ được parser xử lý ở nền để tính token và chi phí.
  • Log được nạp bất đồng bộ vào ClickHouse. Nếu INSERT thất bại, nó sẽ được giữ trong hàng đợi fallback của Supabase và cron sẽ thử lại, nên dù là fire-and-forget tôi vẫn cố tránh mất dữ liệu.
  • Giá model được lưu trong bảng DB và cache theo kiểu stale-while-revalidate với TTL 5 phút. Giá fallback đóng vai trò lưới an toàn cho cold start.

Ban đầu đây chỉ là một proxy đơn giản, nhưng khi dùng thực tế tôi nhận ra điều quan trọng hơn log thô là trace đã được chuẩn hóa. Để tìm ra câu trả lời cho “trace này vì sao chậm?”, không chỉ cần thứ tự lệnh gọi mà còn cần quan hệ cha con, các lệnh gọi song song và Critical Path, nên những tính năng như LangGraph topology view hay Critical Path tự động đã ra đời từ đó.

Stack gồm Next.js 14, Hono, Supabase Postgres, ClickHouse, tất cả nằm trong monorepo TypeScript pnpm.

Những điểm tôi vẫn đang cân nhắc

  • Tôi muốn biến self-hosting thành một lệnh docker run duy nhất, nhưng như vậy sẽ cần kéo cả Supabase Postgres xuống cùng. Hiện tại docker-compose đang giả định dùng managed Supabase và gồm 3 container: web, server, ClickHouse. Tôi đang phân vân liệu nên thêm tùy chọn tự host cả Supabase hay tiếp tục giữ giả định dùng bản managed.

  • Trong tính năng so sánh A/B cho prompts, phần tính Welch t-test đã có, nhưng tôi chưa quyết định liệu nên hiển thị trực tiếp p-value hay chỉ hiện badge kết luận (có ý nghĩa/không). Tôi cũng đang cân nhắc cách hiển thị cảnh báo khi cỡ mẫu nhỏ (n<30).

  • Trong LangGraph topology view, tôi đang trực quan hóa node, edge và Critical Path, còn thay đổi của state channel thì cố ý bỏ ra vì quá nhiều nhiễu. Tôi muốn nghe ý kiến xem trong debug thực tế, việc theo dõi thay đổi state có cần thiết hơn không, hay mức hiện tại là phù hợp.

Đây là một dự án bắt đầu từ những bất tiện tôi tự trải qua và đang được tôi liên tục mài giũa.

[ Spanlens ]
Web: https://www.spanlens.io
GitHub: https://github.com/spanlens/Spanlens
Hướng dẫn self-hosting: https://www.spanlens.io/docs/self-host

Tôi sẽ thật sự biết ơn nếu nhận được phản hồi ở bất kỳ góc độ nào, từ UX dashboard, trực quan hóa trace, cách tích hợp proxy cho tới trải nghiệm self-hosting. Đặc biệt, nếu có provider nào ngoài OpenAI/Anthropic/Gemini mà bạn muốn được hỗ trợ, hãy cho tôi biết trong phần bình luận.

Trên website có bản demo, mong mọi người quan tâm và dùng thử.
Hiện UI đang là tiếng Anh và tôi cũng dự định sớm bổ sung hỗ trợ tiếng Việt.

Chưa có bình luận nào.

Chưa có bình luận nào.