关注腾讯云开发者,一手技术干货提前解锁👇
写在最前面的闲聊
很早就开始尝试用AI辅助写代码,当时的感受是:对于一些不需要太多上下文的小需求(最终的产物是代码片段,直接复制粘贴改改输入一般就能用)还是非常好用的;但当用于完整项目中时就一言难尽了,早期我用cursor最常遇到的是AI"小题大作",直接往项目里各种新增脚本,新增大量的代码,有被无语到,以至于使用频率降低到最后退订(当然这里还存在我自己的问题,对于让AI Coding的需求不够具体)
下半年伴随着各种AI编程助手的进一步产品化,Spec Coding的大火,AI Coding成为了AI落地最快最大规模的领域之一,各种Coding Agent被迅速神化。由于带着前期的偏见,迟迟不愿尝试,直到近两个月看到各种大幅提效的案例后重新入坑,不得不说一句:真香!
大量的市场营销和Coding Agent领域新名词、新技术的出现使其变得格外"性感",容易给人一种错觉:它好像会思考、会规划、会自主完成复杂工程任务、10倍程序员、智能的跃迁等。然而,这种拟人化的叙事反而构成了理解其技术边界的认知迷雾。
在帮助了几位开发or产品同学理解、科学使用AI Coding来解决一些问题后,决定写下这篇文章,希望能够帮忙大家理解Coding Agent本质,从而合理预期其能力边界、更有效地使用这些工具,真正做到从从容容、游刃有余(狗头)
不太性感的事实
即使是效果惊艳的Coding Agent的本质其实是一个 while 循环,加上一整套上下文工程,OVER
从产品体验总结规律
为了避免被产品形态、UI 设计和营销语言干扰,我们先做一个极端抽象:忽略工具名字,忽略品牌,只看系统在做什么。不管是 Claude Code、Cursor 还是 Cline,当你连续使用它们一段时间后,会发现一个非常稳定的共同模式:
- 你提出一个目标
- 系统读取一些代码或环境信息
- 模型输出一个"下一步行动"
- 如果这个行动需要访问外部世界,就调用工具
- 工具执行完后,把结果再交还给模型
- 重复以上过程,中途也可以人工干预这个流程会一直持续,直到模型认为"任务已经完成"。这里有一个关键点:系统本身并不知道"任务是否完成"。结束条件并不是程序判断的,而是LLM在某一次输出中,选择不再调用工具。
从工程实现角度看,你可以试着把任何一个 AI Coding Agent 的行为,用伪代码表达出来,几乎都会收敛到如下结构:
while not done:
observation = collect_context()
action = llm(observation)
if action is tool_call:
result = execute(action)
append_to_context(result)
else:
done = True这不是某一个产品的实现方式,而是所有这类系统的必然形态。
原因也很简单:只要系统的"决策逻辑"在模型中,而LLM本身是无状态的,那么你就只能通过不断重放上下文,来模拟"持续思考"。
所谓 Agent,其实就是用循环,模拟连续性。
真正的复杂性在哪里:不在循环,在上下文
如果控制流如此简单,那为什么这些Coding Agent看起来仍然"很聪明"?
答案在于:复杂性被转移了位置。
在传统软件系统中,复杂性可能主要体现在:
而在 AI Coding Agent 中,这些复杂性被大量挤压、甚至直接消失了。取而代之的,是一种新的工程重心:上下文工程(Context Engineering),上下文工程不是一个学术名词,而是一个非常工程化的事实描述:如何决定“这一轮模型调用时,让模型看到什么”。
模型不会记住过去;模型也不会理解系统;模型只会基于当前输入文本,生成下一个输出。
于是,Agent (在使用相同模型情况下)的能力高低,几乎完全取决于以下问题:
同样是while+context:以前做不到,现在能了?
有个强大的LLM是展示Coding Agent能力不可或缺的,以前还没有足够强的LLM的时候,基于while循环的Agent,可能模型会乱来、状态会对不上、很快跑偏;随着LLM的长上下文能力、多步推理能力、可被 prompt 约束成"角色 + 目标 + 工具使用规范"不断提高,现在才“工程可用”。
重新理解那些“听起来很高级”的概念
首先抓住请求LLM时的本质:每次请求其实就是把"System Prompt + 对话历史"作为输入发送给LLM,这输入就是上下文的全部。所有的优化和差异化都体现在如何构建这个上下文。
5.1 MCP连接万物
有些信息对于很多使用者来说都是需要用到的上下文,MCP server的作用其实就是标准化的上下文提供者。听起来像是新一代 Agent 架构,但从工程角度看,它解决的仍然是同一个问题:如何把外部信息,稳定、可控地塞进模型上下文。
5.2 Rules是系统提示词的补充
很多人把 .cursorrules 当作配置文件,但它本质上是系统提示词的硬编码补丁。如果没有规则,模型必须从概率分布中“猜”你想用 TypeScript 还是 JavaScript。 当你在 .cursorrules 里写下“总是使用 Tailwind CSS”时,你实际上是在系统层面对模型的生成树进行了剪枝,减少熵增,防止模型在无限的可能性中跑偏。
5.3 SpecCoding是提示词的重复
“Vibe Coding”最大的问题是:随着 while 循环的进行,早期的对话会被挤出上下文窗口,模型会“遗忘”最初的需求。Spec Coding 通过引入 AGENTS.md 或 spec.md,给 Agent 挂载了一块“只读存储器(ROM)”。无论循环跑了多少轮,Agent 被强制要求在行动前先读取这个文件,从而拥有持久化的上下文锚点。能够一定程度对抗“上下文漂移”(Context Drift),解决长任务中的健忘症。
5.4 SKILLS还是提示词
没有网上吹的那么神(也不是否认它对生态的重要性)!skills就是个markdown文件+tools,本质上进入大模型的也还是个prompt,并不能让你的agent起飞,它的智能来源于你原来的prompt+tools有多智能,并不能真的让agent更加智能。它的优势在于更像是上下文的动态链接库,用完即走,避免一开始就把所有文档塞进上下文撑爆窗口。
5.5 未来新名词也还是提示词
比如这几天有些热度的Smart Forking号称永久记忆的工具,也还是通过一些策略把对话历史存档,然后检索到后贴进了上下文里。毕竟,现阶段的LLM都是无状态的,它给出的回答全部依赖"System Prompt + 对话历史",因此,延伸出再多的新名词在做的事情都是在把一些提示让LLM能够看到。
丢掉幻想,掌控上下文才能用好编程助手
理解了各种花里胡哨的新名词的本质都还是把一些提示喂给LLM后,我们可以来思考下自己拿Coding Agent做的一些事情是不是make sense,这里举两个使用不当的例子:
因此,大家在使用Coding Agent或其他AI时都建议要掌控上下文,才能抓住它的能力边界!祝大家都不要被AI幻觉所欺骗!
Talk is cheap,200行代码实现
为了进一步加强大家的感受,我们来构建一个AI Coding Agent的最小实现
def execute_bash(command: str) -> str:
def read_file(path: str) -> str:
def write_file(path: str, content: str) -> str:
def list_files(path: str = ".") -> str:Agent 没有能力,只有工具。大模型不会“执行代码”,它只会决定要不要调用某个工具。
TOOLS_SCHEMA = [
{
"type": "function",
"function": {
"name": "read_file",
"description": "...",
"parameters": {...}
}
}
]这正是 MCP 的本质:用结构化方式,把工具能力描述进 prompt。没有魔法。
system_prompt = """
你是一个智能编程助手。
工作原则:
1. 修改前先读文件
2. 修改后尽量运行测试
"""这就是:Rules、Best Practices、Coding Guideline,全部都是 system prompt。
while True: # ← 内层循环:Agent 自主执行
# Think: LLM 推理
response = client.chat.completions.create(
messages=messages, # ← 完整的上下文历史
tools=TOOLS_SCHEMA, # ← 可用工具列表
)
# Act: 执行工具
if message.tool_calls:
for tool_call in message.tool_calls:
result = AVAILABLE_FUNCTIONS[func_name](**func_args)
# Observe: 将结果反馈给 Agent
messages.append({
"role": "tool",
"content": result, # ← 执行结果注入 context
})
continue # ← 继续循环
else:
break # ← 没有工具调用,任务完成这就是 ReAct 模式(Reasoning + Acting):
这就是 AI Coding Agent 的完整控制流,无论是Claude Code、Cline、Cursor都万变不离其中。但是对于对AI来说:只有「下一轮 prompt」是什么
import os
import json
import subprocess
from openai import OpenAI
# =============================================================================
# 1. 定义工具 (Define Tools)
# =============================================================================
def execute_bash(command: str) -> str:
"""执行 Bash 命令"""
print(f" > [执行命令] \033[93m{command}\033[0m")
print(f" > [执行中] {command}")
result = subprocess.run(
command, shell=True, capture_output=True, text=True, timeout=30
)
output = result.stdout + result.stderr
if output:
print(f" > [输出]\n{output.rstrip()}")
# 截断过长的输出
if len(output) > 2000:
output = output[:2000] + "\n... (输出过长已截断)"
return output.strip() or "(无输出)"
def read_file(path: str) -> str:
"""读取文件内容"""
print(f" > [读取文件] {path}")
if not os.path.exists(path):
return f"文件不存在: {path}"
with open(path, "r", encoding="utf-8") as f:
content = f.read()
# 给代码加上行号,方便 Agent 指定修改位置
lines = content.splitlines()
numbered_lines = [f"{i+1:4d} | {line}" for i, line in enumerate(lines)]
return "\n".join(numbered_lines)
def write_file(path: str, content: str) -> str:
"""写入文件内容"""
print(f" > [写入文件] {path}")
os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return f"成功写入 {len(content)} 字节到 {path}"
def list_files(path: str = ".") -> str:
"""列出当前目录下的文件结构"""
print(f" > [列出文件] {path}")
# 简单的递归列出文件
file_list = []
for root, dirs, files in os.walk(path):
for file in files:
if not file.startswith('.'):
rel_path = os.path.relpath(os.path.join(root, file), path)
file_list.append(rel_path)
result = "\n".join(file_list[:50]) # 限制返回数量
print(f" > 包含文件:\n{result}")
return result
# =============================================================================
# 2. 定义工具描述 (Define Tool Schemas)
# =============================================================================
TOOLS_SCHEMA = [
{
"type": "function",
"function": {
"name": "execute_bash",
"description": "执行 shell 命令。用于运行测试、安装依赖或文件操作。",
"parameters": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "要执行的 bash 命令"}
},
"required": ["command"],
},
},
},
{
"type": "function",
"function": {
"name": "read_file",
"description": "读取文件内容。内容会包含行号。",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"}
},
"required": ["path"],
},
},
},
{
"type": "function",
"function": {
"name": "write_file",
"description": "创建或覆盖文件内容。",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"},
"content": {"type": "string", "description": "要写入的完整内容"},
},
"required": ["path", "content"],
},
},
},
{
"type": "function",
"function": {
"name": "list_files",
"description": "列出当前项目的文件结构。",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "目录路径,默认为 '.'"}
},
"required": [],
},
},
},
]
AVAILABLE_FUNCTIONS = {
"execute_bash": execute_bash,
"read_file": read_file,
"write_file": write_file,
"list_files": list_files,
}
# =============================================================================
# 3. Agent 主循环 (The Agent Loop)
# =============================================================================
def run_toy_claude():
client = OpenAI()
# 系统提示词:设定人设
system_prompt = """
你是一个智能编程助手。
你的目标是帮助用户编写、调试和理解代码。
工作原则:
1. 在修改代码前,先使用 list_files 查看项目结构,或使用 read_file 读取相关文件。
2. 每次修改文件后,尽量运行代码或测试来验证修改(使用 execute_bash)。
3. 保持回答简洁,专注于解决问题。
"""
messages = [{"role": "system", "content": system_prompt}]
print("🤖 Toy Claude Code 已启动 (输入 'exit' 退出)")
print("--------------------------------------------")
# 外层循环:处理用户输入 (REPL)
while True:
try:
user_input = input("\n👤 你: ")
if user_input.lower() in ['exit', 'quit']:
print("再见!")
break
messages.append({"role": "user", "content": user_input})
# 内层循环:Agent 自主执行 (Think -> Act -> Observe)
# 只要 Agent 还在调用工具,就一直在这个循环里
while True:
response = client.chat.completions.create(
model="gpt-5-codex",
messages=messages,
tools=TOOLS_SCHEMA,
)
message = response.choices[0].message
messages.append(message)
if message.tool_calls:
print(f"\n🤖 思考: {message.content or '(准备调用工具...)'}")
for tool_call in message.tool_calls:
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
if func_name in AVAILABLE_FUNCTIONS:
# 执行工具
result = AVAILABLE_FUNCTIONS[func_name](**func_args)
else:
result = f"错误: 未知工具 {func_name}"
# 将结果反馈给 Agent
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"name": func_name,
"content": result,
})
else:
# 没有工具调用,说明是最终回复
print(f"\n🤖 回复:\n{message.content}")
break
except KeyboardInterrupt:
print("\n操作已取消。")
break
except Exception as e:
print(f"\n发生错误: {e}")
break
if __name__ == "__main__":
run_toy_claude()我们再来看一下这样一个简单的工具是不是真的能跑起来

