今日已更新 249 条资讯 | 累计 37498 条内容
关于我们

标签:#Python

找到 1119 篇相关文章

AI 资讯

Project Log #21: The Grand Finale. We Shipped. (Plus: Full Setup Guide)

21 build logs. Months of work. One shipped project. Here's the full journey—and how to set up the Phone Agent on your own device. The first build log was published months ago. "I'm building an AI agent that controls a phone." No code. No repo. Just an idea and a cracked phone. Today, after 21 build logs spread across months, the project is shipped. This wasn't a straight line. There were gaps. Weeks where the log went silent—not because the work stopped, but because life doesn't pause for build logs. I took breaks to survive exam season. I paused to ship 9 portfolio websites. I stepped away when the code refused to cooperate and my brain needed rest. But every time I came back, the agent was still there. Waiting. And every log picked up where the last one left off. What We Built An autonomous AI agent that controls an Android phone using natural language commands. It can parse your words into actions, read the screen, tap buttons, type text, switch between apps, verify financial data, and serve a web interface—all offline. The Real Timeline Phase What Happened Days 1-4 Foundation. Gemma 4 + ADB. First working pipeline. Days 5-8 Vision overhaul. UI tree. OCR. Template matching. Days 9-12 Accessibility audit. 30 apps scored. Days 13-16 Multi-app workflows. Task memory. Home reset. Days 17-19 Financial verification. Accuracy from 80% to 94%. Breaks Exams. Portfolio sites. Life. Days 20-21 Web interface. Flask backend. Shipped. 📖 FULL SETUP GUIDE: How to Install and Use the Phone Agent Follow these steps to get the agent running on your own Android phone. Prerequisites An Android phone (Android 7 or later) At least 6GB of free storage space A WiFi connection for the initial download Step 1: Install Termux Do NOT install Termux from the Google Play Store—that version is outdated. Install it from F-Droid instead. Open your phone's browser Go to f-droid.org Download and install the F-Droid app Open F-Droid and search for "Termux" Install Termux from F-Droid Step 2: Set Up

2026-08-09 原文 →
AI 资讯

Beyond Login: Building a Production Authentication Lifecycle in FastAPI

Authentication is often presented as a short sequence: Accept a username and password. Return a JWT. Protect a few endpoints. That is enough for a tutorial, but it is not an authentication lifecycle. Real applications must also answer harder questions: How is an email address verified without storing a reusable secret? What happens to existing sessions after a password reset? Can a user see and revoke a lost device? How do we prevent a rotated refresh token from being replayed? How should TOTP secrets and recovery codes be stored? How can an OIDC identity be linked without trusting email matching? I explored those questions while building FastAPI Production API v1.2.0 , a backward-compatible authentication lifecycle release for an open-source FastAPI backend foundation. This article explains the design decisions behind it—not just the endpoints that were added. 1. Model lifecycle tokens as scoped, single-use credentials Email verification and password recovery look similar from the outside: send a link, receive a token, and update an account. Treating them as interchangeable, however, creates unnecessary risk. The release uses account-action tokens with four important properties: Random: the token is generated as an opaque secret rather than derived from user data. Scoped: a verification token cannot be used as a password-reset token. Expiring: every token has a short, configurable lifetime. Single use: confirmation atomically marks the token as consumed. Only a hash of the token is persisted. The original value exists only long enough to be delivered to the user. This gives email verification and password reset a shared security primitive without making their policies identical. The main endpoints are: POST /auth/email-verification/request POST /auth/email-verification/confirm POST /auth/password-reset/request POST /auth/password-reset/confirm Both request operations return uniform responses. A caller should not be able to determine whether an email belongs to an a

2026-08-09 原文 →
AI 资讯

Weekly Challenge: Uncommon parentheses

