ByteNoteByteNote
实战三:写一个代码助手 Agent
字

字节笔记本

2026年10月24日 · 约 11 分钟读完

实战三:写一个代码助手 Agent

API中转
¥120

Skills

本文是《从 0 写 Agent》系列第 47 章。这个系列基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版),用 Go 从零实现一个生产级 Agent 运行时;本章讲「实战三:代码助手 Agent」。

Claude Code、Codex 都是"代码助手 Agent",读代码、改代码、跑测试、提交。这一章用 agents-go 从 0 写一个迷你版代码助手,理解这类 Agent 的设计。


需求

一个代码助手 Agent 要能:

  1. 读文件,理解项目代码
  2. 改文件,修改代码
  3. 跑命令,执行测试、构建
  4. 多轮修改,改完跑测试,失败再改,循环到通过
  5. 安全,不在不该改的地方乱改

安全

工具定义

go
// 读文件
type readFileArgs struct {
    Path string `json:"path" jsonschema:"description=文件路径(相对于项目根)"`
}
readFile := agents.NewFunctionTool("read_file", "读取文件内容", func(ctx, tc, args readFileArgs) (string, error) {
    // 用 os.Root 限制在项目目录内(第 31 章)
    data, err := projectRoot.ReadFile(args.Path)
    return string(data), err
})

// 改文件(str_replace 风格,像 Codex)
type editFileArgs struct {
    Path       string `json:"path"`
    OldString  string `json:"old_string" jsonschema:"description=要替换的原文"`
    NewString  string `json:"new_string" jsonschema:"description=替换成的新文"`
}
editFile := agents.NewFunctionTool("edit_file", "精确替换文件内容", func(ctx, tc, args editFileArgs) (string, error) {
    content, _ := projectRoot.ReadFile(args.Path)
    newContent := strings.Replace(content, args.OldString, args.NewString, 1)
    if newContent == content {
        return "未找到要替换的内容", nil  // 没替换成功,回喂模型
    }
    projectRoot.WriteFile(args.Path, []byte(newContent), 0644)
    return "修改成功", nil
})

// 跑命令(测试、构建)
type runCmdArgs struct {
    Command string   `json:"command" jsonschema:"description=要执行的命令"`
}
runCmd := agents.NewFunctionTool("run_command", "执行 shell 命令", func(ctx, tc, args runCmdArgs) (string, error) {
    // 用 Sandbox 执行(第 32 章),安全隔离
    result, err := sandbox.Exec(ctx, sandbox.ExecRequest{
        Cmd:     strings.Fields(args.Command),
        Timeout: 60 * time.Second,
    })
    if err != nil { return "", err }
    // 代码错误(exit≠0)回喂模型,让它看报错修代码
    return fmt.Sprintf("exit=%d\nstdout:\n%s\nstderr:\n%s",
        result.ExitCode, result.Stdout, result.Stderr), nil
})

关键设计:edit_file 用 str_replace 不是全量重写

注意 edit_file 是"精确替换"(str_replace),不是"全量重写文件"。为什么?

  • 全量重写,模型要输出整个文件内容。大文件(1000 行)token 爆炸,且容易丢内容。
  • str_replace,模型只输出"改了哪几行"。token 省、改动精确、容易 review。

这是 Claude Code/Codex 的设计选择,agents-go 也用(README 里的 editor.NewTools)。 第 4 章讲的工具错误回喂在这里很有用,"未找到要替换的内容"回喂,模型知道 old_string 不对,下轮调整。


关键设计:run_command 用 Sandbox

执行命令用 Sandbox(第 32 章),不直接在本机跑。因为模型生成的命令可能危险(rm -rf、curl 恶意 URL)。Sandbox 隔离,即使命令恶意,不伤宿主。

代码错误(exit≠0)回喂模型(第 32 章的错误语义反转),模型看到测试失败的 stderr,知道哪里错了,下轮修代码。


Agent 定义

go
codeAgent := &agents.Agent{
    Name: "代码助手",
    Instructions: agents.StaticInstructions(`你是代码助手。
- 修改前先 read_file 理解现有代码
- 用 edit_file 做精确修改(不要全量重写)
- 改完用 run_command 跑测试
- 测试失败就看报错,继续修
- 不要改不相关的文件`),
    Model: "openai/gpt-4o",
    Tools: []agents.Tool{readFile, editFile, runCmd},
}

典型的修改-测试循环

text
用户:"修复 src/auth.go 里的空指针 bug"

Agent: read_file("src/auth.go")
  → 看到代码,发现第 42 行 user.Profile 可能为 nil

Agent: edit_file("src/auth.go",
    old: "return user.Profile.Name",
    new: "if user.Profile != nil { return user.Profile.Name }\nreturn \"\"")
  → "修改成功"

Agent: run_command("go test ./src/auth/...")
  → exit=1, stderr: "undefined: Profile"

Agent Thought: 哦,应该是 user.Profile 改成 userProfile 变量名
Agent: edit_file(...)  // 修正
Agent: run_command("go test ./src/auth/...")
  → exit=0, "PASS"

Agent: "已修复空指针 bug,测试通过。"

这就是 ReAct(第 41 章),边查(读代码)边改(edit)边验证(跑测试)。 每轮都比上轮更接近"测试通过"。


安全设计

代码助手是最危险的 Agent 之一,它能改文件、跑命令。安全措施:

  1. os.Root 限制目录,read_file/edit_file 只能操作项目目录内(第 31 章)
  2. Sandbox 执行命令,run_command 在沙箱里跑(第 32 章)
  3. edit_file 的 str_replace,防止"全量重写覆盖整个文件"
  4. 指令约束,"不要改不相关的文件"
  5. 可选 HITL,关键修改前审批(editFile.NeedsApproval = true)

纵深防御,多层保护,任何一层被突破还有其他层。 这是第 33 章安全哲学的应用。


和 Claude Code/Codex 的对比

这个迷你代码助手和真正的 Claude Code/Codex 的差距:

能力迷你版Claude Code
基本循环✅✅
文件操作✅✅
命令执行✅✅
项目理解(AGENTS.md)❌✅
多 worktree 并行❌✅
流式输出✅(SDK 有)✅
MCP 集成✅(SDK 有)✅
Skills✅(SDK 有)✅

agents-go SDK 提供了基础设施,差距主要在"工程化打磨"和"产品体验"。 但核心原理,ReAct 循环 + 工具 + 安全,完全一致。


小结

代码助手 Agent 用到的核心组件:

  • 工具(read/edit/run)→ 第 3-4 章
  • os.Root 安全读改 → 第 31 章
  • Sandbox 安全执行 → 第 32 章
  • 错误回喂(测试失败→修代码)→ 第 32 章

它最能体现 ReAct 的价值,"边查边改边验证",循环逼近"测试通过"。


下一篇

第 48 章,实战四:多 Agent 客服中心。单个客服 Agent 不够,要做一个客服中心,分诊、退款专员、物流专员、技术支持,多 Agent 协作。用到 Handoff + Agent-as-Tool。


本文是《从 0 写 Agent》系列第 47 章,案例代码基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版)。全系列目录见总览。

相关文章

分享: