news 2026/8/30 17:31:02

AI编程Agent工程实践:从Devin到最小实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI编程Agent工程实践:从Devin到最小实现

AI 编程 Agent 赛道近来最受关注的公司是 Cognition——它打造的 Devin 被描述为“AI 软件工程师”。一条公开融资消息称,该公司正在洽谈新一轮融资,估值中枢可能达到 400 亿美元($40B)量级。融资数字本身会随谈判变化,正式口径应以官方公告为准,但这件事给工程界带来一个值得讨论的信号:AI 编程不再只是编辑器里的自动补全,而是变成能自己读 Issue、改代码、跑测试、提交变更的 Agent 系统。本文从 Cognition 与 Devin 的能力定位出发,不评价资本故事,只拆解 AI 编程 Agent 的任务闭环、控制循环、工具调用和工程落地方式,并给出一个最小可运行的 Agent 骨架,帮助想接入这类能力的团队了解该准备什么、会踩哪些坑。

1. 先建立共识:AI 编程 Agent 解决的不是“帮你补代码”

1.1 从代码补全到任务执行,差了一个执行闭环

很多团队一开始会把 AI 编程 Agent 和编辑器里的 AI 代码补全助手当成同一类产品。实际上它们解决的是两个层次的问题。

代码补全工具的核心能力是“根据当前上下文预测下一段代码”。它假设人类仍然负责理解需求、编写正确流程、运行测试、修复失败,AI 只负责减少重复输入。它的边界很清晰:模型不执行命令,不检查运行结果,也不对最终改动是否符合预期负责。

AI 编程 Agent 的核心能力则是“把一个软件任务从头到尾做出来”。它会先读取 Issue 描述,浏览仓库结构,定位相关文件,修改代码,运行测试,看到测试失败后又继续分析日志、调整实现,直到任务完成或明确报告失败。

两者之间最关键的差距不是参数规模,而是执行闭环。Agent 能看见自己动作的结果,再根据结果决定下一步动作。这个“行动——反馈——再行动”的循环,才是它被称为 Agent 的根本原因。

用一句话概括:补全工具给你一段函数,Agent 给你一个跑通验证的变更。

1.2 Devin 式任务流可以拆成五步

根据 Cognition 对外公开的产品演示和 Devin 的产品定位,一个 AI 编程 Agent 处理真实软件任务时,路径大致可以拆成五个环节:

  1. 理解任务。先读 Issue 描述、仓库文档、相关模块代码,搞清楚“要改什么”和“为什么改”。
  2. 拆解计划。把大任务拆成子任务,决定先看哪个文件、先跑哪条命令,而不是直接开始写代码。
  3. 执行动作。调用工具完成实际操作,包括文件读取、关键词搜索、运行 shell 命令、编辑文件。
  4. 验证结果。运行测试、lint、类型检查,读取输出,判断改动是否正确。
  5. 汇报交付。生成 diff,总结自己改了哪些文件、是否通过验证,以及遗留问题。

这五步不是线性走完的。真实场景中,Agent 会在“执行动作”和“验证结果”之间反复循环:测试失败了,就回到代码修改阶段;日志看不懂,就再搜索相关配置。整个流程更像一个带反馈的控制系统。

1.3 为什么资本关注这类产品,而工程界更应该关注什么

资本关注 AI 编程 Agent,是因为它代表软件研发自动化方向:如果 Agent 能稳定完成初级工程师的一部分重复工作,软件团队的产出模型会被重估。这也是 Cognition 这类公司估值快速上升的背景之一。

工程界关注的重点应该不同。团队真正要回答的问题包括:Agent 能否理解项目的历史约定?能否在受限环境内安全执行命令?能否不破坏现有测试?如何评测它是否真的完成了任务?如果它失败了,日志能不能支撑排查?

