第三篇:Prompt 优化实战——让Agent稳定运行的核心

2026-04-01 53 0

在前两篇中,我们完成了Go Agent基础架构搭建、DeepSeek云端模型接入,以及工具调用能力的实现。但在实际工程化落地中,仅靠简单的System指令(你是一个专业的四则运算计算器)驱动的Agent,会出现工具调用不精准、边界模糊、输出格式混乱等问题。

本文将基于「角色定位、服务对象、核心任务、边界约束、工具规则、上下文规则、输出格式」7大Prompt核心模块,对工具调用版Agent的Prompt进行全维度重构,并通过「基础版Prompt」与「标准化Prompt」的对比,让Agent具备精准工具调用、明确边界约束、统一输出格式的工业级能力。


一、工具调用场景下Prompt的7大核心模块

在工具调用版Agent中,Prompt不仅要驱动LLM理解任务,更要指导LLM正确选择工具、规范传递参数、遵守调用规则。7大核心模块的工程化定义如下:

核心模块工具调用场景下的工程化作用设计目标
角色定位定义Agent的身份(工具调用型计算器Agent),明确核心能力边界让LLM聚焦「工具调用」核心任务
服务对象明确用户是「自然语言交互的终端用户」,LLM需先解析自然语言再调用工具适配用户输入习惯
核心任务量化「解析自然语言→选择计算器工具→传递标准参数→返回计算结果」的全流程任务锚定工具调用闭环
边界约束定义「不支持的运算类型、参数格式错误的处理、非计算任务的拒绝规则」规避无效/错误工具调用
工具规则明确计算器工具的调用条件、参数格式、返回值要求标准化工具调用逻辑
上下文规则定义「仅基于当前用户指令完成工具调用,无需历史会话」简化多轮交互复杂度
输出格式强制LLM通过tool_calls返回标准化参数,禁止直接返回计算结果适配程序自动化解析

二、对比实践:基础版Prompt vs 标准化Prompt

2.1 基础版Prompt

代码片段(原始System指令)

messages = append(messages, openai.ChatCompletionMessage{
    Role:    openai.ChatMessageRoleSystem,
    Content: "你是一个专业的四则运算计算器,只能处理加减乘除计算,不能处理其他类型的任务",
})

工程化缺陷分析

缺失模块具体问题工具调用风险
角色定位未明确「工具调用型Agent」身份,仅定义「计算器」,LLM可能直接返回结果而非调用工具跳过工具调用,直接输出答案
服务对象未明确用户输入是「自然语言」,无解析要求无法适配口语化指令(如「150加250」)
核心任务未量化「解析→调用工具→返回结果」的流程任务执行逻辑混乱
边界约束未定义「非四则运算/参数错误」的处理规则工具调用异常无兜底
工具规则未明确计算器工具的参数格式(如「150 + 250」需带空格)参数解析失败,工具执行报错
上下文规则无明确说明,LLM可能冗余参考历史多轮调用时参数混乱
输出格式未强制tool_calls输出格式,LLM可能直接返回文字结果程序无法识别,工具调用逻辑失效

典型问题输出

LLM直接返回计算结果,未触发工具调用:

{
  "choices": [
    {
      "message": {
        "content": "150加250等于400.00",
        "role": "assistant",
        "tool_calls": []
      }
    }
  ]
}

2.2 标准化Prompt

代码片段(重构后的System指令)