Weekly Challenge 385 Each week Mohammad S. Anwar sends out The Weekly Challenge , a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. Unless otherwise stated, Copilot (and other AI tools) have NOT been used to generate the solution. It's a great way for us all to practice some coding. Challenge , My solutions Task 1: Uncommon Words Task You are given two sentences. Write a script to return list of all uncommon words, order is not important. My solution This is relatively straight forward. I start with a Counter called word_freq which is a special type of dictionary which is ideal for counting frequencies. I take one or more sentences as input. I loop through each sentence, separate them by spaces and increment the word_freq counter. I then return all words that have a frequency of 1 . Since Python 3.6, dictionaries maintain their order. Therefore the words in the output will maintain their order from the supplied sentences. from collections import Counter def uncommon_word ( * sentences : str ) -> list : word_freq = Counter () for sentence in sentences : word_freq . update ( sentence . split ()) return [ word for word in word_freq if word_freq [ word ] == 1 ] Perl does not maintain order of hashes. For the Perl solution, I sort the unique words alphabetically. This is an example of stacking sort , map (to quote strings) and grep (to filter duplicated words) in a single function. sub main (@sentences) { my %word_freq = (); foreach my $sentence ( @sentences ) { foreach my $word ( split /\s+/ , $sentence ) { $word_freq { $word } ++ ; } } say " ( " . join ( " , ", sort map { qq{"$_"} } grep { $word_freq { $_ } == 1 } keys %word_freq ) . " ) "; } Examples $ ./ch-1.py "apple banana apple" "banana orange" ( "orange" ) $ ./ch-1.py "cat dog" "bird fish" ( "cat" , "dog" , "bird" , "fish" ) $ ./ch-1.py "the quick brown fox" "the quick" ( "brown" , "fox" ) $ ./ch-1.py "hello" "hello" () $ ./

2026-08-09 原文 →
AI 资讯

Building an Open-Source NOAA MRMS Radar Renderer in Python

When I started building Weather Experience , I wasn't planning to release an open-source project. I simply wanted to answer a question: Could I build a modern radar rendering pipeline using NOAA's publicly available MRMS data? That question led me down a rabbit hole of GRIB2 decoding, radar products, rendering pipelines, performance benchmarking, and ultimately the release of MRMS Renderer , the first open-source project from Taylor Creative Development. Why MRMS? NOAA's Multi-Radar/Multi-Sensor (MRMS) system provides an incredible amount of weather data. For my use case, I focused on the ReflectivityAtLowestAltitude product because it provides an excellent foundation for radar visualization. The challenge wasn't obtaining the data. The challenge was turning that data into something useful. The Pipeline MRMS Renderer performs the complete workflow: Discover the latest MRMS products directly from NOAA/NCEP Download and decompress GRIB2 data Decode the grid using ecCodes Process reflectivity values with NumPy Render transparent PNG radar frames Generate an animation manifest Display animated radar over OpenStreetMap using Leaflet Everything runs locally. The project intentionally does not provide a hosted radar service. Instead, it demonstrates how developers can work directly with NOAA's publicly available data. Performance One of the biggest questions I had at the beginning was performance. Could this realistically be done fast enough for a modern application? Rather than speculate, I wrote benchmarks. On my M4 Pro MacBook Pro over a standard Wi-Fi connection, the complete pipeline—from downloading the latest MRMS frame through rendering the finished PNG—consistently completed in around two seconds . The surprising result wasn't the renderer. The renderer itself was already highly optimized using NumPy vectorization. The largest source of latency turned out to be downloading the GRIB2 data itself. That finding helped shape later architectural decisions for Weather E

2026-08-09 原文 →
AI 资讯

Deploying and committing to git are not the same "done" — the trap of assuming uploaded means synced

Near the end of a release, every file transfer to the production server succeeded, and the version file that triggers distribution was updated too. With that confirmed, the release got reported as complete — except the local git repository never actually had those changes committed. Note: "Deploying" here means transferring changed files to the production server (via scp, for example) so they're actually live for users. "git push" is a separate operation that records the change history in a remote repository. What happened This release involved transferring seven files to the production server: five landing-page update-notice files, the version file that triggers distribution, and a progress-log file. The transfer itself succeeded completely, and the production site confirmed it was showing the new version number. The problem: after editing these files locally, the work moved straight to the transfer step without ever committing . The files on the production server were fully up to date, but the local git repository had no record of those changes — and the release got reported as complete in that state. Why this is easy to miss Transferring files with scp and recording them in the repository with git commit / git push are completely independent operations, both as commands and as goals. Verifying production (HTTP 200, checking the rendered content) confirms "did the deployment succeed" — a different question from "is the local change history recorded." Treat the first check as proof of "done," and the second check quietly never happens. When both steps get mentally bundled into one "release complete" state, there's no natural moment to notice that only one of them actually finished. In this case, it surfaced because someone else looking at the repo noticed it hadn't been committed yet. The fix — treat "uploaded" and "git synced" as two separate checks Add a git-sync verification step to the deploy checklist, independent from the file-transfer confirmation. # Commit