所以“估值多少”不决定“能不能用”。融资热度说明市场在押注方向,但具体到代码仓库里,一个 Agent 是否值得接入,取决于它的控制循环、工具权限、上下文管理和评测机制是否可靠。

2. Agent 的核心机制:控制循环、工具调用和沙箱

2.1 Agent 的本质是模型外套一个循环

把视觉上的“智能”剥开,AI 编程 Agent 的骨架其实很简单:一个 LLM 负责推理,一个循环负责反复调用它,一组工具负责执行动作。伪代码如下:

while not done: response = llm(messages, tools) if response 没有请求调用工具: done = true else: for tool_call in response.tool_calls: result = execute(tool_call) messages.append(tool_result)

这个循环必须存在,是因为模型本身无法执行代码。它只能根据当前对话历史输出“我想运行 pytest”这样的意图。真正去运行 pytest、读取输出、把输出塞回上下文,必须由代码完成。

这里有一个常见误解:很多人以为把大段日志直接返回给模型,模型就能表现更好。实际上,工具结果会占用上下文窗口,如果输出太乱、太长,模型反而会丢失重点。好的 Agent 骨架不仅要执行工具,还要负责控制信息的质量和长度。

2.2 工具调用:把自然语言意图转成结构化命令

为了让模型可以操作外部环境,通常使用 Function Calling(也叫 Tool Calling)机制。模型不再直接输出一段自由文本,而是输出一个结构化的 JSON,描述要调用的函数名和参数。

例如模型可能输出:

{ "name": "run_command", "arguments": { "command": "pytest tests/test_demo.py" } }

代码拿到这个 JSON 后,会先校验函数名是否在白名单里,再解析参数,最后执行命令。这种设计的好处是:

  • 模型输出与代码执行解耦。
  • 调用方可以拦截、记录、限制危险命令。
  • 审计日志能清楚记录模型每一步想做什么。
  • 多模型可以复用同一套工具执行层,只需保证模型支持 Function Calling。

最小工具集建议只保留四个:read_filewrite_filelist_filesrun_command。工具太多会加重模型的决策负担,经常出现选错工具、参数拼错的问题。

2.3 沙箱环境为什么不可省略

让 Agent 在不加限制的本地环境里执行 shell 命令是一件危险的事。它可能误删文件、安装不兼容依赖、改写全局配置,甚至执行不可逆操作。由于模型是概率输出,任何一条命令都可能出错。

沙箱的作用是限制“破坏半径”。常见做法包括:

  • 使用 Docker 容器,任务结束后直接销毁。
  • 使用临时目录作为工作区,禁止访问目录之外的文件。
  • 在 CI 环境中运行,每次任务从干净镜像重新构建。
  • 为 Agent 创建独立系统用户,限制权限。

说白了,Agent 是在“猜测”下一步该做什么,它不需要被信任,环境需要被设计成“即使猜错了也坏不了大事”。

2.4 SWE-bench 类评测能说明什么、不能说明什么

SWE-bench 是当前讨论 AI 编程 Agent 时绕不开的评测基准。它从真实 GitHub 仓库中提取 Issue、代码和测试补丁,要求模型根据 Issue 生成补丁,再通过隐藏测试来判断是否解决。

这类评测的价值在于:它比“让模型写一个排序算法”更接近真实工程。它要求模型理解已有代码库、定位问题、修改正确位置,并且最终要通过测试。

但它不能说明全部问题。真实软件任务还包括代码审查、跨仓库依赖、历史上下文、风格兼容、测试不充分的场景。模型在 SWE-bench 上拿高分,不代表它能独立完成一个生产级 Pull Request。

落地建议是:不要只信公共基准。团队应该把自己仓库过去 20 个真实合并的 PR 做成回归集,去掉答案后定期跑一次,看 Agent 在自己业务场景下的真实趋势。

3. 最小骨架:一个能读 Issue、改文件、跑测试的编程 Agent