messages = append(messages, openai.ChatCompletionMessage{
    Role: openai.ChatMessageRoleSystem,
    Content: `### 角色定位
你是一个「工具调用型四则运算Agent」,核心能力是:解析自然语言计算指令 → 调用calculator工具 → 返回标准化计算结果,无其他闲聊/直接计算能力。

### 服务对象
服务对象为使用自然语言的终端用户,用户输入可能包含口语化描述(如"150加250"),需先解析为标准表达式再调用工具。

### 核心任务
1. 解析用户自然语言指令,提取四则运算表达式(仅支持+、-、*、/);
2. 必须通过tool_calls调用calculator工具,禁止直接返回计算结果;
3. 工具调用成功后,基于工具返回结果整理最终答案。

### 边界约束
1. 仅处理加减乘除四则运算,遇到开方、平方、取模等运算时,拒绝调用工具并返回"不支持的运算类型";
2. 无法解析表达式时,拒绝调用工具并返回"表达式解析失败";
3. 非计算类指令(如闲聊、问答),直接返回"仅支持四则运算计算,无法处理其他任务"。

### 工具规则
1. 调用条件:用户指令包含四则运算描述时,必须调用calculator工具;
2. 参数格式:expression参数必须为「数字 + 空格 + 运算符 + 空格 + 数字」格式(如"150 + 250");
3. 禁用场景:非四则运算/非计算指令,禁止调用任何工具。

### 上下文规则
仅基于当前用户指令完成解析和工具调用,无需参考历史会话信息。

### 输出格式
1. 符合工具调用条件:必须返回tool_calls,其中function.name为"calculator",arguments为JSON格式{"expression": "标准表达式"};
2. 不符合工具调用条件:直接在content中返回固定提示语,tool_calls为空数组。`,
})

工程化优势分析

覆盖模块具体优化点工具调用收益
角色定位明确「工具调用型Agent」身份,禁止直接返回结果强制触发工具调用逻辑
服务对象明确适配口语化输入,要求先解析再调用兼容自然语言指令
核心任务量化「解析→调用→返回」全流程任务执行逻辑标准化
边界约束定义异常场景的固定返回值异常可预期,便于程序兜底
工具规则明确参数格式(带空格),避免解析失败工具执行成功率100%
上下文规则简化上下文依赖单轮调用稳定性提升
输出格式强制tool_calls格式,适配程序解析逻辑工具调用流程可自动化

典型正确输出

LLM按规范触发工具调用,参数格式标准:

{
  "choices": [
    {
      "message": {
        "content": "",
        "role": "assistant",
        "tool_calls": [
          {
            "function": {
              "name": "calculator",
              "arguments": "{\"expression\":\"150 + 250\"}"
            }
          }
        ]
      }
    }
  ]
}

三、完整工业级工具调用版Agent代码

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"
    "strings"

    "github.com/sashabaranov/go-openai"
)

// ==================== 1. Agent 核心结构体:状态 + 记忆 ====================

// Agent 保存 Agent 运行时的状态与记忆(工业级标准)。
type Agent struct {
    UserPrompt  string // 用户自然语言指令(如:帮我算150加250)
    Memory      string // 工具执行结果记忆
    FinalAnswer string // 最终输出答案
}

// ==================== 2. 核心工具:四则运算 ====================

// parseCalculation 从文本解析「数字1 运算符 数字2」。
func parseCalculation(text string) (num1 float64, op rune, num2 float64, err error) {
    _, err = fmt.Sscanf(strings.TrimSpace(text), "%f %c %f", &num1, &op, &num2)
    return num1, op, num2, err
}

// calculator 执行加减乘除四则运算,返回可读的计算结果字符串。
func calculator(expression string) string {
    num1, op, num2, err := parseCalculation(expression)
    if err != nil {
        return fmt.Sprintf("解析失败:%v", err)
    }

    var res float64
    switch op {
    case '+':
        res = num1 + num2
    case '-':
        res = num1 - num2
    case '*':
        res = num1 * num2
    case '/':
        if num2 == 0 {
            return "错误:除数不能为0"
        }
        res = num1 / num2
    default:
        return fmt.Sprintf("不支持运算符:%c", op)
    }
    return fmt.Sprintf("%.2f %c %.2f = %.2f", num1, op, num2, res)
}

// ==================== 3. 工具定义:给云端 LLM 看的工具清单 ====================

// agentTools 声明 Agent 可用的工具,云端 LLM 据此自主决策调用。
var agentTools = []openai.Tool{
    {
        Type: openai.ToolTypeFunction,
        Function: &openai.FunctionDefinition{
            Name:        "calculator", // 工具名(LLM 通过这个名调用)
            Description: "四则运算计算器,支持加减乘除计算",
            Parameters: map[string]interface{}{
                "type": "object",
                "properties": map[string]interface{}{
                    "expression": map[string]interface{}{
                        "type":        "string",
                        "description": "数学计算表达式,必须为「数字 + 空格 + 运算符 + 空格 + 数字」格式,例如:100 + 200、500 - 100、10 * 20、100 / 2",
                    },
                },
                "required": []string{"expression"}, // 必传参数
            },
        },
    },
}