2026-08-09 原文 →
AI 资讯

Measuring diffusion video performance on a MacBook: one speedup and a large gap

Last month, I published a benchmark showing a 1.125× speedup from block-residual caching on 4-bit FLUX . The main lesson was not the multiplier. It was that my original quality metrics had been measuring the wrong thing, and that acceleration claims often combine speed, trajectory preservation, and perceptual quality into one number. For the follow-up, I chose a stricter target: real-time autoregressive diffusion video on an Apple M5 Max , with the definition of "real time" frozen before results were visible. The tested configuration did not meet that target. The fastest claim-eligible result was 1.418 native generated frames per second , compared with a 16 FPS target. That is an 11.28× gap . I am publishing the result because the measured bottleneck, one systems improvement, and two rejected hypotheses are useful even without a real-time result. The evidence can be checked from a repository checkout: git clone https://github.com/kkjcodes/liveframe cd liveframe python -m pip install liveframe liveframe verify \ artifacts/liveframe-publication-claims.v1.json \ --artifacts-root . liveframe recompute \ artifacts/liveframe-publication-claims.v1.json The setup LiveFrame evaluates Wan2.1-T2V-1.3B-based causal video models across NVIDIA H100 CUDA and Apple M5 Max MLX/Metal. The experiments include: Causal Forcing++ for the clean M5 performance fixture Rolling Forcing for the CUDA-to-MLX portability study Frame-wise Causal Forcing++ for the H100 cache-reuse experiment The clean M5 fixture produces 81 pixel frames at 480×832, corresponding to 5.06 seconds at the model's native 16 FPS. Before holdout results were visible, the relevant protocols froze their prompts, seeds, content strata, horizons, thresholds, aggregation rules, and stop rules. For the cross-runtime experiment, stochastic inputs were serialized once as BF16 tensors. CUDA and MLX consumed byte-identical tensors rather than relying on nominally matching random seeds. LiveFrame separates four claim layers: Numeri

2026-08-09 原文 →
AI 资讯

Cuando tu clasificador parpadea: histéresis para señales que oscilan

Tienes una señal que a cada observación te dice en qué estado estás: un monitor de salud que dice OK o CAÍDO , un detector de conectividad, un clasificador de modo. Y cerca del umbral oscila : OK, CAÍDO, OK, CAÍDO, OK . Cada cambio dispara algo —una alerta, un failover, entrar o salir de una posición— y de repente tu sistema está temblando por ruido, no por una transición real. Es el mismo problema que resuelve el termostato de tu casa desde hace un siglo, y la solución tiene nombre: histéresis . No cambies de estado hasta que el nuevo se haya sostenido. La regla, en una frase Un estado nuevo solo se confirma tras repetirse N observaciones consecutivas. Si el candidato cambia o revierte antes de llegar a N , la cuenta se reinicia. El estado vigente se mantiene estable; los parpadeos se ignoran. Lo empaqueté como librería — hysteresis-state , Python puro, sin dependencias— porque lo reescribía una y otra vez: from hysteresis_state import HysteresisState estado = HysteresisState ( " OK " , confirmations = 3 ) for lectura in stream : # "OK" / "CAIDO" actual = estado . update ( lectura ) # solo cambia tras 3 lecturas seguidas if estado . changed : # ¿esta lectura provocó la transición? alertar ( actual ) Aliméntalo con OK, CAÍDO, OK, CAÍDO, OK y no pasa nada: ningún candidato se sostuvo. Hacen falta tres CAÍDO seguidos para que el cambio se confirme. El detalle que casi siempre falta: histéresis asimétrica Un umbral único tiene un problema sutil. Si exiges 3 confirmaciones para entrar en fallo, también tardas 3 en salir — y a veces quieres justo lo contrario: caer rápido a lo seguro, volver despacio a lo arriesgado . Es el comportamiento de un disyuntor eléctrico: salta a la primera, se rearma con cautela. Se resuelve dejando que el umbral dependa de la transición: # 1 confirmación para caer a "CAIDO", 5 para volver a "OK" conf = lambda desde , hacia : 1 if hacia == " CAIDO " else 5 estado = HysteresisState ( " OK " , confirmations = conf ) estado . update ( " CAIDO " )