假设当前目录下有个脚本buggy_math.py
def add(a, b):
return a - b # 错误:应该是加法
print(f"1 + 1 = {add(1, 1)}")当前目录下的 buggy_math.py 运行结果不对,请帮我修复它。

Finally
理解并实现一个Coding Agent并不难,但是把这样一个产品做好却是很艰难的。致敬各种AI Coding工具包括公司内的CodeBuddy(让白嫖了好多外部高级模型的token)。
每一代工具,都会逼迫工程师重新理解自己的角色。AI Coding Agent 并没有消灭软件工程,它只是把一部分执行成本,压缩到了极低。
在这个过程中,最重要的能力迁移发生在于:从“如何写代码”,转向“如何设计约束、上下文和验证机制”。
理解 Agent 的本质是为了避免在幻觉中做错误决策。当你真正看清它只是一个 while 循环 + 上下文工程时,你反而会更清楚—— 哪些事情,仍然必须由人来完成。
-End-
原创作者|陈铭豪
感谢你读到这里,不如关注一下?👇

你对本文内容有哪些看法?同意、反对、困惑的地方是?欢迎留言,我们将邀请作者针对性回复你的评论,欢迎评论留言补充。我们将选取1则优质的评论,送出腾讯云定制文件袋套装1个(见下图)。3月10日中午12点开奖。

扫码领取腾讯云开发者专属服务器代金券!





