第3讲:Prompt 实验——如何科学地测试 Prompt 改动


一、Prompt 实验的特殊性

复制代码
传统软件实验                     Prompt 实验
────────────────────────────────────────────────────
修改代码 → 部署 → 验证           修改 Prompt → 调用 LLM → 评估输出
结果确定可复现                   结果概率性波动
回归测试容易自动化               "正确"答案需要人工判断
性能指标客观(延迟/QPS)         质量指标主观(相关性/完整性)
版本回滚简单                     Prompt 版本管理复杂

核心难题:同一个 Prompt 跑两次得到两个不同的回答,如何区分是 Prompt 改好了还是运气好?


二、Prompt 实验的变量类型

变量维度 可调参数 示例
指令​ system prompt 措辞、语气、约束条件 "你是一个专业的医生" vs "你是一个科普作者"
结构​ 段落顺序、格式要求、分隔符 先给示例再给指令 vs 先给指令再给示例
示例​ few-shot 数量、示例选择、示例顺序 3 个示例 vs 5 个示例
参数​ temperature、top_p、max_tokens 0.1 vs 0.8
格式​ 输出格式约束 JSON vs Markdown vs 纯文本
角色​ 角色设定、人格特征 专家 vs 助手 vs 朋友

三、整体架构

复制代码
┌──────────────────────────────────────────────────────────────┐
│                     Prompt 实验系统                            │
│                                                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐    │
│  │模板管理  │  │版本管理  │  │参数配置  │  │实验编排  │    │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘    │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                     实验执行引擎                                │
│                                                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐    │
│  │分流器    │  │渲染引擎  │  │LLM 调用  │  │结果收集  │    │
│  │(第2讲)   │  │(模板)    │  │(第2讲)   │  │          │    │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘    │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                     评估系统                                    │
│                                                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐    │
│  │自动评估  │  │LLM Judge│  │人工评估  │  │统计分析  │    │
│  │(BLEU/ROUGE)│  │(打分)    │  │(众包)    │  │(第1讲)   │    │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘    │
└──────────────────────────────────────────────────────────────┘

四、Prompt 模板引擎

复制代码
// prompt/template.go
package prompt

import (
    "bytes"
    "encoding/json"
    "fmt"
    "strings"
    "text/template"
    "time"
)

// ─── 模板定义 ───

// Template Prompt 模板
type Template struct {
    ID          string            `json:"id"`
    Name        string            `json:"name"`
    Version     string            `json:"version"`
    System      string            `json:"system"`      // system prompt 模板
    User        string            `json:"user"`        // user message 模板
    Variables   []VariableDef     `json:"variables"`   // 变量定义
    Constraints map[string]string `json:"constraints"` // 约束条件
    CreatedAt   time.Time         `json:"created_at"`
}

// VariableDef 变量定义
type VariableDef struct {
    Name        string `json:"name"`
    Type        string `json:"type"`        // string, int, list, object
    Required    bool   `json:"required"`
    Default     string `json:"default"`
    Description string `json:"description"`
}

// RenderInput 渲染输入
type RenderInput struct {
    TemplateID string                 `json:"template_id"`
    Variables  map[string]interface{} `json:"variables"`
    Params     LLMParams              `json:"params"`
}

// LLMParams LLM 调用参数
type LLMParams struct {
    Temperature float64 `json:"temperature"`
    TopP        float64 `json:"top_p"`
    MaxTokens   int     `json:"max_tokens"`
    FrequencyPenalty float64 `json:"frequency_penalty"`
    PresencePenalty  float64 `json:"presence_penalty"`
}

// ─── 模板引擎 ───

// TemplateEngine 模板引擎
type TemplateEngine struct {
    templates map[string]*Template
}

// NewTemplateEngine 创建模板引擎
func NewTemplateEngine() *TemplateEngine {
    return &TemplateEngine{
        templates: make(map[string]*Template),
    }
}

