promptfoo自定义prompt生成器

prompt较为复杂,需组合多个文档,为了保持原有文档结构,便于管理,写一个python文件用于组装prompt

复制代码
# promptfooconfig.yaml - HelloWorld项目配置
# 用于测试和评估prompt质量

prompts:
  # 从 project_docs/{doc_id} 文件夹读取文件并拼接生成 prompt
  - file://prompts/prompt_from_doc.py:main

providers:
  # 使用Python自定义Provider对接公司 LLM
  - id: python:./providers/llm.py
    label:  LLM

# LLM评判器配置(用于llm-rubric等模型评估断言)
defaultTest:
  options:
    providers:
      - python:./providers/llm.py

tests:
  - name: brief标签结构验证
    description: 返回结果必须包含<brief></brief>部分,且以此为开头
    providers:
      - python:./providers/llm.py
    vars:
      doc_id: "1"
      system_prompt: system_prompts/prompt1.0.md
    assert:
      - type: regex
        value: '^<brief>'
      - type: contains
        value: '</brief>'
  - name: 标题完整性检查
    description: 必须包含评估报告的5个一级标题
    providers:
      - python:./providers/llm.py
    vars:
      doc_id: "1"
      system_prompt: system_prompts/prompt1.0.md
    assert:
      - type: contains
        value: "综合评估摘要"
      - type: contains
        value: "分阶段评估详情"
      - type: contains
        value: "关键问题清单"
      - type: contains
        value: "改进建议"
      - type: contains
        value: "归档决策建议"
  - name: 分数校验
    description: 检查总分是否等于各维度分数相加(需求符合性35+架构设计合理性35+设计完整性15+可实施性15=100)
    providers:
      - python:./providers/llm.py
    vars:
      doc_id: "1"
      system_prompt: system_prompts/prompt1.0.md
    assert:
      - type: llm-rubric
        provider: python:./providers/llm.py
        value: |
          请仔细检查评估报告中的分数计算是否正确。
          
          评分体系:
          - 需求符合性:满分35分
          - 架构设计合理性:满分35分
          - 设计完整性:满分15分
          - 可实施性:满分15分
          - 总分应该等于上述四个维度分数的算术和(满分100分)
          
          检查要点:
          1. 从报告中提取"需求符合性"、"架构设计合理性"、"设计完整性"、"可实施性"四个维度的分数
          2. 计算四个维度的算术和
          3. 检查报告中标注的"总分"或"综合评分"是否与上述算术和一致
          4. 如果发现分数计算错误(例如总分不等于维度之和),则不通过
          
          输出要求:
          - 如果分数计算完全正确,输出PASS
          - 如果发现任何分数计算错误,解释错误原因并输出FAIL
  - name: truth分数范围校验
    description: 检查评分是否符合truth.md中的预期(分数在60-80之间)
    providers:
      - python:./providers/llm.py
    vars:
      doc_id: "2"
      system_prompt: system_prompts/prompt1.0.md
      truth_range: "60-80"
    assert:
      - type: llm-rubric
        provider: python:./providers/llm.py
        value: |
          你是一位评分验证专家。请根据以下预期范围验证评估报告的评分是否合理。
          
          预期分数范围:60-80分(即大于60分,小于等于80分)
          
          验证步骤:
          1. 从评估报告中提取"总分"、"综合评分"或类似的总分数据
          2. 判断提取的总分是否在预期范围内(60-80分)
          3. 同时检查评级的合理性,例如:
             - 60-69分应对应C级
             - 70-84分应对应B级
             - 85-100分应对应A级
          
          输出要求:
          - 如果评分在预期范围内且评级一致,输出PASS
          - 如果评分超出预期范围或评级不一致,解释问题并输出FAIL
output:
  - type: table
    filename: results/table.html
  - type: json
    filename: results/output.json

python文件