到这里进入实践环节。下面实现一个最小 Agent 骨架。它不追求产品化,目的是让你看清“控制循环 + 工具调用 + 沙箱”是怎么组合在一起的。

示例使用 OpenAI-compatible 接口,因为很多模型服务都提供兼容端点,切换成本低。实际落地时,要根据自己的模型服务调整base_urlMODEL_NAME

3.1 准备环境

建议使用 Python 3.9 或更高版本。先创建项目目录和虚拟环境:

mkdir agent-demo && cd agent-demo python -m venv venv source venv/bin/activate pip install openai python-dotenv

然后创建.env文件,写入模型服务信息:

API_KEY=your-api-key API_BASE=https://your-openai-compatible-endpoint MODEL_NAME=gpt-4o-mini

这里的API_BASE取决于你使用的服务。本地部署的模型服务如果兼容 OpenAI 接口,也可以直接填本地地址。出于安全考虑,不要把这个文件提交到 Git。

3.2 项目结构

agent-demo/ ├── .env ├── requirements.txt ├── demo_agent.py ├── issue.txt └── workspace/

workspace是 Agent 的沙箱目录。所有命令都限制在这个目录里执行,Agent 只能修改它。

3.3 核心实现:控制循环

新建demo_agent.py

import os import json import subprocess from openai import OpenAI from dotenv import load_dotenv load_dotenv() client = OpenAI( api_key=os.getenv("API_KEY"), base_url=os.getenv("API_BASE"), ) MODEL = os.getenv("MODEL_NAME", "gpt-4o-mini") WORKSPACE = os.path.abspath("workspace") MAX_ITERATIONS = 8 TOOLS = [ { "type": "function", "function": { "name": "run_command", "description": "在项目工作区中执行 shell 命令,适合运行测试、查看文件等", "parameters": { "type": "object", "properties": { "command": { "type": "string", "description": "要执行的 shell 命令" } }, "required": ["command"] } } } ] def execute_command(command: str) -> str: try: result = subprocess.run( command, shell=True, cwd=WORKSPACE, capture_output=True, text=True, timeout=30, ) return ( f"exit_code={result.returncode}\n" f"stdout:\n{result.stdout}\n" f"stderr:\n{result.stderr}" ) except subprocess.TimeoutExpired: return "command timed out after 30s" def run_agent(issue_text: str): messages = [ { "role": "system", "content": ( "你是运行在沙箱中的编程 Agent。" "你的任务是完成用户给出的软件任务。" "只能通过 run_command 工具查看文件和执行命令。" "不要删除重要文件。执行有限次数后如果仍无法完成,请说明原因。" ), }, { "role": "user", "content": f"工作目录:{WORKSPACE}\n任务:\n{issue_text}", }, ] for step in range(MAX_ITERATIONS): response = client.chat.completions.create( model=MODEL, messages=messages, tools=TOOLS, tool_choice="auto", temperature=0.2, ) message = response.choices[0].message messages.append(message) if not message.tool_calls: print("最终回复:", message.content) return for tool_call in message.tool_calls: if tool_call.function.name != "run_command": continue args = json.loads(tool_call.function.arguments or "{}") command = args.get("command", "") print(f"step {step}: 执行 {command}") result_text = execute_command(command) messages.append( { "role": "tool", "tool_call_id": tool_call.id, "content": result_text, } ) print("达到最大迭代次数,任务未在约束内完成。") if __name__ == "__main__": issue = open("issue.txt", encoding="utf-8").read() run_agent(issue)

关键点有三个。

第一,tool_choice="auto"让模型自己决定是调用工具还是直接输出最终回复。测试时可以把值改成{"type": "function", "function": {"name": "run_command"}},强制模型每一步都调用工具,便于观察循环是否正常。

第二,temperature=0.2。编程任务追求确定性,过高的随机性会让模型输出不稳定命令。

第三,工具消息必须带上tool_call_id,否则接口会报错。这是 OpenAI-compatible Function Calling 的硬性要求。