// RegisterTemplate 注册模板
func (te *TemplateEngine) RegisterTemplate(t *Template) error {
    // 验证模板语法
    if _, err := template.New("system").Parse(t.System); err != nil {
        return fmt.Errorf("system template syntax error: %w", err)
    }
    if _, err := template.New("user").Parse(t.User); err != nil {
        return fmt.Errorf("user template syntax error: %w", err)
    }

    te.templates[t.ID] = t
    return nil
}

// Render 渲染 Prompt
func (te *TemplateEngine) Render(input RenderInput) (*RenderedPrompt, error) {
    tmpl, ok := te.templates[input.TemplateID]
    if !ok {
        return nil, fmt.Errorf("template not found: %s", input.TemplateID)
    }

    // 1. 验证必需变量
    for _, v := range tmpl.Variables {
        if v.Required {
            if _, exists := input.Variables[v.Name]; !exists {
                return nil, fmt.Errorf("missing required variable: %s", v.Name)
            }
        }
    }

    // 2. 填充默认值
    vars := make(map[string]interface{})
    for k, v := range input.Variables {
        vars[k] = v
    }
    for _, v := range tmpl.Variables {
        if _, exists := vars[v.Name]; !exists {
            vars[v.Name] = v.Default
        }
    }

    // 3. 渲染 system prompt
    systemBuf := new(bytes.Buffer)
    sysTmpl, _ := template.New("system").Parse(tmpl.System)
    if err := sysTmpl.Execute(systemBuf, vars); err != nil {
        return nil, fmt.Errorf("render system prompt: %w", err)
    }

    // 4. 渲染 user prompt
    userBuf := new(bytes.Buffer)
    userTmpl, _ := template.New("user").Parse(tmpl.User)
    if err := userTmpl.Execute(userBuf, vars); err != nil {
        return nil, fmt.Errorf("render user prompt: %w", err)
    }

    // 5. 合并参数(模板默认参数可以被覆盖)
    params := mergeParams(tmpl.Constraints, input.Params)

    return &RenderedPrompt{
        TemplateID:  input.TemplateID,
        Version:     tmpl.Version,
        SystemPrompt: systemBuf.String(),
        UserPrompt:   userBuf.String(),
        Params:      params,
        RenderedAt:  time.Now(),
    }, nil
}

// RenderedPrompt 渲染后的 Prompt
type RenderedPrompt struct {
    TemplateID   string    `json:"template_id"`
    Version      string    `json:"version"`
    SystemPrompt string    `json:"system_prompt"`
    UserPrompt   string    `json:"user_prompt"`
    Params       LLMParams `json:"params"`
    RenderedAt   time.Time `json:"rendered_at"`
}

func mergeParams(constraints map[string]string, params LLMParams) LLMParams {
    if v, ok := constraints["temperature"]; ok {
        fmt.Sscanf(v, "%f", &params.Temperature)
    }
    if v, ok := constraints["max_tokens"]; ok {
        fmt.Sscanf(v, "%d", &params.MaxTokens)
    }
    return params
}

// ─── 模板示例 ───

// 示例 1:客服 Prompt
var customerServiceTemplate = &Template{
    ID:      "cs_v1",
    Name:    "客服助手",
    Version: "1.0.0",
    System: `你是一个专业的客服助手。请遵循以下原则:
1. 始终保持礼貌和专业
2. 回答要简洁明了,不超过 {{.max_length}} 字
3. 如果无法解决问题,提供 {{.escalation_option}}
4. 使用{{.language}}回复

当前上下文:
- 用户等级:{{.user_level}}
- 历史对话轮数:{{.history_count}}`,
    User: "用户问题:{{.question}}",
    Variables: []VariableDef{
        {Name: "question", Type: "string", Required: true, Description: "用户提问"},
        {Name: "max_length", Type: "int", Required: false, Default: "200"},
        {Name: "language", Type: "string", Required: false, Default: "中文"},
        {Name: "user_level", Type: "string", Required: false, Default: "普通"},
        {Name: "history_count", Type: "int", Required: false, Default: "0"},
        {Name: "escalation_option", Type: "string", Required: false, Default: "转接人工"},
    },
}