python 复制代码
"""
prompt生成器 - 根据编号读取project_docs目录下对应文件夹的内容并拼接
使用方法:在 promptfooconfig.yaml 中配置 raw: "file://prompts/prompt_from_doc.py"
测试时通过 vars.doc_id 指定文件夹编号,vars.system_prompt 指定system prompt路径
渲染逻辑:替换system_prompt文件中的 {project.md} 为 project_docs/{doc_id}/project.md 内容
"""

import os
import re
from typing import Dict, Any

# 基础目录,相对于当前工作目录
BASE_DIR = "project_docs"


def read_file_content(filepath: str) -> str:
    """读取单个文件内容"""
    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            return f.read()
    except Exception as e:
        return f"[读取文件失败: {filepath}, 错误: {e}]"


def generate_prompt(doc_id: str, system_prompt_path: str, base_dir: str = BASE_DIR) -> str:
    """
    根据文件夹编号和system prompt路径生成完整prompt
    
    Args:
        doc_id: 文件夹编号
        system_prompt_path: system prompt文件路径
        base_dir: project_docs基础目录路径
        
    Returns:
        渲染后的完整prompt字符串
    """
    # 检查system prompt文件是否存在
    if not os.path.isfile(system_prompt_path):
        return f"错误:找不到system prompt文件 {system_prompt_path}"
    
    # 读取system prompt模板
    system_template = read_file_content(system_prompt_path)
    
    # 获取project.md文件内容
    project_file = os.path.join(base_dir, str(doc_id), "project.md")
    project_content = read_file_content(project_file)
    
    # 替换 {project.md} 占位符
    rendered = system_template.replace("{project.md}", project_content)
    
    return rendered


def main(context: Dict[str, Any]) -> str:
    """
    promptfoo调用的主入口函数
    
    Args:
        context: 包含vars等上下文的字典
        
    Returns:
        生成的prompt字符串
    """
    vars = context.get("vars", {})
    
    # 从vars中获取文件夹编号,默认为"1"
    doc_id = vars.get("doc_id", "1")
    
    # 从vars中获取system prompt路径
    system_prompt_path = vars.get("system_prompt", "system_prompts/prompt2.0.md")
    
    # 可选:自定义基础目录
    custom_base_dir = vars.get("doc_base_dir", BASE_DIR)
    
    return generate_prompt(doc_id, system_prompt_path, custom_base_dir)


# 便捷测试
if __name__ == "__main__":
    import sys
    
    # 参数: doc_id system_prompt_path
    test_id = sys.argv[1] if len(sys.argv) > 1 else "1"
    test_system = sys.argv[2] if len(sys.argv) > 2 else "system_prompts/prompt2.0.md"
    
    result = main({"vars": {"doc_id": test_id, "system_prompt": test_system}})
    print(f"=== 生成的Prompt (doc_id={test_id}, system_prompt={test_system}) ===")
    print(result)
    print("=" * 50)
相关推荐
_waylau1 小时前
“Java+AI全栈工程师”问答02:Spring Boot 自动配置原理
java·开发语言·spring boot·后端·spring
JAVA面经实录9171 小时前
Java架构师最终完整版学习路线图
java·开发语言·学习
上海蓝色星球1 小时前
从工具到资产:CER V2.0 造价机器人如何重构企业核心竞争力
java·数据库·mysql
spencer_tseng1 小时前
System2.java
java·system
222you1 小时前
Claude Code接入DeepSeek-v4模型
java·服务器·前端
i220818 Faiz Ul1 小时前
高校教务|教务管理|基于springboot+vue的高校教务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·高校教务系统
Ting-yu1 小时前
SpringCloud快速入门(4)---- nacos安装与使用
java·spring·spring cloud
木子墨5161 小时前
工程算法实战 | 从LRU到手写本地缓存:LinkedHashMap → 双向链表+哈希表 → Caffeine 原理
java·数据结构·算法·链表·缓存
无尽冬.2 小时前
个人八股之三层架构
java·经验分享·后端·架构·异世界