← 返回列表
🔗 原文
即将到来的循环
The Coming Loop
Armin Ronacher's Thoughts and Writings

The Coming Loop

written on June 23, 2026

I don’t prompt Claude anymore. I have loops running that prompt Claude and figuring out what to do. My job is to write loops.

— Boris Cherny

Over the last months I have watched more and more people build something on top of coding agents that feels meaningfully different from just using a coding agent. Some of this happens on top of Pi which is cool to see for sure! The pattern is the same everywhere though: work is put into a queue of sorts, a machine picks it up, attempts it, stops, and then some harness decides whether that was actually the end.

If not, the harness continues the same session, injects another message, starts a fresh session with modified context, or sends the task to another machine. The task stays alive beyond the point where the model by itself would normally have said: “I am done.”

I think about that type of loop more than I want to admit.

There is already an agent loop inside every coding agent. The model calls a tool, incorporates the result, calls another tool, reads a file, edits a file, runs tests, and eventually produces some answer. That loop is one we have been quite familiar with for a long time. The other loop is the harness level loop: the loop outside the agent loop. That loop is also not new. We have been doing versions of this since early Claude Code days, but that loop is becoming ever more present in agentic engineering and in recent weeks it has started to dominate the Twitter discourse.

I Am Not Good At This Yet

My current status is that I have not had much success with this way of working for code I deeply care about which turns out to be quite a lot of code.

Part of that is taste and part of it is control. I attempt to set a high bar for what I want code to look like, and I want to understand the code I ship. Under pressure, or in a discussion with another human, I want to be able to explain what the system does without first having to ask a clanker to explain it to me. Now there is obviously a question if this desire to understand the code is one that I will still have a few years from now. For now I have not moved past the point of comprehension being important to me.

Given this desire, there is something I lack with my experience of code written without me paying attention, particularly from loops. Present-day models tend to produce code that is too defensive, too complex, too local in its reasoning. They avoid strong invariants. They add fallbacks instead of making bad states impossible. They duplicate code, invent bad abstractions, and paper over unclear design with more machinery. Worse though: I so far see very little progress of this improving. If anything, on that front it feels to me that we might even be making steps in the wrong direction. At least for my taste, present-day hands-off harnesses like Claude Code with ultracode produce worse code than what we were producing last autumn. That’s because Claude Code, with Fable for instance will be working uninterrupted on a problem for thirty minutes or more, when previously the process would have been much more human in the loop.

Furthermore it’s well understood that models tend to observe some local failure and add a local defense. Karpathy mentioned how they are “mortally terrified of exceptions”. In systems with important invariants, especially persisted data formats or core infrastructure, the right fix is not “handle every malformed case.” The right fix is to make the malformed case unrepresentable or impossible to write in the first place. Yet even with a lot of manual steering, that type of code does not come out of LLMs naturally, and even if the code comes out naturally like that, they will still attempt to handle now impossible errors.

When you take that behavior and you put it behind loops, you tend to amplify it. If each iteration adds another small defense, the system slowly becomes less understandable while appearing more robust. The more hands-off you are, the more that happens. It also teaches really bad practices when tools like this are given to juniors without clear guidance. Because if you ask them, why they are doing all that, they will convincingly argue their case.

Where Loops Work

At the same time, it would be dishonest to pretend the loop pattern does not work because it already works astonishingly well in some domains.

Porting code one of them. There are already impressive examples of large automatic porting efforts, including the reported work around moving parts of Bun from Zig to Rust. I have used it with success myself to port MiniJinja to Go. Performance explorations are another case where this works beautifully. A machine can try experiments, benchmark them, discard failures, and keep searching. Security scanning fits naturally too and so does almost any type of research: asking a system to explore a complex problem space and report back without necessarily committing lasting code. One thing that many of these have in common is that they either do not generate new code, but transform code that already exists, or they produce code that intentionally does not have a long shelf life. They either produce proof of concepts or ideas, surface findings or are more akin to mechnical transformation.

I believe that loops that produce artifacts without necessity of longevity or that create some form of clearly verifiable mechnical translation matters more than the general ability of a harness to mechanically measure a goal. Many successful applications of loops use another LLM as a judge or as an orchestrator. The mechnical translation case can be verified with a binary test case, but it can also be judged by an LLM instead!

Claude Code, for instance, is increasingly good at creating entire experimental workflows that it will then execute. Sure, the code it produces is slop, but that’s more the fault of the model than the harness not being a good judge on if a step in the workflow resulted in a net improvement or completion.

The harness just needs some signal that lets it continue. It does not have to be objective or binary, it just has to be useful enough to drive another iteration.

I absolutely love loops already that take the boring parts out of my day to experiment and measure and to give me ideas.

Software As Organism

On the other hand using that same looping methodology to write lasting code does not yet sit well with me. The metaphor I like to reach for is one of moving from software as a deterministic machine to software as an organism.

I became a software engineer in an enviornment that encouraged me to understand the machine. There was always a layer you could peel off to deepen your understanding. Machines that did not exhibit deterministic observable behavior were maybe accepted, but generally seen as not exactly optimal. Software architecture-wise, I saw it as desireable to push further towards more determinism rather than less. Likewise the ability to understand the code has been an undeniable goal. In practice not always possible we still took pride in writing code so that it became possible even for new engineers to navigate complex code bases through clever architecture. On well designed systems there were always engineers that knew where the invariantes lived, which parts were load-bearing and which changes were safe. Ideally all of that was also well documented. Where that understanding was lacking, it was generally regarded as something to improve upon.

Obviously that ideal has always been strained. Many software systems, especially very successful ones had periods where engineers on the team were able to keep them clean. Large software systems are not infrequently too big, too dynamic and too dependent on external services to fit into anyone’s head. Even without LLMs we already diagnose distributed systems somewhat like doctors in that we observe symptoms, form hypotheses, “order more tests”, try some remedies, and observe again.