// 示例 2:代码审查 Prompt
var codeReviewTemplate = &Template{
    ID:      "cr_v1",
    Name:    "代码审查",
    Version: "1.0.0",
    System: `你是一个资深的 {{.language}} 开发者。请审查以下代码:

审查重点:
1. 代码正确性(是否有 bug)
2. 性能问题(是否有优化空间)
3. 安全漏洞(SQL 注入、XSS 等)
4. 代码风格(是否符合 {{.style_guide}})

输出格式:
- 严重问题:🔴 [严重] 描述
- 一般问题:🟡 [建议] 描述
- 优化建议:🟢 [优化] 描述`,
    User: "```{{.language}}\n{{.code}}\n```",
    Variables: []VariableDef{
        {Name: "code", Type: "string", Required: true},
        {Name: "language", Type: "string", Required: true},
        {Name: "style_guide", Type: "string", Required: false, Default: "Google Style Guide"},
    },
}

五、Prompt 版本管理

复制代码
// prompt/version.go
package prompt

import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "sort"
    "sync"
    "time"
)

// ─── 版本管理 ───

// VersionManager Prompt 版本管理器
type VersionManager struct {
    versions map[string][]*PromptVersion // templateID -> versions
    mu       sync.RWMutex
}

// PromptVersion Prompt 版本
type PromptVersion struct {
    Version   string    `json:"version"`   // semver: 1.0.0, 1.1.0
    Template  *Template `json:"template"`
    Diff      string    `json:"diff"`      // 与前版本的差异描述
    Author    string    `json:"author"`
    CreatedAt time.Time `json:"created_at"`
    Checksum  string    `json:"checksum"`  // SHA256 of template content
}

// NewVersionManager 创建版本管理器
func NewVersionManager() *VersionManager {
    return &VersionManager{
        versions: make(map[string][]*PromptVersion),
    }
}

// CreateVersion 创建新版本
func (vm *VersionManager) CreateVersion(tmpl *Template, author string, diff string) (*PromptVersion, error) {
    vm.mu.Lock()
    defer vm.mu.Unlock()

    // 1. 计算 checksum
    checksum := computeChecksum(tmpl)

    // 2. 自动生成版本号
    version := vm.nextVersion(tmpl.ID, checksum)

    pv := &PromptVersion{
        Version:   version,
        Template:  tmpl,
        Diff:      diff,
        Author:    author,
        CreatedAt: time.Now(),
        Checksum:  checksum,
    }

    vm.versions[tmpl.ID] = append(vm.versions[tmpl.ID], pv)
    return pv, nil
}

// GetVersions 获取所有版本
func (vm *VersionManager) GetVersions(templateID string) []*PromptVersion {
    vm.mu.RLock()
    defer vm.mu.RUnlock()
    return vm.versions[templateID]
}

// Rollback 回滚到指定版本
func (vm *VersionManager) Rollback(templateID string, version string) (*Template, error) {
    vm.mu.RLock()
    defer vm.mu.RUnlock()

    versions, ok := vm.versions[templateID]
    if !ok {
        return nil, fmt.Errorf("template not found: %s", templateID)
    }

    for _, pv := range versions {
        if pv.Version == version {
            return pv.Template, nil
        }
    }

    return nil, fmt.Errorf("version not found: %s/%s", templateID, version)
}

// CompareVersions 比较两个版本
func (vm *VersionManager) CompareVersions(templateID string, v1, v2 string) (*VersionDiff, error) {
    t1, err := vm.Rollback(templateID, v1)
    if err != nil {
        return nil, err
    }
    t2, err := vm.Rollback(templateID, v2)
    if err != nil {
        return nil, err
    }

    return &VersionDiff{
        TemplateID:  templateID,
        VersionFrom: v1,
        VersionTo:   v2,
        SystemDiff:  diffText(t1.System, t2.System),
        UserDiff:    diffText(t1.User, t2.User),
    }, nil
}

func (vm *VersionManager) nextVersion(templateID string, checksum string) string {
    versions := vm.versions[templateID]

    // 如果是第一个版本
    if len(versions) == 0 {
        return "1.0.0"
    }

    // 检查是否内容相同(防止重复提交)
    last := versions[len(versions)-1]
    if last.Checksum == checksum {
        return last.Version
    }

    // 递增 minor 版本号
    var major, minor, patch int
    fmt.Sscanf(last.Version, "%d.%d.%d", &major, &minor, &patch)
    return fmt.Sprintf("%d.%d.%d", major, minor+1, patch)
}

func computeChecksum(tmpl *Template) string {
    content := tmpl.System + "|||" + tmpl.User
    hash := sha256.Sum256([]byte(content))
    return hex.EncodeToString(hash[:8])
}

// VersionDiff 版本差异
type VersionDiff struct {
    TemplateID  string `json:"template_id"`
    VersionFrom string `json:"version_from"`
    VersionTo   string `json:"version_to"`
    SystemDiff  string `json:"system_diff"`
    UserDiff    string `json:"user_diff"`
}

// diffText 简单文本差异(生产环境建议使用 difflib)
func diffText(a, b string) string {
    if a == b {
        return "无变化"
    }
    return fmt.Sprintf("--- 旧版本\n+++ 新版本\n-%s\n+%s", truncate(a, 100), truncate(b, 100))
}

func truncate(s string, n int) string {
    runes := []rune(s)
    if len(runes) <= n {
        return s
    }
    return string(runes[:n]) + "..."
}

六、实验执行引擎

复制代码
// prompt/experiment.go
package prompt

import (
    "context"
    "fmt"
    "sync"
    "time"
)

// ─── Prompt 实验 ───

// PromptExperiment Prompt 实验
type PromptExperiment struct {
    ID          string
    Name        string
    TemplateID  string
    Variants    []PromptVariant
    Evaluator   Evaluator
    Collector   *ResultCollector
    Splitter    interface{ Assign(userID string) string }
    Status      string // running, paused, completed
    mu          sync.Mutex
}

// PromptVariant Prompt 变体
type PromptVariant struct {
    ID       string
    Name     string
    Template *Template // 覆盖的模板内容
    Params   LLMParams // 覆盖的参数
}

// ─── 评估器 ───

// Evaluator 评估器接口
type Evaluator interface {
    Evaluate(ctx context.Context, result *LLMResult) (*Evaluation, error)
}

// Evaluation 评估结果
type Evaluation struct {
    Score     float64           `json:"score"`     // 0-1
    Passed    bool              `json:"passed"`
    Details   map[string]float64 `json:"details"`  // 各项子分数
    Feedback  string            `json:"feedback"`
}

// LLMResult LLM 调用结果
type LLMResult struct {
    Prompt    *RenderedPrompt `json:"prompt"`
    Response  string          `json:"response"`
    Latency   time.Duration   `json:"latency"`
    TokenUsed int             `json:"token_used"`
    Error     error           `json:"error,omitempty"`
}

// ─── 自动评估器 ───

// AutoEvaluator 自动评估器
type AutoEvaluator struct {
    criteria []Criterion
}

type Criterion struct {
    Name   string
    Weight float64
    Check  func(response string, expected string) float64
}

// NewAutoEvaluator 创建自动评估器
func NewAutoEvaluator() *AutoEvaluator {
    return &AutoEvaluator{
        criteria: []Criterion{
            {Name: "关键词覆盖率", Weight: 0.3, Check: keywordCoverage},
            {Name: "长度合规", Weight: 0.2, Check: lengthCompliance},
            {Name: "格式正确性", Weight: 0.3, Check: formatCorrectness},
            {Name: "禁止词检测", Weight: 0.2, Check: bannedWordCheck},
        },
    }
}

