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

标签:#go

找到 1230 篇相关文章

AI 资讯

How to build a pitch deck triage agent with LangGraph and Nango

In this guide you will build an AI agent that reads pitch-deck emails from Gmail, judges each deck against a fixed investment thesis with an LLM, and posts a Slack message when a deck is a fit. LangGraph orchestrates the steps; Nango handles the Gmail and Slack connections and exposes them to the graph over MCP. By the end you will have: Three Nango actions - search Gmail for pitch-deck emails, download an attachment, post to Slack - deployed and callable. A LangGraph pipeline that runs those actions in a fixed order and, in between, asks OpenAI for a { fit, reasoning, evidenceQuote } verdict grounded in a real quote from the deck. A working end-to-end run: email a PDF to yourself, run one command, get a Slack message. Why is it hard to build a pipeline like this? You need two separate OAuth integrations - Gmail and Slack - each with its own token lifecycle, scopes, and refresh flow. Get either wrong and the pipeline fails days later when a token expires, not on your first test. Gmail's API does not hand you a pitch deck in one call. Searching an inbox returns message metadata; getting an attachment's bytes is a second request keyed off an attachmentId from the first. And Gmail returns those bytes base64url-encoded, not standard base64, so a naive decode produces a broken PDF. Then there's the LLM. It's easy to get a model to say "yes, this fits". It's harder to make it say why , and prove the why by quoting the actual document rather than paraphrasing something half-remembered from the prompt. Why use Nango for this Nango gives you the OAuth flow, token storage, and refresh logic for Gmail and Slack out of the box. You connect an account once in a hosted popup; every call after that carries a valid token without your code touching it. You write the provider logic as small server-side functions called actions - input schema, output schema, and an exec body. Deploy one and it's a versioned endpoint, and Nango automatically exposes it as a tool on its hosted MCP serve

2026-09-07 原文 →
AI 资讯

The Dumb Prompt

Exact paths, exact signatures, one command - and nothing left to interpret. 👋 I'm Anton - a software engineer working mostly in PHP/Symfony and Go, currently carving a live PHP monolith into Go services. Part 2 of this series was about how small a unit of work has to get before anyone can execute it blind. This part is about the text of that unit: what I write down, and the phrases I've banned from my own writing. Notes: github.com/brilliant-almazov . Maybe this is useful to you, maybe you already do it better, maybe you read it completely differently. As before: these are my habits on one codebase, not advice for yours. Three holes in one page I once wrote a task the way I'd write it for a person sitting two desks away. It read fine. It also had three phrases in it that weren't instructions at all: instead of the contract: take the contract from the neighbouring spec instead of the values: check against the previous implementation instead of a decision already made: agree on the approach The executor fell into all three, in order. The first one sent it reading neighbouring packages, because "the neighbouring spec" is an address, and an address has to be resolved before it can be used. The second one made it pick a sample - and the sample it picked was not the one I had in mind, because I never said which one I had in mind. The third one ended the run: it came back with a clarifying question, having produced nothing. That's not a bad day and it isn't a bad executor. It's three holes in one page of text, each one dug by a phrase I wrote myself. the task I wrote what the executor did ────────────────────────────────── ───────────────────────────────── "take the contract from the ──▶ read the neighbouring packages neighbouring spec" "check against the previous ──▶ picked a sample - the wrong one implementation" "agree on the approach" ──▶ came back with a question, produced nothing The diagnosis A task is executed literally. Anything phrased as a choice becomes the exe

2026-09-07 原文 →
AI 资讯

The grant money already exists. My AI kept inventing foundations to spend it on

This is a submission for Weekend Challenge: Generosity Edition What I Built The money exists. A small NGO just cannot find it. That is what a generosity problem looks like at the small end. The giving has already happened — foundations with open, rolling, unclaimed programmes, sitting there — and it is spread across a few thousand pages nobody has time to read. After the 2025–26 collapse of USAID funding, organisations that had one funder now need six, and the people doing that searching are the same people running the programme: a director who is also the grant writer, working evenings. Generosity is not the scarce thing here. Attention is. So an AI grant finder is an obvious idea. It is also a dangerous one, because the failure mode is not "unhelpful." A three-person NGO that spends a week writing an application against a deadline that never existed has lost a week it cannot get back, and it will not find out until it submits. The tool would have taken the one thing that was actually scarce. FundFinderAI is the response, in one sentence: it searches the live web for currently open grants that fit your NGO, and then it refuses to trust its own model about any of them. Every application URL Gemini produces is independently fetched before you see it, and the card tells you what happened when we tried. The interesting part is not that it searches. It is everything the app does to establish that the search actually happened and that the result actually exists. Demo Live: fundfinder-ai.vercel.app — describe an NGO, get grants, open a drafted Letter of Inquiry. Give it 30–120 seconds. It is running ten to thirty real Google searches and then fetching every URL that comes back, and the page shows you the clock while it does. Paste this in if you would rather not invent an NGO: NGO name: Kisumu STEM Girls Collective Location: Kisumu, Kenya Mission: We run after-school robotics and coding clubs for girls aged 12-17 in Kisumu, Kenya, and train their teachers to keep the club