3.4 准备一个最小任务

issue.txt中写一个可验证的任务:

在 workspace 中创建 greet.py,包含函数 greet(name: str) -> str,返回 "Hello, <name>!"。 再创建 test_greet.py,使用 pytest 验证 greet("world") 返回 "Hello, world!"。 最后运行 pytest,确保测试通过。

workspace目录保持为空,让 Agent 从零开始创建文件。

3.5 运行验证

执行:

python demo_agent.py

正常运行时,控制台会输出类似下面的日志:

step 0: 执行 ls -la step 1: 执行 cat > greet.py << 'EOF' ... step 2: 执行 cat > test_greet.py << 'EOF' ... step 3: 执行 pytest -q 最终回复: 任务完成。greet.py 和 test_greet.py 已创建,pytest 通过。

如果模型不支持 Function Calling,它可能不会生成tool_calls,而是直接输出“我应该先创建文件”这类叙述。此时需要换成支持 Function Calling 的模型,或调整提示词。

这个示例能跑通之后,可以把run_command工具替换成更安全的内部 API,比如只允许执行白名单命令的工具。这样才能往生产环境方向走。

4. 参数、权限和生产部署:从能跑到敢上线

4.1 影响 Agent 行为的关键参数

同一个 Agent 骨架,换一组参数,行为会截然不同。下面是落地时必须关注的参数。

参数含义初始推荐值调大影响调小影响
temperature输出随机性0 到 0.3更灵活但代码不稳定更稳定但容易机械重复
max_iterations最大循环次数5 到 15完成率更高但成本增大成本低但可能半途而废
timeout单条命令超时30 到 120 秒容忍慢测试更快发现问题但易误伤
max_tokens单次模型输出上限1000 到 2000支持长工具调用参数输出可能被截断
context 长度消息历史加工具输出按模型上限能保留更多上下文早期信息易被遗忘

这些数值不是精确标准。模型版本、任务复杂度、仓库大小都会影响最优值。建议先用小任务调试,再扩大到真实 Issue。

4.2 学习环境与生产环境的差异

本地演示重在跑通循环。生产环境则要额外考虑权限、审计、回滚和网络隔离。

场景Agent 权限文件系统外部网络审计要求
本地演示可执行任意 shell 命令临时目录不限制
CI 流水线只允许构建和测试命令每次重建工作区依赖源白名单
生产操作只读代码加审批制写操作权限边界严格禁止或白名单必须审计

不要把本地演示的 Agent 直接部署到生产服务器上。本地环境为了调试方便,允许了shell=True,这在生产环境是不可接受的。

4.3 权限模型:最小授权和审计

一个稳妥的做法是把 Agent 当作“只能产生变更”的角色,而不是“直接执行变更”的角色。推荐流程如下:

  1. Agent 在沙箱中读取代码、编写补丁、运行测试。
  2. Agent 生成 diff 或提交 Pull Request。
  3. 人在 Pull Request Review 阶段审查改动。
  4. 只有人工批准后,CI 才能合并并部署。

这种模式既保留 Agent 的自动化价值,又把最终风险控制权交给人。如果一定要让 Agent 直接执行写操作,至少要做到:

  • 命令白名单:只允许pytestnpm testgo testgit diff等安全命令。
  • 禁止危险命令:拒绝rm -rfDROP TABLEsudo、网络下载脚本后直接执行。
  • 操作日志:记录每次工具调用的参数、工作目录、耗时和结果摘要。
  • 操作回滚:代码改动必须可回滚,环境销毁后必须能重建。

4.4 成本与并发控制

Agent 的成本不只是每次调用的 token 费用,更重要的是“循环次数 × 每次工具返回”。一条超长的 pytest 输出会塞进下一轮请求,导致 token 消耗非线性增长。