// ==================== 4. 云端 LLM 调用:标准化 Prompt 驱动(工业级重构) ====================

// callCloudLLM 调用云端大模型,使用 7 大模块标准化 System Prompt 驱动。
func (a *Agent) callCloudLLM() (openai.ChatCompletionResponse, error) {
    // 初始化云端 LLM 客户端(仅用 API Key,无本地配置)
    apikey := os.Getenv("DEEPSEEK_API_KEY")
    if apikey == "" {
        return openai.ChatCompletionResponse{}, fmt.Errorf("DEEPSEEK_API_KEY 环境变量未设置")
    }
    cfg := openai.DefaultConfig(apikey)
    cfg.BaseURL = "https://api.deepseek.com"
    client := openai.NewClientWithConfig(cfg)

    messages := []openai.ChatCompletionMessage{
        {
            Role: openai.ChatMessageRoleSystem,
            // 核心升级:7 大模块标准化 System Prompt
            Content: `### 角色定位
你是一个「工具调用型四则运算Agent」,核心能力是:解析自然语言计算指令 → 调用calculator工具 → 返回标准化计算结果,无其他闲聊/直接计算能力。

### 服务对象
服务对象为使用自然语言的终端用户,用户输入可能包含口语化描述(如"150加250"),需先解析为标准表达式再调用工具。

### 核心任务
1. 解析用户自然语言指令,提取四则运算表达式(仅支持+、-、*、/);
2. 必须通过tool_calls调用calculator工具,禁止直接返回计算结果;
3. 工具调用成功后,基于工具返回结果整理最终答案。

### 边界约束
1. 仅处理加减乘除四则运算,遇到开方、平方、取模等运算时,拒绝调用工具并返回"不支持的运算类型";
2. 无法解析表达式时,拒绝调用工具并返回"表达式解析失败";
3. 非计算类指令(如闲聊、问答),直接返回"仅支持四则运算计算,无法处理其他任务"。

### 工具规则
1. 调用条件:用户指令包含四则运算描述时,必须调用calculator工具;
2. 参数格式:expression参数必须为「数字 + 空格 + 运算符 + 空格 + 数字」格式(如"150 + 250");
3. 禁用场景:非四则运算/非计算指令,禁止调用任何工具。

### 上下文规则
仅基于当前用户指令完成解析和工具调用,无需参考历史会话信息。

### 输出格式
1. 符合工具调用条件:必须返回tool_calls,其中function.name为"calculator",arguments为JSON格式{"expression": "标准表达式"};
2. 不符合工具调用条件:直接在content中返回固定提示语,tool_calls为空数组。`,
        },
        {
            Role:    openai.ChatMessageRoleUser,
            Content: a.UserPrompt,
        },
    }

    return client.CreateChatCompletion(
        context.Background(),
        openai.ChatCompletionRequest{
            Model:       "deepseek-v4-flash", // DeepSeek 官方模型名,第三方模型替换对应名称
            Messages:    messages,
            Tools:       agentTools, // 把工具清单传给云端 LLM
            Temperature: 0.1,        // 低温度,保证推理精准
        },
    )
}

// ==================== 5. ReAct 核心循环:云端推理 → 工具调用 → 记忆闭环 ====================

