编码代理是一种AI系统,它使用大型语言模型作为其推理核心,能够自主编写、执行和迭代代码。与代码助手不同,后者在编辑器中生成补全建议后停止,而编码代理运行完整的观察-决策-行动循环:它读取文件、写入更改、运行命令、检查输出,并不断修正,直到任务完成。
本文解释了该循环的工作原理——包括规划器、LLM推理层、工具和沙箱化执行环境——然后展示了如何使用Novita的LLM API和Agent Sandbox构建一个编码代理。
什么是编码代理
代码助手和编码代理之间的区别在于执行。代码助手生成一个建议后停止。而AI编码代理生成代码,运行它,读取结果,并持续进行直到目标达成——或者直到陷入困境。
这种执行能力有三个具体需求:
- 工具访问 —— 读取文件、写入文件和运行shell命令的能力
- 沙箱化环境 —— 一个执行代码的地方,如果出现问题也不会损坏主机系统
- 持久化上下文 —— 工具输出反馈到模型的上下文中,以便它可以推理发生了什么
缺少任何一项,你只有一个能写代码的聊天机器人。拥有全部三项,你就有了一个代理。
“代码代理”这个术语被松散地用于涵盖从IDE内联建议到完全自主的系统,这些系统可以接受一个模糊指定的任务,找出涉及哪些文件,进行更改,并验证它们是否工作——而无需人工参与每一步。当开发者比较“最佳代码代理”选项时,他们通常指的是后者:能够可靠地完成多步编码任务且只需最少辅助的系统。
编码代理的四个层次
每个生产级AI编码代理都有四个可识别的组件。实现细节各不相同——不同的框架、不同的LLM、不同的沙箱提供商——但架构是一致的。
1. 规划器
规划器接收任务描述并将其分解为代理将执行的步骤。对于简单任务,这隐含在模型的推理中发生。对于复杂任务——例如“将这个服务迁移为使用新的认证库”——一个明确的规划步骤会生成一个编号的任务列表,模型依次完成,并在每个步骤后更新状态。
规划还决定了何时停止。没有完成标准的代理会无限地添加改进,或者更糟,在失败的步骤上循环。大多数实现将任务成功条件编码在系统提示中,让模型决定何时完成。
2. LLM推理层
LLM是任何AI编码代理的推理核心。它决定接下来调用哪个工具、传递什么参数以及如何解释结果。这个决策被表达为一个结构化的工具调用——一个包含函数名称和参数的JSON对象——框架将其分派到实际的执行层。
对于编码代理,LLM需要可靠地处理长上下文(工具结果快速累积),一致地返回格式良好的工具调用(10步工作流中的第6步出现一个结构错误的JSON会破坏整个运行),并推理跨多个顺序工具调用的状态变化。
推理提供商在这里很重要。你需要一个API,支持OpenAI兼容格式的函数调用、结构化输出以在模型级别强制生成有效的JSON,以及足够的并发限制以处理产生并行子任务的代理工作负载。Novita AI的LLM API通过一个OpenAI兼容的端点覆盖了所有这三个方面,这意味着你可以交换模型而无需重写工具调用解析逻辑。
3. 工具层
工具是代理与世界的接口。一个最小的编码代理需要四个工具:
| 工具 | 功能 |
|---|---|
read_file |
返回给定路径的文件内容 |
write_file |
将字符串写入文件路径 |
run_command |
执行shell命令并返回stdout和stderr |
list_directory |
列出路径下的文件和目录 |
每个工具必须返回完整的输出。截断的结果或静默失败会破坏代理对代码库的模型,并导致后续错误的累积。run_command工具尤其需要捕获stdout和stderr——代理通常从错误输出中学到的东西比从成功输出中学到的更多。
一些代理添加了search_files工具用于跨代码库的grep式查找,或者fetch_url工具用于读取外部文档。正确的工具集取决于任务领域。对于纯代码工作,上述四个工具涵盖了大多数情况。
4. 沙箱
沙箱是一个完全隔离的Linux环境,代理的命令实际上在其中运行。这很重要有两个原因。
首先,安全性。代理从用户提示、检索到的文档和推断出的模式生成代码。即使是善意的代理也可能产生删除文件、打开网络连接或消耗无限资源的代码。沙箱将任何损害限制在隔离环境内。
其次,状态性。一个良好的沙箱在会话内的工具调用之间保留文件系统状态。如果代理在第2步创建了一个文件,它需要在第8步仍然存在。无状态容器方法——每个命令都在一个全新环境中运行——不适用于真实的编码任务。
Novita Agent Sandbox 基于Firecracker微虚拟机构建,提供比标准容器更强的内核级隔离。会话可以运行长达24小时,文件系统状态在命令之间持久化,冷启动时间低于200毫秒。这足够快,等待沙箱启动不会中断交互式工作流。
执行循环如何工作
一个具体的例子使循环更容易理解。假设任务是:“为/login端点添加速率限制。”
- 规划 —— 模型读取任务并识别需要什么:找到登录路由,理解当前处理程序,添加速率限制中间件,通过测试运行验证。
- 观察 —— 代理调用
list_directory查找路由文件,然后对登录处理程序调用read_file。文件内容被附加到模型上下文。 - 决策 —— 模型推理当前代码并决定做什么:安装速率限制库,修改处理程序,添加测试。
- 行动 —— 代理调用
run_command("pip install slowapi"),然后使用修改后的处理程序调用write_file,再调用run_command("pytest tests/test_login.py")。 - 再次观察 —— 测试输出反馈到上下文。如果测试失败,模型读取回溯,识别错误,并写入修正后的文件。
- 完成 —— 当测试通过且模型没有待处理步骤时,它返回最终摘要。
这个循环在单个会话内运行。上下文窗口是代理的工作记忆——每次文件读取、每次命令输出、每次工具调用都累积在那里。这就是为什么上下文长度对编码代理如此重要:一个真实的代码重构任务在第15步时很容易填满100K token。 了解代理如何以不同于单轮聊天的方式给推理提供商带来压力。
使用Novita构建编码代理
以下示例将Novita的LLM API和Agent Sandbox连接在一起。它使用指向Novita端点的Python OpenAI SDK——Novita的模型使用与OpenAI API相同的函数调用接口,因此集成不需要自定义解析。
import os
import json
from openai import OpenAI
from novita_sandbox.code_interpreter import Sandbox
client = OpenAI(
base_url="https://api.novita.ai/openai",
api_key=os.environ["NOVITA_API_KEY"],
)
sandbox = Sandbox.create(timeout=1800)
def read_file(path: str) -> str:
try:
return sandbox.files.read(path)
except Exception as e:
return f"Error: {e}"
def write_file(path: str, content: str) -> str:
try:
sandbox.files.write(path, content)
return f"Written to {path}"
except Exception as e:
return f"Error: {e}"
def run_command(cmd: str) -> str:
try:
result = sandbox.commands.run(cmd)
return str(result)
except Exception as e:
return f"Error: {e}"
tools = [
{
"type": "function",
"function": {
"name": "read_file",
"description": "Read the contents of a file",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
},
},
},
{
"type": "function",
"function": {
"name": "write_file",
"description": "Write content to a file",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"content": {"type": "string"},
},
"required": ["path", "content"],
},
},
},
{
"type": "function",
"function": {
"name": "run_command",
"description": "Run a shell command in the sandbox and return output",
"parameters": {
"type": "object",
"properties": {"cmd": {"type": "string"}},
"required": ["cmd"],
},
},
},
]
dispatch = {
"read_file": read_file,
"write_file": write_file,
"run_command": run_command,
}
def run_agent(task: str, model: str) -> str:
messages = [
{
"role": "system",
"content": (
"You are a coding agent with access to a Linux sandbox. "
"Complete tasks by calling tools. When done, return a plain-text summary."
),
},
{"role": "user", "content": task},
]
while True:
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
tool_choice="auto",
)
msg = response.choices[0].message
messages.append(msg)
if not msg.tool_calls:
return msg.content
for call in msg.tool_calls:
fn = dispatch[call.function.name]
args = json.loads(call.function.arguments)
result = fn(**args)
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": result,
}
)
# Replace <model-id> with a function-calling model from novita.ai/docs
result = run_agent(
task="Write a Python script that counts words in a text file and run it on a sample input",
model="<model-id>",
)
print(result)
sandbox.kill()
关于这个实现需要注意几点:
while True循环运行直到模型返回没有工具调用的消息——这是代理认为任务完成的信号。- 工具结果作为
role: tool条目附加到messages中。这就是跨步骤构建共享上下文的方式。 sandbox.kill()释放计算资源。在会话结束时务必调用。
有关支持的函数调用模型ID,请查看Novita函数调用文档。如需包含Gradio UI的更完整教程,请参阅使用Novita的Agent Sandbox构建编码代理。
为编码代理选择合适的LLM
HumanEval和SWE-bench衡量单轮代码生成。代理工作负载则不同——真正导致生产级编码代理出问题的是工具调用格式失败。一个在基准测试中得分很高但偶尔在复杂的多轮会话中返回格式错误JSON的模型,会以难以调试的方式失败。
AI编码代理的实际评估标准:
- 工具调用可靠性 —— 模型在20步以上的会话中返回格式良好的工具调用的一致性如何?
- 上下文保持 —— 模型是否正确引用了40步前读取的文件?
- 指令遵循 —— 代理是否专注于任务,还是开始修改无关的文件?
- 代码正确性 —— 生成的代码是否实际运行,还是需要多次修正循环?
运行一组具有代表性的真实编码任务并衡量任务完成率,比任何公开基准测试都更有参考价值。从你自己的代码库中挑选20-30个任务,在候选模型上运行它们,并统计无需人工干预即可完成的数量。
推理定价在代理规模下迅速累积。一个会话可能消耗200K-500K token(所有轮次)。当你每天运行数百个代理会话时,提供提示缓存和有竞争力的每token价格的提供商会显著改变经济性。
开源模型作为经济高效的选择
闭源前沿模型在编码基准测试中一直领先,但与顶级开源模型之间的差距已显著缩小。像DeepSeek V3和Qwen3这样的模型现在在代码生成和工具使用任务上具有竞争力——而且由于它们通过OpenAI兼容的API提供,切换只需更改model参数一行代码。
两者都可通过Novita的LLM API获得。你获得相同的端点、相同的函数调用接口和相同的Agent Sandbox集成——无需自己管理GPU基础设施。这很重要,因为GPU编排、批处理和可靠性工程不简单;将它们委托给托管的API,你可以专注于代理逻辑。
为什么这对编码代理特别重要:每会话token成本比许可费用更能决定代理工作负载的经济性。一个每天运行200个编码代理会话的团队,如果使用开源模型达到可比较的任务完成率,可以大幅减少推理支出,而无需更改他们的集成代码。
实际测试:用你的目标模型运行50个代表性编码任务,衡量工具调用成功率和任务完成率,然后与每会话成本进行比较。基准测试数字无法回答这个问题——你的实际工作负载会。
常见问题
编码代理和代码助手有什么区别?
代码助手(如GitHub Copilot的内联建议)生成补全然后停止。编码代理执行代码,读取输出,并迭代。定义性特征是执行循环:读取、决策、行动、观察、重复。请参阅CLI与IDE编码代理以了解不同代理形式因素如何使用此循环的比较。
构建编码代理需要沙箱吗?
是的,如果代理将运行从用户输入或外部来源生成的代码。没有隔离,有缺陷的代码生成可能损坏主机文件系统或消耗无限资源。即使对于仅内部使用的场景,沙箱也能防止失控进程影响主机。容器提供基本隔离;像Novita这样基于微虚拟机的沙箱为多租户或安全敏感工作负载提供更强的内核级分离。
编码代理可以在没有互联网访问的情况下工作吗?
对于大多数纯编码任务,是的。文件读/写和本地命令执行覆盖了大多数工作流。在沙箱内限制出站实际上是一个好的默认设置——它防止生成的代码发出意外的外部请求,并简化你的威胁模型。
什么决定了给定任务的最佳编码代理?
工具调用可靠性和在你实际工作负载上的任务完成率。公开基准测试排名是筛选模型的起点,而不是最终答案。运行你的代表性任务,衡量完成率,并考虑每会话token成本。对于进行轻量级重构的小型初创公司来说,最好的编码代理可能与运行大规模自动化PR审查的企业团队的最佳选择有很大不同。
编码代理会话可以运行多长时间?
这取决于沙箱提供商。Novita Agent Sandbox支持长达24小时的会话,文件系统状态在命令之间持久化,这甚至涵盖了扩展的代码重构或迁移任务,而无需在代理代码中实现检查点/恢复逻辑。
