Harness Academy
Tài liệu gốc 24 15 phútTiếng Việt + English
docs/adr/0014-opik-observability.mdXem bản gốc
Ngôn ngữ bài đọc
Bản dịch tiếng Việt

Bản dịch đầy đủ theo đúng cấu trúc tài liệu; code, liên kết và định danh kỹ thuật được giữ nguyên.

0014. Khả năng quan sát của Opik - dấu vết cho mỗi lượt, lượt chạy và subagent

Trạng thái: Đã chấp nhận Ngày: 2026-07-05

Bối cảnh

decode có vòng lặp agent (REPL) và headless decode run con đường, cả hai đều điều khiển agent pydantic-ai, cộng với phân xuất subagent M9, ghi lại bộ nhớ và nén — tất cả đều thực hiện cuộc gọi LLM. Cho đến bây giờ không có cách nào để biết một lượt thực sự đã làm gì: lệnh gọi mô hình nào đã xảy ra, với cái gì đầu vào/đầu ra, số lượng token, độ trễ bao nhiêu và chi phí. ADR-0013 §9 được trì hoãn rõ ràng "Chi tiêu token con là vô hình cho đến khi Opik hạ cánh (M10)". Cột mốc 10 đã chứng minh được điều đó: giám sát (truy tìm), không đánh giá - evals là M13.

Những hạn chế hình thành nên thiết kế:

  • Mọi địa điểm gọi LLM ở src/ đã là agent pydantic-ai — vòng lặp chính (agent/factory.build_agent), ghi lại bộ nhớ (memory/extract.py), sự nén chặt (context/compaction.py) và các subagent (nhập lại CÙNG agent thông qua tools/agent.py). Vì vậy, một lệnh gọi thiết bị toàn cầu có thể bao quát mọi bề mặt cùng một lúc mà không cần mã trang web cho mỗi cuộc gọi.
  • Quy tắc nội bộ: cơ sở hạ tầng nhập khẩu, không trừu tượng; điều đơn giản nhất mà có hiệu quả. Ưu tiên tích hợp bị xử phạt trên các nhịp cán bằng tay; một mô-đun nhỏ, không có đường nối đầu cơ.
  • Quy tắc chung: không bao giờ đọc/thay đổi os.environ đi sâu vào các trang web cuộc gọi; cấu hình sống trong settings.py. Công thức mặc định của tài liệu Opik định cấu hình xuất qua toàn cầu OTEL_EXPORTER_OTLP_* env vars - nhưng kitaru→zenml cũng cung cấp SDK OpenTelemetry và việc gây ô nhiễm môi trường OTEL toàn cầu có thể chuyển hướng của nó đo từ xa nữa. Việc xuất phải được định cấu hình theo chương trình từ cài đặt, chỉ được đính kèm với logfire's nhà cung cấp công cụ theo dõi.
  • Hỗ trợ dựa trên hiện diện, giống hệt byte khi tắt. Giống như mọi bề mặt tùy chọn trước đó (hộp cát, runtime), việc chạy không có phím phải không thể phân biệt được với ngày nay.
  • filterwarnings=["error"] — cảnh báo của người phụ trách mới và mọi định dạng công cụ đo lường không được dùng nữa sẽ thất bại bộ phần mềm.