2026-09-07 原文 →
AI 资讯

Handover: small charities know what hurts, not what skill they are missing

This is a submission for Weekend Challenge: Generosity Edition What I Built Handover takes a plain description of what is going wrong inside a small charity and works out the role that would fix it. Not the role they asked for. The one they actually need. You type something like "our books are a mess, and we have missed two filing deadlines". It comes back with a full trustee role: the diagnosis, what the person would do, a deliberately short list of essential skills, an honest time commitment, and an advert you can paste straight into your newsletter. Then a volunteer pastes their CV, badly, and gets scored against every open role with a reason and an honest note on where the fit is thin. Why A couple of days ago I got an email saying Reach Volunteering is closing after 45 years. It genuinely hurt to read. Reach connected small UK charities with people who wanted to give them professional skills. Last year it placed 5,996 volunteers and trustees across 2,440 organisations. The people it placed contributed around £60 million in expertise. Ninety-six per cent of those organisations ran on under £1 million a year, and nearly half on under £50,000. It is not closing because the work stopped mattering. It is closing because funding for the infrastructure that helps small charities build capacity has dried up. Reach was the largest single source of trustees in the sector, and it is shutting at the peak of its impact. I volunteer as a digital navigator, which mostly means sitting with people who have been handed a system that assumes a confidence nobody ever gave them. You watch someone decide they are the problem, when the thing in front of them was just badly built. Reach existed to stop small charities from feeling like that about their own gaps, and now it is shutting down. I cannot rebuild 45 years of relationships in a weekend. So I picked the one piece of what Reach did that was pure expertise rather than headcount, and rebuilt that. The thing everyone gets wrong E

2026-09-07 原文 →
AI 资讯

Why I Rewrote Four Services in Go

I had four small services. Each one was a Model Context Protocol adapter — a thin wrapper that lets an AI agent call out to some external thing. One talked to Replicate for image generation. One talked to a Nostr-friendly social poster. One was a Git-aware research helper. One was a Tavily-powered web search. They were all written in Python. They all ran on Knative on a small Kubernetes cluster. They all worked. And they were all just slightly too slow to use. A six-second cold start is fine for nothing. It is the precisely wrong amount of time — slow enough to be noticed, fast enough to feel almost loaded. An AI agent waiting six seconds for a single tool call does not know it is waiting for a cold start; it just knows the tool is sluggish. The user does not know either. The user just thinks the agent is broken. And six seconds was a good day. Some of the services took longer. So I rewrote them in Go. This is what that cost me, and what the measurements actually were before and after. The actual problem Cold starts on serverless platforms are an old problem with a well-known shape. The platform spins your container up only when traffic arrives, so the first request after an idle period pays the full startup tax — image pull (or warm cache hit), container start, language runtime initialisation, application bootstrap. For Python, application bootstrap is where the bill arrives. The interpreter has to start. import statements run. The dependency tree gets walked. If you have ever wondered why a hello world Flask app feels so much heavier than a hello world Go binary, this is why. Python is doing real work before your code runs. Go has already started. On a small Kubernetes cluster — small as in I am paying for it personally — you do not keep a fleet of warm replicas around. You scale-to-zero. You scale-to-zero because that is the entire point of using serverless on small infrastructure. The trade-off is that every idle service eats a cold start the next time it is inv

2026-09-06 原文 →
AI 资讯

The ledger asks the model to show its work before it counts the money

This is a submission for Weekend Challenge: Generosity Edition What I Built A donation ledger for a group too small to buy software. It is a Google Sheet, some Apps Script, and one public page a donor can open. The group I had in mind is the kind that exists on every street: a neighbourhood fund, a school parents' group, a committee that collects for winter coats. Money arrives over WhatsApp and leaves in cash, and somebody keeps it in a notebook. The arithmetic is not the hard part. The hard part arrives three months later when a donor asks where their money went, and answering needs the notebook, the person holding it, and an afternoon. Software for this exists and is priced for organisations with a finance team. So the ledger stays in a spreadsheet a volunteer already knows how to open, and the only thing added is what a spreadsheet cannot do alone: read messy human messages, refuse to trust its own reading , and publish the page that answers the question before it is asked. Demo The public page a donor opens → That page is the deployed page, byte for byte, with one line changed: where the Apps Script version writes <?= data ?> , the demo fetches the same JSON from a file so you can read it without a Google account. The JSON is produced by running the sample month through the same recordEntry() and publicView() the real script uses, so if the ledger rules change, the demo changes with them or the build fails. The sample month deliberately includes the things that go wrong: a receipt two volunteers forwarded, a donation typed with one zero too many and later corrected, and a reading the checks refused to trust. What the model read, and whether it was allowed to count → The ledger page shows the result. This one shows the part worth showing. Pick any of six real donation messages and it highlights the exact characters Gemini says it read the amount from, lists the three checks with their outcomes, and says why the row was posted or held. It is fed by a recorded run