func (ae *AutoEvaluator) Evaluate(ctx context.Context, result *LLMResult) (*Evaluation, error) {
    totalScore := 0.0
    details := make(map[string]float64)

    for _, c := range ae.criteria {
        score := c.Check(result.Response, "")
        details[c.Name] = score
        totalScore += score * c.Weight
    }

    return &Evaluation{
        Score:    totalScore,
        Passed:   totalScore >= 0.7,
        Details:  details,
        Feedback: fmt.Sprintf("综合得分: %.2f", totalScore),
    }, nil
}

func keywordCoverage(response string, _ string) float64 {
    keywords := []string{"你好", "请问", "帮助"}
    matched := 0
    for _, kw := range keywords {
        if strings.Contains(response, kw) {
            matched++
        }
    }
    return float64(matched) / float64(len(keywords))
}

func lengthCompliance(response string, _ string) float64 {
    runeCount := len([]rune(response))
    if runeCount >= 50 && runeCount <= 500 {
        return 1.0
    }
    if runeCount < 50 {
        return float64(runeCount) / 50.0
    }
    return 500.0 / float64(runeCount)
}

func formatCorrectness(response string, _ string) float64 {
    // 检查是否包含标点符号
    hasPunct := strings.ContainsAny(response, ",。!?,.!?")
    if hasPunct {
        return 1.0
    }
    return 0.5
}

func bannedWordCheck(response string, _ string) float64 {
    bannedWords := []string{"不知道", "无法回答", "error"}
    for _, bw := range bannedWords {
        if strings.Contains(response, bw) {
            return 0.0
        }
    }
    return 1.0
}

// ─── LLM-as-Judge 评估器 ───

// LLMJudgeEvaluator 使用 LLM 作为评判者
type LLMJudgeEvaluator struct {
    judgePrompt string
    llmClient   interface{ Chat(ctx context.Context, req *ChatRequest) (*ChatResponse, error) }
}

func (je *LLMJudgeEvaluator) Evaluate(ctx context.Context, result *LLMResult) (*Evaluation, error) {
    judgeReq := &ChatRequest{
        Messages: []Message{
            {Role: "system", Content: je.judgePrompt},
            {Role: "user", Content: fmt.Sprintf(`
请评估以下 AI 回答的质量:

用户问题:%s
AI 回答:%s

请从以下维度打分(0-10):
1. 相关性:回答是否切题
2. 准确性:信息是否正确
3. 完整性:是否全面覆盖问题
4. 清晰度:表达是否清楚

输出 JSON 格式:{"relevance": N, "accuracy": N, "completeness": N, "clarity": N}
`, result.Prompt.UserPrompt, result.Response)},
        },
    }

    resp, err := je.llmClient.Chat(ctx, judgeReq)
    if err != nil {
        return nil, err
    }

    // 解析 JSON 评分
    var scores map[string]float64
    json.Unmarshal([]byte(resp.Content), &scores)

    total := 0.0
    for _, v := range scores {
        total += v / 10.0
    }
    avg := total / float64(len(scores))

    return &Evaluation{
        Score:   avg,
        Passed:  avg >= 0.7,
        Details: scores,
    }, nil
}

// ─── 结果收集器 ───

// ResultCollector 结果收集器
type ResultCollector struct {
    results map[string][]*ExperimentResult
    mu      sync.Mutex
}

type ExperimentResult struct {
    UserID      string      `json:"user_id"`
    VariantID   string      `json:"variant_id"`
    Input       interface{} `json:"input"`
    Output      string      `json:"output"`
    Evaluation  *Evaluation `json:"evaluation"`
    Latency     int64       `json:"latency_ms"`
    TokenUsed   int         `json:"token_used"`
    Timestamp   time.Time   `json:"timestamp"`
}

func (rc *ResultCollector) Record(result *ExperimentResult) {
    rc.mu.Lock()
    defer rc.mu.Unlock()
    rc.results[result.VariantID] = append(rc.results[result.VariantID], result)
}