Yet with LLMs we’re pushing much further in that direction and much quicker. We use them to write the code and we also use them for diagnosis and remedy. There are plenty of engineers that already live in a world in which the first step after the occurrence of a production issue is followed by having a clanker read logs, propose root causes and proactively put up a patch. The resulting patch is then often picked up by another machine that reviews, sometimes even landing it on main without any human supervision.

Obviously that is powerful and I cannot deny that it sounds appealing. But giving in to that idea, particularly with less and less human oversight means accepting that we may no longer understand the whole system in the same way. We treat it, we monitor it, we stabilize it, but we do not necessarily comprehend it.

I have no doubts that for some software, that is okay. Not every line of code deserves human authorship and worse code might have been written in the past.

But do I want all software to be authored this way?

You Cannot Quite Opt Out

What’s very uncomfortable is that opting out of this fully machine-driven future may not be an option.

Security is the clearest example today. Even if you do not use loops to build your software, other people will use loops against your software. Attackers will run machines continuously and even if it’s not attackers, then security researchers will and some of that automated work will throw up dust but also find real issues. And both the signal and the noise will come your way at a volume that makes it almost impossible to deal with unless you yourself throw a machine at the problem.

Daniel Stenberg’s post about curl’s summer of bliss is a good example of the pressure maintainers are already under. As far as I know, AI does not play a tremendous role in the core development of curl today. Yet despite all of this, maintainers are overwhelmed by reports, most of which are now AI-generated ones.

If attackers and reporters loop, defenders will eventually need to loop too to keep up. Maybe not to write patches directly, maybe just to triage and reproduce and pressure will increase.

The same is true competitively as some teams will out-build others through raw speed. Some projects will suddenly move faster because a tiny group figures out how to orchestrate machines effectively. Some startups will do with five people what used to require fifty. Some people might literally put a machine against your product in a loop and ask it to “make it like the other one.” And if their users are happy, does it really matter?

Not all software will be equally affected. Some domains will punish sloppiness and demand trust and responsibility, but a lot of software lives in a world where raw speed, quick experimentation, and vast coverage matter enormously.

Building New Dependencies

The scariest part to me is that we become dependent on these new machines in new ways. Software has always depended on tools. I remember the time when I had to pay for compilers. These new tools are a flashback to times where creating software came with real costs. But now it’s no longer a one-time payment, it’s a constant dependency. Not just a dependency on a filled wallet, but also a cognitive dependency.

If a codebase is produced by loops, reviewed by loops, patched by loops, and kept alive by loops, what happens when you no longer have access to the same class of systems? What happens when some trade restrictions take away access to the most powerful models? What if just the cost becomes unbearable? What if you and your team just lose the last remaining ability to understand the code without using the machine?

We may create codebases that are not merely hard to maintain by humans, but that assume machine participation as part of their maintenance model. This is already happening! It’s not happening everywhere, and it might not even be happening in ways that are seen as problematic, but we see more and more of it. People more and more merge code they cannot fully explain. People lose their ability to create issue reports or discuss things in chat, without augmenting or rephrasing their messages with the context provided by a clanker. Too many people increasingly rely on a machine to summarize or contextualize it. More and more do I encounter people who converse with me through the indirection of an LLM.

Again, maybe that is not even going to be wrong, but it’s a massive change to how we did things.

Future Harnesses

I have little doubt that this is where things are going but going there will require us to do something about our tooling everywhere, and not just in the coding agents.

Just orchestrating more loops won’t be enough. Better visualizations of changes or orchestration or agents will not restore our understanding. Either we need to find clever ways to jolt the human back into the loop and make the changes of the loops legible long term, or we need to find better ways to compose these ever more complex systems.

This is also where my thinking about the role of Pi is changing. Pi has been cautious, and I think that caution is good. I do not want a future where every interaction turns into an uncontrolled swarm of machines making changes I cannot follow. I would not want Pi to become an unmaintainable mess in an effort to win the race towards software that writes itself and I would not want Pi to promote this type of engineering either. At the same time Pi is a harness and harnesses are at the center of people running these new types of experiments.

Task queues for coding tasks, orchestration of agents, subagents, durable sessions will matter more and more. Even those of us who have their reservations and are not blindly embracing loops will have to start doing those experiments. We need to, because we need to understand how to make this future bounded and survivable.

Controlling Loops

As you can read from this post, I’m very uneasy about this future. Not cause of fear, but because of caution given experiences with this technology so far.

Adopting the idea of harness loops means that the harness decides when work is finished. In the agent loop, the model eventually says “done” and I review. Even before that, I usually steer along the way. I am involved and I enjoy learning along the way. In the harness operated loop I’m not sure what my role even is. Even the “done” signal loses all meanings and just becomes communicated to yet another machine that judges. My role is reduced to that of a messenger.

Today I do not like much of the code that I see from systems built that way and neither do I enjoy interacting with too much of software built with AI assistence. Looping is powerful but it removes responsibility more and more, and it at least today very much encourages us to give in to the machine.

And yet I have no doubts that this looping future is going to be our future despite the fact that I presently resent it. I already see astonishingly small teams building at impossible speed and I see codebases turning more and more into obscure and confusing organisms that can only be diagnosed by more machines. Those codebases are simultaniously useful and messy.

So I guess I’m coming to terms with that the question is not whether we will loop because clearly we will. Maybe the question is that in a future of loops, how do we don’t abdicate judgment, how we can retain rules of good engineering, how we can ensure that responsible human can continue to supervise, how we need to re-think how we architect code to retain sanity along the way.

This entry was tagged ai and pi

🤖 AI 总结
文章描述了基于编码代理的任务循环模式:工作入队、机器处理、评估结果,未完成则继续循环,使任务超越模型自然终点。
Armin Ronacher's Thoughts and Writings

The Coming Loop

written on June 23, 2026

I don’t prompt Claude anymore. I have loops running that prompt Claude and figuring out what to do. My job is to write loops.

