🦞 一只用 AI Agent 搭副业产线的程序员


你有没有遇到过这种情况——Agent 生成的代码跑不起来,它自己不知道,还一脸自信地说「代码已完成」?

我遇到过。Agent 生成了一段 Go 代码,var 声明了一个变量但没 import 对应的包,连编译都过不去。但它交付的时候那叫一个自信。

AI 对自己的输出有一种天然的「盲目自信」。它不会像人一样写完代码后背上冒冷汗——「等等,我刚才写的那个对不对?」

自我反思就是给 Agent 加上这个「冒冷汗」的能力。


为什么 AI 不会自己纠错

LLM 生成文本时,每个 token 都是基于前面的 token 预测的。一旦前面写错了,后面的内容会基于错误继续写——它不会「回头检查」,只会「往前走」。

自我反思打破了这种单向性:生成 → 停下来 → 用自己的输出当输入 → 重新审视。


三种反思模式

模式 机制 成本 效果
内省式 同一个 Agent 审查自己的输出 低(1 次额外调用) 中等
角色扮演 同一个 Agent 切换到 Critic 角色审查 中(1 次 + Prompt 切换)
独立审查 独立 Critic Agent 审查 高(1 个独立 Agent) 最好

独立审查效果最好——因为上一篇讲了,同一个人批自己卷子有确认偏差。但对于日常任务,角色扮演模式性价比最高。


完整实现

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type SelfReflectiveAgent struct {
	apiKey   string
	maxReflections int // 最大反思次数
}

func NewSelfReflectiveAgent(apiKey string) *SelfReflectiveAgent {
	return &SelfReflectiveAgent{
		apiKey:          apiKey,
		maxReflections:  3,
	}
}

func (a *SelfReflectiveAgent) callLLM(messages []Message, temperature float64) string {
	body, _ := json.Marshal(map[string]interface{}{
		"model":       "deepseek-v4-pro",
		"messages":    messages,
		"temperature": temperature,
	})
	req, _ := http.NewRequest("POST",
		"https://api.deepseek.com/anthropic/v1/chat/completions",
		bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+a.apiKey)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 60 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Sprintf("调用失败: %v", err)
	}
	defer resp.Body.Close()

	data, _ := io.ReadAll(resp.Body)
	var result struct {
		Choices []struct {
			Message struct {
				Content string `json:"content"`
			} `json:"message"`
		} `json:"choices"`
	}
	json.Unmarshal(data, &result)
	if len(result.Choices) > 0 {
		return result.Choices[0].Message.Content
	}
	return ""
}

// ──────────── 核心:反思循环 ────────────

type ReflectionResult struct {
	FinalOutput string   `json:"final_output"`
	Iterations  int      `json:"iterations"`
	IssuesFound []string `json:"issues_found"`
	Improvements []string `json:"improvements"`
}

// GenerateWithReflection 生成 → 反思 → 修正 → 再反思
func (a *SelfReflectiveAgent) GenerateWithReflection(
	task string,
	qualityCriteria string,
) ReflectionResult {
	reflection := ReflectionResult{}

	// 第 1 轮:初始生成
	fmt.Println("📝 第 1 轮:生成初始输出...")
	initialOutput := a.callLLM([]Message{
		{Role: "system", Content: "你是一个 Go 代码生成专家。生成完整可编译的代码。"},
		{Role: "user", Content: task},
	}, 0.2)

	currentOutput := initialOutput

	// 第 2-N 轮:反思 + 修正循环
	for i := 0; i < a.maxReflections; i++ {
		fmt.Printf("🔍 第 %d 轮反思:审查输出...\n", i+2)

		// 反思:找问题
		reviewResult := a.review(currentOutput, task, qualityCriteria)

		if !reviewResult.HasIssues {
			fmt.Println("✅ 未发现问题,输出通过审查")
			break
		}

		fmt.Printf("⚠️ 发现 %d 个问题:\n", len(reviewResult.Issues))
		for _, issue := range reviewResult.Issues {
			fmt.Printf("  - %s\n", issue)
			reflection.IssuesFound = append(reflection.IssuesFound, issue)
		}

		// 修正:根据审查意见改进
		fmt.Println("🔧 修正中...")
		improvedOutput := a.revise(currentOutput, reviewResult.Issues)
		reflection.Improvements = append(reflection.Improvements,
			fmt.Sprintf("第%d轮修正了%d个问题", i+1, len(reviewResult.Issues)))

		currentOutput = improvedOutput
	}

	reflection.FinalOutput = currentOutput
	reflection.Iterations = len(reflection.IssuesFound)
	return reflection
}

// ──────────── 反思 Prompt ────────────

type ReviewResult struct {
	HasIssues bool
	Issues    []string
}

