Author: Richard Seroter

  • Daily Reading List – May 18, 2026 (#786)

    I’m about to fly up to San Jose to be at Google I/O tomorrow. It’s probably recency bias, but this looks like our biggest builder-focused I/O since I got here. Some fantastic announcements coming!

    [article] From Open Source Software to Open Source Strategy. What a great essay. Full of genuine insights into open source, and how the landscape is shifting.

    [blog] Don’t Outsource the Learning. LLMs cater to finishing a task, not teaching us. You need to be intentional about how you continue to develop your skills.

    [article] Why Doesn’t Anyone Teach Developers About Context Management? Get good at context management. It’s worth the upfront and ongoing investment to feed relevant info into your LLMs.

    [blog] Beyond SQL: How BigQuery Evolved into a Complete AI and Graph Platform. BigQuery’s built-in graph and AI functions make tasks like this much more straightforward.

    [blog] Everything You Know About Scaling Web Apps Breaks When You Serve an LLM. Great stuff. Tokens matter more than requests, memory management is different, load balancing isn’t the same, and tackle cost optimization differently when serving models.

    [article] Opinion: Vibe coding needs an on-ramp — and seat belts. Sure. There’s still a LOT of implicit knowledge needed to build software correctly. But new tools and baking that knowledge into the process.

    [blog] Just Fucking Use Go. I can’t pinpoint who’s voice I heard in my head while reading this. But man, I laughed a few times, and so much of this is spot on.

    [blog] Open and Closed: The Pursuit of Frontier Models. Deep analysis by Steve here. Closed beats open today on innovation and performance, but the cycle needed to close the gap keeps shrinking.

    [article] Engineering roles shift from developing code to managing AI. I suspect this won’t be a long-term shift to just reviewing AI output. That’ll be a relatively solved problem, and the work shifts again to primarily “thinking” and orchestrating.

    [blog] Who Determines Done? Why Agentic AI Needs Escalation, Not More Loops. Great question. Do you just “loop harder”? Keith looks at when escalation is needed and how to think about it.

    [blog] How I use LLMs as a staff engineer in 2026. Agents are good right now. That fact may change what tasks you throw at them. But own the core of your job.

    [article] Forward deployed engineer is AI’s hottest job as OpenAI and Google race to hire. Here’s how to become one. Definitely a cool job. Probably one I would have pursued at some point in my career. But it’s based on knowledge, so get hands-on experience!

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – May 15, 2026 (#785)

    No-meeting-Friday held today, and I needed it. Email backlog cleared, RSS reader emptied, writing done, and multiple explorations completed. Enjoy your weekend!

    [article] Are You Meeting the Needs of the People You Lead? What a great article. Do leaders fail because of bad judgement or character, or because there’s a mismatch between what they provide and followers need?

    [article] How does deepening AI fluency change what it means to be a software developer? Where do developers want to spend their time, and where do they want AI involved? This is an important read for any engineering manager.

    [blog] Cloud CISO Perspectives: How Google + Wiz changes multicloud strategy for CISOs. I get a firsthand look at the threats Google faces every day, and see how important it is to have a proactive, comprehensive security approach for all your infrastructure.

    [article] Is Software Losing Its Head? Where does product defensibility move when all you are is an API? This a16z post mentions proprietary data, network coordination, and a couple other areas.

    [article] Anthropic puts Claude agents on a meter across its subscriptions. It’s a trend. But not uniformly applied, and OpenAI is trying to take advantage of developer frustration with Anthropic. More here.

    [blog] Announcing Genkit Middleware: Intercept, extend, and harden your agentic apps. My team builds this framework, and it’s an underrated way to add AI to new or existing apps.

    [blog] Vercel AI SDK Middleware vs Genkit Middleware: A Hands-On Comparison. Speaking of Genkit, I just read this extremely thorough look at the different philosophies and implementations of these popular AI frameworks.

    [article] How to build a skills library for your engineering team. I expect to see more posts like this. How do you collect and share the right context across teams, not just for yourself?

    [blog] New updates to A2UI and Flutter’s GenUI package. While this might mean you write MORE code, not less, this seems like a smart update to our package that helps you build generative UIs.

    [blog] Why GKE for your AI Agent. I’m sold. Good case for using Kubernetes, especially if you already have models or workloads running there.

    [blog] xAI Enters the Coding Agent Race With Grok Build. Not a surprise, and I wouldn’t remotely discount the chances that this team builds something very useful and powerful. More here.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – May 14, 2026 (#784)

    Today’s list feels a bit more cynical (pragmatic?) about AI than usual. That’s ok as it’s important to see the full range of perspectives.

    [blog] The Road More Traveled. A look back at what the point of microservices was, and the impact of AI on architecture.

    [blog] Build Long-running AI agents that pause, resume, and never lose context with ADK. Can your agent run for a week without getting messed up? Here’s a reference system that you can browse and learn from.

    [article] Tokenmaxxing is super dumb. Again, I get the reason people do it. I’ve asked my own team to think AI-first, but it doesn’t mean I care how many tokens they consume each day.

    [blog] An Engineer’s Guide to Better AI Skills: Implementing a Testing Process to Optimize Agent Performance in Any Repository or Skill.The Pinterest engineering team shares their thoughts on evals and such.

    [article] Google Cloud is hiring an army of AI deployment engineers. It’s true. Customers need to own their AI journey, but we’re going to be invested in their success.

    [blog] Google Cloud Next 2026 Data Content. Skim through this list and find a talk or two that you’d like to watch.

    [article] AI is creating a generation of developers who can’t debug their own code. Can you explain your code? Fix an issue? If you’re hiring juniors who say no to both, you’ll need to change something.

    [article] Chamber 🏰 of Tech Secrets #56: Signal in the Noise. Brian pulled out some relatively durable points about AI from a recent in-person event.

    [article] Burnout and Cognitive Debt. This doesn’t tread any new ground, but still offers a useful, sobering reminder to consider the human impact of our AI-fueled work.

    [blog] Learning Software Architecture. Like most things, software design is best learned through hands-on experience. But this post also points out that an application’s architecture is usually shaped by an org’s incentives and social dynamics.

    [blog] Code Review Responses: Add Context When It Counts. The gist of this advice is “say more.”

    [blog] MCP vs. CLI: Which Is Better for Agentic AI? Both work well, it just depends on your use case and the environment your agent is running in.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – May 13, 2026 (#783)

    My day reflected some of the articles below. My brain can’t hold what it needs to hold, and I need fewer interruptions by technology. There are some suggested fixes in today’s list.

    [article] Escape from agentic loop. This proposes that the human-in-the-loop workflow of AI is exhausting and fake productivity. Instead, be on-the-loop and use AI managers that follow your guidance.

    [blog] Meet the latest Database Center, now with Gemini-powered fleet intelligence. Can’t just use one database engine? Ok, but now you have a problem trying to manage all these distinct engines. Our Database Center pulls it together.

    [article] 12 model-level deep cuts to slash AI training costs. Smart list of ways you can be more efficient with training and make good architectural adjustments in your ML pipeline.

    [article] The engineering management memory crisis. Is your brain running out of RAM? Mine is. this is a good lesson about having an LLM that points to personal context.

    [article] Your AI Problem Is a Data Problem. Some good data points here, and reminders that AI isn’t a procurement decision; you need a strong data layer.

    [blog] Tutorial Series : Gemini Enterprise Agent Platform. Terrific five part series from Romin that lays out how you build, scale, govern, and optimize agents.

    [article] Why agent harnesses fail inside cloud-native systems. Can your AI agent harness do real work within distributed systems? Or is the lack of a realistic and isolated test bed giving you false confidence?

    [blog] Why Real-Time Authorization Is Best For Agentic AI. Long argument for giving agents short-lived creds and specific access.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – May 12, 2026 (#782)

    I try say “yes” to customer presentation requests whenever possible. It gives me an excuse to try out new messaging, gather new context from real users, and build demo systems. I might have overdone it this week as I’m doing some new types of talks with little notice. What could go wrong?

    [blog] The Android Show: I/O Edition 2026. So many cool updates today! From more proactive AI to new Googlebook laptops, we shipped some fun stuff.

    [article] The Best Leaders Embrace the Role of Supporting Character. Main-character energy doesn’t help your team. Serve others, be curious, and yes, still inspire and steer the team the right way.

    [article] Symptoms of Bad Software Design. Call it tech debt, or maybe just the wrong decisions. Either way, you want to tackle these areas if you hope to scale or reduce operational cost.

    [blog] Google Antigravity beats Claude at coding—but only if you stop acting like a programmer. It’s a great tool. They all have merit. But you’ll want to change your workflow, not cram your existing one into these tools.

    [article] How do Next Edit Suggestions in AI-integrated IDEs introduce new security risks? We’ve moved beyond tab completion, but this guidance still holds when you’re generating whole swaths of code at once.

    [blog] Debugging Event-Driven Systems: 5 Problems Teams Create. Good pattern, but it’s not your whole architecture, as Derek explains here.

    [article] How corporate politics actually work. Your situation could be different, but I’d imagine we all have something to take away from the many lessons shared here.

    [blog] MCP Configuration for Google Workspace with Gemini CLI. Useful example with instructions I haven’t seen elsewhere. William also uses this newsletter as an example, so how can I not read the post?

    [article] Fighting Tool Sprawl: The Case for AI Tool Registries. The solutions aren’t super mature at the moment, but I buy that shared tool registries are important.

    [blog] Top 15 CI/CD Metrics: What to Track & Why They Matter. I can’t imagine that too many people are tracking ALL of these, but it’s a good list to pick from.

    [article] What happens when engineering teams reorganize around AI agents. This article offers up quick bursts of advice from different teams adopting AI agents.

    [blog] If AI Writes Your Code, Why Use Python? Would you? Or something else entirely suited to the specific use case?

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – May 11, 2026 (#781)

    On Friday I mentioned that I wanted to hack around with the Dart programming language this weekend. I fought my instincts to just build something with AI and review the results. Instead, I crawled around the documentation for a couple of hours and used AI to clarify things I didn’t understand. That felt like a better way to learn!

    [arricle] The Best Risk Mitigation Strategy in Data? A Single Source of Truth. This is all about semantic layers and one access point for data. Not a team as the gatekeeper, but a more centralized data approach.

    [blog] A Cognitive Fitness routine for Software Engineers. If we’re not coding as much, how do we stay sharp? I like the idea of taking a proactive approach here.

    [blog] GTIG AI Threat Tracker: Adversaries Leverage AI for Vulnerability Exploitation, Augmented Operations, and Initial Access. Feel a little anxious when you read this. That’s ok. Then do something about it.

    [article] Yum Brands’ tech chief on building its ‘AI backbone.’ Yum Brands gets it. You can’t do AI stuff well if your fundamentals are busted. They’re focusing on getting their data house in order first.

    [blog] Cloud Storage Rapid: Turbocharged object storage for AI and analytics. Just silly performance numbers here, but that’s what you should demand for today’s AI training and inference workloads.

    [article] Leadership in AI-Assisted Engineering. Same playbook as before? Different one? I think different. Same big ideas, but think differently about goals, expectations, skills development, compliance checking, and such.

    [blog] Ship code within minutes with the Gemini CLI DevOps Extension. I can’t imagine that CI/CD stays the same as we offload work to AI. But either way, I like solutions like this which abstract some of the CI/CD infrastructure.

    [article] How Fast-Growing Companies Can Make Better Decisions. Happens in fast-growing and moving teams too. This article proposes “structured empowerment” as a way to decentralize responsibly.

    [blog] Run your Gen AI Functions quicker and (up to 90%) cheaper in BigQuery with Gemini Context Caching. Wow, instead of paying for a ton of context when AI gets applied to each row in your warehouse, use context caching.

    [article] Tokenmaxxing, Promomaxxing, and Misaligned Incentives in Tech. You get the behavior you incentivize. Good wisdom in this post.

    [article] The Pulse: AI load breaks GitHub – why not other vendors? AI is stretching all sorts of platforms, but GitHub didn’t seem ready for it.

    [article] Why Open Source Maintainers Are Done With AI Slop. Old assumptions don’t work anymore, as the trust contract is gone.

    [blog] GitLab Act 2. A restructuring of teams and priorities. “The world we’re building for” and “architectural bets we’re making” are clear.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – May 8, 2026 (#780)

    I spent time today with our team’s head of engineering for Google-sponsored languages. Dart has an interesting history, and is critical to some key services from Google. I’m going to hack around with it this weekend.

    [article] Cognitive Surrender. This is when the AI’s output becomes your output. It leads to cognitive debt. Where might you allow yourself your surrender, and how do you protect against it?

    [article] The “AI Job Apocalypse” Is a Complete Fantasy. You might roll your eyes at this headline in the face of the stream of “the business is great but we’re laying people off because of AI” stories (see: Cloudflare). But the amount of work to be done isn’t fixed, and this post brings some receipts from the recent past.

    [article] Tech job postings hit 3-year high. Tons of open tech jobs, with more added each month. A reconfiguration doesn’t mean the work goes away.

    [article] The stuff nobody tells you about startup marketing. We’re all in sales and marketing, whether we realize it or not. Good advice here for those trying to land their startup products.

    [blog] Automating modernization: Migrating legacy Express to Next.js with Google Antigravity & AI Agents. Constructive journey through a modernization scenario. I’ve seen a few of those this week, and skills + agent teams is proving to be a strong pattern.

    [article] The company that made RAG mainstream is now betting against it. The half-life of a “best practice” in this industry is like 3 months. Pinecone who got famous as a vector database serving RAG use cases, now pushes knowledge upstream into artifacts used by agents.

    [blog] With faster node startup for GKE, say goodbye to cold-start latency. New provisioning logic means your Kubernetes nodes come online even faster now.

    [blog] New Bigtable in-memory tier for sub-millisecond read latency. This might already be the highest performing database on the Internet, and now it can be even faster.

    [blog] Write-Only Code. Is this the final-boss of agentic coding? Maybe. If you are comfortable with this, you’ve knocked down every concern. Or you’ve embraced YOLO engineering.

    [article] 10 Hacks Every Google Meet User Should Know. Some of us spend a lot of time in virtual meetings. I’m grateful each day that I get to use Meet.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – May 7, 2026 (#779)

    There’s the usual stash of AI content below, but also a few pieces that give you a break from the non-stop AI train.

    [article] Why SaaS freemium playbooks don’t work in AI, and what to do instead. Good piece. Instead of the classic “give basic features for free and put premium behind a paywall”, AI products are upselling on usage intensity, outcomes, and most compute-heavy experiences.

    [article] Designing front-end systems for cloud failure. Since I’m not a frontend guy, I haven’t thought much about this topic. But I liked the insights about degraded frontend experiences and the breadth of things that you need to guard against.

    [blog] 10 Lessons for Agentic Coding. Today. Lessons for today. Who knows what the “good practices” will look like tomorrow?

    [blog] Monitoring reliably at scale. What if your observability stack depends on the same systems that it monitors? Yikes. Airbnb wanted to break these circular dependencies.

    [youtube-video] How a Group of Developers Took Back Control from Enterprise Java | Spring: The Documentary. Wonderful video documentary that looks at the history and impact of the Spring Framework.

    [blog] Cold Starts Are Costing You: Fix Them with GKE Pod Snapshots. Wow, this’ll come in very handy for many people. Save app, file system, and network state into a snapshot used for subsequent Kubernetes pod startups.

    [blog] Coding with AI Agents is Now a Baseline Expectation for Managers. Fun post. You might reignite your passion for tech by throwing yourself into the toolchain your team is using.

    [blog] Gemini 3.1 Flash-Lite is now generally available on Gemini Enterprise Agent Platform. It’s a terrific model that’s fast and cost effective. Now GA across surfaces.

    [blog] Choosing between APIs, MCP, and Agent-to-Agent architectures. Lak wants us to all default to REST APIs first, and only bring in MCPs and A2A when needed by reasoning agents.

    [blog] AlphaEvolve: How our Gemini-powered coding agent is scaling impact across fields. It’s so great to see advances like this, where AI is contributing original insights.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – May 6, 2026 (#778)

    Today’s list has a terrific mix of opinions, not all in agreement. But what’s important is that we’re learning things together.

    [article] Agent Skills. Developers are loving these twenty agent skills. They serve the SDLC as a whole and force your agents through the same stages an engineer goes through.

    [article] How To Be Direct And Strategic. You can be a straight-shooter and still be thoughtful in how you deliver the message. Good post on how to be strategic and direct.

    [article] When an Executive Asks You an Unexpected Question. Related to the above, in some way. It’s not just about the fast and factual answer; you need to understand where the question is coming from.

    [blog] Five must-have guides to move agents into production with Gemini Enterprise Agent Platform. Thinking of getting your agents to production? There’s a lot to consider, and these guides point out areas of architecture, governance, and more.

    [article] Designing the AI-native engineering organization. This group says that AI-native engineering looks like shorter planning cycles, more frequent releases, smaller squads of engineers, and more types of contributors.

    [article] Agentic Coding is a Trap. I definitely get the arguments here, and the author makes a reasonable case. Some is speculation and anecdotal, but no one really knows how agentic coding is going to impact the field of software engineering.

    [blog] The AWS MCP Server is now generally available. Every platform, including clouds, need to talk MCP or make themselves agent friendly.

    [blog] Pioneering AI-assisted code migration: How Google achieved 6x faster migration from TensorFlow to JAX. This wasn’t just about asking an AI agent to plow through a codebase and do work, It used a strong pattern with specialized agents.

    [blog] Closing the Loop: Google Just Validated Deterministic Code in the Loop. Keith noticed that the above post proved what he’s been seeing. He’s been doing some important analysis lately!

    [article] Improving AI agents through better evaluations. Good advice, and some very specific calls to action at the end. Invest here!

    [blog] What’s new in IAM: Security, governance, and runtime defense. The identity management space heated up quick. Check this out for what you should be expecting from your modern IAM solutions.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • How to force your custom agent to stop and seek human approval

    Autonomous agents are cool and all, but we all know there are plenty of circumstances where we want human review. Your custom built agent might have instructions to stop and get input, but that doesn’t guarantee it happens. The Agent Development Kit recently added a human-in-the-loop feature, and I decided to try it out.

    Let’s build an agent that generates product tutorials on-demand. The agent takes in a request, and uses tools to ground its output. Before it publishes a tutorial, it requires explicit approval from outside its own workflow. ADK now offers both a simple boolean approval, and a more sophisticated option. I’ll test out both, using the built-in web UI and the raw REST API. FYI, all my source code is here.

    First, here’s the architecture:

    Simple Tutorial Agent

    First, there’s a basic tutorial agent that uses the simple action confirmation.

    Below is the core agent code. The agent definition (at the bottom) has instructions, a model, and some tools. I’m using the Google Search tool and our Developer Knowledge API MCP server tools. The former grounds results in Google Search results, and the latter points to Google’s core documentation.

    func createTutorialAgent(ctx context.Context, llmModel model.LLM) (agent.Agent, error) {
    	transport, err := mcpTransport(ctx)
    	if err != nil {
    		return nil, err
    	}
    	mcpToolSet, _ := mcptoolset.New(mcptoolset.Config{Transport: transport})
    
    	publishTool, _ := functiontool.New(functiontool.Config{
    		Name:                "publish_tutorial",
    		Description:         "Publish the tutorial. Requires user confirmation.",
    		RequireConfirmation: true,
    	}, publishTutorial)
    
    	searchAgent, _ := llmagent.New(llmagent.Config{
    		Name:        "search_agent",
    		Model:       llmModel,
    		Tools:       []tool.Tool{geminitool.GoogleSearch{}},
    		Instruction: "Search the web.",
    	})
    
    	return llmagent.New(llmagent.Config{
    		Name:        "tutorial_agent",
    		Model:       llmModel,
    		Description: "Simple tutorial agent.",
    		Instruction: `You are a technical writer. Draft a Google Cloud tutorial based on user requests.
    Once the draft is complete, show it to the user and ask them if they would like to start the publishing process. The 'publish_tutorial' tool will then handle the review and approval.`,
    		Tools:    []tool.Tool{agenttool.New(searchAgent, nil), publishTool},
    		Toolsets: []tool.Toolset{mcpToolSet},
    	})
    }
    

    Notice that the publish_tutorial tool has a RequireConfirmation attribute. The corresponding publishTutorial function only gets called for a true result.

    To start the agent (after setting environment variables for Google Cloud project and any credentials) for the built-in web UI, I used this command:

    ADK_AGENT=tutorial go run . web api webui
    

    This provides me a localhost web UI to explore my agent. I asked for a tutorial and saw it use the right tools to generate a response.

    I then ask to start the publishing process, and the confirmation flow kicks in. Notice the small free text box asking for my response.

    The accepts a JSON payload of {"confirmed": true} only. When I provide that value, the control returns to the agent and it publishes the Markdown tutorial to the local folder.

    Let’s do the same workflow with the REST API.

    This time, I started up the agent with this command to get the API endpoint only:

    ADK_AGENT=tutorial go run . web api
    

    The http://localhost:8080/api/list-apps endpoint shows that I have one “app” named tutorial_agent. To start, I need to create a session with my agent. I’ll set the username and (optionally) the session ID. If I just post to the session endpoint, I get back a random session ID.

    curl -X POST http://localhost:8080/api/apps/tutorial_agent/users/u_123/sessions/s_123 \
      -H "Content-Type: application/json"
    

    Now I can send in a prompt to my agent. Notice that I’m passing in the user ID and session ID from above.

    curl -X POST http://localhost:8080/api/run \
    -H "Content-Type: application/json" \
    -d '{
    "appName": "tutorial_agent",
    "userId": "u_123",
    "sessionId": "s_123",
    "newMessage": {
        "role": "user",
        "parts": [{
        "text": "create a tutorial for deploying a container to an existing GKE Autopilot cluster. use the cli."
        }]
    }
    }'
    

    I get back a pile of JSON that includes the tutorial itself. Like with the web UI experience, I’m asked if I want to kickstart the publishing process. I send in a follow-on message like this:

    curl -X POST http://localhost:8080/api/run \
    -H "Content-Type: application/json" \
    -d '{
    "appName": "tutorial_agent",
    "userId": "u_123",
    "sessionId": "s_123",
    "newMessage": {
        "role": "user",
        "parts": [{
        "text": "yes, start the process to publish the tutorial"
        }]
    }
    }'
    

    There’s another pile of JSON to parse through, and I need to find the ID tied to the adk_request_confirmation object.

    [ ... "content":{"role":"model","parts":[{"functionCall":{"id":"adk-e6d32a67-c1d4-46b8-87f6-6922a3af3d98","name":"adk_request_confirmation","args":{"originalFunctionCall":{"id":"adk-0e6c0394-a22b-4d5f-9af6-e27284bc175f","args":{"content":"# Deploy a Container to an Existing GKE Autopilot Cluster using the gcloud CLI and kubectl\n\nThis tutorial guides you through" ...]
    

    The next REST API call includes the ID from above and the familiar “confirmed” property.

    curl -X POST http://localhost:8080/api/run \
    -H "Content-Type: application/json" \
    -d '{
     "appName": "tutorial_agent",
     "userId": "u_123",
     "sessionId": "s_123",
        "newMessage": {
            "role": "user",
            "parts": [
                {
                    "functionResponse": {
                        "name": "adk_request_confirmation",
                        "id": "adk-e6d32a67-c1d4-46b8-87f6-6922a3af3d98",
                        "response": {
                            "confirmed": true
                        }
                    }
                }
            ]
        }
    }'
    

    After this call, I see the Markdown tutorial written to disk.

    Advanced Tutorial Agent

    This agent itself looks nearly the same as the prior one, but has a more sophisticated toolset. Here’s the agent code, with the confirmation behavior in the downstream function, not the function tool.

    func publishTutorialAdvanced(ctx tool.Context, args PublishTutorialArgs) (map[string]any, error) {
    	confirmation := ctx.ToolConfirmation()
    	if confirmation == nil {
    		ctx.RequestConfirmation(
    			"Reviewer approval required to publish.",
    			map[string]any{
    				"status": "approved", // approved, rejected, update
    				"notes":  "",
    			},
    		)
    		return map[string]any{"status": "Pending reviewer approval."}, nil
    	}
    
    	payload := confirmation.Payload.(map[string]any)
    	status, _ := payload["status"].(string)
    	notes, _ := payload["notes"].(string)
    
    	if strings.ToLower(status) == "approved" {
    		if !strings.HasSuffix(args.Filename, ".md") {
    			args.Filename += ".md"
    		}
    
    		if err := os.MkdirAll("tutorials", 0755); err != nil {
    			return nil, fmt.Errorf("failed to create tutorials directory: %w", err)
    		}
    
    		fullPath := filepath.Join("tutorials", filepath.Base(args.Filename))
    		if err := os.WriteFile(fullPath, []byte(args.Content), 0644); err != nil {
    			return nil, fmt.Errorf("failed to save tutorial: %w", err)
    		}
    		return map[string]any{"status": "published", "path": fullPath}, nil
    	}
    
    	return map[string]any{"status": status, "notes": notes}, nil
    }
    
    func createAdvancedTutorialAgent(ctx context.Context, llmModel model.LLM) (agent.Agent, error) {
    	transport, err := mcpTransport(ctx)
    	if err != nil {
    		return nil, err
    	}
    
    	mcpToolSet, err := mcptoolset.New(mcptoolset.Config{Transport: transport})
    	if err != nil {
    		return nil, err
    	}
    
    	publishTool, err := functiontool.New(functiontool.Config{
    		Name:        "publish_tutorial_advanced",
    		Description: "Publishes the tutorial. Requires status and notes from a reviewer.",
    	}, publishTutorialAdvanced)
    	if err != nil {
    		return nil, err
    	}
    
    	searchAgent, _ := llmagent.New(llmagent.Config{
    		Name:        "search_agent",
    		Model:       llmModel,
    		Description: "Web search agent.",
    		Instruction: "Search the web for info.",
    		Tools:       []tool.Tool{geminitool.GoogleSearch{}},
    	})
    
    	return llmagent.New(llmagent.Config{
    		Name:        "advanced_tutorial_agent",
    		Model:       llmModel,
    		Description: "Tutorial agent with advanced confirmation.",
    		Instruction: `You are a technical writer. Draft a Google Cloud tutorial based on user requests.
    Once the draft is complete, show it to the user and ask them if they would like to start the publishing process.
    The 'publish_tutorial_advanced' tool will then handle the multi-stage review and approval.
    If the reviewer provides feedback (status 'update' or 'rejected'), revise the draft and try again.`,
    		Tools:    []tool.Tool{agenttool.New(searchAgent, nil), publishTool},
    		Toolsets: []tool.Toolset{mcpToolSet},
    	})
    }
    

    Notice the RequestConfirmation command in the publishTutorialAdvanced function. Here, we have more control over sending notifications or doing whatever to solicit the confirmation approval.

    Note that I tried to get this agent to work with the built-in web UI but couldn’t get it. The same text box pops up for approval confirmation, but no values seem to get the agent to proceed. The only way I got this to work was with a bunch of custom handling in the agent.

    So let’s go straight to the REST API. I started the agent using this command:

    ADK_AGENT=advanced go run . web api
    

    When I check the list-apps endpoint, I see that my agent app is called advanced_tutorial_agent. So just like above, we start by creating a session with the correct agent name.

    curl -X POST http://localhost:8080/api/apps/advanced_tutorial_agent/users/u_123/sessions/s_123 \
      -H "Content-Type: application/json"
    

    Now I can prompt the agent with a tutorial request.

    curl -X POST http://localhost:8080/api/run \
    -H "Content-Type: application/json" \
    -d '{
    "appName": "advanced_tutorial_agent",
    "userId": "u_123",
    "sessionId": "s_123",
    "newMessage": {
        "role": "user",
        "parts": [{
        "text": "create a cli tutorial for creating a pub/sub topic."
        }]
    }
    }'
    

    My custom tutorial comes back in a second, and I can kick off the publishing process.

    curl -X POST http://localhost:8080/api/run \
    -H "Content-Type: application/json" \
    -d '{
    "appName": "advanced_tutorial_agent",
    "userId": "u_123",
    "sessionId": "s_123",
    "newMessage": {
        "role": "user",
        "parts": [{
        "text": "yes start the publishing process"
        }]
    }
    }'
    

    After parsing the JSON response, I again find the adk_request_confirmation object and snag the ID.

    [ ... "content":{"role":"model","parts":[{"functionCall":{"id":"adk-7744ece0-a82d-4796-96b7-f376d96bf925","name":"adk_request_confirmation","args":{"originalFunctionCall":{"id":"adk-30c04217-3c0b-4519-a866-b914e9357872","args":{"content":"## Creating a Pub/Sub Topic with the `gcloud` CLI\n\nThis tutorial guides you through creating a Google Cloud Pub/Sub topic using the" ...]
    

    In the next REST call, see that I can now send a richer payload back to the agent and react accordingly.

    curl -X POST http://localhost:8080/api/run \
    -H "Content-Type: application/json" \
    -d '{
     "appName": "advanced_tutorial_agent",
     "userId": "u_123",
     "sessionId": "s_123",
        "newMessage": {
            "role": "user",
            "parts": [
                {
                    "functionResponse": {
                        "name": "adk_request_confirmation",
                        "id": "adk-7744ece0-a82d-4796-96b7-f376d96bf925",
                        "response": {
                            "confirmed": true,
                            "payload": {
                                "status": "approved",
                                "notes": "Tutorial looks great, proceed with publishing"
                            }
                        }
                    }
                }
            ]
        }
    }'
    

    And once again, I get a Markdown file saved to disk.

    After this initial demo, I may play with putting an A2UI interface on top of the agent, or even building it again using Genkit Go. But either way, make sure you give yourself some discrete moments where your agent stops and asks for input!