— Boris Cherny

Over the last months I have watched more and more people build something on top of coding agents that feels meaningfully different from just using a coding agent. Some of this happens on top of Pi which is cool to see for sure! The pattern is the same everywhere though: work is put into a queue of sorts, a machine picks it up, attempts it, stops, and then some harness decides whether that was actually the end.

If not, the harness continues the same session, injects another message, starts a fresh session with modified context, or sends the task to another machine. The task stays alive beyond the point where the model by itself would normally have said: “I am done.”

I think about that type of loop more than I want to admit.

There is already an agent loop inside every coding agent. The model calls a tool, incorporates the result, calls another tool, reads a file, edits a file, runs tests, and eventually produces some answer. That loop is one we have been quite familiar with for a long time. The other loop is the harness level loop: the loop outside the agent loop. That loop is also not new. We have been doing versions of this since early Claude Code days, but that loop is becoming ever more present in agentic engineering and in recent weeks it has started to dominate the Twitter discourse.

I Am Not Good At This Yet

My current status is that I have not had much success with this way of working for code I deeply care about which turns out to be quite a lot of code.

Part of that is taste and part of it is control. I attempt to set a high bar for what I want code to look like, and I want to understand the code I ship. Under pressure, or in a discussion with another human, I want to be able to explain what the system does without first having to ask a clanker to explain it to me. Now there is obviously a question if this desire to understand the code is one that I will still have a few years from now. For now I have not moved past the point of comprehension being important to me.

Given this desire, there is something I lack with my experience of code written without me paying attention, particularly from loops. Present-day models tend to produce code that is too defensive, too complex, too local in its reasoning. They avoid strong invariants. They add fallbacks instead of making bad states impossible. They duplicate code, invent bad abstractions, and paper over unclear design with more machinery. Worse though: I so far see very little progress of this improving. If anything, on that front it feels to me that we might even be making steps in the wrong direction. At least for my taste, present-day hands-off harnesses like Claude Code with ultracode produce worse code than what we were producing last autumn. That’s because Claude Code, with Fable for instance will be working uninterrupted on a problem for thirty minutes or more, when previously the process would have been much more human in the loop.

Furthermore it’s well understood that models tend to observe some local failure and add a local defense. Karpathy mentioned how they are “mortally terrified of exceptions”. In systems with important invariants, especially persisted data formats or core infrastructure, the right fix is not “handle every malformed case.” The right fix is to make the malformed case unrepresentable or impossible to write in the first place. Yet even with a lot of manual steering, that type of code does not come out of LLMs naturally, and even if the code comes out naturally like that, they will still attempt to handle now impossible errors.

When you take that behavior and you put it behind loops, you tend to amplify it. If each iteration adds another small defense, the system slowly becomes less understandable while appearing more robust. The more hands-off you are, the more that happens. It also teaches really bad practices when tools like this are given to juniors without clear guidance. Because if you ask them, why they are doing all that, they will convincingly argue their case.

Where Loops Work

At the same time, it would be dishonest to pretend the loop pattern does not work because it already works astonishingly well in some domains.

Porting code one of them. There are already impressive examples of large automatic porting efforts, including the reported work around moving parts of Bun from Zig to Rust. I have used it with success myself to port MiniJinja to Go. Performance explorations are another case where this works beautifully. A machine can try experiments, benchmark them, discard failures, and keep searching. Security scanning fits naturally too and so does almost any type of research: asking a system to explore a complex problem space and report back without necessarily committing lasting code. One thing that many of these have in common is that they either do not generate new code, but transform code that already exists, or they produce code that intentionally does not have a long shelf life. They either produce proof of concepts or ideas, surface findings or are more akin to mechnical transformation.

I believe that loops that produce artifacts without necessity of longevity or that create some form of clearly verifiable mechnical translation matters more than the general ability of a harness to mechanically measure a goal. Many successful applications of loops use another LLM as a judge or as an orchestrator. The mechnical translation case can be verified with a binary test case, but it can also be judged by an LLM instead!

Claude Code, for instance, is increasingly good at creating entire experimental workflows that it will then execute. Sure, the code it produces is slop, but that’s more the fault of the model than the harness not being a good judge on if a step in the workflow resulted in a net improvement or completion.

The harness just needs some signal that lets it continue. It does not have to be objective or binary, it just has to be useful enough to drive another iteration.

I absolutely love loops already that take the boring parts out of my day to experiment and measure and to give me ideas.

Software As Organism

On the other hand using that same looping methodology to write lasting code does not yet sit well with me. The metaphor I like to reach for is one of moving from software as a deterministic machine to software as an organism.

I became a software engineer in an enviornment that encouraged me to understand the machine. There was always a layer you could peel off to deepen your understanding. Machines that did not exhibit deterministic observable behavior were maybe accepted, but generally seen as not exactly optimal. Software architecture-wise, I saw it as desireable to push further towards more determinism rather than less. Likewise the ability to understand the code has been an undeniable goal. In practice not always possible we still took pride in writing code so that it became possible even for new engineers to navigate complex code bases through clever architecture. On well designed systems there were always engineers that knew where the invariantes lived, which parts were load-bearing and which changes were safe. Ideally all of that was also well documented. Where that understanding was lacking, it was generally regarded as something to improve upon.

Obviously that ideal has always been strained. Many software systems, especially very successful ones had periods where engineers on the team were able to keep them clean. Large software systems are not infrequently too big, too dynamic and too dependent on external services to fit into anyone’s head. Even without LLMs we already diagnose distributed systems somewhat like doctors in that we observe symptoms, form hypotheses, “order more tests”, try some remedies, and observe again.

Yet with LLMs we’re pushing much further in that direction and much quicker. We use them to write the code and we also use them for diagnosis and remedy. There are plenty of engineers that already live in a world in which the first step after the occurrence of a production issue is followed by having a clanker read logs, propose root causes and proactively put up a patch. The resulting patch is then often picked up by another machine that reviews, sometimes even landing it on main without any human supervision.