控制成本可以从几个方向入手:

  • 限制MAX_ITERATIONS,防止模型陷入死循环。
  • 截断工具输出,默认只保留最近 2000 到 5000 个字符。
  • 使用长期记忆或摘要,避免历史消息无限膨胀。
  • 启用 Prompt Caching,减少重复前缀的计费。
  • 限制并发 Agent 数量,避免同一时间多个 Agent 同时写一个仓库。

成本监控同样重要。建议每条任务记录:模型名、输入 token、输出 token、迭代次数、耗时、是否成功。这样既能定位高成本任务,也能评估 ROI。

5. 实际接入时最容易踩的五个坑

5.1 Agent 陷入循环,反复执行同一条命令

现象:Agent 不断执行pytestpip install,每次结果相同,但它仍然继续重复。

原因:模型没有看到新的信息,却仍然选择再试一次。缺少循环上限和重复动作检测。

解决方式:设置MAX_ITERATIONS;如果连续两次执行完全相同的命令,直接终止并报告“该动作已重复执行,需要人工介入”。

5.2 修改文件后不重新读取,基于旧代码继续操作

现象:Agent 写入文件后,没有重新读取就继续生成新代码,最终提交的内容和它自己的假设不一致。

原因:很多模型不知道文件写入是否成功,也不知道写入后的完整内容。

解决方式:文件写入工具返回修改后的文件路径、文件长度和关键 diff 片段,让模型基于实际内容继续。

5.3 工具输出太长,上下文爆炸

现象:Agent 跑完测试后,把 10000 行日志全部塞给模型,后续回答质量明显下降。

原因:上下文窗口被无意义日志占满,关键错误信息被淹没。

解决方式:工具层截断输出。测试日志保留最后 50 到 100 行,并格式化出错误摘要,例如“失败用例 2 个:test_login、test_pay”。

5.4 本地可以,部署到线上就失败

现象:Agent 在本地能完成任务,换到 CI 或服务器后经常因为路径、环境变量、依赖版本不同而失败。

原因:开发环境与运行环境不一致。

解决方式:用容器固定环境镜像;Agent 启动时输出系统信息、Python 版本和工作目录;把 Agent 任务放进 CI 的同环境运行。

5.5 公共评测集高分,自建场景不达标

现象:模型在 SWE-bench 等基准上表现很好,到自己仓库里却连最简单的需求都处理不稳定。

原因:公共评测分布和真实业务代码差异较大,真实 Issue 往往包含大量隐式上下文。

解决方式:用自己仓库的已办 Issue 和对应 PR 构建回归集,离线去除答案后定期跑,持续观察趋势。

6. 可复用清单和扩展方向

6.1 接入前检查清单

在正式评估 AI 编程 Agent 之前,先确认这些问题:

  • 模型是否支持 Function Calling 或 Tool Calling。
  • 沙箱是否隔离,Agent 能否访问工作区之外的文件。
  • 是否配置了命令白名单,是否可以拦截危险命令。
  • 是否设置了最大迭代次数和单条命令超时。
  • 工具输出是否有截断策略。
  • 是否记录每次工具调用的审计日志。
  • 是否配置了成本上限,token 超限后能否停止任务。
  • 是否有失败回滚方案,代码变更能否安全撤销。

这条清单可以打印出来,作为 Agent 上线前的强制检查项。

6.2 发布前检查清单

Agent 通过演示不等于可以发布。上线前还要确认:

  • Pull Request 是否必须经过人工 Review。
  • Agent 是否能报告自己“无法完成”而不是静默失败。
  • 测试是否覆盖关键路径,不只是看 Agent 是否结束了循环。
  • 生产环境是否有监控,任务失败能否及时告警。
  • 操作是否可回滚,日志是否完整保存。

6.3 从单 Agent 到多 Agent 协作

如果任务太复杂,单个 Agent 往往既要做规划、又要写代码、又要验证,容易上下文混乱。可以拆成两个角色:

  • 规划 Agent:只负责阅读 Issue、拆解任务、生成执行计划,不执行代码。
  • 执行 Agent:按计划逐步调用工具,修改代码和运行测试。