// AggregateResults 汇总结果
func (rc *ResultCollector) AggregateResults() map[string]*AggregatedResult {
    rc.mu.Lock()
    defer rc.mu.Unlock()

    aggregated := make(map[string]*AggregatedResult)

    for variantID, results := range rc.results {
        agg := &AggregatedResult{VariantID: variantID}
        var totalScore, totalLatency float64

        for _, r := range results {
            agg.Count++
            totalScore += r.Evaluation.Score
            totalLatency += float64(r.Latency)
        }

        if agg.Count > 0 {
            agg.AvgScore = totalScore / float64(agg.Count)
            agg.AvgLatency = totalLatency / float64(agg.Count)
        }

        aggregated[variantID] = agg
    }

    return aggregated
}

type AggregatedResult struct {
    VariantID  string  `json:"variant_id"`
    Count      int     `json:"count"`
    AvgScore   float64 `json:"avg_score"`
    AvgLatency float64 `json:"avg_latency_ms"`
}

七、完整实验流程示例

复制代码
// cmd/prompt_experiment.go
package main

import (
    "context"
    "fmt"
    "math/rand"
    "time"
)

func main() {
    // 1. 注册模板
    engine := prompt.NewTemplateEngine()
    engine.RegisterTemplate(customerServiceTemplate)

    // 2. 创建版本
    vm := prompt.NewVersionManager()
    vm.CreateVersion(customerServiceTemplate, "alice", "初始版本")

    // 3. 创建实验变体
    // 变体 A:原始 Prompt
    variantA := prompt.PromptVariant{
        ID:   "A",
        Name: "控制组 - 简洁风格",
        Template: &prompt.Template{
            System: `你是一个专业的客服助手。请保持回答简洁,不超过 100 字。`,
            User:   "用户问题:{{.question}}",
        },
        Params: prompt.LLMParams{Temperature: 0.3},
    }

    // 变体 B:详细风格
    variantB := prompt.PromptVariant{
        ID:   "B",
        Name: "实验组 - 详细风格",
        Template: &prompt.Template{
            System: `你是一个专业的客服助手。请提供详细的解答,包括:
1. 问题原因分析
2. 解决方案步骤
3. 预防措施建议
不少于 200 字。`,
            User: "用户问题:{{.question}}",
        },
        Params: prompt.LLMParams{Temperature: 0.7},
    }

    // 4. 创建实验
    exp := &prompt.PromptExperiment{
        ID:         "exp_cs_001",
        Name:       "客服 Prompt 风格实验",
        TemplateID: "cs_v1",
        Variants:   []prompt.PromptVariant{variantA, variantB},
        Evaluator:  prompt.NewAutoEvaluator(),
        Collector:  &prompt.ResultCollector{results: make(map[string][]*prompt.ExperimentResult)},
        Status:     "running",
    }

    // 5. 模拟实验运行
    testQuestions := []string{
        "我的订单怎么还没到?",
        "我想退货怎么办?",
        "你们有优惠券吗?",
        "账号被盗了怎么办?",
        "如何修改收货地址?",
    }

    fmt.Println("=== Prompt 风格实验 ===\n")

    for i := 0; i < 100; i++ {
        userID := fmt.Sprintf("user_%04d", i)
        question := testQuestions[rand.Intn(len(testQuestions))]

        // 分流
        variantID := "A"
        if rand.Float64() < 0.5 {
            variantID = "B"
        }

        // 渲染 Prompt
        var tmpl *prompt.Template
        if variantID == "A" {
            tmpl = variantA.Template
        } else {
            tmpl = variantB.Template
        }

        rendered, _ := engine.Render(prompt.RenderInput{
            TemplateID: "cs_v1",
            Variables:  map[string]interface{}{"question": question},
            Params:     prompt.LLMParams{Temperature: 0.5},
        })

        // 模拟 LLM 调用(实际应调用真实 API)
        mockResponse := fmt.Sprintf("这是对「%s」的回答(变体 %s)", question, variantID)
        time.Sleep(time.Millisecond * time.Duration(rand.Intn(200)))

        // 评估
        eval, _ := exp.Evaluator.Evaluate(context.Background(), &prompt.LLMResult{
            Prompt:   rendered,
            Response: mockResponse,
        })

        // 记录结果
        exp.Collector.Record(&prompt.ExperimentResult{
            UserID:     userID,
            VariantID:  variantID,
            Input:      question,
            Output:     mockResponse,
            Evaluation: eval,
            Latency:    int64(rand.Intn(500)),
            Timestamp:  time.Now(),
        })
    }

    // 6. 输出结果
    results := exp.Collector.AggregateResults()
    fmt.Printf("%-10s %-10s %-10s %-15s\n", "变体", "样本数", "平均分", "平均延迟(ms)")
    for _, r := range results {
        fmt.Printf("%-10s %-10d %-10.3f %-15.0f\n",
            r.VariantID, r.Count, r.AvgScore, r.AvgLatency)
    }
}