Obviously that is powerful and I cannot deny that it sounds appealing. But giving in to that idea, particularly with less and less human oversight means accepting that we may no longer understand the whole system in the same way. We treat it, we monitor it, we stabilize it, but we do not necessarily comprehend it.

I have no doubts that for some software, that is okay. Not every line of code deserves human authorship and worse code might have been written in the past.

But do I want all software to be authored this way?

You Cannot Quite Opt Out

What’s very uncomfortable is that opting out of this fully machine-driven future may not be an option.

Security is the clearest example today. Even if you do not use loops to build your software, other people will use loops against your software. Attackers will run machines continuously and even if it’s not attackers, then security researchers will and some of that automated work will throw up dust but also find real issues. And both the signal and the noise will come your way at a volume that makes it almost impossible to deal with unless you yourself throw a machine at the problem.

Daniel Stenberg’s post about curl’s summer of bliss is a good example of the pressure maintainers are already under. As far as I know, AI does not play a tremendous role in the core development of curl today. Yet despite all of this, maintainers are overwhelmed by reports, most of which are now AI-generated ones.

If attackers and reporters loop, defenders will eventually need to loop too to keep up. Maybe not to write patches directly, maybe just to triage and reproduce and pressure will increase.

The same is true competitively as some teams will out-build others through raw speed. Some projects will suddenly move faster because a tiny group figures out how to orchestrate machines effectively. Some startups will do with five people what used to require fifty. Some people might literally put a machine against your product in a loop and ask it to “make it like the other one.” And if their users are happy, does it really matter?

Not all software will be equally affected. Some domains will punish sloppiness and demand trust and responsibility, but a lot of software lives in a world where raw speed, quick experimentation, and vast coverage matter enormously.

Building New Dependencies

The scariest part to me is that we become dependent on these new machines in new ways. Software has always depended on tools. I remember the time when I had to pay for compilers. These new tools are a flashback to times where creating software came with real costs. But now it’s no longer a one-time payment, it’s a constant dependency. Not just a dependency on a filled wallet, but also a cognitive dependency.

If a codebase is produced by loops, reviewed by loops, patched by loops, and kept alive by loops, what happens when you no longer have access to the same class of systems? What happens when some trade restrictions take away access to the most powerful models? What if just the cost becomes unbearable? What if you and your team just lose the last remaining ability to understand the code without using the machine?

We may create codebases that are not merely hard to maintain by humans, but that assume machine participation as part of their maintenance model. This is already happening! It’s not happening everywhere, and it might not even be happening in ways that are seen as problematic, but we see more and more of it. People more and more merge code they cannot fully explain. People lose their ability to create issue reports or discuss things in chat, without augmenting or rephrasing their messages with the context provided by a clanker. Too many people increasingly rely on a machine to summarize or contextualize it. More and more do I encounter people who converse with me through the indirection of an LLM.

Again, maybe that is not even going to be wrong, but it’s a massive change to how we did things.

Future Harnesses

I have little doubt that this is where things are going but going there will require us to do something about our tooling everywhere, and not just in the coding agents.

Just orchestrating more loops won’t be enough. Better visualizations of changes or orchestration or agents will not restore our understanding. Either we need to find clever ways to jolt the human back into the loop and make the changes of the loops legible long term, or we need to find better ways to compose these ever more complex systems.

This is also where my thinking about the role of Pi is changing. Pi has been cautious, and I think that caution is good. I do not want a future where every interaction turns into an uncontrolled swarm of machines making changes I cannot follow. I would not want Pi to become an unmaintainable mess in an effort to win the race towards software that writes itself and I would not want Pi to promote this type of engineering either. At the same time Pi is a harness and harnesses are at the center of people running these new types of experiments.

Task queues for coding tasks, orchestration of agents, subagents, durable sessions will matter more and more. Even those of us who have their reservations and are not blindly embracing loops will have to start doing those experiments. We need to, because we need to understand how to make this future bounded and survivable.

Controlling Loops

As you can read from this post, I’m very uneasy about this future. Not cause of fear, but because of caution given experiences with this technology so far.

Adopting the idea of harness loops means that the harness decides when work is finished. In the agent loop, the model eventually says “done” and I review. Even before that, I usually steer along the way. I am involved and I enjoy learning along the way. In the harness operated loop I’m not sure what my role even is. Even the “done” signal loses all meanings and just becomes communicated to yet another machine that judges. My role is reduced to that of a messenger.

Today I do not like much of the code that I see from systems built that way and neither do I enjoy interacting with too much of software built with AI assistence. Looping is powerful but it removes responsibility more and more, and it at least today very much encourages us to give in to the machine.

And yet I have no doubts that this looping future is going to be our future despite the fact that I presently resent it. I already see astonishingly small teams building at impossible speed and I see codebases turning more and more into obscure and confusing organisms that can only be diagnosed by more machines. Those codebases are simultaniously useful and messy.

So I guess I’m coming to terms with that the question is not whether we will loop because clearly we will. Maybe the question is that in a future of loops, how do we don’t abdicate judgment, how we can retain rules of good engineering, how we can ensure that responsible human can continue to supervise, how we need to re-think how we architect code to retain sanity along the way.

This entry was tagged ai and pi

原文
The Coming Loop
Armin Ronacher's Thoughts and Writings

The Coming Loop

written on June 23, 2026

I don’t prompt Claude anymore. I have loops running that prompt Claude and figuring out what to do. My job is to write loops.

— Boris Cherny

Over the last months I have watched more and more people build something on top of coding agents that feels meaningfully different from just using a coding agent. Some of this happens on top of Pi which is cool to see for sure! The pattern is the same everywhere though: work is put into a queue of sorts, a machine picks it up, attempts it, stops, and then some harness decides whether that was actually the end.

If not, the harness continues the same session, injects another message, starts a fresh session with modified context, or sends the task to another machine. The task stays alive beyond the point where the model by itself would normally have said: “I am done.”