2026-09-06 原文 →
AI 资讯

Catch Bad Validation Tags at Compile Time with checkerlint

Struct tags are just strings — a typo'd checker name, a wrong-typed field, or a renamed cross-field target all compile fine and fail silently at runtime. checkerlint catches all three before you ship. Struct tags are string literals. The Go compiler checks that your struct compiles — it has no idea what checkers:"eq-field:Passwrd" means, so a typo in a field name, a checker applied to a field of the wrong type, or a renamed field that a cross-field rule still points at all compile fine. They fail later, at runtime, sometimes silently, sometimes as a panic in the middle of handling a request. type Registration struct { Password string `checkers:"trim required"` ConfirmPassword string `checkers:"required eq-field:Passwrd"` // typo: no such field Age int `checkers:"email"` // email is string-only } Nothing here trips go build , go vet , or a normal linter — they all treat checkers:"..." as an opaque string. The first bug only surfaces the moment someone submits a registration form and eq-field can't find a field called Passwrd . The second is worse: email assumes a string under the hood, so calling it on an int field panics at validation time instead of returning a normal error. checkerlint is a go/analysis -based static analyzer, shipped as its own module in the Checker repo, that reads these tags at build/lint time and catches exactly this class of bug before it ships: ./registration.go:3:2: checkerlint: eq-field references field "Passwrd", which doesn't exist on this struct ./registration.go:4:2: checkerlint: email requires a string, but the field's type is int What it actually checks Three things, all specific to how checkers / validate tags can go wrong: Unknown checker names. Every token in the tag has to be a registered checker, normalizer, field-relative checker, omitempty , or a name your own code registered via RegisterMaker / RegisterFieldMaker with a string literal. Typo requird instead of required and checkerlint flags it — nothing else in your toolchain w

2026-09-06 原文 →
AI 资讯

99.7% Rejected in 84ms: Why I Stopped Making the Generator Smarter

I wrote a puzzle generator whose acceptance rate is 0.26% . It throws away 99.7% of everything it produces, and that is the design working as intended, not failing. Generating five valid puzzles takes 1,947 attempts and 84 milliseconds. The point is not the puzzles. The point is that the generator makes no correctness guarantee at all, and a verifier makes every one of them. Once you split those two responsibilities, "make the generator smarter" stops being the obvious optimisation — and that is exactly the position you are in when the generator is an LLM. The loop verigen is a Go CLI that produces cryptarithmetic puzzles — alphametics, the SEND + MORE = MONEY genre, where each letter stands for a distinct digit and the sum has to hold. The known answer to that one is 9567 + 1085 = 10652 . There is one rule, and everything else follows from it: The generator guarantees nothing. Every guarantee lives in the verifier. The generator throws plausible-looking letter combinations at the wall. The verifier does an exhaustive search and confirms two things: that a solution exists, and that it is unique. Anything that fails either check is discarded and the loop asks for another candidate. The loop itself knows nothing about cryptarithmetic. Implement a Domain interface and any other puzzle rides the same loop. What the log actually says Five puzzles, seed 7: ── Puzzle 2 [hard] HAIKU + BONSAI = KOKORO Answer: 96542 + 378165 = 474707 (attempts before this seed landed: 624) === generate/verify loop [alphametic] === seed=7 output=5 puzzles total attempts=1947 elapsed=84ms acceptance rate = 0.2568% (average 389 generations per puzzle) --- rejection reasons --- no unique solution 770 (39.55%) no solution 695 (35.70%) more than 10 distinct letters 477 (24.50%) ok 5 ( 0.26%) Nearly 40% of candidates have more than one valid solution. Another 36% have none. A quarter cannot possibly have one and are rejected before the search starts. Five survive. Filtering by difficulty makes it wo

2026-09-06 原文 →
AI 资讯

Returning RFC 9457 Problem Details from Go Validation Errors