八、本讲坑位

# 坑位 现象 解决方案
1 Prompt 泄露​ 用户通过特殊输入套出 system prompt 严格过滤 + 输出约束 + 审计日志
2 模板注入​ 用户变量中包含模板语法导致渲染异常 转义变量内容 + 沙箱渲染
3 评估噪声​ LLM Judge 自身也有随机性 多次评估取平均 + 使用固定 seed
4 过度拟合​ Prompt 在测试集上高分但在线上翻车 构建多样化测试集 + 线上 AB 验证
5 版本混乱​ 多人同时修改 Prompt 导致冲突 Git 式版本管理 + 锁定机制
6 成本爆炸​ 每次实验都调用 LLM 评估,费用飙升 先用自动评估筛选,再用 LLM Judge 精评

九、本讲小结

复制代码
┌─────────────────────────────────────────────────────────────┐
│                     第3讲 核心收获                            │
│                                                              │
│  ✅ 理解了 Prompt 实验与传统实验的本质区别                    │
│  ✅ 掌握了 Prompt 模板引擎的实现(Go template 渲染)          │
│  ✅ 实现了 Prompt 版本管理(semver + checksum + 回滚)        │
│  ✅ 实现了自动评估器和 LLM-as-Judge 评估器                   │
│  ✅ 实现了实验结果采集和汇总                                 │
│  ✅ 完成了完整的 Prompt 实验流程                             │
│  ✅ 知道了 6 个常见坑位及其应对方案                          │
│                                                              │
│  核心理念:Prompt 优化不是玄学,是可测量、可重复的实验        │
└─────────────────────────────────────────────────────────────┘

下一讲预告:《RAG 实验------检索策略与切片效果的量化评估》

从切片粒度到检索算法,从 Top-K 到重排序,用实验数据告诉你哪种 RAG 配置最适合你的场景。


相关推荐
前进的李工18 小时前
Agent 学习笔记 05:系统提示词应该怎么写
笔记·学习·prompt·agent·个人开发·few-shot·ai应用开发
我真会写代码19 小时前
LangChain 从0入门:模型、消息、Prompt、Tools、记忆、Agent与LangSmith实战
langchain·prompt
刘胡子大叔1 天前
评测驱动优化:失败样本回流 Prompt 的闭环
人工智能·prompt
后台模板学习1 天前
大模型 Prompt 工程在电商订单系统中的踩坑记录
java·python·prompt
IT古董2 天前
《Agent开发工程师成长指南》- 第4章 第2节:Prompt设计五要素——任务、角色、上下文、约束与输出格式
大数据·人工智能·prompt
吴名氏.2 天前
Java转AI Agent教程(第五周)Prompt工程
java·人工智能·prompt·ai agent
一枕槐安4872 天前
微调 vs Prompt:业务场景下两种方案成本 & 效果对比
大数据·人工智能·prompt·混合架构·推理成本·行为固化·知识边界
小哈里2 天前
【Agent】从Prompt工程、Context工程到 Harness工程,让 AI 能真正完成工作
人工智能·prompt·agent·ai编程·harness
技灵AI2 天前
AI 开箱视频怎么做?用创意开场带出商品展示
人工智能·prompt·aigc