func (a *SelfReflectiveAgent) review(output, task, criteria string) ReviewResult {
	reviewPrompt := fmt.Sprintf(`你是严格的代码审查员。按以下标准审查输出:

质量标准:
%s

原始任务:
%s

待审查的输出:
%s

审查时回答:
1. 输出是否符合质量标准?(逐条检查)
2. 有没有编译错误、逻辑错误、边界条件遗漏?
3. 代码是否完整可运行?(检查 import、package 声明)

如果发现问题,用以下格式列出:
ISSUE: [问题描述]
ISSUE: [问题描述]
...

如果完全没有问题,回复:NO_ISSUES`, criteria, task, truncateForReview(output, 2000))

	result := a.callLLM([]Message{
		{Role: "user", Content: reviewPrompt},
	}, 0.0)

	if strings.Contains(result, "NO_ISSUES") {
		return ReviewResult{HasIssues: false}
	}

	var issues []string
	for _, line := range strings.Split(result, "\n") {
		if strings.HasPrefix(strings.TrimSpace(line), "ISSUE:") {
			issues = append(issues, strings.TrimPrefix(strings.TrimSpace(line), "ISSUE: "))
		}
	}

	return ReviewResult{HasIssues: len(issues) > 0, Issues: issues}
}

func (a *SelfReflectiveAgent) revise(output string, issues []string) string {
	issuesList := strings.Join(issues, "\n- ")
	revisePrompt := fmt.Sprintf(`修复以下输出中的问题。

原始输出:
%s

发现的问题:
- %s

请输出修正后的完整代码。不要解释修改了什么。`, output, issuesList)

	return a.callLLM([]Message{
		{Role: "system", Content: "你是代码修复专家。输出修正后的完整代码,不要包含解释性文字。"},
		{Role: "user", Content: revisePrompt},
	}, 0.1)
}

func truncateForReview(s string, maxLen int) string {
	if len(s) > maxLen {
		return s[:maxLen] + "\n...(输出超过长度限制,已截断)"
	}
	return s
}

// ──────────── 对比实验 ────────────

func main() {
	agent := NewSelfReflectiveAgent(os.Getenv("DEEPSEEK_API_KEY"))

	task := `用 Go 写一个函数 CalculateDiscount,功能:
用户等级分为 normal/vip/super-vip
normal 不打折
vip 打 9 折
super-vip 打 8 折
满 1000 元额外打 95 折(折上折)
输入:等级(string)和消费金额(float64)
输出:折扣后金额`

	qualityCriteria := `1. 代码必须包含 package 和正确的 import
2. 必须处理无效的用户等级(返回错误)
3. 必须处理负金额(返回错误)
4. 所有计算必须使用浮点,不丢失精度
5. 函数签名必须包含 error 返回值`

	// ── 不加反思 ──
	fmt.Println("===== 不加反思:直接生成 =====")
	noReflectionOutput := agent.callLLM([]Message{
		{Role: "system", Content: "你是 Go 代码生成专家。"},
		{Role: "user", Content: task},
	}, 0.2)
	fmt.Println(noReflectionOutput)

	// ── 加反思 ──
	fmt.Println("\n===== 加反思:生成→审查→修正 =====")
	result := agent.GenerateWithReflection(task, qualityCriteria)
	fmt.Println("\n===== 最终输出 =====")
	fmt.Println(result.FinalOutput)
	fmt.Printf("\n共进行 %d 轮迭代,发现并修复了以下问题:\n", result.Iterations)
	for _, issue := range result.IssuesFound {
		fmt.Printf("  ❌ %s\n", issue)
	}
}

实测数据

我对同一个任务(上面的折扣计算函数),不加反思和加反思各跑了 5 次,统计结果:

指标 不加反思 加反思
编译通过率 3/5(60%) 5/5(100%)
错误处理完整 1/5(20%) 5/5(100%)
边界条件正确 2/5(40%) 4/5(80%)
平均 token 消耗 350 950

加反思的 token 消耗多了近 3 倍,但换取的是 100% 编译通过率和接近 100% 的错误处理完整性。

值得吗?取决于场景。 如果输出会直接部署到生产环境——值的。如果只是草稿——不值得。


什么时候用反思

场景 用反思? 理由
API 响应生成 不推荐 太慢,用户等不起
代码提交前检查 强烈推荐 编译错误是硬伤
文章/报告撰写 推荐 事实错误和逻辑漏洞很常见
数据处理脚本 推荐 数据错了很难发现
聊天回复 不需要 过度设计

一个实用的阈值:如果输出错误会导致大于 1 小时的修复时间——加反思。


反思的三种 Prompt 模式

1. 通用审查:

"检查输出中的事实错误、逻辑漏洞和遗漏"

2. 逐条核对:

"逐条确认以下要求是否满足:1.XXX 2.XXX 3.XXX"

3. 反向检查:

"如果你是这段代码的使用者,你会对哪些地方不满意?"

第三种特别有用——让 AI 从「作者」视角切换到「用户」视角,往往能发现意料之外的问题。


总结

自我反思不是让 Agent 变聪明,是让它多了一次检查的机会

人写代码要 review,Agent 写代码也要 review。区别在于——人的 review 靠同事,Agent 的 review 可以自己做。

下一篇我们给 Agent 加上最后一道防线——Human-in-the-Loop,人机协作的正确姿势。

关注我,别错过。


🦞 一只用 AI Agent 搭副业产线的程序员

全平台同名:虾哥不加班
需要定制 AI 工具?来聊聊 → lob_ai

源码:GitHub - lobster-bujiaban

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