How to turn struct-tag validation failures into a standard "application/problem+json" response — the RFC 9457 format — with one method call, no hand-rolled error envelope. Most Go APIs invent their own validation error shape. One team returns {"errors": [...]} , another {"field_errors": {...}} , a third just a flat {"error": "message"} and hopes the client parses it. Every one of those is a private contract the client has to learn from your docs, because there's no shared shape for "here's what's wrong with your request." RFC 9457 — Problem Details for HTTP APIs — is the IETF standard that fixes this: a application/problem+json body with type , title , status , and room for problem-specific extensions. Checker now builds one of these directly from a failed struct validation, via CheckErrors.ProblemDetails() . The shape RFC 9457 defines four base members — type , title , status , detail , instance — and lets a specific problem type add its own. For validation errors, RFC 9457 §3.1 sketches exactly this extension: an invalid-params array listing which fields failed and why. That's what Checker produces. From a failed struct to a problem+json body Take a struct with a missing required field: type Person struct { Name string `checkers:"required"` } person := & Person {} errs , ok := checker . CheckStruct ( person ) if ! ok { data , _ := json . Marshal ( errs . ProblemDetails ()) fmt . Println ( string ( data )) } { "type" : "about:blank" , "title" : "Your request parameters failed validation." , "status" : 400 , "invalid-params" : [ { "name" : "Name" , "reason" : "Required value is missing." , "code" : "REQUIRED" } ] } One method call — errs.ProblemDetails() — turns the same CheckErrors you'd otherwise call .JSON() on into a *ProblemDetails value, ready to marshal. type defaults to "about:blank" (RFC 9457's own default for "no more specific problem type registered"), status defaults to 400 , and each invalid-params entry carries the field name , a localized human-readab

2026-09-06 原文 →
AI 资讯

Fifty seconds for half a megabyte: the optimisation that fixed the constant, not the order

A cryptography library had a bottleneck no test could see : encrypting half a megabyte took fifty seconds. Every test passed. They had been passing for months. The cause is a trap that keeps recurring: a correct, well-documented optimisation that fixes the constant and not the order — and whose comment, precisely because it is well written, convinces the reader the problem is already solved. What the code did Quipu renders encrypted data as a sequence of symbols. To do that it converts the whole message into a single huge integer and repeatedly divides it to extract digits, the same way you would convert a base-10 number to base 2 by hand. The code did not divide one digit at a time. It carried a sensible optimisation: divide by the largest power of the base that fits in a machine word, extracting nine digits per pass instead of one. The comment explaining it opened by saying that doing it one at a time would be quadratic , and then described the improvement. All true. And the result was still quadratic: extracting nine digits per pass divides the work by nine; it does not change how the work grows. That sentence — "doing it this way would be quadratic" — reads in the past tense, as if it described the previous state. It described the current one. The measurement, which is the only thing that says so Size Time Factor per doubling 64 KiB 0.79 s — 128 KiB 3.16 s ×4.0 256 KiB 12.6 s ×4.0 512 KiB 50.7 s ×4.0 Exactly four, three times running. That is textbook quadratic: every time the input doubles, the time quadruples. Extrapolating, ten megabytes would have cost about five and a half hours . And here is the point: a correctness test sees none of this . A slow algorithm produces exactly the same bytes as a fast one. The suite stayed green, and would have stayed green forever. The fix is two hundred years old Nothing had to be invented. Divide-and-conquer radix conversion is a classical algorithm: instead of peeling digits off one end, you split the number in half — div

2026-09-06 原文 →
AI 资讯

Multi-Agent Does Not Mean Parallel: Safe Workflows with Google ADK

“Let’s split it into agents” has become the AI equivalent of “let’s make it a microservice.” Sometimes the boundary is useful. Sometimes it only creates more state, more coordination, and a harder failure to explain. The most dangerous assumption is that separate agents should run in parallel. Parallelism is safe only when the branches are genuinely independent. If one branch changes the world while another is evaluating it, both agents can make locally reasonable decisions that are unsafe together. Google ADK 2.0 makes workflow topology explicit through graph-based Workflow objects. That is valuable because sequences, branches, and joins become part of the program instead of an agreement hidden in a supervisor prompt. Series note: This is Part 5 of Reliable Google AI Agents in TypeScript . The examples were checked against @google/adk 2.0.0 in September 2026. Start with the dependency, not the agent count Imagine a system preparing a hotel recommendation. It needs live inventory, company travel policy, and a final recommendation. Inventory lookup and policy evaluation can run concurrently because both observe the same request and neither changes shared state. The final decision must wait for both. Now consider a different pair of operations: one agent changes the reservation; another calculates an upgrade using the current reservation. Those branches are not independent. Running them concurrently can make the upgrade decision depend on state that no longer exists. Before drawing a parallel branch, ask: Do both operations only read the same starting state? Can either operation change data the other consumes? Can either produce an irreversible side effect? Is there a deterministic way to combine their results? What happens when one succeeds and the other times out? If those answers are unclear, parallel is an optimization you have not earned yet. Encode safe parallelism as fan-out and join ADK’s TypeScript Workflow graph can express two independent branches and a joi

2026-09-05 原文 →