在字节搬砖五年后,我用AI重塑了自己的开发工作流

曹浩然☆
2026-07-15 17:54
阅读 782

上周四晚上十一点半,我盯着屏幕上第37个报错的单元测试,突然意识到一件事:我在这个工位上坐了快五年了。

从19年校招进字节,到现在在基础架构组搬砖,经历了三次组织架构调整,换了两个leader,唯一没变的是我桌上那台从大学用到现在的MacBook Pro——当然,中间换过两块电池和一次键盘。Windows?不存在的,我连虚拟机都懒得开,最多用Parallels跑个IE给前端同事测兼容性。

坐标杭州,这边阿里和网易的机会确实多,去年秋招的时候好几个前同事跳去了蚂蚁。但我这人比较懒,字节的基础架构虽然卷,但技术深度确实够,能让我这种"老油条"继续折腾点新东西。最近半年,公司内部在大力推AI提效,我也被leader"安排"去研究了一些AI相关的技术落地方案。

今天这篇文章,就想聊聊这半年我在技术探索上的一些实践和思考。不是那种高大上的架构设计,就是一个普通后端开发在日常工作中怎么用AI工具给自己"续命"的真实记录。

被大模型逼出来的效率革命

事情要从去年Q4说起。

当时组里接了个新需求,要给内部的RPC框架加一套智能诊断能力。说白了就是当线上服务出问题的时候,能自动分析调用链路,给出可能的根因建议。需求评审的时候,产品经理说了一句让我血压飙升的话:"这个功能竞品都有了,我们两周内能上线吗?"

两周?我看了看需求文档,光是要对接的日志系统就有四五个,加上链路追踪、指标采集、告警平台……我当场就想掀桌子。但转念一想,硬刚肯定不行,得想点别的办法。

也就是这个时候,我开始认真琢磨怎么用AI来提效。

通义千问:我的第一个AI编程搭子

说实话,在这之前我也用过ChatGPT,但受限于网络环境,用起来总是断断续续的。直到我开始用通义千问,体验才真正上了一个台阶。

先说个真实场景。当时我要写一个Go的日志解析模块,需要处理各种诡异的日志格式。以前这种活儿,我至少得花半天时间写正则、调格式、处理边界情况。但这次我直接把几段典型的日志丢给通义千问,让它帮我生成解析代码。

// 通义千问生成的日志解析核心逻辑
func ParseRPCLog(rawLog string) (*RPCLogEntry, error) {
    // 支持多种日志格式:JSON、KV、纯文本
    patterns := []string{
        `(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})`,
        `(?P<level>DEBUG|INFO|WARN|ERROR|FATAL)`,
        `(?P<service>[a-zA-Z][\w.-]*)`,
        `(?P<method>[a-zA-Z][\w.]*)`,
        `(?P<traceId>[a-f0-9]{32})`,
        `(?P<duration>\d+\.?\d*)`,
        `(?P<statusCode>\d{3})`,
    }
    
    // 先尝试JSON格式
    var jsonEntry map[string]interface{}
    if err := json.Unmarshal([]byte(rawLog), &jsonEntry); err == nil {
        return parseFromJSON(jsonEntry)
    }
    
    // 降级到正则匹配
    combinedPattern := strings.Join(patterns, `\s+`)
    re := regexp.MustCompile(combinedPattern)
    match := re.FindStringSubmatch(rawLog)
    if match == nil {
        return nil, fmt.Errorf("unrecognized log format: %s", rawLog)
    }
    
    entry := &RPCLogEntry{}
    for i, name := range re.SubexpNames() {
        if i != 0 && name != "" {
            switch name {
            case "duration":
                entry.Duration, _ = strconv.ParseFloat(match[i], 64)
            case "statusCode":
                entry.StatusCode, _ = strconv.Atoi(match[i])
            default:
                reflect.ValueOf(entry).Elem().FieldByName(
                    strings.Title(name)).SetString(match[i])
            }
        }
    }
    return entry, nil
}

这段代码我拿到手之后,大概花了20分钟做调整和优化。但关键是,它帮我省掉了至少3个小时的"从零开始"的时间。而且通义千问对中文的理解确实到位,我用中文描述需求,它返回的注释和变量命名都很符合国内开发者的习惯。

后来我总结了一个技巧:给AI喂上下文比让它凭空生成重要得多。我会把相关的接口定义、数据结构、甚至之前写的类似模块的代码一起丢给它,这样生成的代码质量会高很多。

扣子:低代码时代的AI工作流

如果说通义千问是我的"编程搭子",那扣子(Coze)就是我搭建自动化工作流的利器。

回到那个RPC诊断的需求。光解析日志还不够,我需要把解析后的数据跟链路追踪、监控指标关联起来,然后给出诊断建议。这个流程涉及多个系统的API调用,逻辑还挺复杂的。

以前我可能会写一堆胶水代码来串联这些系统。但这次我试了试用扣子来搭建工作流。

我的思路是这样的:

  1. 告警触发后,自动调用链路追踪API获取trace信息
  2. 把trace信息丢给大模型做初步分析
  3. 根据分析结果,调用监控API拉取相关指标
  4. 最终汇总成一份诊断报告