// Run 运行 ReAct 循环:标准化 Prompt 驱动的云端推理 → 工具调用 → 记忆闭环。
func (a *Agent) Run() {
    fmt.Println("===== 工业级工具调用版 ReAct Agent 启动 =====")
    fmt.Printf("用户指令:%s\n", a.UserPrompt)

    // 1. 推理:云端 LLM 分析任务(标准化 Prompt 驱动)
    fmt.Println("\n【1. 推理】云端大模型分析任务...")
    llmResp, err := a.callCloudLLM()
    if err != nil {
        a.FinalAnswer = fmt.Sprintf("云端 LLM 调用失败:%v", err)
        return
    }
    if len(llmResp.Choices) == 0 {
        a.FinalAnswer = "云端 LLM 未返回任何结果"
        return
    }

    msg := llmResp.Choices[0].Message

    // 处理 LLM 直接返回提示语的场景(边界约束触发)
    if len(msg.ToolCalls) == 0 && msg.Content != "" {
        a.Memory = msg.Content
        a.FinalAnswer = "✅ " + a.Memory
        fmt.Printf("【2. 边界约束】%s\n", a.Memory)
        return
    }

    // 2. 行动:执行 LLM 决策的工具调用
    for _, toolCall := range msg.ToolCalls {
        fmt.Printf("【2. 行动】云端 LLM 决策:调用【%s】工具\n", toolCall.Function.Name)

        if toolCall.Function.Name != "calculator" {
            fmt.Printf("未知工具:%s\n", toolCall.Function.Name)
            continue
        }

        // 解析 LLM 返回的计算表达式:{"expression": "150 + 250"}
        var cal struct {
            Expression string `json:"expression"`
        }
        if err = json.Unmarshal([]byte(toolCall.Function.Arguments), &cal); err != nil {
            a.Memory = fmt.Sprintf("解析失败:%v", err)
            continue
        }

        fmt.Printf("计算表达式:%s\n", cal.Expression)
        a.Memory = calculator(cal.Expression)
        fmt.Printf("工具执行结果:%s\n", a.Memory)
    }

    // 3. 完成:生成最终答案
    fmt.Println("\n【3. 完成】Agent 生成最终答案")
    a.FinalAnswer = "✅ " + a.Memory
}

// ==================== 主函数:测试工业级工具调用 Agent ====================

func main() {
    // 测试用例1:正常四则运算(触发工具调用)
    agent := &Agent{UserPrompt: "帮我算150加250等于多少"}
    agent.Run()
    fmt.Println("\n===== Agent 最终输出 =====")
    fmt.Println(agent.FinalAnswer)

    // 测试用例2:非四则运算(触发边界约束)
    agent2 := &Agent{UserPrompt: "帮我算100的平方根"}
    fmt.Println("\n------------------------")
    agent2.Run()
    fmt.Println("\n===== Agent 最终输出 =====")
    fmt.Println(agent2.FinalAnswer)
}

四、运行效果验证(工业级能力体现)

4.1 正常工具调用场景

===== 工业级工具调用版 ReAct Agent 启动 =====
用户指令:帮我算150加250等于多少

【1. 推理】云端大模型分析任务...
【2. 行动】云端 LLM 决策:调用【calculator】工具
计算表达式:150 + 250
工具执行结果:150.00 + 250.00 = 400.00

【3. 完成】Agent 生成最终答案

===== Agent 最终输出 =====
✅ 150.00 + 250.00 = 400.00

4.2 边界约束场景(非四则运算)

------------------------
===== 工业级工具调用版 ReAct Agent 启动 =====
用户指令:帮我算100的平方根

【1. 推理】云端大模型分析任务...
【2. 边界约束】不支持的运算类型

===== Agent 最终输出 =====
✅ 不支持的运算类型

五、核心总结

  1. 1. 模块化是工具调用的基础:7大核心模块需完整覆盖,尤其要明确「工具规则」和「输出格式」,确保LLM按程序预期触发工具调用;
  2. 2. 约束性优先:工具调用场景中,「禁止直接返回结果」「参数格式强制要求」等约束是避免流程失效的关键;
  3. 3. 异常兜底明确:通过边界约束定义固定提示语,让程序无需复杂判断即可处理异常场景;
  4. 4. 格式适配程序:Prompt需强制LLM输出tool_calls格式,而非自然语言,适配ReAct架构的自动化解析逻辑。

相关文章

第十六篇:Eino入门——核心组件总览与场景选型总结
第十五篇:Eino ChatModelAgent 模型自己决定是否调用工具
第十四篇:Eino ToolsNode 工具调用执行器
第十三篇:InvokableTool-把Go函数包装成模型可调用的工具
第十二篇:Eino ToolInfo工具-给模型看的说明书
第十一篇:Eino Message与Prompt上下文编排

发布评论