2026-08-09 原文 →
AI 资讯

Your CNN's Advantage Is One Assumption — and I Measured What Happens When It Breaks

A small convolutional network beats a plain flatten-and-feed-it-forward network by 7.0 points on CIFAR-10. That's convolutions, pooling, normalisation and skip connections doing honest work. Then I shuffled the rows of every image, destroying no information at all, and that 7.0-point margin fell to 0.3 . Same architecture. Same data, in a strict sense I'll defend in a moment. Almost the entire advantage, gone. The experiment Take one fixed permutation of the 32 row indices. Apply it to every image in the training set and every image in the test set — the same permutation, every time. import torch g = torch . Generator (). manual_seed ( 1234 ) row_perm = torch . randperm ( 32 , generator = g ) def shuffle_rows ( x ): # x: (C, H, W) return x [:, row_perm , :] print ( row_perm [: 8 ]. tolist ()) # [15, 9, 8, 1, 4, 12, 30, 7] That's the whole intervention. Then train two models twice each — once on natural images, once on shuffled ones: Model Params Natural rows Shuffled rows Flatten → 512 → 10 (MLP) 1,578,506 51.4% 51.7% Small CNN 94,538 58.4% 52.0% CNN's margin +7.0 pts +0.3 pts The baseline is a real fully-connected network, not a single linear layer — Flatten → Linear(3072, 512) → ReLU → Linear(512, 10) . It has the capacity to learn anything the CNN can; what it lacks is any reason to look at pixels near each other. Two things in that table are worth sitting with. The CNN wins the natural case with sixteen times fewer parameters — that's the prior paying for itself. And in the shuffled case it doesn't just lose its lead; it drops 6.4 points in absolute terms, down to roughly where the linear model already was. "You destroyed the data" — no, and this is the important part This is the objection everyone raises, so let's take it seriously, because the experiment is worthless if the objection holds. A fixed permutation is a bijection . Nothing is added, nothing is removed, nothing is averaged or blurred: img = torch . arange ( 3 * 32 * 32 , dtype = torch . float32 ). r

2026-08-08 原文 →
AI 资讯

My AI Answered in 5.8 Seconds and Said Nothing Useful. I Almost Blamed the Model.

I put an AI into a Google Meet call. It transcribed Japanese, generated a reply, and spoke it out loud. Total new spend: $0 . Then I asked it the one question I actually needed answered, and it said: "I think there's still room for discussion. How about we set up a session to align our understanding?" That is exactly what a person says when they don't know. TL;DR: I had a latency problem and an "is this model smart enough" problem. Neither was real. Same model, same question, 5.80s → 5.68s — 2,545 characters of context turned a deflection into a claim you could argue with. The stack, and what it replaced I wanted an AI participant in a real meeting. Not a note-taker — something that answers when someone demands specifics. The obvious stack bills you three times: a hosted meeting-bot API, a speech-to-text vendor, and a text-to-speech vendor. I replaced all three. Layer Obvious choice What I used Why Meeting bot Recall.ai, $0.50/hour Attendee (OSS, self-hosted) no per-hour billing Speech-to-text Deepgram / AssemblyAI Google Meet's own captions the meeting already generates them Text-to-speech Google Cloud TTS raw audio POST (below) no GCP project at all Reasoning + voice LLM + TTS, two hops Gemini Live (speech-to-speech) one model, one hop Attendee is 699 stars, last pushed 2026-08-07. Google Meet exposes no bot API, so it drives a full Chrome instance — which is why setup hurt before anything else did. The setup tax, compressed Two problems were routine. The image pins FROM --platform=linux/amd64 ubuntu:22.04 , and my machine is Apple Silicon, so colima with Rosetta: colima start --vm-type = vz --vz-rosetta --cpu 6 --memory 12 --disk 60 docker run --rm --platform = linux/amd64 alpine:3.20 uname -m # x86_64, 5.6s cold Then the build died at step 35 of 42 with the --chmod option requires BuildKit — colima's docker CLI ships without the buildx plugin. brew install docker-buildx , point ~/.docker/config.json at /opt/homebrew/lib/docker/cli-plugins via cliPluginsExtraDirs