在扣子上搭建这个流程,大概花了两个小时。最让我惊喜的是它的插件生态,很多内部系统的API都有现成的插件可以用,省去了我写HTTP客户端的麻烦。

这里分享一个踩坑经验:扣子的工作流节点之间的数据传递,一定要做好字段映射。我第一次搭的时候,因为字段名没对齐,导致大模型拿到的数据全是空的,排查了半小时才发现这个问题。

# 扣子工作流配置片段(简化版)
workflow:
  name: rpc-diagnosis
  trigger:
    type: webhook
    config:
      alert_id: "${alert.id}"
  
  nodes:
    - id: fetch_trace
      type: plugin
      plugin: internal-trace-api
      input:
        trace_id: "${trigger.trace_id}"
        time_range: "30m"
      
    - id: ai_analysis
      type: llm
      model: qwen-max
      prompt: |
        你是一个RPC链路分析专家。请根据以下链路信息,
        分析可能的性能瓶颈和异常原因:
        
        链路数据:${fetch_trace.output}
        
        请从以下几个维度分析:
        1. 耗时最长的节点
        2. 错误率异常的节点  
        3. 可能的根因推断
        
      output_format: json
      
    - id: fetch_metrics
      type: plugin
      plugin: internal-monitor-api
      input:
        service: "${ai_analysis.output.suspicious_service}"
        metrics: ["cpu", "memory", "qps", "latency_p99"]
        
    - id: generate_report
      type: llm
      model: qwen-max
      prompt: |
        请综合以下信息,生成一份简洁的诊断报告:
        
        链路分析:${ai_analysis.output}
        监控指标:${fetch_metrics.output}
        
        报告格式要求:
        - 问题概述(一句话)
        - 根因分析(分点列出)
        - 建议操作(可执行的步骤)

上线之后,这个诊断工作流把原来需要值班同学花15-20分钟排查的问题,缩短到了2分钟出报告。虽然准确率大概只有80%左右,但对于初步定位问题来说已经够用了。

Aider:终端里的AI编程助手

说到AI编程工具,就不得不提Aider

作为一个常年在终端里干活的后端开发,我对IDE的依赖其实没那么重。vim用习惯了,切到VS Code总觉得哪里不对劲。但Aider的出现,让我在终端里也能享受到AI辅助编程的便利。

Aider最打动我的一点是:它能直接理解你整个代码仓库的上下文。不像网页版的AI工具,每次都要手动复制粘贴代码,Aider会自动索引你的git仓库,在对话中直接引用相关的文件和函数。

举个实际的例子。上周我要给一个中间件加prometheus的metrics埋点。如果用以前的方式,我得先找到所有需要埋点的位置,然后一个个加代码。但用Aider,我只需要说:

帮我在pkg/handler/目录下所有的HTTP handler函数里加上请求耗时和状态码的prometheus metrics埋点,参考pkg/middleware/metrics.go里已有的实现方式。

然后Aider就会自动分析相关的代码,给出修改建议,甚至直接帮你改好。

# Aider的实际使用示例
$ aider --model qwen-max --git

> 在pkg/gateway/handler/下所有handler加上metrics埋点

Analyzing repository context...
Found 12 handler files in pkg/gateway/handler/
Reference implementation: pkg/middleware/metrics.go

Proposed changes:

--- pkg/gateway/handler/user.go
+++ pkg/gateway/handler/user.go
@@ -15,6 +15,8 @@
 import (
     "net/http"
+    "github.com/prometheus/client_golang/prometheus"
+    "myproject/pkg/middleware"
 )
 
 func HandleGetUser(w http.ResponseWriter, r *http.Request) {
+    start := time.Now()
+    defer func() {
+        middleware.RequestDuration.WithLabelValues(
+            "GetUser", 
+            strconv.Itoa(r.Response.StatusCode),
+        ).Observe(time.Since(start).Seconds())
+    }()
+    
     // ... existing code
 }

Apply changes? [Y/n/eskip]: 

当然,Aider也不是万能的。它有时候会"过度发挥",改一些你不想让它改的地方。我的经验是:每次让Aider改代码之前,先git commit一下,这样改炸了可以随时回滚。另外,对于复杂的业务逻辑,还是得自己把關,AI生成的代码只能作为参考。

AI Agent:从工具到"同事"

上面说的这些工具,本质上还是"人驱动AI"的模式——我提出问题,AI给出答案。但最近半年,我一直在思考一个更进阶的方向:能不能让AI主动发现问题、主动解决问题?

这就是AI Agent的概念。

我们组最近在一个内部项目里做了个尝试:搭建了一个基于Agent的自动化运维助手。跟传统的告警机器人不同,这个Agent不只是发个消息通知你,而是能自己去排查问题、执行修复操作。

架构大概是这样的:

┌─────────────────────────────────────────────────┐
│                  AI Agent Core                   │
│  ┌───────────┐  ┌───────────┐  ┌─────────────┐ │
│  │  Planning  │  │  Reasoning │  │   Memory    │ │
│  │   Module   │  │   Module   │  │   Module    │ │
│  └───────────┘  └───────────┘  └─────────────┘ │
├─────────────────────────────────────────────────┤
│                   Tool Layer                     │
│  ┌────────┐ ┌────────┐ ┌────────┐ ┌─────────┐  │
│  │  K8s   │ │ Monitor│ │  Log   │ │  Config │  │
│  │  API   │ │  API   │ │  API   │ │  Center │  │
│  └────────┘ └────────┘ └────────┘ └─────────┘  │
└─────────────────────────────────────────────────┘

核心思路是用ReAct(Reasoning + Acting)的模式,让Agent在收到告警后:

  1. 先思考可能的原因(Reasoning)
  2. 调用相应的工具去验证(Acting)
  3. 根据验证结果更新判断(Observation)
  4. 循环直到得出结论或执行修复

这里有个关键的技术点:工具调用的权限控制。你肯定不能让AI Agent随便执行rm -rf之类的危险操作。我们的做法是定义了一套严格的工具权限体系,Agent只能调用预定义的、安全的操作。

// Agent工具权限定义
type ToolPermission struct {
    ToolName    string   `json:"tool_name"`
    AllowedOps  []string `json:"allowed_ops"`
    RequireAuth bool     `json:"require_auth"`
    DryRunOnly  bool     `json:"dry_run_only"`
}

var defaultPermissions = []ToolPermission{
    {
        ToolName:    "k8s-pod-restart",
        AllowedOps:  []string{"get", "describe", "logs"},
        RequireAuth: true,
        DryRunOnly:  true, // 重启操作需要人工确认
    },
    {
        ToolName:    "config-center",
        AllowedOps:  []string{"get", "diff"},
        RequireAuth: false,
        DryRunOnly:  false, // 查看配置不需要dry-run
    },
    {
        ToolName:    "scale-service",
        AllowedOps:  []string{"get"},
        RequireAuth: true,
        DryRunOnly:  true, // 扩容操作必须人工确认
    },
}

// Agent执行工具前的权限检查
func (a *Agent) checkPermission(toolName, operation string) error {
    for _, perm := range a.permissions {
        if perm.ToolName == toolName {
            if !contains(perm.AllowedOps, operation) {
                return fmt.Errorf("operation %s not allowed for tool %s", 
                    operation, toolName)
            }
            if perm.DryRunOnly {
                return &DryRunRequiredError{Tool: toolName}
            }
            return nil
        }
    }
    return fmt.Errorf("tool %s not found in permission list", toolName)
}

实际效果怎么样呢?说实话,还在迭代中。目前这个Agent能处理大概60%的常见告警场景,比如Pod OOM、服务超时、配置变更导致的异常等。但对于一些复杂的、需要跨系统关联分析的问题,它还是会"犯傻"。

最搞笑的一次是,有个服务因为下游依赖的数据库慢查询导致超时,Agent分析了一圈,最后给出的建议是"建议重启数据库"。当时运维同学看到这个回答,直接在群里发了个"???"。

一些掏心窝子的体会

写了这么多,最后分享几点我这半年折腾AI工具的感受。

第一,AI不会取代程序员,但会用AI的程序员会取代不会用的。

这不是句空话。就拿我们组来说,同样一个需求,我用AI辅助大概能比半年前快30%-40%。不是说AI帮我写了多少代码,而是它帮我跳过了很多"从零到一"的痛苦过程。以前写个新模块,光是搭架子、想命名、写注释就得花不少时间,现在这些机械性的工作AI都能帮你搞定,你可以把精力集中在核心逻辑上。

第二,Prompt Engineering是真的有用。

刚开始用AI工具的时候,我总觉得"这玩意儿生成的代码不行啊"。后来发现,很大程度上是我自己的问题——prompt写得太模糊了。现在我会花几分钟把需求描述清楚,包括输入输出格式、边界条件、性能要求等等,AI返回的结果质量会好很多。

第三,不要迷信AI,保持独立思考。

AI生成的代码,一定要自己review。它有时候会引入一些看起来很"聪明"但实际上有隐患的写法。比如上次通义千问帮我生成的一段并发代码,用了一个很tricky的channel用法,虽然能跑,但可读性很差,而且有个隐藏的deadlock风险。这种地方就得靠自己的经验去把关。

第四,技术探索要结合实际业务场景。

我见过有些人为了用AI而用AI,搞一些花里胡哨但实际没用的东西。我觉得技术探索的核心还是要解决实际问题。就像我们做的那个诊断Agent,虽然还不完美,但确实帮值班同学省了不少时间,这就是有价值的。


说了这么多,其实就想表达一个意思:技术在快速变化,我们做开发的也得跟着变。AI不是洪水猛兽,也不是万能药,它就是一个工具。关键是你怎么用这个工具,让它帮你把事情做得更好。

好了,不说了,leader在群里@我了,说是那个Agent又出了个bug,把测试环境的Redis给flush了……我得赶紧去擦屁股了。

下次再聊。

评论 0

最热最新
暂无评论
曹浩然☆Lv.1
0
影响力
0
文章
0
粉丝