I think about that type of loop more than I want to admit.

There is already an agent loop inside every coding agent. The model calls a tool, incorporates the result, calls another tool, reads a file, edits a file, runs tests, and eventually produces some answer. That loop is one we have been quite familiar with for a long time. The other loop is the harness level loop: the loop outside the agent loop. That loop is also not new. We have been doing versions of this since early Claude Code days, but that loop is becoming ever more present in agentic engineering and in recent weeks it has started to dominate the Twitter discourse.

I Am Not Good At This Yet

My current status is that I have not had much success with this way of working for code I deeply care about which turns out to be quite a lot of code.

Part of that is taste and part of it is control. I attempt to set a high bar for what I want code to look like, and I want to understand the code I ship. Under pressure, or in a discussion with another human, I want to be able to explain what the system does without first having to ask a clanker to explain it to me. Now there is obviously a question if this desire to understand the code is one that I will still have a few years from now. For now I have not moved past the point of comprehension being important to me.

Given this desire, there is something I lack with my experience of code written without me paying attention, particularly from loops. Present-day models tend to produce code that is too defensive, too complex, too local in its reasoning. They avoid strong invariants. They add fallbacks instead of making bad states impossible. They duplicate code, invent bad abstractions, and paper over unclear design with more machinery. Worse though: I so far see very little progress of this improving. If anything, on that front it feels to me that we might even be making steps in the wrong direction. At least for my taste, present-day hands-off harnesses like Claude Code with ultracode produce worse code than what we were producing last autumn. That’s because Claude Code, with Fable for instance will be working uninterrupted on a problem for thirty minutes or more, when previously the process would have been much more human in the loop.

Furthermore it’s well understood that models tend to observe some local failure and add a local defense. Karpathy mentioned how they are “mortally terrified of exceptions”. In systems with important invariants, especially persisted data formats or core infrastructure, the right fix is not “handle every malformed case.” The right fix is to make the malformed case unrepresentable or impossible to write in the first place. Yet even with a lot of manual steering, that type of code does not come out of LLMs naturally, and even if the code comes out naturally like that, they will still attempt to handle now impossible errors.

When you take that behavior and you put it behind loops, you tend to amplify it. If each iteration adds another small defense, the system slowly becomes less understandable while appearing more robust. The more hands-off you are, the more that happens. It also teaches really bad practices when tools like this are given to juniors without clear guidance. Because if you ask them, why they are doing all that, they will convincingly argue their case.

Where Loops Work

At the same time, it would be dishonest to pretend the loop pattern does not work because it already works astonishingly well in some domains.

Porting code one of them. There are already impressive examples of large automatic porting efforts, including the reported work around moving parts of Bun from Zig to Rust. I have used it with success myself to port MiniJinja to Go. Performance explorations are another case where this works beautifully. A machine can try experiments, benchmark them, discard failures, and keep searching. Security scanning fits naturally too and so does almost any type of research: asking a system to explore a complex problem space and report back without necessarily committing lasting code. One thing that many of these have in common is that they either do not generate new code, but transform code that already exists, or they produce code that intentionally does not have a long shelf life. They either produce proof of concepts or ideas, surface findings or are more akin to mechnical transformation.

I believe that loops that produce artifacts without necessity of longevity or that create some form of clearly verifiable mechnical translation matters more than the general ability of a harness to mechanically measure a goal. Many successful applications of loops use another LLM as a judge or as an orchestrator. The mechnical translation case can be verified with a binary test case, but it can also be judged by an LLM instead!

Claude Code, for instance, is increasingly good at creating entire experimental workflows that it will then execute. Sure, the code it produces is slop, but that’s more the fault of the model than the harness not being a good judge on if a step in the workflow resulted in a net improvement or completion.

The harness just needs some signal that lets it continue. It does not have to be objective or binary, it just has to be useful enough to drive another iteration.

I absolutely love loops already that take the boring parts out of my day to experiment and measure and to give me ideas.

Software As Organism

On the other hand using that same looping methodology to write lasting code does not yet sit well with me. The metaphor I like to reach for is one of moving from software as a deterministic machine to software as an organism.

I became a software engineer in an enviornment that encouraged me to understand the machine. There was always a layer you could peel off to deepen your understanding. Machines that did not exhibit deterministic observable behavior were maybe accepted, but generally seen as not exactly optimal. Software architecture-wise, I saw it as desireable to push further towards more determinism rather than less. Likewise the ability to understand the code has been an undeniable goal. In practice not always possible we still took pride in writing code so that it became possible even for new engineers to navigate complex code bases through clever architecture. On well designed systems there were always engineers that knew where the invariantes lived, which parts were load-bearing and which changes were safe. Ideally all of that was also well documented. Where that understanding was lacking, it was generally regarded as something to improve upon.

Obviously that ideal has always been strained. Many software systems, especially very successful ones had periods where engineers on the team were able to keep them clean. Large software systems are not infrequently too big, too dynamic and too dependent on external services to fit into anyone’s head. Even without LLMs we already diagnose distributed systems somewhat like doctors in that we observe symptoms, form hypotheses, “order more tests”, try some remedies, and observe again.

Yet with LLMs we’re pushing much further in that direction and much quicker. We use them to write the code and we also use them for diagnosis and remedy. There are plenty of engineers that already live in a world in which the first step after the occurrence of a production issue is followed by having a clanker read logs, propose root causes and proactively put up a patch. The resulting patch is then often picked up by another machine that reviews, sometimes even landing it on main without any human supervision.

Obviously that is powerful and I cannot deny that it sounds appealing. But giving in to that idea, particularly with less and less human oversight means accepting that we may no longer understand the whole system in the same way. We treat it, we monitor it, we stabilize it, but we do not necessarily comprehend it.

I have no doubts that for some software, that is okay. Not every line of code deserves human authorship and worse code might have been written in the past.

But do I want all software to be authored this way?