2026-08-08 原文 →
AI 资讯

Launch Day Fire: How I Fixed a "Silent" Production Crash on My Legal AI Infrastructure

A lesson in dependency wars, version pinning, and the reality of building in public. Every founder dreams of a perfect launch. You hit "Deploy," the logo appears, and the users start flowing in. For Lawyie, my intelligent legal infrastructure for Africa, the launch started exactly that way. But then, the screen went blank. "Error running app." No red lines in the code. No obvious bugs in my logic. Just a silent failure at the very moment the world was starting to look. As the lead architect at Sunverse AI, I had to move from "Creator" to "Digital Detective." I pulled the logs from the Streamlit Cloud and found a cryptic traceback: TypeError: GZipResponder.__init__() missing 1 required keyword-only argument: 'thread_minimum_size' This wasn't an AI hallucination. This wasn't a database leak. This was an Infrastructure War. It turns out I had fallen victim to an industry-wide conflict. A core library called Starlette had recently updated to version 0.37.0+, changing its grammar for handling GZip compression. Meanwhile, the server environment hadn't caught up. In my requirements.txt , I hadn't specified a version. I just said "install it." Because I didn't "lock the door," the latest (and broken) version walked right in and crashed my entire engine. In a "Unicorn" startup, you don't just wait for things to get better. You force stability. I applied Version Pinning to my requirements. By hard-coding the stable version of the library, I overrode the server's defaults and restored the infrastructure: # The Pinned Shield streamlit>=1.35.0 starlette==0.36.3 # The specific fix for the GZip error supabase groq fpdf2 Building Lawyie from Abuja, Nigeria, taught me three things today: The Latest isn't always the Best: In production, stability beats "newness." Always pin your critical dependencies. Logs are your best friend: When the screen goes blank, don't panic. Read the trace. The answer is always in the bytes. Transparency builds Trust: When my community on Dev.to pointed out

2026-08-08 原文 →
AI 资讯

Dos formas en que un backtest te miente (y cómo evitarlas)

Pruebas una estrategia, o un modelo, sobre datos históricos. El backtest da un número bonito. Y luego, en real, no aparece. Casi siempre es una de estas dos ilusiones — y las dos se descartan con muy poco código. Empaqueté las dos correcciones como librería: honest-eval , Python puro, sin dependencias. Salieron de un bot de trading, pero el rigor no tiene nada de específico al trading. Ilusión 1: el modelo vio el futuro Partir los datos con el clásico train_test_split aleatorio es correcto para datos independientes. En una serie temporal es un desastre silencioso: mete muestras de mañana en el conjunto de entrenamiento, y el modelo "predice" en el test cosas que en producción todavía no habrían pasado. La métrica sale inflada, y confías en un edge que no existe. El test honesto es siempre el futuro : el tramo más reciente en el tiempo. from honest_eval import temporal_split train_idx , test_idx = temporal_split ( timestamps , test_frac = 0.20 , embargo = 24 ) X_tr , X_te = X [ train_idx ], X [ test_idx ] Devuelve índices, así lo aplicas a numpy, pandas o listas por igual. El embargo cierra una fuga más sutil: si tu etiqueta mira h pasos adelante, una muestra de entrenamiento a menos de h del corte ya conoce parte del resultado del test. embargo=h descarta ese borde. La métrica baja — pero por fin es la real out-of-sample . Ilusión 2: la variante ganó por suerte Tienes varias variantes y quieres la mejor. Eliges la de mayor media. Error: con pocas muestras, eso premia la varianza, no la ventaja . La variante más ruidosa suele quedar arriba por azar. Dos correcciones, ambas dentro de select_best_variant : Aparear. Mide variante y baseline sobre el mismo ensayo y trabaja con δ = variante − baseline . La varianza común del ensayo se cancela en la resta, y te quedas con la señal. Exigir cota inferior de confianza > 0. Gradúa una variante solo si media − z·SE > 0 : "incluso siendo pesimista dentro del margen de confianza, sigue por encima del baseline". from honest_eval i