Sự thật đã được xác minh dựa trên các gói đã cài đặt + tài liệu chính thức trước khi quyết định:

  • Công thức chính thức (comet.com/docs/opik/integrations/pydantic-ai): logfire.configure(send_to_logfire=False)
    • logfire.instrument_pydantic_ai(); nhóm chủ đề thông qua with logfire.span("chat_turn", thread_id=…); lập trình additional_span_processors=[…] đường dẫn là mẫu riêng của tài liệu (OpikSpanProcessor ví dụ).
  • Nhập OTLP (comet.com/docs/opik/integrations/opentelemetry): Điểm cuối của Opik đọc tiêu chuẩn gen_ai.usage.input_tokens|output_tokens|total_tokens + gen_ai.request/response.model + gen_ai.system; nền tảng đám mây https://www.comet.com/opik/api/v1/private/otel, tự chủ http://localhost:5173/api/v1/private/otel, tiêu đề Authorization + Comet-Workspace + projectName; nhà xuất khẩu dấu vết http là opentelemetry.exporter.otlp.proto.http.trace_exporter.OTLPSpanExporter.
  • Chi phí (comet.com/docs/opik/tracing/advanced/cost_tracking): Opik ước tính chi phí phía máy chủ từ (nhà cung cấp, mô hình, cách sử dụng) và "tổng hợp chi phí từ tất cả các nhịp trong một dấu vết"; chi phí là None cho các mô hình không được hỗ trợ. Gemini có giá (Google AI); Các mô hình mở OpenRouter/Modal có thể không được định giá.
  • pydantic-ai InstrumentationSettings mặc định (pydantic.dev/docs/ai): include_content=True, include_binary_content=True, version=5 (2–4 phát ra PydanticAIDeprecationWarning), use_aggregated_usage_attribute_names=True (tổng số do agent điều hành theo tùy chỉnh gen_ai.aggregated_usage.* không gian tên để các phần phụ trợ tổng hợp — Opik — không tính hai lần; lá Các nhịp LLM giữ tiêu chuẩn gen_ai.usage.*). Các giá trị mặc định hoàn toàn chính xác — chúng tôi không chuyển bất kỳ cài đặt nào.
  • logfire mang lại tạm thời opentelemetry-exporter-otlp-proto-http + opentelemetry-sdk + protobuf (đã được xác minh trên PyPI), do đó, nhà xuất khẩu sẽ đến với MỘT cấp cao nhất mới.
  • logfire.testing tàu TestExporter / capfire / CaptureLogfire / exported_spans_as_dict
    • con đường khẳng định khoảng cách kín.
  • kitaru.current_execution_id() là bản xuất cấp cao nhất (đã cài đặt kitaru 0.18) trả về id thực thi đang hoạt động bên trong phạm vi luồng - không có đầu thread_id nguồn.
  • Tuân theo vòng đời (harness/runner.py): Runner._run_turn điều khiển một trình tạo không đồng bộ mỗi lượt và cuộc gọi agen.aclose() một cách dứt khoát trong finally, vậy một AgentTurnHandler.__call__ == một lượt == một nhịp gốc.

Thuật ngữ - pydantic-ai, logfire, OpenTelemetry và Opik phù hợp như thế nào

Bốn tên trong ADR này là bốn lớp của một đường ống, không phải bốn tên thay thế — được ghi ở đây bởi vì "tại sao lại là OTLP mà không phải Opik SDK?" là lần đọc sai đầu tiên một cách tự nhiên (họ không phản đối):