You Cannot Quite Opt Out

What’s very uncomfortable is that opting out of this fully machine-driven future may not be an option.

Security is the clearest example today. Even if you do not use loops to build your software, other people will use loops against your software. Attackers will run machines continuously and even if it’s not attackers, then security researchers will and some of that automated work will throw up dust but also find real issues. And both the signal and the noise will come your way at a volume that makes it almost impossible to deal with unless you yourself throw a machine at the problem.

Daniel Stenberg’s post about curl’s summer of bliss is a good example of the pressure maintainers are already under. As far as I know, AI does not play a tremendous role in the core development of curl today. Yet despite all of this, maintainers are overwhelmed by reports, most of which are now AI-generated ones.

If attackers and reporters loop, defenders will eventually need to loop too to keep up. Maybe not to write patches directly, maybe just to triage and reproduce and pressure will increase.

The same is true competitively as some teams will out-build others through raw speed. Some projects will suddenly move faster because a tiny group figures out how to orchestrate machines effectively. Some startups will do with five people what used to require fifty. Some people might literally put a machine against your product in a loop and ask it to “make it like the other one.” And if their users are happy, does it really matter?

Not all software will be equally affected. Some domains will punish sloppiness and demand trust and responsibility, but a lot of software lives in a world where raw speed, quick experimentation, and vast coverage matter enormously.

Building New Dependencies

The scariest part to me is that we become dependent on these new machines in new ways. Software has always depended on tools. I remember the time when I had to pay for compilers. These new tools are a flashback to times where creating software came with real costs. But now it’s no longer a one-time payment, it’s a constant dependency. Not just a dependency on a filled wallet, but also a cognitive dependency.

If a codebase is produced by loops, reviewed by loops, patched by loops, and kept alive by loops, what happens when you no longer have access to the same class of systems? What happens when some trade restrictions take away access to the most powerful models? What if just the cost becomes unbearable? What if you and your team just lose the last remaining ability to understand the code without using the machine?

We may create codebases that are not merely hard to maintain by humans, but that assume machine participation as part of their maintenance model. This is already happening! It’s not happening everywhere, and it might not even be happening in ways that are seen as problematic, but we see more and more of it. People more and more merge code they cannot fully explain. People lose their ability to create issue reports or discuss things in chat, without augmenting or rephrasing their messages with the context provided by a clanker. Too many people increasingly rely on a machine to summarize or contextualize it. More and more do I encounter people who converse with me through the indirection of an LLM.

Again, maybe that is not even going to be wrong, but it’s a massive change to how we did things.

Future Harnesses

I have little doubt that this is where things are going but going there will require us to do something about our tooling everywhere, and not just in the coding agents.

Just orchestrating more loops won’t be enough. Better visualizations of changes or orchestration or agents will not restore our understanding. Either we need to find clever ways to jolt the human back into the loop and make the changes of the loops legible long term, or we need to find better ways to compose these ever more complex systems.

This is also where my thinking about the role of Pi is changing. Pi has been cautious, and I think that caution is good. I do not want a future where every interaction turns into an uncontrolled swarm of machines making changes I cannot follow. I would not want Pi to become an unmaintainable mess in an effort to win the race towards software that writes itself and I would not want Pi to promote this type of engineering either. At the same time Pi is a harness and harnesses are at the center of people running these new types of experiments.

Task queues for coding tasks, orchestration of agents, subagents, durable sessions will matter more and more. Even those of us who have their reservations and are not blindly embracing loops will have to start doing those experiments. We need to, because we need to understand how to make this future bounded and survivable.

Controlling Loops

As you can read from this post, I’m very uneasy about this future. Not cause of fear, but because of caution given experiences with this technology so far.

Adopting the idea of harness loops means that the harness decides when work is finished. In the agent loop, the model eventually says “done” and I review. Even before that, I usually steer along the way. I am involved and I enjoy learning along the way. In the harness operated loop I’m not sure what my role even is. Even the “done” signal loses all meanings and just becomes communicated to yet another machine that judges. My role is reduced to that of a messenger.

Today I do not like much of the code that I see from systems built that way and neither do I enjoy interacting with too much of software built with AI assistence. Looping is powerful but it removes responsibility more and more, and it at least today very much encourages us to give in to the machine.

And yet I have no doubts that this looping future is going to be our future despite the fact that I presently resent it. I already see astonishingly small teams building at impossible speed and I see codebases turning more and more into obscure and confusing organisms that can only be diagnosed by more machines. Those codebases are simultaniously useful and messy.

So I guess I’m coming to terms with that the question is not whether we will loop because clearly we will. Maybe the question is that in a future of loops, how do we don’t abdicate judgment, how we can retain rules of good engineering, how we can ensure that responsible human can continue to supervise, how we need to re-think how we architect code to retain sanity along the way.

This entry was tagged ai and pi

中文翻译
即将到来的循环
Armin Ronacher 的思想与文章

即将到来的循环

写于 2026 年 6 月 23 日

我不再给 Claude 提供提示了。我有正在运行的循环,它们给 Claude 提供提示并决定做什么。我的工作就是写循环。

—— Boris Cherny

过去几个月,我看到越来越多的人在编码智能体的基础上构建一些东西,这些东西给人的感觉与仅仅使用编码智能体有着本质上的不同。其中一些构建在 Pi 之上,这当然很值得一看!不过,模式在各处都是一样的:工作被放入某种队列,机器拾取它,尝试执行,然后停止,接着一些外层框架决定这是否真的是终点。

如果不是,框架会继续同一会话,注入另一条消息,用修改后的上下文启动一个新会话,或者将任务发送给另一台机器。任务的生命周期超出了模型自身通常会说“我完成了”的那个点。

我对此类循环的思考比我愿意承认的要多。

