AI 资讯
Your web app is invisible to AI search (and ranking on Google won't fix it)
You did the hard part. You designed it, you built it, you shipped it. The product is good. And still, the users do not come. I have been in that exact spot more than once. You refresh the analytics, you tell yourself it is early, and quietly a worse question starts to form: what if people are not ignoring my app, what if they simply never see it? Here is the thing almost nobody tells builders in 2026. For a growing share of your future users, the front door to the internet is no longer a list of blue links. It is a sentence. Someone opens ChatGPT, Perplexity, or Google's AI Mode and types "what is the best tool for X." The model replies with a short list of names. If your product is not one of them, you do not exist in that moment. There is no page two to claw your way onto. There is one answer, and you are either in it or you are not. Three things are probably true about your app right now, and you cannot see any of them Your app might render blank to the machines that decide. If you built a single-page app (React, Vue, most modern stacks), the raw HTML a crawler receives can be an almost empty . Most AI crawlers do not run JavaScript. They read what your server sends and leave. To them, your beautiful app has no words, no product, no reason to be cited. You can rank number one on Google and still be missing from the answer. In one large 2025 study, roughly 68 percent of the pages cited in AI Overviews were not even in the top ten organic results. Ranking and being cited have quietly become two different games. Winning the old one no longer wins you the new one. A model may already be describing your product to strangers, and getting it wrong. A feature you do not have. A price that is out of date. A category that is not yours. You are being represented in rooms you will never enter, by a narrator you never hired, and the only way to fix the story is to give the machines a cleaner one to read. None of this shows up in your dashboard. That is what makes it dangerous
AI 资讯
Bundling a CLI Binary as a Tauri v2 Sidecar: Lessons from Building a Desktop App
When you build a desktop app with Tauri v2 , sooner or later you'll hit a question: how do I bundle and manage an external CLI binary inside my app? Maybe it's ffmpeg for video processing. Maybe it's a database engine. Maybe — as in my case — it's frpc , the reverse-proxy client from the popular frp project. This post walks through the full lifecycle: bundling, spawning, lifecycle management, and even self-updating the binary at runtime — all from Rust. 1. Declaring the Sidecar In tauri.conf.json , declare the binary under bundle.externalBin : { "bundle" : { "externalBin" : [ "binaries/frpc" ] } } Tauri identifies the target platform by a filename suffix convention . You need to place the correctly-named binary in your project: Platform Filename macOS (Apple Silicon) frpc-aarch64-apple-darwin macOS (Intel) frpc-x86_64-apple-darwin Windows (x64) frpc-x86_64-pc-windows-msvc.exe Tauri automatically strips the suffix at runtime and loads the right binary for the current platform. 2. Spawning the Process Use tauri_plugin_shell to spawn the sidecar: use tauri_plugin_shell ::{ ShellExt , process :: CommandEvent }; #[tauri::command] async fn start_frpc ( app : tauri :: AppHandle ) -> Result < (), String > { let sidecar = app .shell () .sidecar ( "frpc" ) .map_err (| e | e .to_string ()) ? ; let ( mut rx , child ) = sidecar .args ([ "-c" , "frpc.toml" ]) .spawn () .map_err (| e | e .to_string ()) ? ; // Store the child handle so we can kill it later app .state :: < std :: sync :: Mutex < Option < tauri_plugin_shell :: process :: CommandChild >>> () .lock () .unwrap () .replace ( child ); // Listen to stdout/stderr in a background task tauri :: async_runtime :: spawn ( async move { while let Some ( event ) = rx .recv () .await { match event { CommandEvent :: Stdout ( line ) => { // Parse log line, update UI state... } CommandEvent :: Stderr ( line ) => { /* ... */ } CommandEvent :: Terminated ( _ ) => { // Process exited — update state machine } _ => {} } } }); Ok (()) } The
AI 资讯
Java & AI: What Developers Need to Know
Stop the ReAct Chaos: Building Deterministic Multi-Agent Cycles with Spring AI Graph If you are still letting LLMs freely decide their next execution step in an unconstrained ReAct loop, you are burning cloud budget on infinite loops and non-deterministic failures. In 2026, enterprise-grade AI requires the strict guardrails of stateful, cyclic graphs where transitions are governed by code, not LLM vibes. Why Most Developers Get This Wrong Naive ReAct Loops: Relying entirely on prompt-based tool calling to determine flow, which inevitably derails after 3-4 turns. Stateless Agents: Passing massive, unmanaged chat histories back and forth instead of maintaining a single, thread-safe state object. Lack of Edge Controls: Failing to hardcode conditional transitions, letting the LLM hallucinate its way into non-existent API endpoints. The Right Way The solution is to model your multi-agent system as a deterministic, cyclic graph where the LLM only executes node-level tasks, while Java code controls the state transitions. Define an Immutable State: Use Java record types to represent the thread-safe state passed between nodes. Explicit Nodes and Edges: Map agents (e.g., Writer, Critic) to discrete nodes and use conditional routers to decide the next transition. Spring AI Graph API: Leverage Spring AI 1.2.0's StatefulGraph to manage state persistence and concurrent transitions out-of-the-box. Model Specialization: Use fast, cheap models (like Llama 3.3) for routing decisions, and reasoning models (like Claude 3.5 Sonnet) only for complex node tasks. Show Me The Code (or Example) // Define stateful graph with immutable State record var workflow = new StatefulGraph < AgentState >() . addNode ( "writer" , state -> writerAgent . call ( state )) . addNode ( "critic" , state -> criticAgent . call ( state )) . addEdge ( START , "writer" ) . addEdge ( "writer" , "critic" ) . addConditionalEdge ( "critic" , state -> { return state . isApproved () ? END : "writer" ; // Deterministic cy
AI 资讯
Quieting PHP 8.2+ deprecated noise from older WP-CLI — three layers to keep JSON parse clean
Our multi-site maintenance tool fires wp plugin list --format=json against the sites it manages. One day, against a specific shared host (Xserver in Japan), this call started failing — and the failure mode was unusually subtle. Both the SSH connection test and the WP-CLI path test ( wp --version ) came back green. Users saw "all diagnostics pass, but the actual operation fails," a frustrating asymmetry. Tracing it back, the root cause was PHP Deprecated warnings emitted by older WP-CLI (2.x) under PHP 8.2+ leaking into the JSON output. This post walks through the three-layer defense we used to structurally absorb the noise without losing real failures. What was happening — Deprecated warnings on stdout The raw output on a problem host looked like this: PHP Deprecated: Creation of dynamic property WP_CLI\Dispatcher\CompositeCommand::$longdesc is deprecated in phar:///usr/bin/wp/vendor/wp-cli/wp-cli/php/... [ {"name":"akismet","status":"active","update":"none", ...}, ... ] Since PHP 8.2, assigning to a dynamic property on a class without #[\AllowDynamicProperties] emits a Deprecated warning. Xserver's /usr/bin/wp (an older WP-CLI 2.x) leans on dynamic properties internally, so running it on PHP 8.2+ produces a steady stream of those warnings. Note: PHP 8.2's dynamic-property deprecation is a healthy direction for the language. But during the transition, you get many libraries that "warn but still work" — WP-CLI was one of them. The actual problem is the host's php.ini : depending on display_errors , those warnings end up on stdout instead of stderr . Calling wp plugin list --format=json returns stdout containing both the warnings and the JSON, and json_decode() fails on the mixed input. Why diagnostics stayed green but operations failed The frustrating asymmetry came from how each test was checking the output: SSH connection test : runs echo ok — passes as long as ok appears somewhere in stdout, extra lines are fine WP-CLI path test : runs wp --version — passes as lon
AI 资讯
Building a real-time gold & FX price ticker with WebSocket (Socket.IO)
If you build apps for jewelers, fintech dashboards, or e-commerce price automation, you eventually need one thing: reliable, low-latency gold and currency prices . Scraping fragile sources breaks constantly. A dedicated price API solves this. In this post I'll show how to consume real-time gold (gram, quarter, coin) and FX rates over both REST and WebSocket (Socket.IO) using the Hasfiyat Gold & Currency API . Why a price API instead of scraping? Stability — a documented contract instead of HTML that changes without notice. Low latency — prices are pushed as the market moves, not on a slow cron. Multiple sources with failover — if one provider drops, the feed keeps flowing. 1. Polling with REST The simplest integration: request the prices you need with your API key. curl -X GET \ 'https://api.hasfiyat.com/api/prices?symbols=HAS,GRAM,CEYREK' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Accept: application/json' // Node.js const res = await fetch ( " https://api.hasfiyat.com/api/prices?symbols=HAS,GRAM,CEYREK " , { headers : { Authorization : " Bearer YOUR_API_KEY " } } ); const data = await res . json (); console . log ( data ); REST is ideal for periodic reporting, server-side jobs, and updating e-commerce product prices. 2. Live updates with Socket.IO For price screens, signage, and mobile apps where every tick matters, keep a connection open and let the server push changes: import { io } from " socket.io-client " ; const socket = io ( " https://api.hasfiyat.com " , { auth : { token : " YOUR_API_KEY " } }); socket . on ( " gold_prices " , ( data ) => { // { symbol: "HAS", type: "Has Altın", buy: 2450.85, sell: 2455.10, timestamp: "14:32:01.045" } console . log ( data ); }); No polling, no hammering the server — each market move arrives instantly. 3. A minimal live ticker in the browser <div id= "gold" ></div> <script src= "https://cdn.socket.io/4.7.5/socket.io.min.js" ></script> <script> const socket = io ( " https://api.hasfiyat.com " , { auth : { token : " YOUR
AI 资讯
Fixing the 550 SPF Check Failed Error: A Technical Step-by-Step Troubleshooting Guide
Understanding the 550 SPF Check Failed Error The "550 SPF Check Failed" error indicates that a receiving mail server rejected an incoming email. This rejection occurs because the sender's domain failed its Sender Policy Framework (SPF) validation. SPF is an email authentication protocol defined in RFC 7208 . SPF helps prevent email spoofing. It allows domain owners to specify which mail servers are authorized to send email on behalf of their domain. Receiving mail servers perform an SPF check by querying the sender's DNS for an SPF TXT record. If the sending server's IP address is not listed in the domain's SPF record, the SPF check fails. The receiving server then rejects the email based on its configured policy, often resulting in a 550 error. This error protects recipients from unauthorized emails and enhances email security. Initial Diagnosis: Identifying the Root Cause Diagnosing an SPF failure requires examining the bounce message and the domain's DNS records. The bounce message often provides specific details about the SPF failure. Look for phrases like "SPF validation failed," "unauthorized sender," or "IP address not permitted." Common reasons for a 550 SPF Check Failed error include: Missing SPF Record: No SPF TXT record exists for the sending domain. Incorrect SPF Syntax: The SPF record contains errors, making it unreadable or invalid. Incomplete SPF Record: The SPF record does not list all legitimate sending IP addresses or hostnames. DNS Lookup Limit Exceeded: The SPF record requires more than 10 DNS lookups, violating RFC 7208. DMARC Policy Enforcement: A DMARC (Domain-based Message Authentication, Reporting, and Conformance) policy ( RFC 7489 ) with p=reject or p=quarantine is in place, enforcing strict SPF failure handling. To begin diagnosis, use our SPF checker to verify your domain's SPF record and its validity. This tool quickly identifies syntax errors and lookup issues. Step-by-Step Troubleshooting and Resolution Resolving SPF failures involves
AI 资讯
Configuring DMARC p=quarantine: A Technical Step-by-Step Guide to Secure Your Domain and Improve Deliverability
Introduction to DMARC and the p=quarantine Policy DMARC (Domain-based Message Authentication, Reporting, and Conformance), defined in RFC 7489 , is an email authentication protocol. It builds upon SPF and DKIM to provide domain owners with the ability to protect their domain from unauthorized use. DMARC enables senders to specify how receiving mail servers should handle unauthenticated emails originating from their domain. It also provides a mechanism for receiving servers to report back to the domain owner about authentication results. DMARC policies dictate the action receiving mail servers should take when an email fails DMARC authentication. The three primary policies are: p=none : Monitor mode. Receiving servers take no action on failed messages but send reports. This is the initial deployment phase. p=quarantine : Receiving servers should treat failed messages as suspicious. They are typically placed in the recipient's spam folder or flagged for further review. p=reject : Receiving servers should outright reject messages that fail DMARC authentication. This is the strongest enforcement policy. Implementing p=quarantine is a critical step towards full domain protection. It allows domain owners to mitigate spoofing and phishing attempts without immediately blocking legitimate, but misconfigured, email streams. This policy provides a balance between security enforcement and minimizing potential deliverability disruptions. Prerequisites for DMARC p=quarantine Implementation Before deploying a p=quarantine policy, proper configuration of SPF and DKIM is mandatory. DMARC relies on these underlying authentication mechanisms and their alignment with the sending domain. SPF (Sender Policy Framework) SPF, specified in RFC 7208 , allows domain owners to publish a list of authorized sending IP addresses in their DNS. Receiving mail servers check the SPF record to verify if an incoming email originated from an authorized server. An SPF record is a TXT record at the root of
AI 资讯
How to Compress Images in the Browser with Canvas API (No Uploads, No Server)
How to Compress Images in the Browser with Canvas API Every image you upload to a "free" online compressor is sent to a server — often without you knowing what happens to it afterward. For a tool that processes your private photos, that's a terrible design. Here's how to build (or use) an image compressor that runs entirely in the browser using the HTML5 Canvas API. No uploads, no server costs, and unlimited file sizes. The Core Technique: Canvas toBlob() The key API is HTMLCanvasElement.toBlob() : js const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const img = new Image(); img.onload = () => { canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.drawImage(img, 0, 0); canvas.toBlob((blob) => { const url = URL.createObjectURL(blob); }, 'image/jpeg', 0.8); }; img.src = 'your-image.jpg'; The second parameter is the MIME type (image/jpeg, image/png, image/webp, image/avif). The third is quality (0–1). Step-Down Resizing for Large Images If you're compressing a 6000×4000 px photo, drawing it at full resolution onto a canvas can eat 70+ MB of memory. Step-down resizing halves the dimensions repeatedly: function stepDownEncode(img, maxDim, quality) { let w = img.naturalWidth; let h = img.naturalHeight; let src = img; while (w > maxDim * 2 || h > maxDim * 2) { w = Math.floor(w / 2); h = Math.floor(h / 2); const temp = document.createElement('canvas'); temp.width = w; temp.height = h; temp.getContext('2d').drawImage(src, 0, 0, w, h); src = temp; } const canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; canvas.getContext('2d').drawImage(src, 0, 0, w, h); return new Promise((resolve) => { canvas.toBlob((blob) => resolve(blob), 'image/jpeg', quality); }); } This prevents memory crashes and actually produces better quality (step-down preserves more detail than a single jump). Comparing Real-World Results Format Avg Original Avg Compressed Avg Savings JPEG → JPEG (Q80) 3.2 MB 0.8 MB 75% PNG → We
AI 资讯
AGENTS.md, Hands-On: Build One Step by Step (and Watch an Agent Use It)
In the field guide I covered what an AGENTS.md is and what belongs in it. This is the hands-on follow-up: we'll build a complete AGENTS.md for a real project, one section at a time, then point an AI coding agent at it and watch the difference it makes. By the end you'll have a working file — and you'll have seen it pay off. New to AGENTS.md? It's a single Markdown file at the root of your repo that tells AI coding agents how to work in it — build steps, tests, conventions, guardrails. The "why" behind each section is in the field guide . The project we'll use We'll write the AGENTS.md for a small but real service: a URL shortener API in Python — FastAPI, SQLite, pytest. A couple of endpoints, a thin data layer, a test suite. Follow along with this, or swap in your own repo — the steps are identical. Its shape: linkshort/ app/ main.py # FastAPI routes db.py # SQLite access models.py # Pydantic models migrations/ # generated SQL — not hand-edited tests/ requirements.txt Step 0 — Start with an empty file At the repo root: touch AGENTS.md That's the whole step. We'll fill it in one section at a time, building toward a file an agent can read in thirty seconds. Step 1 — Orientation: one line Tell the agent what it's looking at. Add: # AGENTS.md A URL shortener API in Python — FastAPI, SQLite, pytest. One sentence sets the agent's priors: it knows the language, framework, and storage before it reads a single line of code. Step 2 — Setup and run The agent can't help if it can't start the project. Add the real, copy-pasteable commands: ## Setup python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt ## Run uvicorn app.main:app --reload # http://localhost:8000 Use the commands that actually work in your repo — no placeholders. Step 3 — Tests: the agent's feedback loop This is the most important section, because tests are how the agent checks its own work. Add: ## Test — all must pass before a change is done pytest ruff check . mypy app Now the agent
AI 资讯
Building Instant Translation Assistance for Book Translations with Python and LLMs
How we integrated real-time phrase translation feedback into our AI-powered book translation workflow, and what we learned about latency, context, and prompt engineering. When we launched LectuLibre, our AI-powered book translation platform, users loved the quality of full-chapter translations. But they kept asking for something else: while reading a partially translated book, they'd stumble on an untranslated phrase or an awkward auto-translation and want to quickly get a better version without leaving the page. So we built 即时翻译求助 (Instant Translation Help)—a feature that lets readers highlight any phrase and get a context-aware, human-quality translation within seconds, along with a brief explanation of tricky parts. Here's how we built it, the technical challenges we faced, and the lessons we learned about stitching LLMs into a real-time reading experience. Problem: Real-time, Context-Aware Translation Inside a Book Most web apps offer generic translation via API calls—send a sentence to Google Translate, get a result. But that doesn't work for literary texts. A phrase like "She let the cat out of the bag" needs to be translated idiomatically, and the appropriate rendering depends heavily on the surrounding paragraphs (is the tone formal? sarcastic? part of a metaphor chain?). Our existing translation pipeline processes entire chapters in bulk with carefully crafted prompts, but for instant help, we needed sub-second latency while preserving that same depth of context. Our Approach: Server‑Sent Events and a Smart Prompt Buffer We chose Server-Sent Events (SSE) over WebSockets because the communication is one-directional (server pushes translation tokens) and SSE is simpler to implement with FastAPI. The client (a React app) sends a POST request with: The phrase to translate The book ID and the exact location (chapter/paragraph index) The target language Our backend retrieves the surrounding text from PostgreSQL (we store the original book in chunks), feeds a care
AI 资讯
Solon 4.0 ReActAgent: A Practical Guide to Building AI Agents That Think and Act
If you've ever wanted an AI that doesn't just chat but actually does things — queries databases, calls APIs, makes decisions, and learns from results — you're in the right place. In this tutorial, I'll show you how to build production-ready AI agents using Solon 4.0's ReActAgent . By the end, you'll have built an agent that can reason through complex problems, use external tools, and adapt its behavior based on real-world feedback. What Makes ReActAgent Different? Traditional LLMs are great at generating text, but they hit a wall when they need to interact with the real world — checking a database, fetching live data, or performing calculations. ReActAgent (Reason + Act) breaks through that wall. It implements a cognitive loop: Thought → Action → Observation → (repeat or finish) The agent thinks about what to do next, acts by calling a tool, observes the result, and decides whether to continue or deliver the final answer. This isn't just theory. Solon's ReActAgent has been used in production for automated customer support, intelligent data analysis, and multi-step workflow automation. 1. Adding the Dependency First, add the solon-ai-agent module to your project: <dependency> <groupId> org.noear </groupId> <artifactId> solon-ai-agent </artifactId> </dependency> Note : If you're using Solon's parent POM, the version is managed automatically. Otherwise, use the latest Solon version. 2. Building a ChatModel (The Agent's Brain) Every agent needs a "brain" — a ChatModel that powers reasoning. Let's build one using the fluent API: import org.noear.solon.ai.chat.ChatModel ; ChatModel chatModel = ChatModel . of ( "https://api.moark.com/v1/chat/completions" ) . apiKey ( "your-api-key-here" ) . model ( "Qwen3-32B" ) . build (); You can also configure it via YAML and inject it: solon.ai.chat : demo : apiUrl : " http://127.0.0.1:11434/api/chat" provider : " ollama" model : " llama3.2" @Inject ( "${solon.ai.chat.demo}" ) ChatConfig chatConfig ; ChatModel chatModel = ChatModel . o
AI 资讯
Convertir des images en lot (HEIC, WebP, JPG) gratuitement — Guide pratique
📖 Article original : GitHub Gist Un guide technique par Mohamed ben mallessa Le problème Recevoir un dossier de 500 fichiers HEIC à convertir en WebP pour un site web est une situation courante pour tout développeur. Les solutions traditionnelles ont leurs limites : ImageMagick nécessite des codecs spécifiques, les convertisseurs en ligne sont limités en taille, et le traitement manuel est exclu à cette échelle. La solution Photopea (Photoshop gratuit dans le navigateur) supporte nativement tous les formats d'image courants. En l'utilisant comme moteur de conversion piloté par script, on obtient un pipeline batch rapide et fiable. Formats supportés Entrée Sorties possibles HEIC / HEIF JPG, PNG, WebP JPEG WebP, PNG, PSD PNG JPG, WebP WebP PNG, JPG PSD PNG, JPG, WebP SVG PNG, JPG TIFF PNG, JPG, WebP Pipeline Dossier source (500 HEIC) → Photopea → Dossier sortie (500 WebP) Le script préserve la structure des sous-dossiers, applique le redimensionnement et la qualité configurés, et livre les fichiers organisés. Paramètres typiques --format webp # Format de sortie --quality 80 # Qualité (1-100) --resize 1920 # Redimensionnement (côté long) --output ./web/ # Dossier de destination Avantages Un seul outil pour tous les formats d'entrée Aucun codec à installer (Photopea gère tout nativement) Gratuit et sans abonnement Local — les fichiers ne quittent pas votre machine Structure préservée — l'arborescence est conservée Mohamed ben mallessa — Full-stack developer & solutions B2B 🔗 GitHub · LinkedIn opensource #webp #python #tutorial 💻 Vous avez un projet technique ? Développement full-stack, automatisation IA, solutions B2B sur mesure. 🔗 GitHub 💼 LinkedIn 🎨 Behance Article initialement publié sur GitHub Gist
AI 资讯
Solon 4.0 ChatModel: A Practical Guide to Building LLM-Powered Applications
If you've ever tried integrating a large language model (LLM) into a Java application, you've probably written a lot of boilerplate: HTTP clients, JSON parsing, streaming handling, session management. Solon 4.0's ChatModel abstracts all of that away with a clean, builder-oriented API. In this guide, I'll walk through building real, working AI features using ChatModel — from a simple chat call to a streaming chatbot with conversation memory. 1. What Is ChatModel? ChatModel (package org.noear.solon.ai.chat ) is a unified LLM client in Solon's AI ecosystem. Instead of writing raw HTTP calls for different model providers, you use a single API that supports: Synchronous calls — one-shot request, full response Streaming calls — reactive streaming via Project Reactor ( Flux<ChatResponse> ) Tool/Function Calling — let the LLM invoke your Java methods Chat Sessions — automatic conversation memory Multi-modal messages — text, images, audio Dialect adaptation — works with OpenAI, Ollama, Anthropic, Gemini, DashScope, and more The best part? It uses a dialect pattern — you point it at any compatible LLM endpoint, and it adapts automatically. 2. Setting Up Add the dependency to your pom.xml (no parent POM needed — Solon works standalone): <dependency> <groupId> org.noear </groupId> <artifactId> solon-ai </artifactId> <version> ${solon.version} </version> </dependency> This pulls in all built-in dialects (OpenAI, Ollama, Gemini, Anthropic, DashScope). 3. Configuration 3.1 Via YAML (Recommended) solon.ai.chat : demo : apiUrl : " http://127.0.0.1:11434/api/chat" # Full URL, not baseUrl provider : " ollama" # Dialect identifier model : " llama3.2" # Model name headers : x-demo : " demo1" Then create a @Bean to get a ready-to-use ChatModel : import org.noear.solon.ai.chat.ChatConfig ; import org.noear.solon.ai.chat.ChatModel ; import org.noear.solon.annotation.Bean ; import org.noear.solon.annotation.Configuration ; import org.noear.solon.annotation.Inject ; @Configuration public cla
AI 资讯
Effort Levels in Practice: I Benchmarked low Through max on Real Tasks
The current Claude models give you an effort knob with five settings: low , medium , high , xhigh , max . The docs tell you what each is for. I wanted numbers, so I ran the same three real tasks across all five levels and measured tokens, latency, and quality. The results changed how I set effort, and one of them surprised me. Here is the data and what I do with it now. What effort controls Effort is not just "how much the model thinks." It controls overall token spend: how much it thinks and how it acts. Lower effort means fewer, more consolidated tool calls, less preamble, terser output. Higher effort means more exploration before answering. The default is high if you omit it. const response = await client . messages . create ({ model : " claude-opus-4-8 " , max_tokens : 16000 , thinking : { type : " adaptive " }, output_config : { effort : " medium " }, // the knob messages , }); The three tasks I picked tasks that span the range of what I actually do: Classification : label a contract finding as low/medium/high/critical. Short, scoped. Code generation : write a TypeScript function with edge-case handling. Medium difficulty. Multi-step audit : analyze a 200-line contract for vulnerabilities across functions. Hard, agentic. I ran each at all five effort levels, three times, and averaged. I scored quality against a known-correct answer for tasks 1 and 3, and by manual review for task 2. The results Task 1, classification. Quality was flat across every effort level. The right label is the right label, and the model nailed it at low just as well as at max . But token usage climbed steeply: max used roughly 8x the tokens of low for an identical answer. Latency tracked tokens. The lesson: for genuinely simple, scoped tasks, high effort is pure waste. I set classification to low . Task 2, code generation. Quality improved from low to high , then plateaued. At low the model sometimes skipped an edge case. At high it caught them. xhigh and max produced essentially the sam
AI 资讯
The 2026 AI CLI Landscape: Claude Code, Gemini CLI (Antigravity CLI), and OpenClaw
Terminal-based AI agents have evolved considerably over the past few months, and several changes are significant enough that developers relying on these tools should be aware of them. Most notably, Google has begun retiring Gemini CLI for individual users in favor of Antigravity CLI — a closed-source successor that has drawn some pushback from the community that built out Gemini CLI's open-source ecosystem. Meanwhile, Claude Code has moved to the Opus 4.8 and Fable 5 models with a 1M-token context window, and OpenClaw, the open-source "always-on" agent, has grown into one of the most-starred projects on GitHub — alongside a documented CVE worth knowing about before deployment. I've just published an updated, fact-checked comparison covering: What actually changed with Gemini CLI's retirement, and what it means if you have scripts or CI/CD pipelines depending on it Claude Code's current model lineup, context window, and new Dynamic Workflows feature OpenClaw's architecture, extensibility via ClawHub, and the security considerations that come with deep system access A full feature-comparison table (cost, context window, open-source status, setup complexity) A practical case study walking through how all three tools can work together on a real project Would be curious to hear which of these you're using day-to-day, and whether the Gemini → Antigravity transition has affected your workflow. Full article here: Devlycan - Technology & Programming Insights Devlycan - Technology, programming, AI, lifestyle, and future trends—simple insights for the new digital generation. devlycan.com
AI 资讯
I Spent 30 Days Comparing Startup and Enterprise AI APIs
I Spent 30 Days Comparing Startup and Enterprise AI APIs Look, I'm just a dude building a SaaS side project. Not enterprise, not Fortune 500, just me and a few friends trying to ship something useful. So when I started hitting AI API walls, I went down the rabbit hole of figuring out what the heck to do. And honestly? Most guides out there are written by people who clearly have never had to choose between buying groceries or paying for OpenAI credits. They're either too corporate ("here's our enterprise procurement guide!") or too naive ("just use the cheapest API!"). So I figured I'd write the guide I WISH existed when I started. And I'm gonna throw in some enterprise stuff too because I consulted for a bigger company last year and saw what THEY deal with. Different worlds, I tell ya. Let me break this down properly. Why I Almost Just Used DeepSeek Directly Okay so here's the thing. When I first started, I was like "DeepSeek is dirt cheap, let me just sign up there and call it a day." I mean, the pricing was wild. Like cents per million tokens. How could I lose? Then I tried to actually sign up. Chinese phone number required. WeChat Pay or Alipay only. No PayPal. No Visa. Nothing. And I get it, that's their home market, but for me sitting here in my apartment in the US? Absolute dead end. So I started looking at aggregators. Tried like four of them. Some had weird pricing. Some had models that didn't actually work. One of them straight up charged me for tokens I never used (still salty about that). Then I landed on Global API and honestly I gotta say, it just worked. Email signup, PayPal, and I could test DeepSeek AND Claude AND Qwen all with one key. That's when I realised going direct to providers is kind of a trap if you're small. Let me show you the actual problem with going direct. The "Go Direct" Trap Here's what happens when you sign up direct with various providers: Problem What Happens to You Locked to one vendor Your whole app depends on their uptime Paym
AI 资讯
No messages table! The data model behind my own Claude-based chatbot
This tutorial was written by Néstor Daza . This is the second article in a series about building Claudius , my own Claude-based chatbot ( Github ). The prologue made the case for building it, and for choosing MongoDB as its foundation. Open the conversations collection in Claudius’ database and you find the usual fields of a thread header but nothing else: a userId , a title , some timestamps , and so on, but no array of messages, no messages collection sitting beside it either! The text of every conversation lives somewhere else entirely, in the LangGraph checkpointer, which I wire up later in this series. This absence is a modeling decision, and how I came up with the database schema for my chatbot is the theme of this article. If you come from a relational background, you're used to modeling the data first when designing a database. For a project like this, you would start by finding the entities and normalizing them, and the final schema would come out of the data's structure: a conversations table and a messages table with a foreign key between them, because that is what the data looks like. Document modeling runs the other way. You start from how the application reads and writes, and the shape of the document follows the access patterns. Claudius never reads conversation messages without the agent's full working state wrapped around them, and that state is persisted using the LangGraph checkpointer. A separate messages table would add nothing, since the app would always have to join it back to that state on every read. The access pattern says the messages belong with the agent state, so that is where they go, and conversations are left as the lightweight header the list view actually needs. That inversion, modeling around use rather than around the data, runs through everything below. Schema-flexible is not schemaless This is the misconception lots of people often carry, and it is worth killing on the way in. A document database does not mean no schema; it mea
AI 资讯
Build a Real-Time Crypto Trading Dashboard with Python, WebSockets, and React
Build a Real-Time Crypto Trading Dashboard with Python, WebSockets, and React Real-time data is the difference between catching a move and reading about it later. This tutorial walks through a minimal but complete stack: a Python WebSocket client pulling live prices, a simple signal generator, and a React frontend displaying everything. You will end up with a dashboard that shows live Binance prices, basic momentum signals, and auto-updates without polling. Why this stack Binance provides a clean WebSocket API for tickers and trades. Python handles the backend connection and lightweight analysis. React keeps the UI reactive and simple to extend. No heavy frameworks, no paid data feeds. Prerequisites Python 3.11+ Node 20+ A Binance API key (read-only is fine for prices) Step 1: Python price stream Install the client library: pip install python-binance pandas Create price_stream.py : import asyncio import json from binance import AsyncClient , BinanceSocketManager import pandas as pd from datetime import datetime async def main (): client = await AsyncClient . create () bm = BinanceSocketManager ( client ) ts = bm . trade_socket ( ' BTCUSDT ' ) async with ts as tscm : while True : res = await tscm . recv () price = float ( res [ ' p ' ]) qty = float ( res [ ' q ' ]) ts = datetime . fromtimestamp ( res [ ' T ' ] / 1000 ) print ( f " { ts } | BTCUSDT { price : . 2 f } | { qty : . 4 f } BTC " ) if __name__ == " __main__ " : asyncio . run ( main ()) Run it: python price_stream.py You should see a live feed of trades. Keep this running as your data source. Step 2: Add a simple momentum signal Extend the script to calculate a 20-trade rolling average and flag when price deviates more than 0.3%: # inside the loop, after parsing res prices . append ( price ) if len ( prices ) > 20 : prices . pop ( 0 ) avg = sum ( prices ) / len ( prices ) deviation = ( price - avg ) / avg * 100 if abs ( deviation ) > 0.3 : print ( f " ⚡ Signal: { deviation : + . 2 f } % from 20-trade avg " )
AI 资讯
How to Automate OG Image Generation for Your Blog Using a Screenshot API
Every blog post needs an OG image. Without one, your links look blank on Twitter, LinkedIn, and Slack — just a plain URL that nobody clicks. Most developers solve this by spinning up a headless browser, loading an HTML template, taking a screenshot, and uploading it somewhere. It works, but now you're maintaining a Puppeteer instance, dealing with font rendering quirks, and burning server resources on something that should be simple. There's a faster approach: design your OG images as HTML templates and let a screenshot API handle the rendering. The Idea: HTML Templates as OG Images Think of your OG image as a tiny webpage. You already know HTML and CSS. Build a 1200×630 template with your blog title, author name, maybe a gradient background — whatever fits your brand. Host it or pass it as raw HTML. Then call an API to screenshot it. Done. A basic template might look like this: <div style= "width:1200px;height:630px;display:flex;align-items:center; justify-content:center;background:linear-gradient(135deg,#1a1a2e,#16213e); font-family:Inter,sans-serif;padding:60px" > <div style= "color:#fff;text-align:center" > <h1 style= "font-size:48px;margin:0" > {{title}} </h1> <p style= "font-size:24px;color:#8892b0;margin-top:20px" > {{author}} · {{date}} </p> </div> </div> Replace the placeholders on your server, then send the resulting HTML (or a URL pointing to it) to the API. Calling the API With ScreenshotRun , a single curl request captures the rendered template as a PNG: curl -X POST "https://api.screenshotrun.com/v1/screenshot" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://yourblog.com/og-template?title=My+Post+Title", "viewport_width": 1200, "viewport_height": 630, "format": "png" }' The response gives you the image file. Save it to your CDN, set the og:image meta tag, and you're done. No browser to manage, no Chrome binary eating RAM on your CI server. Wiring It Into Your Build If you publish with a static sit
AI 资讯
Applied Creativity and Concept Generation - Brainstorming
Thomas Edison put it plainly: "To have a great idea, have a lot of them." Steve Jobs said something similar. "Creativity is just having enough dots to connect... to connect experiences and to synthesise new things." Both of them are saying the same thing. Your first idea is rarely your best one. The reason why people you call creative can come up with great ideas easily is that they have had more experiences or have thought more about their experiences than other people. So the question becomes: how do you get more ideas, faster? The Most Used Method for Applied Creativity The answer has a name. It was coined by advertising executive Alex Osborn in the 1940s. He called it brainstorming - using the brain to storm a creative problem, with each person in the room attacking the same objective. It sounds simple. Most teams think they already do it. Most of them are wrong. Real brainstorming is a structured process with rules. Break the rules, and you get something that looks like brainstorming but produces far fewer useful ideas. Why Most Brainstorming Sessions Fail Here is what kills a brainstorming session before it even starts. Someone says an idea. Someone else says, "That won't work." The room goes quiet. People stop sharing. That is it. That is the whole problem. When people fear judgment, they self-censor. They only say the safe, obvious ideas. The interesting ones, the ones that could actually lead somewhere, stay locked inside people's heads. Most teams have that one gaffer who has already decided which ideas are worth hearing before anyone has finished their sentence. Or the one who gives you the floor, listens patiently, and then quietly bins everything you said, not because it was bad, but because it was not theirs. Both types do the same damage. The room reads it. People stop sharing. And just like that, the best idea in the session never gets spoken. The goal of brainstorming is to get more ideas. That means the number one rule is: defer judgment . The Rule