2026-08-08 原文 →
AI 资讯

The Same Setting, Three Different Answers: Why 0.0.0.0 Isn't Always What You Want

There is a line in almost every Python web tutorial that nobody explains: uvicorn main:app --host 0.0.0.0 --port 8000 I copied it for weeks without thinking about it. Then I deployed the same application three times — to a local VM, to a production server, and into a container — and the correct value was different every time. Twice it was 0.0.0.0 . Once, in the place that mattered most, it was not. That gap is worth writing about, because the setting itself is trivial and the reasoning behind it is not. What the Flag Actually Controls A server process doesn't "open a port." It creates a socket and binds it to an address. The bind address answers one question: which network interfaces should this socket accept connections from? A machine has more than one interface: lo (loopback) — reachable only from inside the machine ( 127.0.0.1 ). Packets addressed there never reach a physical network card; the kernel loops them straight back. 0.0.0.0 — a wildcard meaning every interface this machine has , including ones added later. So the flag isn't about security or convenience. It's about reachability — and reachability depends entirely on what sits in front of the process. Case 1: The Local VM — 0.0.0.0 I was running the service inside a Multipass VM and wanted to hit it from the browser on my laptop. The laptop is outside the VM, so binding to loopback would have made the service invisible to it. curl inside the VM would work; the browser outside would get connection refused. Decision: wildcard bind. Nothing sits in front of the process, and nothing needs protecting. Case 2: Production — 127.0.0.1 Here I copied the same line at first, and it was wrong. The production box has a public IP. Binding to 0.0.0.0 there means the application is directly exposed to the internet: no TLS, no rate limiting, no authentication. Within hours of provisioning that server, its SSH logs showed hundreds of automated login attempts against usernames like admin and oracle . The same scanners try

2026-08-07 原文 →
AI 资讯

Canaries, Not Faith: Auditing Where Your Coding Agent Actually Writes

When people discuss AI agents escaping their boundaries, the mental image is usually dramatic: a jailbreak, a rogue prompt, an obvious disaster. What I've actually seen in practice is duller and more dangerous. The agent finishes its task successfully, the tests pass, and only later does someone notice it edited a file three directories up, or that a "helpful cleanup" deleted something it shouldn't have. Silent drift, not explosions. Last month I wrote about building a prompt regression harness that runs entirely on free tiers. This piece extends the same instinct from what the model says to what the agent does : I wanted a cheap, repeatable way to answer one narrow question — when my agent uses its tools, which parts of this machine does it actually reach? The specific risk I'm measuring A typical coding agent gets handed some mix of shell access, filesystem tools, and HTTP. The failure that matters most in day-to-day use isn't an adversarial attack. It's ordinary helpfulness with sloppy scope: An instruction like "find the relevant config" becomes a walk up the directory tree into your dotfiles. A refactoring task spills into a sibling repository because both were visible. A scratch file gets written somewhere outside the intended workspace and quietly persists. A fetch tool designed for one documentation site ends up POSTing context somewhere else. Notice that nothing here requires a malicious model. A cooperative model with generous tool permissions produces the same outcome. So the question isn't "can I trick the agent into misbehaving" — it's "does the sandbox I believe in actually exist." A probe harness you can run tonight The approach: hand the agent tasks engineered to invite scope violations, record every filesystem change it makes, and compare those changes against an explicit allowlist. Anything outside the list fails the run. The script below is pure standard-library Python. Instead of strace or eBPF (which need privileges you often don't have), it sna

2026-08-07 原文 →
AI 资讯

How to Detect Overtraining Before It Hits: Analyzing HRV with Python and Isolation Forests 🏃‍♂️📉