每个编码智能体内部早已存在一个智能体循环。模型调用一个工具,整合结果,调用另一个工具,读取文件,编辑文件,运行测试,最终产生某个答案。这个循环我们已经熟悉很久了。另一个循环是框架级别的循环:即智能体循环外部的那个循环。这个循环也 并不新鲜。从早期 Claude Code 的时代起我们就一直在做各种版本的它,但如今在智能体工程中它变得越来越普遍,并且在最近几周开始主导 Twitter 上的讨论。

我对此尚不擅长

我目前的状况是,对于我真正在意的代码——其实大部分代码都是——这种工作方式并没有给我带来太多成功。

部分原因是品味,部分原因是控制。我试图为代码的质量设定一个高标准,并且我想理解我所发布的代码。在压力之下,或者在与他人讨论时,我希望能够解释系统做了什么,而不必先问一个“机器助手”来解释给我听。显然,这里有一个问题:几年后我是否还会保持这种理解代码的渴望?至少目前,我还没有跨过“理解对我来说很重要”这个阶段。

鉴于这种渴望,对于我没有亲自关注而写出的代码——尤其是来自循环的代码——我缺少一些经验。当前的模型倾向于产生过于防御性、过于复杂、推理范围过于局部的代码。它们避免强不变量。它们添加后备方案,而不是让坏状态不可能出现。它们重复代码、发明糟糕的抽象、并用更多的机制来掩盖不清晰的设计。更糟糕的是:到目前为止,我几乎没有看到这方面有任何改善的进展。如果说有什么变化的话,我甚至觉得在这一方面我们可能正在朝着错误的方向前进。至少以我的品味,像 Claude Code 结合 ultracode 这样的现代“甩手式”框架产生的代码,比去年秋天我们产生的结果更差。这是因为,例如 Claude Code 配合 Fable 时,会连续工作三十分钟或更久,而以前这个过程会有人类更多地参与在循环中。

此外,众所周知,模型倾向于观察到局部的失败,然后添加局部的防御。Karpathy 曾提到它们“对异常怕得要命”。在具有重要不变量(尤其是持久化数据格式或核心基础设施)的系统中,正确的修复不是“处理每一种畸形情况”。正确的修复是让畸形情况从一开始就无法表示或无法写入。然而,即使有大量的人工引导,这类代码也不会自然地从 LLM 中产生;即使代码以那种方式自然产生,它们仍然会试图处理现在不可能出现的错误。

当你把这种行为放到循环后面时,往往会放大它。如果每次迭代都增加一点小的防御,系统会逐渐变得难以理解,尽管看起来更健壮。你越是甩手不管,这种情况就越会发生。当这样的工具在没有明确指导的情况下交给初级人员时,还会教给他们非常糟糕的实践。因为如果你问他们为什么要做所有这些,他们会令人信服地为自己辩护。

循环的用武之地

与此同时,假装循环模式不成功也是不诚实的,因为它在某些领域已经出奇地好用了。

代码移植就是其中之一。已经有令人印象深刻的大规模自动移植案例,包括关于将部分 Bun 从 Zig 移植到 Rust 的报道。我自己也成功地在将 MiniJinja 移植到 Go 项目中使用过它[链接]。性能探索是另一个效果极好的例子。机器可以尝试实验、基准测试、丢弃失败并继续搜索。安全扫描自然也适用,几乎所有类型的研究也是如此:让系统探索一个复杂的问题空间并汇报结果,而不必留下持久化的代码。这些例子中很多都有一个共同点:它们要么不生成新代码,而是转换已有代码;要么产生的代码故意设计为没有很长的保质期。它们要么产生概念验证或想法,要么发现结果,要么更像是机械化的转换。

我相信,能够产生不需要长期存在的工件,或者创建某种清晰可验证的机械化转换的循环,比框架机械地度量一个目标的一般能力更重要。许多成功的循环应用使用另一个 LLM 作为评判者或编排者。机械化转换的情况可以通过二进制测试用例验证,但也可以由 LLM 来评判!

例如,Claude Code 在创建完整的实验性工作流方面越来越强,然后它会执行这些工作流。当然,它产生的代码可能很乱,但这更多是模型的错,而不是框架不能很好地判断工作流中的步骤是否带来了净改进或完成。

框架只需要一些信号让它继续下去。这个信号不必是客观的或二元的,只需足够有用以推动下一次迭代。

我已经非常喜欢那些能把无聊的部分从我的日常工作中剥离出来,让我去实验、测量并获得灵感的循环了。

软件作为有机体

另一方面,使用同样的循环方法来编写持久的代码,对我来说还不太合适。我喜欢用的比喻是从“软件作为确定性机器”转向“软件作为有机体”。

我成为软件工程师的环境鼓励我理解机器。总有一层你可以剥开以深化理解。那些不表现确定性可观察行为的机器或许可以被接受,但通常被认为并非最佳。在软件架构方面,我曾认为更倾向于确定性而非相反是可取的。同样,理解代码的能力一直是一个不可否认的目标。在实践中,虽然并非总是可能,但我们仍然自豪于编写代码,使得即使是新工程师也能通过巧妙的架构在复杂的代码库中导航。在精心设计的系统上,总有一些工程师知道不变量在哪里,哪些部分是承重的,哪些改动是安全的。理想情况下,所有这些都有良好的文档记录。在缺乏这种理解的地方,通常被视为需要改进的地方。

显然,这种理想一直受到挑战。许多软件系统,尤其是非常成功的系统,都曾经历过团队中的工程师能够保持其整洁的时期。大型软件系统常常过于庞大、过于动态,并且过于依赖外部服务,以至于无法装进任何一个人的脑袋。即使没有 LLM,我们在诊断分布式系统时也多少有点像医生:观察症状、形成假设、“安排更多检查”、尝试一些补救措施,然后再次观察。

然而,有了 LLM,我们正朝着那个方向走得更远、更快。我们用它们来编写代码,也用于诊断和补救。已经有很多工程师生活在这样的世界里:生产问题出现后的第一步,是让一个机器助手读取日志、提出根本原因并主动提交补丁。随后,得到的补丁往往又被另一台机器接手审查,有时甚至在没有人类监督的情况下直接合并到主分支。