LớpNó là gìĐang decode
pydantic-aikhuôn khổ LLM có agent thực hiện cuộc gọi - điều đang được quan sátvòng lặp chính, ghi lại bộ nhớ, nén, subagent
đốt cháySDK thiết bị của Pydantic — một lớp bao bọc mỏng xung quanh OpenTelemetryinstrument_pydantic_ai(), configure(), span() trong tracing.py
OpenTelemetry (OTel)tiêu chuẩn + SDK trung lập với nhà cung cấp dành cho dấu vết/số liệu/nhật ký; định dạng dây là OTLPOTLPSpanExporter, BatchSpanProcessor, cái gen_ai.* thuộc tính nhịp
Opikphần phụ trợ tiếp nhận các nhịp và định giá chúngđiểm cuối OTLP mà nhà xuất khẩu POST tới
  • OpenTelemetry là ngôn ngữ phổ biến. span là một đơn vị công việc (LLM hoặc lệnh gọi công cụ) với tên, thời gian và khóa-giá trị thuộc tính; kéo dài tổ thành một dấu vết (một lượt REPL) thông qua lan truyền bối cảnh không đồng bộ; OTLP là giao thức vận chuyển chúng. Tên thuộc tính được tiêu chuẩn hóa (gen_ai.usage.*, gen_ai.request.model, gen_ai.system — Quy ước ngữ nghĩa GenAI của Otel chính xác là những gì Opik đọc để hiển thị token + chi phí không có mã keo. Bởi vì Otel TracerProvider là process-global, cả hai thư viện đều định cấu hình nó xung đột - đây là nguyên nhân cốt lõi của xung đột kitaru→zenml buộc cài đặt theo hướng (không phải toàn cầu-OTEL_*-env) xuất khẩu (quyết định phụ 2).
  • logfire là Otel bên dưới (logfire.span → một khoảng OTel; configure() → nhà cung cấp Otel). Chúng tôi sử dụng thư viện, không phải nền tảng lưu trữ của Pydantic — send_to_logfire=False chỉ mượn nó thôi thiết bị đo đạc và chuyển hướng kéo dài đến Opik. Nó giành được vị trí của mình theo hai cách: (a) instrument_pydantic_ai() là sự tích hợp một cuộc gọi toàn cầu (Pydantic tạo ra cả hai thư viện), so với. việc viết tay trải dài ở mọi địa điểm gọi bằng Otel thô; (b) cài đặt logfire mang lại sự chuyển tiếp Nhà xuất khẩu OTLP + SDK OTel, vì vậy toàn bộ quy trình sẽ có một phụ tùng cấp cao nhất.
  • "OTLP" là tài liệu vận chuyển riêng của Opik cho công thức logfire/pydantic-ai (comet.com/docs/opik/integrations/pydantic-ai) — không bỏ qua SDK Opik. Sự lựa chọn hạn hẹp chúng tôi thực sự đã làm là OTLPSpanExporter trên SDK opik OpikSpanProcessor: cả hai đều là Opik đường đi, nhưng nhà xuất khẩu vận chuyển miễn phí bằng logfire trong khi OpikSpanProcessor cần toàn bộ opik gói dưới dạng gói phụ thứ 2 để không tăng phạm vi bao phủ (được giữ làm lối thoát một dòng trong quyết định phụ 8).

Quyết định

Cơ chế = tích hợp logfire chính thức, được trang bị trên toàn cầu, được xuất theo chương trình sang Đánh giá về OTLP. So sánh với các lựa chọn thay thế:

Cơ chếNó là gìBảo hiểmDeps mớiChi phí / tokenBản án
SDK OTel đơn giản (các khoảng thời gian thủ công)thiết bị cầm tay mỗi lệnh gọi LLM/công cụ với dữ liệu thô opentelemetry-sdk kéo dài + gen_ai.* thuộc tínhmọi thứ, nhưng bạn viết nó ở mọi trang web cuộc gọinhà xuất khẩu otlpbạn đặt gen_ai.usage.* chính bạn từ run.usage()bị từ chối - phát minh lại những gì instrument_pydantic_ai() cho miễn phí; chạm vào mọi bề mặt; giòn
logfire-chính thức (CHOSEN)logfire.configure(send_to_logfire=False) + toàn cầu instrument_pydantic_ai() + nhà xuất khẩu OTLP theo hướng cài đặt → OpikTẤT CẢ agent pydantic-ai (chính, bộ nhớ, nén, subagent) trong một lệnh gọi; không có mã cho mỗi cuộc gọilogfire chỉ (mang đến nhà xuất khẩu OTLP)gen_ai.usage.* trên nhịp LLM → giá Opik; aggregated_usage tránh tính hai lầnđược chọn — con đường được phê duyệt; một cuộc gọi toàn cầu; khớp với phạm vi bị khóa
trang trí SDK opik (@opik.track / OpikSpanProcessor)bọc các điểm vào trong @opik.track và/hoặc đăng ký OpikSpanProcessor để hợp nhất các phạm vi logfire vào @track dấu vếtchỉ lối vào được trang trí; hợp nhất cần bộ xử lý+opik (và vẫn còn cháy rừng)giá đường dẫn SDK opik đáng tin cậyhoãn lại — trang trí thứ 2 + cho mỗi điểm vào; chỉ kiếm được nếu chi phí OTLP không được chứng minh (cửa thoát bên dưới)
Tái thiết sau thời gianxây dựng lại dấu vết ngoại tuyến từ nhật ký phiên JSONL/điểm kiểm tra Kitarumột phần, mất mátkhôngkhông có độ trễ/token/chi phí trực tiếpbị từ chối — mất dữ liệu, không có tín hiệu trực tiếp, trùng lặp OTel

Các quyết định phụ (tất cả đều được ghi lại để không bị khởi kiện lại):

  1. Kích hoạt = sự hiện diện của OPIK_API_KEY. Đặt → dò theo đám mây Comet; OPIK_URL_OVERRIDE chuyển cơ sở OTLP sang Opik tự lưu trữ; không → init_tracing() là một im lặng no-op và decode là byte giống hệt nhau. Toàn bộ nội dung tin nhắn/công cụ được ghi lại (pydantic-ai include_content=True mặc định). Một dòng INFO khi hoạt động (dòng bảng điều khiển REPL + dòng nhật ký).

  2. Xuất theo hướng cài đặt, không theo hướng env. init_tracing() xây dựng OTLPSpanExporter(endpoint="<base>/v1/traces", headers={Authorization, Comet-Workspace, projectName}) từ settings và đính kèm nó thông qua logfire.configure(send_to_logfire=False, bổ sung_span_processors=[BatchSpanProcessor(exporter)]). No global OTEL_* env var được đặt, vì vậy SDK OTel của kitaru/zenml không bị ảnh hưởng.

  3. Thiết bị đo có tính chung và được cấu hình mặc định. logfire.instrument_pydantic_ai() không có InstrumentationSettings — các giá trị mặc định cung cấp đầy đủ nội dung, định dạng v5 (không có cảnh báo không dùng nữa) và use_aggregated_usage_attribute_names=True vì vậy Opik định giá LLM lá kéo dài mà không tính hai lần khoảng runtime agent tổng hợp. Một cuộc gọi bao gồm vòng lặp chính, ghi lại bộ nhớ, nén và agent phụ.

  4. Hình dạng dấu vết.

    • REPL: một dấu vết mỗi lượt — một nhịp gốc (chat_turn, thread_id = id phiên) kết thúc toàn bộ AgentTurnHandler.__call__ phần thân, vì vậy chân phê duyệt/tiếp tục của công cụ kiểm soát vẫn giữ nguyên độ trễ theo dõi và quay vòng một cách trung thực bao gồm cả thời gian chờ cổng. Các bối cảnh cùng nhiệm vụ lồng vào mô hình/công cụ trải dài dưới nó. (Cẩn thận: with nhịp vượt qua yields; tính quyết định của người chạy aclose() giải phóng nó khi kết thúc bình thường, có lỗi và hủy bỏ - một cuộc kiểm tra sẽ bảo vệ điều này.)
    • headless: một dấu vết mỗi lần chạy — một nhịp gốc (decode_run / decode_run_hitl, thread_id = current_execution_id()) kết thúc tốt đẹp run_sync, được mở bên trong luồng SAU _config_from_secret_store() vậy là một cửa hàng bí mật OPIK_API_KEY được vinh danh.
    • Free-rider: quá trình ghi lại và nén bộ nhớ diễn ra như những dấu vết nhỏ của riêng chúng (hoặc tổ, khi ở trong một ngã rẽ) thông qua cùng một thiết bị đo tổng thể — không cần nối dây.
    • Các subagent lồng bên trong dấu vết của lượt cha (cùng một nhiệm vụ/bối cảnh), đóng ADR-0013 §9's "chi tiêu token con vô hình cho đến khi Opik hạ cánh (M10)".
    • Đầu vào/đầu ra của khoảng gốc được đặt rõ ràng (bản sửa lỗi sau 091, đã được xác minh trực tiếp). riêng của pydantic-ai nhịp (agent run / chat … / running tool) nhận I/O miễn phí từ toàn cầu thiết bị đo đạc, nhưng được mở thủ công của chúng tôi chat_turn / decode_run[_hitl] rễ bắt đầu trống - và Opik lấy đầu vào/đầu ra của dấu vết từ khoảng ROOT của nó, không bao giờ từ con cháu. Vì vậy rễ mang theo lượt/chạy đầu vào (lời nhắc / nhiệm vụ) như một input thuộc tính và, ở cuối lượt/chạy, văn bản trợ lý cuối cùng dưới dạng output thuộc tính thông qua observability.record_output(span, …). của Opik OTLP nhập các nhóm này bằng khớp tiền tố trên khóa thuộc tính (input* → ĐẦU VÀO, output* → ĐẦU RA — đã được xác minh trong opik's GeneralMappingRules). Đây là khả năng chịu tải cho Threads: Thread Chế độ xem được xây dựng từ đầu vào/đầu ra cấp dấu vết (first_message.input / last_message.output), vậy không có nó, toàn bộ cuộc trò chuyện sẽ trở nên trống rỗng - các hàng ngay cả khi các khoảng LLM lồng nhau có I/O.
  5. Đường may ban đầu = observability.init_tracing(), bình thường (quy trình toàn cầu logfire.configure), được gọi là từ run_app (REPL) và bên trong mỗi @flow cơ thể (headless). Một mô-đun nhỏ (observability/tracing.py) giữ init_tracing / is_tracing_active / root_span / record_output / reset_tracing.

  6. Deps = logfire chỉ. Nó mang lại opentelemetry-exporter-otlp-proto-http; không logfire[httpx], không opik. Lưu ý: Các chân OTel/protobuf của logfire phải đồng phân giải với kitaru→zenml + modal tại thời điểm khóa.

  7. Kiểm tra cách ly. Tự động sử dụng _no_opik_tracing khoảng trống cố định opik_api_key những bài kiểm tra thông thường không bao giờ định cấu hình xuất thực (mirror _no_real_provider_key); các bài kiểm tra xác nhận nhịp chọn tham gia bằng chìa khóa giả + logfire.testingnhà xuất khẩu trong bộ nhớ; reset_tracing() xóa cờ mô-đun.

  8. Đường dẫn chi phí — decode tem chi phí; cái opik KHÔNG cần đến cửa thoát SDK. Kết quả chỉ dành cho token mà phần này cho phép là "chấp nhận được" hóa ra lại là kết quả dành cho cả ba nhà cung cấp, vì vậy nó hiện đã được sửa trong observability/cost.py + CostAnnotatingExporter (tracing.py). Những gì đã được xác minh:

    • Quá trình nhập OTLP của Opik đọc chính xác một khóa chi phí — gen_ai.usage.cost - và một cách rõ ràng giá trị đoản mạch phía máy chủ của nó (provider, model) tra cứu giá, vì vậy nó hoạt động ngay cả đối với một model mà Opik chưa từng nghe tới. Được tìm thấy trong các quy tắc ánh xạ phụ trợ của opik và không có trong nó tài liệu công khai — một hợp đồng có thật nhưng không có giấy tờ, vì vậy nó đã được xác nhận dựa trên Opik trực tiếp thay vì được tin cậy: một lượt OpenRouter được theo dõi (deepseek/deepseek-chat) đã trở lại với total_estimated_cost = 0.00467553 trên llm span, khớp giá genai với chữ số cuối cùng. Opik có không hàng giá bộ định tuyến mở, vì vậy số đó chỉ có thể là thuộc tính đang được đọc. các cùng một hoạt động cho thấy agent run tại cost=None, xác nhận tính năng bảo vệ đếm kép trên dữ liệu trực tiếp. Nếu bản phát hành Opik trong tương lai đổi tên khóa, chi phí sẽ biến mất một cách lặng lẽ; triệu chứng cần theo dõi có token, giá trống.
    • pydantic-ai đã định giá mọi thứ mà danh mục genai-price biết, nhưng xuất bản nó dưới dạng operation.cost — một phím Opik không đọc được. Đó không phải là một mức giá còn thiếu, đó là khoảng trống Gemini.
    • Bảng giá của Opik có **không openrouter hoàn toàn không có hàng ** và điểm cuối Modal tự lưu trữ (Qwen/Qwen3.6-35B-A3B-FP8) không bao giờ có thể có được.

    Vì vậy, nhà xuất khẩu chuyển tiếp giá danh mục của pydantic-ai theo khóa mà Opik đọc và quay trở lại LLM_COST_{INPUT,OUTPUT}_USD_PER_MTOK khi danh mục không có hàng. Cả hai tỷ lệ không được đặt (0,0) có nghĩa là Thuộc tính chi phí "không xác định" và no được viết — chi phí trống đánh bại chi phí bịa đặt. Mỗi nhà cung cấp, điều này thực sự mua được gì:

    • Song Tử — đã sửa hoàn toàn; danh mục định giá nó và cây cầu cung cấp nó.
    • OpenRouter — đã sửa lỗi cho các phần mềm phổ biến mà danh mục biết (anthropic/…, google/…, openai/…, deepseek/…, meta-llama/…). Thiếu danh mục (ví dụ: qwen/qwen3-235b-a22b) nhu cầu tỷ lệ thủ công. Mặc định openrouter/free bộ định tuyến thực sự là $0, vì vậy giá trống là sửa ở đó chứ không phải thiếu. OpenRouter không trả lại chi phí đã tính chính xác thông qua việc sử dụng kế toán, nhưng pydantic-ai bỏ lĩnh vực đó - nắm bắt nó là một công việc sửa ống nước thực sự, không phải là một cái núm, và cố tình không được thực hiện ở đây.
    • Modal — luôn miễn phí THEO MỤC ĐÍCH. Điểm cuối tự lưu trữ tính phí GPU-giây, vì vậy không cần con số trên mỗi token mô tả số tiền thực sự được thanh toán; tỷ giá không được sử dụng để sản xuất một. Dấu vết phương thức mang token và độ trễ, đồng thời chi tiêu tồn tại trong thanh toán của chính Modal. Hai hậu quả đáng được đặt tên: đó là trình bao bọc nhà xuất khẩu, không phải bộ xử lý nhịp, bởi vì on_end đưa ra một ảnh chụp nhanh nhịp bất biến; và chỉ những nhịp mang theo gen_ai.request.model được định giá, bởi vì pydantic-ai lặp lại cách sử dụng tổng hợp của lần chạy trên cấp độ gốc agent run span và Opik SUMS span chi phí - cả hai mức giá sẽ báo cáo mỗi lần chạy ở mức gấp đôi chi tiêu thực tế của nó. Cửa thoát hiểm (vẫn mở, vẫn chưa sử dụng): nếu hợp đồng thuộc tính bị phá vỡ, hãy đổi sang opik SDK (additional_span_processors=[…, OpikSpanProcessor()]) vẫn là một thay đổi một dòng.
  9. M13 (sau này) thêm đánh giá / thử nghiệm / tính điểm trực tuyến lên trên các dấu vết này — không phải bây giờ.

Sơ đồ

Span flow — REPL quay và chạy headless cung cấp dữ liệu cho một thiết bị đo toàn cầu, được xuất từ cài đặt sang Opik.

flowchart TD
    subgraph repl["REPL turn (single asyncio task)"]
        rt["AgentTurnHandler.__call__<br/>root span 'chat_turn' · thread_id = session_id"]:::root
        rlegs["legs: model-request + tool + gated approve/resume<br/>(pydantic-ai spans nest via shared task context)"]:::leg
        rt --> rlegs
    end
    subgraph headless["decode run (Kitaru @flow)"]
        ht["@flow body<br/>root span 'decode_run' · thread_id = current_execution_id()"]:::root
        hlegs["KitaruAgent.run_sync → model/tool spans<br/>(per-call worker loops: nesting best-effort)"]:::leg
        ht --> hlegs
    end
    subgraph sub["Subagent (agent tool, same task)"]
        cspans["child agent.run() model/tool spans<br/>nest INSIDE the parent turn (closes ADR-0013 §9)"]:::child
    end
    rlegs -.-> cspans

    instr["logfire.instrument_pydantic_ai() — GLOBAL<br/>main + memory write-back + compaction + subagents"]:::instr
    rlegs --> instr
    hlegs --> instr
    cspans --> instr

    instr --> lf["logfire tracer provider<br/>configure(send_to_logfire=False)"]:::lf
    lf --> exp["BatchSpanProcessor → OTLPSpanExporter<br/>endpoint + headers from settings (NO global OTEL env)"]:::exp
    exp --> opik[("Opik / Comet<br/>OTLP ingest → gen_ai.usage.* → tokens + cost<br/>thread_id → conversation thread")]:::opik

    off["OPIK_API_KEY unset → init_tracing() no-op<br/>zero spans · decode byte-identical"]:::off
    off -.-> instr

    classDef root fill:#1e293b,stroke:#0ea5e9,color:#e2e8f0
    classDef leg fill:#334155,stroke:#a855f7,color:#e9d5ff
    classDef child fill:#14532d,stroke:#22c55e,color:#dcfce7
    classDef instr fill:#713f12,stroke:#eab308,color:#fef9c3
    classDef lf fill:#334155,stroke:#38bdf8,color:#bae6fd
    classDef exp fill:#3b0764,stroke:#c084fc,color:#f3e8ff
    classDef opik fill:#052e16,stroke:#4ade80,color:#dcfce7
    classDef off fill:#0f172a,stroke:#64748b,color:#cbd5e1

hậu quả

  • Một cuộc gọi toàn cầu bao trùm mọi bề mặt. Việc thêm trang web cuộc gọi LLM pydantic-ai trong tương lai sẽ được theo dõi cho miễn phí - không có công việc trên mỗi trang web.
  • decode giống hệt byte mà không có OPIK_API_KEY. Toàn bộ tính năng sẽ không hoạt động cho đến khi một khóa được đặt; REPL không in dòng nào, thiết bị xuất chuẩn của lệnh chạy headless không thay đổi, các nhịp bằng 0 được phát ra.
  • Cấu hình vẫn còn settings.py. Không có toàn cầu OTEL_* env var bị đột biến, do đó OTel của kitaru/zenml SDK không bị ảnh hưởng.
  • Chi phí được nỗ lực tối đa theo mô hình. Token luôn; chi phí cho các mẫu có giá (Gemini vâng; các mẫu mở qua OpenRouter/Modal có thể chỉ có token). các opik-SDK Escape Hatch là hoán đổi một dòng nếu cần.
  • Lồng nhau ở cấp độ chạy headless là nỗ lực tốt nhất trong checkpoint_strategy="calls". Nhân viên theo cuộc gọi các vòng lặp có thể phá vỡ việc truyền bá bối cảnh OTel, vì vậy một số nhịp mô hình có thể xuất dưới dạng dấu vết anh chị em thay vì hơn được lồng dưới gốc chạy; mọi nhịp vẫn mang token. Một trần nhà được ghi lại, giống như ADR runtime.
  • ADR-0013 §9 đã bị đóng. Phần con phụ kéo dài lồng trong dấu vết của lượt cha mẹ với phần con hiển thị việc sử dụng token.
  • Rủi ro cần xác nhận trong quá trình triển khai: (a) uv add logfire đồng giải quyết các chân OTel/protobuf của nó với kitaru→zenml + modal (một trình chặn cứng nếu nó bị lỗi); (b) vượt qua nhịp của trình tạo không đồng bộ yieldnó sẽ kết thúc đúng một lần khi hủy bỏ (được bảo vệ bởi người chạy aclose() + bài kiểm tra); (c) liệu Chi phí Gemini hiện thực hóa thông qua OTLP thuần túy (được kiểm tra bằng khói trực tiếp; cửa thoát hiểm đã sẵn sàng); (d) logfire.configurenhà cung cấp dịch vụ theo dõi toàn cầu quy trình của cùng tồn tại với bất kỳ bộ kitaru/zenml của nhà cung cấp nào trong con đường headless.
  • Kỷ luật (không thay đổi): filterwarnings=["error"], ngày giờ nhận biết UTC, chú thích đầy đủ, thư viện nhật ký mã (không bao giờ print()), tests/ gương src/ 1:1, TDD-first, không có mạng trong CI (FunctionModel
    • logfire.testing; một làn khói sống skipif-được bảo vệ).
Mục lục nguồn