We’ve all been there: you're crushing your workouts, feeling like a beast, and then suddenly— bam . You can’t get out of bed, your resting heart rate is through the roof, and your motivation has evaporated. Welcome to Overtraining Syndrome (OTS) . In the world of sports science, Heart Rate Variability (HRV) is the gold standard for tracking recovery. By analyzing the tiny fluctuations between heartbeats (R-R intervals), we can peek into our Autonomic Nervous System (ANS). Today, we’re going to build a Python-based pipeline to fetch data from the Oura Cloud API , calculate key HRV metrics like SDNN and RMSSD , and use an Isolation Forest model to detect when you're pushing a bit too hard. Whether you're a biohacker or a developer interested in wearable data analysis , this guide will show you how to turn raw health data into actionable recovery insights. The Architecture: From Pulse to Prediction 🏗️ Before we dive into the code, let's visualize how the data flows from your finger to our anomaly detection model. graph TD A[Oura Ring] -->|Sync| B(Oura Cloud API) B -->|Raw R-R Intervals| C{Data Preprocessing} C -->|Filtering Artifacts| D[Feature Extraction] D -->|SDNN & RMSSD| E[Isolation Forest Model] E -->|Normal| F[Keep Training! 🚀] E -->|Anomaly| G[Rest Day Required! 🛑] Prerequisites 🛠️ To follow along, you’ll need a few tools in your tech_stack : Python 3.9+ Scikit-learn : For our machine learning magic. SciPy/NumPy : For the heavy math lifting. Oura Cloud API Access : To get that sweet, sweet biometric data. pip install scikit-learn scipy pandas requests Step 1: Fetching R-R Intervals from Oura 💍 The Oura Ring records "R-R intervals" (the time between successive heartbeats in milliseconds) during sleep. This is much more granular than a simple "Heart Rate" average. import requests import pandas as pd def fetch_oura_hrv_data ( api_token , start_date , end_date ): url = f ' https://api.ouraring.com/v2/usercollection/heart_rate ' headers = { ' Authorization ' : f ' B

2026-08-07 原文 →
AI 资讯

Deploying Qwen3.8 Max as a Task‑Oriented Agent in Python

You need a model that can plan, reason, and act across multiple steps. Qwen3.8 Max claims the top spot on the agentic index, but that alone doesn't guarantee a smooth integration. What You'll Learn Wrap Qwen3.8 Max in a reusable agent class. Compare its performance to GPT‑4 on a planning benchmark. Identify failure modes like hallucinations and token limits. Optimize cost and latency with batching and caching. Quick Start: Install and Load The Qwen library is available on PyPI. Install it and load the 3.8‑Max checkpoint. ## Install the Qwen package ! pip install qwen ## Load the model and tokenizer from qwen import QwenLM model = QwenLM . from_pretrained ( " qwen/qwen-3.8b-max " ) The code uses the official qwen package. It pulls the checkpoint from the Hugging Face hub and prepares the tokenizer. Building a Simple Agent Wrapper Below is a minimal agent that sends a prompt, receives a response, and can be extended with tool calls. class QwenAgent : def __init__ ( self , model , max_tokens = 512 ): self . model = model self . max_tokens = max_tokens def run ( self , prompt , ** kwargs ): # Forward the prompt to the model response = self . model . generate ( prompt , max_new_tokens = self . max_tokens , ** kwargs ) return response The wrapper keeps the interface simple: run(prompt) returns the raw text. You can add tool‑calling logic later. Benchmarking Agentic Behavior We test the agent on a short planning task: "Plan a 3‑day trip to Paris." We compare Qwen3.8 Max with GPT‑4. from openai import OpenAI client = OpenAI ( api_key = " YOUR_OPENAI_KEY " ) prompt = " Plan a 3-day trip to Paris, including activities, meals, and transport. " ## Qwen qwen_agent = QwenAgent ( model ) qwen_output = qwen_agent . run ( prompt ) ## GPT‑4 gpt_output = client . chat . completions . create ( model = " gpt-4o-mini " , messages = [{ " role " : " user " , " content " : prompt }], max_tokens = 512 ). choices [ 0 ]. message . content print ( " Qwen output: \n " , qwen_output ) print ( " \

2026-08-07 原文 →
开发者

Por qué tu bot recibe 403 de Cloudflare (y cómo endurecer un cliente ccxt)

Si automatizas un exchange con ccxt , tarde o temprano lo verás en los logs: rachas cortas de 403 Forbidden que pegan a fetch_balance , a los OHLCV o al saldo de earn, y que desaparecen solas a los pocos minutos. No es que tu API key esté mal. Es el WAF (Cloudflare) que muchos exchanges ponen delante de su REST, challengueando a algo que "parece un bot". Y tu bot es un bot — pero uno legítimo , operando tu propia cuenta contra la API oficial. El problema no es de permisos, es de reputación de cliente HTTP. Esto va de reducir los falsos positivos del WAF, no de evadir ningún control de acceso. Dos capas que lo mitigan Saqué este patrón de un bot propio sobre OKX, tras varias rachas de 403, y lo publiqué como librería: ccxt-resilience (Apache-2.0). 1. Que el WAF challengue menos: harden Un cliente ccxt por defecto se anuncia como lo que es. Ajustar un User-Agent de navegador, la cabecera Accept-Language y un timeout holgado hace que Cloudflare lo desafíe con menos frecuencia: import ccxt from ccxt_resilience import harden exchange = harden ( ccxt . okx ({ " apiKey " : ..., " secret " : ..., " password " : ..., })) harden toca un cliente ya construido , devuelve el mismo objeto (encadenable) y nunca rompe su construcción: si algo falla al fijar los atributos, los deja como estaban. 2. Reintentar solo lo que se debe: with_retry La tentación es envolver todo en un try/except que reintente. Es una trampa: reintentar un error de credenciales o de fondos solo gasta tiempo, termina igual de mal, y esconde bugs de lógica detrás de esperas. La clave es reintentar únicamente lo transitorio —403/Cloudflare, 429, timeouts— con backoff exponencial y jitter, y re-lanzar los errores reales en el acto: from ccxt_resilience import with_retry balance = with_retry ( exchange . fetch_balance ) ohlcv = with_retry ( exchange . fetch_ohlcv , " BTC/USDT " , timeframe = " 1m " , attempts = 4 , base = 1.0 , max_s = 8.0 ) Un error de autenticación se re-lanza inmediatamente, sin reintentar. Y s

2026-08-07 原文 →
AI 资讯

ASYNCIO.LOCK

Why Does Python Need asyncio.Lock? INTRODUCTION After understanding asyncio.Semaphore , I thought I had learned everything required to control multiple coroutines. A semaphore limits how many coroutines can execute simultaneously. Then another question came to my mind. If Python's event loop executes only one coroutine at a time, why do we even need a Lock? Initially, I assumed a lock was unnecessary because there was only one thread. But after experimenting with shared variables, I realized that even though only one coroutine executes at a particular instant, multiple coroutines can still interfere with each other. In this article, I'll explain the problem that led to asyncio.Lock , how it works, and why almost every backend application uses it. What You Will Learn Why asyncio.Lock exists What is a race condition What is a critical section How Lock works internally Practical examples Real-world backend use cases Prerequisites Before learning asyncio.Lock , you should understand: Coroutines Event Loop await asyncio.Semaphore The Problem Suppose we have a shared variable. counter = 0 Now imagine two coroutines trying to increment it. async def increment (): global counter temp = counter await asyncio . sleep ( 1 ) counter = temp + 1 Initially I expected the final value to become 2 because two coroutines are incrementing the counter. But that wasn't what happened. Let's See What Actually Happens Initially counter = 0 Now Coroutine A starts executing. Read counter ↓ temp = 0 ↓ await The coroutine reaches await . The event loop suspends it and starts another coroutine. Now Coroutine B executes. Read counter ↓ temp = 0 ↓ await Notice something interesting. Both coroutines have already read counter = 0 Now Coroutine A resumes. counter = 1 Then Coroutine B resumes. counter = 1 The final value becomes 1 instead of 2 This is called a Race Condition . Why Did This Happen? Initially I blamed the Event Loop. Later I realized, the Event Loop didn't do anything wrong. Its job is

2026-08-07 原文 →