显然,这很强大,我不能否认这听起来很有吸引力。但屈服于这种想法——尤其是在人类监督越来越少的情况下——意味着接受我们可能不再以同样的方式理解整个系统。我们治疗它、监控它、稳定它,但未必理解它。

我毫不怀疑,对于某些软件来说,这是可以的。并不是每一行代码都值得人类亲笔撰写,而且过去可能也写过更差的代码。

但我希望所有软件都这样编写吗?

你无法完全置身事外

令人非常不安的是,完全摆脱这种机器驱动的未来可能不是一个选项。

安全性是今天最明显的例子。即使你不用循环来构建你的软件,别人也会用循环来攻击你的软件。攻击者会持续运行机器,即使不是攻击者,安全研究人员也会这样做,其中一些自动化工作会扬起灰尘,但也会发现真正的问题。无论是信号还是噪音,都会以极高的数量向你涌来,使得除非你自身也投入机器来处理问题,否则几乎无法应对。

Daniel Stenberg 关于 curl 的 夏日幸福 一文就是维护者已经承受压力的一个好例子。据我所知,目前 AI 在 curl 的核心开发中并没有发挥巨大作用。然而尽管如此,维护者还是被报告淹没了,其中绝大多数现在都是 AI 生成的。

如果攻击者和报告者循环,那么防御者最终也需要循环才能跟上。也许不是直接写补丁,也许只是进行分类和复现,压力会不断增加。

竞争方面也是如此:一些团队会通过原始速度超越其他团队。一些项目会突然加速,因为一个小团体找到了有效编排机器的方法。一些初创公司能用五个人完成过去需要五十人才能完成的工作。有些人可能会直接让机器循环对着你的产品说“把它做得像另一个一样”。如果他们的用户满意,这真的重要吗?

并非所有软件都会同等程度地受到影响。某些领域会惩罚草率,要求信任和责任,但很多软件生活在这样一个世界里:原始速度、快速实验和广泛覆盖极其重要。

构建新依赖

对我而言,最可怕的是我们以新的方式依赖这些新机器。软件一直依赖于工具。我记得过去必须为编译器付费的时代。这些新工具让人回想起创建软件伴随着真实成本的时代。但现在不再是一次性付款,而是持续性的依赖。不仅仅是金钱上的依赖,还有认知上的依赖。

如果一个代码库由循环生成、由循环审查、由循环打补丁、由循环维持存活,那么当你不再能访问同一类系统时会发生什么?当某些贸易限制让你无法使用最强大的模型时怎么办?如果成本变得无法承受呢?如果你和你的团队失去了最后一点不借助机器理解代码的能力呢?

我们可能会创造出不仅人类难以维护,而且把机器参与作为维护模式一部分的代码库。这已经在发生!它并非到处发生,甚至可能还没有以被视为有问题的方式发生,但我们看到了越来越多的迹象。越来越多的人合并他们自己无法完全解释的代码。人们失去了无需机器助手增强或重新措辞消息就能创建问题报告或在聊天中讨论事情的能力。太多人越来越多地依赖机器来总结或语境化信息。我越来越多地遇到通过 LLM 的间接方式与我交谈的人。

再说一次,也许这甚至不会错,但这是对我们做事方式的一种巨大改变。

未来的框架

我毫不怀疑这是未来的发展方向,但要达到那里,我们需要在各个方面对我们的工具做些什么,而不仅仅是编码智能体。

仅仅编排更多的循环是不够的。更好的变更可视化、编排或智能体并不会恢复我们的理解。要么我们需要找到巧妙的方式将人类拉回循环中,并使循环的变更长期可读;要么我们需要找到更好的方式来组合这些日益复杂的系统。

这也是我对 Pi 角色的看法正在改变的地方。Pi 一直很谨慎,我认为这种谨慎是好的。我不希望未来每一次互动都变成不受控制的机器蜂群,做出我无法跟踪的变更。我不希望 Pi 为了赢得“软件自我编写”的竞赛而变成一个无法维护的烂摊子,也不希望 Pi 推广这种类型的工程。与此同时,Pi 是一个框架,而框架正是人们运行这些新型实验的核心。

编码任务的任务队列、智能体及子智能体的编排、持久会话将变得越来越重要。即使是我们这些有所保留、并没有盲目拥抱循环的人,也必须开始进行这些实验。我们必须这样做,因为我们需要理解如何让这个未来变得有限且可生存。

控制循环

正如你可以从这篇文章中读到的,我对这个未来感到非常不安。不是出于恐惧,而是出于基于迄今为止与这项技术的经验的谨慎。

接受框架循环的想法意味着框架决定工作何时完成。在智能体循环中,模型最终说“完成了”,然后我审查。甚至在那之前,我通常一路引导。我参与其中,并享受沿途学习的过程。在框架操作的循环中,我甚至不确定我的角色是什么。就连“完成”信号也失去了所有意义,只是被传递给另一个机器去评判。我的角色被降级为一个信使。

今天,我不太喜欢从这样构建的系统中看到的代码,也不喜欢与太多用 AI 辅助构建的软件交互。循环很强大,但它越来越多地剥夺责任,而且至少在今天,它非常鼓励我们向机器屈服。

然而,我毫不怀疑这个循环化的未来将是我们的未来,尽管我目前对此感到反感。我已经看到小得惊人的团队以不可思议的速度构建,也看到代码库越来越多地变成晦涩混乱的有机体,只能由更多机器来诊断。这些代码库同时既有用又混乱。

所以我想我逐渐接受了这个事实:问题不在于我们是否会循环——显然我们会。也许问题是,在一个循环的未来中,我们如何不放弃判断,如何保留优秀工程的原则,如何确保负责任的人类能够继续监督,如何重新思考我们架构代码的方式以保持理智。

本文标记为 AIPi