多 Agent 会带来消息传递、状态同步、成本翻倍等问题,建议先单 Agent 跑通,再考虑拆分。

6.4 面向 Java 生态的扩展

如果项目技术栈是 Java 和 Spring Boot,可以关注 Spring AI。它提供了统一的 ChatClient 接口和 Function Calling 抽象,适合把 Agent 能力嵌入现有业务服务。

但要注意,Spring AI 解决的只是接入层。沙箱、权限、审计、成本控制这些工程问题仍然需要自己设计。

6.5 本地部署模型时的调整

如果要求数据不出域,可以选择本地部署模型。本地部署的推理成本更低、隐私控制更强,但有两个额外问题要处理:

  • 模型必须输出稳定的结构化 tool call。开源模型有时需要写专门的 prompt 模板或约束格式。
  • 上下文长度有限时,工具输出必须压缩得更激进,否则早期任务信息很快被挤掉。

本地部署不是“换个模型地址”这么简单,建议先在小模型上跑通最小骨架,确认工具调用格式没问题,再上真实任务。

融资新闻会继续更新,但 AI 编程 Agent 的工程问题不会因为估值变化而消失。沿着控制循环、工具调用、沙箱、权限、评测和成本这六条线去设计,团队才能真正把演示变成可维护的生产能力。新手可以先运行最小 Agent 骨架,理解消息循环和工具返回机制,再逐步加入日志、权限和回归评测。这条路不长,但每一步都需要用工程标准去验证。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 17:27:07

表单做了五件事,自然语言只保留了一件

开场白先给出观点和背景&#xff1a;自然语言交互被当成“表单杀手”已经有一阵子了。大模型能读懂一句含糊的话&#xff0c;能自动补全字段&#xff0c;能生成 JSON&#xff0c;于是很多产品开始想把表单扔进垃圾桶。这个标题其实是一句很精准的观察&#xff1a;传统表单在完整…

作者头像 李华
网站建设 2026/8/30 17:26:32

基于SpringBoot的中国历史知识学习系统毕业设计项目源码

联系博主 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 …

作者头像 李华
网站建设 2026/8/30 17:26:29

基于SpringBoot的中医药文化科普系统设计与实现毕业设计项目源码

联系博主 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 …

作者头像 李华
网站建设 2026/8/30 17:23:07

用Grok Build构建火星模拟游戏:自然语言驱动的交互原型开发

如果你第一次听说“Grok Build”&#xff0c;可能会以为它又是一个“AI 能写代码”的营销概念。但当你真的用它把一个火星基地从概念变成可点击、可交互的网页游戏时&#xff0c;你会发现&#xff0c;真正有价值的不是“自动生成代码”这个噱头&#xff0c;而是它把 从想法到原…

作者头像 李华
网站建设 2026/8/30 17:19:52

线上问医系统毕业设计实战:Java Web部署与源码解析

这次我们来看一个毕业设计项目&#xff1a;线上问医系统的设计与实现。它不是一个只能跑个 Demo 的小功能&#xff0c;而是一套相对完整的 Java Web 线上问医项目包&#xff0c;标题里已经写明附带源码、文档报告、代码讲解、万字论文和 PPT。这类项目在 CSDN 上很常见&#xf…

作者头像 李华
网站建设 2026/8/30 17:17:47

MCP / A2A / ACP 协议解析-Day31

一、为什么需要 Agent 开放协议 1.1 问题&#xff1a;Agent 生态的"巴别塔困境" 在 2024~2025 年&#xff0c;多 Agent 系统面临一个根本问题&#xff1a;每个框架都有自己的内部通信协议。LangGraph Agent 只能与 LangGraph Agent 对话&#xff0c;AutoGen Agent …

作者头像 李华