AI/LLM应用开发实战(二):Prompt Engineering 核心技巧与实战

📚 本文是《AI/LLM 应用开发实战》系列第 2 篇。上一篇我们完成了第一个 LLM 对话应用,本篇将深入 Prompt Engineering 的核心技巧。

前言

很多开发者在接入 LLM API 后发现:**同样的模型,提示词写得好不好,输出质量天差地别**。

Prompt Engineering(提示词工程)不是玄学,而是一套有章可循的工程方法。本篇将介绍 5 个最实用的技巧,每个都配有可运行的 Python 代码,让你立刻上手。

一、System Prompt:给模型一个"人设"

System Prompt 是最基础也最重要的提示词技巧。它定义了模型的角色、行为边界和输出风格。

1.1 设计原则

一个好的 System Prompt 应该包含:

  • **角色定义**:你是谁,擅长什么
  • **行为约束**:什么该做,什么不该做
  • **输出格式**:期望的回答风格和结构
  • 1.2 实战代码

    
    import os
    from openai import OpenAI
    from dotenv import load_dotenv
    
    load_dotenv()
    client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
    
    def ask_with_system_prompt(system_prompt: str, user_query: str) -> str:
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_query}
            ],
            temperature=0.7
        )
        return response.choices[0].message.content
    
    # 对比:没有 System Prompt vs 精心设计的 System Prompt
    query = "解释一下什么是 Docker?"
    
    # 普通回答
    print("【普通模式】")
    print(ask_with_system_prompt("你是一个AI助手。", query))
    print()
    
    # 精心设计的 System Prompt
    expert_prompt = """你是一位资深 DevOps 工程师,拥有 10 年容器化技术经验。
    回答要求:
    1. 先用一句话给出核心定义
    2. 用生活中的比喻帮助理解
    3. 列出 3 个最常见的使用场景
    4. 回答控制在 200 字以内
    语气:专业但通俗易懂,像在和同事聊天。"""
    
    print("【专家模式】")
    print(ask_with_system_prompt(expert_prompt, query))
    

    专家模式的输出会更加结构化、专业且易懂,这就是 System Prompt 的威力。

    二、Few-shot Learning:用示例教会模型

    Few-shot(少样本学习)是指在提示词中提供几个输入-输出示例,让模型"学会"你期望的模式。

    2.1 适用场景

  • 特定格式的文本转换
  • 分类任务
  • 风格统一的内容生成
  • 2.2 实战:智能命名转换器

    
    def few_shot_naming_converter(variable_name: str) -> str:
        """利用 Few-shot 让模型学会变量命名风格转换"""
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": "你是一个编程命名风格转换工具。将输入的变量名转换为所有常见风格。"},
                # Few-shot 示例 1
                {"role": "user", "content": "user_login_count"},
                {"role": "assistant", "content": """camelCase: userLoginCount
    PascalCase: UserLoginCount
    kebab-case: user-login-count
    UPPER_SNAKE: USER_LOGIN_COUNT"""},
                # Few-shot 示例 2
                {"role": "user", "content": "get_all_items"},
                {"role": "assistant", "content": """camelCase: getAllItems
    PascalCase: GetAllItems
    kebab-case: get-all-items
    UPPER_SNAKE: GET_ALL_ITEMS"""},
                # 实际输入
                {"role": "user", "content": variable_name}
            ],
            temperature=0
        )
        return response.choices[0].message.content
    
    # 测试
    print(few_shot_naming_converter("create_new_project"))
    print()
    print(few_shot_naming_converter("send_email_notification"))
    

    通过 2 个示例,模型就能精确地按照我们期望的格式输出,无需复杂的规则描述。

    三、Chain-of-Thought(CoT):让模型"思考"

    CoT(思维链)是提升模型推理能力的关键技巧。核心思想:**要求模型展示推理过程,而不是直接给答案**。

    3.1 三种 CoT 方式

    | 方式 | 做法 | 适用场景 |

    |------|------|---------|

    | Zero-shot CoT | 加一句"请一步步思考" | 简单推理 |

    | Few-shot CoT | 提供带推理过程的示例 | 复杂推理 |

    | 自动 CoT | 让模型自己生成推理链 | 批量任务 |

    3.2 实战:代码 Bug 分析器

    
    def analyze_bug_with_cot(code_snippet: str) -> str:
        """使用 CoT 技巧分析代码中的 Bug"""
        system_prompt = """你是一位资深代码审查专家。分析用户提供的代码时,请严格按以下步骤思考:
    
    第一步:逐行阅读代码,理解意图
    第二步:检查常见错误类型(边界条件、类型错误、逻辑错误、资源泄漏)
    第三步:定位具体的 Bug 位置和原因
    第四步:给出修复方案和修复后的代码
    
    请展示完整的思考过程。"""
    
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": f"请分析以下代码的 Bug:\n\n```python\n{code_snippet}\n```"}
            ],
            temperature=0.3
        )
        return response.choices[0].message.content
    
    # 测试:一段有 Bug 的代码
    buggy_code = """
    def calculate_average(numbers):
        total = 0
        for num in numbers:
            total += num
        return total / len(numbers)
    
    result = calculate_average([])
    print(f"平均值: {result}")
    """
    
    print(analyze_bug_with_cot(buggy_code))
    

    模型会按步骤分析出空列表导致的 `ZeroDivisionError`,并给出防御性编程的修复方案。

    四、结构化输出:让 LLM 返回可解析的数据

    在实际应用中,我们通常需要 LLM 返回 JSON 等结构化数据,而不是自由文本。

    4.1 实战:智能简历解析器

    
    import json
    
    def parse_resume(resume_text: str) -> dict:
        """将非结构化的简历文本解析为结构化 JSON"""
        system_prompt = """你是一个简历解析引擎。将用户提供的简历文本解析为 JSON 格式。
    
    严格按以下 JSON Schema 输出,不要输出任何其他内容:
    {
        "name": "姓名",
        "email": "邮箱",
        "phone": "电话",
        "education": [{"school": "学校", "degree": "学历", "major": "专业", "year": "毕业年份"}],
        "skills": ["技能1", "技能2"],
        "experience_years": 数字
    }
    
    如果某个字段信息缺失,填 null。只输出 JSON,不要加 markdown 标记。"""
    
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": resume_text}
            ],
            temperature=0,
            response_format={"type": "json_object"}
        )
    
        return json.loads(response.choices[0].message.content)
    
    # 测试
    sample_resume = """
    张三,男,5年Python开发经验
    邮箱:[email]  电话:[phone_number]
    2018年毕业于北京大学计算机科学与技术专业,硕士学位
    熟练掌握 Python、Go、Docker、Kubernetes、MySQL、Redis
    曾在字节跳动担任高级后端工程师
    """
    
    result = parse_resume(sample_resume)
    print(json.dumps(result, ensure_ascii=False, indent=2))
    

    输出示例:

    
    {
      "name": "张三",
      "email": "[email]",
      "phone": "[phone_number]",
      "education": [
        {
          "school": "北京大学",
          "degree": "硕士",
          "major": "计算机科学与技术",
          "year": "2018"
        }
      ],
      "skills": ["Python", "Go", "Docker", "Kubernetes", "MySQL", "Redis"],
      "experience_years": 5
    }
    
    💡 **关键技巧**:使用 `response_format={"type": "json_object"}` 可以强制 GPT 输出合法 JSON,大幅降低解析失败率。

    五、组合技巧:构建实用的 Prompt 模板引擎

    实际项目中,我们需要把上述技巧组合起来,构建可复用的 Prompt 模板。

    
    from dataclasses import dataclass
    from typing import Optional
    
    @dataclass
    class PromptTemplate:
        """可复用的 Prompt 模板"""
        role: str
        task: str
        constraints: list[str]
        output_format: str
        examples: Optional[list[dict]] = None
    
        def build_system_prompt(self) -> str:
            prompt = f"# 角色\n{self.role}\n\n# 任务\n{self.task}\n"
            if self.constraints:
                prompt += "\n# 约束条件\n"
                for i, c in enumerate(self.constraints, 1):
                    prompt += f"{i}. {c}\n"
            prompt += f"\n# 输出格式\n{self.output_format}\n"
            return prompt
    
        def build_messages(self, user_input: str) -> list[dict]:
            messages = [{"role": "system", "content": self.build_system_prompt()}]
            if self.examples:
                for ex in self.examples:
                    messages.append({"role": "user", "content": ex["input"]})
                    messages.append({"role": "assistant", "content": ex["output"]})
            messages.append({"role": "user", "content": user_input})
            return messages
    
    # 使用示例:创建一个 SQL 生成器模板
    sql_generator = PromptTemplate(
        role="你是一位精通 SQL 的数据库专家",
        task="根据用户的自然语言描述生成对应的 SQL 查询语句",
        constraints=[
            "只生成 SELECT 查询,不生成修改数据的语句",
            "使用标准 SQL 语法,兼容 MySQL 和 PostgreSQL",
            "对复杂查询添加注释说明"
        ],
        output_format="直接输出 SQL 代码,用 ```sql 包裹",
        examples=[
            {
                "input": "查询所有年龄大于18岁的用户",
                "output": "```sql\nSELECT * FROM users WHERE age > 18;\n```"
            }
        ]
    )
    
    messages = sql_generator.build_messages("查询每个部门的平均工资,只显示平均工资超过1万的部门")
    response = client.chat.completions.create(model="gpt-4o", messages=messages, temperature=0)
    print(response.choices[0].message.content)
    

    总结:Prompt Engineering 核心原则

    经过本篇的学习,总结 5 条核心原则:

    1. **明确角色**:用 System Prompt 定义清晰的角色和边界

    2. **提供示例**:Few-shot 比长篇描述更高效

    3. **引导思考**:CoT 让模型的推理更可靠

    4. **约束输出**:结构化输出让结果可编程处理

    5. **模板复用**:将技巧封装为可复用的模板,提升开发效率

    ---

    📖 **下一篇预告**:《AI/LLM应用开发实战(三):Function Calling 与工具调用——让 LLM 连接真实世界》—— 我们将学习如何让 LLM 调用外部函数和 API,实现查天气、查数据库、执行代码等真实场景的能力扩展!

    ---

    🔔 如果觉得有帮助,欢迎点赞、收藏、关注,后续更新不迷路!
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