第18章 提示词工程工具链

0 阅读28分钟

本章导读

提示词工程的效率和质量很大程度上取决于所使用的工具。本章将系统介绍当前主流的提示词工程工具链,包括提示词开发工具、测试调试工具、管理工具以及大模型应用开发框架,帮助读者构建完整的提示词工程工具体系。

18.1 提示词开发工具:LangChain Prompt Hub、DSPy

18.1.1 LangChain Prompt Hub

LangChain Prompt Hub是一个用于发现、分享和管理提示词的中心化平台。

核心功能:

# LangChain Prompt Hub 使用示例
"""
# 安装
pip install langchain langchainhub

# 从Hub拉取提示词
from langchain import hub

# 获取特定提示词
prompt = hub.pull("hwchase17/openai-functions-template")

# 查看提示词内容
print(prompt.template)
"""

class PromptHubConcept:
    """Prompt Hub 核心概念说明"""
    
    CONCEPTS = {
        "repository": {
            "description": "提示词仓库",
            "features": [
                "版本控制:每个提示词都有版本历史",
                "分类标签:按用途、领域、模型分类",
                "社区贡献:开源社区共享优质提示词",
                "搜索发现:通过关键词快速找到所需提示词"
            ]
        },
        "versioning": {
            "description": "版本管理",
            "features": [
                "语义化版本:major.minor.patch",
                "变更历史:记录每次修改的内容",
                "回滚能力:随时回退到历史版本",
                "版本对比:比较不同版本的差异"
            ]
        },
        "collaboration": {
            "description": "协作功能",
            "features": [
                "Fork:基于现有提示词创建变体",
                "PR:提交改进建议",
                "评论:讨论提示词优化方案",
                "评分:社区评分机制"
            ]
        }
    }

自定义Prompt Hub实现:

from typing import Dict, List, Optional
from dataclasses import dataclass, asdict
from datetime import datetime
import json

@dataclass
class HubPrompt:
    """Hub提示词定义"""
    id: str
    name: str
    description: str
    template: str
    tags: List[str]
    author: str
    version: str
    variables: List[str]
    created_at: datetime
    updated_at: datetime
    usage_count: int = 0
    rating: float = 0.0
    
    def to_dict(self) -> Dict:
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict) -> 'HubPrompt':
        return cls(**data)

class LocalPromptHub:
    """本地Prompt Hub实现"""
    
    def __init__(self, storage_path: str = "./prompt_hub"):
        self.storage_path = storage_path
        self.prompts: Dict[str, HubPrompt] = {}
        self.index = {"tags": {}, "authors": {}}
        self._load_prompts()
    
    def _load_prompts(self):
        """加载已保存的提示词"""
        import os
        if os.path.exists(f"{self.storage_path}/prompts.json"):
            with open(f"{self.storage_path}/prompts.json", "r") as f:
                data = json.load(f)
                for prompt_id, prompt_data in data.items():
                    self.prompts[prompt_id] = HubPrompt.from_dict(prompt_data)
                    self._update_index(self.prompts[prompt_id])
    
    def _save_prompts(self):
        """保存提示词到文件"""
        import os
        os.makedirs(self.storage_path, exist_ok=True)
        
        data = {k: v.to_dict() for k, v in self.prompts.items()}
        with open(f"{self.storage_path}/prompts.json", "w") as f:
            json.dump(data, f, indent=2, default=str)
    
    def _update_index(self, prompt: HubPrompt):
        """更新索引"""
        # 标签索引
        for tag in prompt.tags:
            if tag not in self.index["tags"]:
                self.index["tags"][tag] = []
            if prompt.id not in self.index["tags"][tag]:
                self.index["tags"][tag].append(prompt.id)
        
        # 作者索引
        if prompt.author not in self.index["authors"]:
            self.index["authors"][prompt.author] = []
        if prompt.id not in self.index["authors"][prompt.author]:
            self.index["authors"][prompt.author].append(prompt.id)
    
    def publish(self, prompt: HubPrompt) -> str:
        """发布提示词"""
        if prompt.id in self.prompts:
            # 更新现有提示词
            prompt.updated_at = datetime.now()
            # 版本升级
            parts = prompt.version.split(".")
            parts[2] = str(int(parts[2]) + 1)
            prompt.version = ".".join(parts)
        else:
            prompt.created_at = datetime.now()
            prompt.updated_at = datetime.now()
        
        self.prompts[prompt.id] = prompt
        self._update_index(prompt)
        self._save_prompts()
        
        return prompt.id
    
    def pull(self, prompt_id: str, version: str = None) -> Optional[HubPrompt]:
        """拉取提示词"""
        prompt = self.prompts.get(prompt_id)
        if prompt:
            prompt.usage_count += 1
            self._save_prompts()
        return prompt
    
    def search(self, query: str = None, tags: List[str] = None, 
              author: str = None) -> List[HubPrompt]:
        """搜索提示词"""
        results = list(self.prompts.values())
        
        if query:
            query_lower = query.lower()
            results = [
                p for p in results
                if query_lower in p.name.lower() 
                or query_lower in p.description.lower()
                or query_lower in p.template.lower()
            ]
        
        if tags:
            results = [
                p for p in results
                if any(tag in p.tags for tag in tags)
            ]
        
        if author:
            results = [p for p in results if p.author == author]
        
        # 按评分和使用量排序
        results.sort(key=lambda p: (p.rating, p.usage_count), reverse=True)
        
        return results
    
    def get_popular(self, limit: int = 10) -> List[HubPrompt]:
        """获取热门提示词"""
        sorted_prompts = sorted(
            self.prompts.values(),
            key=lambda p: p.usage_count,
            reverse=True
        )
        return sorted_prompts[:limit]
    
    def rate(self, prompt_id: str, rating: float):
        """评分"""
        if prompt_id in self.prompts:
            prompt = self.prompts[prompt_id]
            # 简单的加权平均
            prompt.rating = (prompt.rating * prompt.usage_count + rating) / (prompt.usage_count + 1)
            self._save_prompts()

# 使用示例
hub = LocalPromptHub()

# 创建并发布提示词
new_prompt = HubPrompt(
    id="summarization/basic",
    name="基础摘要生成",
    description="生成简洁的文本摘要",
    template="""请为以下文本生成摘要:

文本:
{text}

要求:
- 摘要长度:不超过{max_length}字
- 包含主要观点
- 保持客观中立

摘要:""",
    tags=["summarization", "nlp", "basic"],
    author="user001",
    version="1.0.0",
    variables=["text", "max_length"],
    created_at=datetime.now(),
    updated_at=datetime.now()
)

hub.publish(new_prompt)

# 搜索提示词
results = hub.search(query="摘要", tags=["nlp"])
print(f"找到 {len(results)} 个相关提示词")

# 拉取提示词
pulled = hub.pull("summarization/basic")
if pulled:
    print(f"提示词模板:{pulled.template[:100]}...")

18.1.2 DSPy框架

DSPy(Declarative Self-improving Python)是一个用于算法优化提示词和权重的框架。

核心概念:

"""
DSPy 核心概念:

1. Signatures(签名)
   - 声明式定义任务的输入输出
   - 替代手工编写的提示词

2. Modules(模块)
   - 预置的提示模式(ChainOfThought, ReAct等)
   - 可组合的构建块

3. Optimizers(优化器)
   - 自动优化提示词和示例
   - 基于训练数据学习最佳配置
"""

# DSPy 概念性示例(实际使用需要安装dspy库)
"""
import dspy

# 定义签名
class Summarize(dspy.Signature):
    document = dspy.InputField()
    summary = dspy.OutputField(desc="简洁的文档摘要")

# 使用模块
summarizer = dspy.ChainOfThought(Summarize)

# 执行
result = summarizer(document="长文本内容...")
print(result.summary)

# 优化
from dspy.teleprompt import BootstrapFewShot

optimizer = BootstrapFewShot(metric=summarize_metric)
optimized_summarizer = optimizer.compile(summarizer, trainset=train_data)
"""

DSPy风格提示词优化实现:

from typing import Callable, List, Dict, Any
from dataclasses import dataclass
import random

@dataclass
class Example:
    """训练示例"""
    inputs: Dict[str, Any]
    outputs: Dict[str, Any]
    
@dataclass
class Signature:
    """任务签名"""
    name: str
    input_fields: List[str]
    output_fields: List[str]
    instructions: str
    
class DSPyStyleOptimizer:
    """DSPy风格的提示词优化器"""
    
    def __init__(self, signature: Signature, llm_client):
        self.signature = signature
        self.llm_client = llm_client
        self.demos = []  # 示例集合
        self.optimized_prompt = None
    
    def add_demo(self, example: Example):
        """添加示例"""
        self.demos.append(example)
    
    def build_base_prompt(self) -> str:
        """构建基础提示词"""
        prompt = f"{self.signature.instructions}\n\n"
        
        # 添加输入字段说明
        prompt += "输入字段:\n"
        for field in self.signature.input_fields:
            prompt += f"- {field}\n"
        
        # 添加输出字段说明
        prompt += "\n输出字段:\n"
        for field in self.signature.output_fields:
            prompt += f"- {field}\n"
        
        return prompt
    
    def build_few_shot_prompt(self, num_demos: int = 3) -> str:
        """构建Few-shot提示词"""
        prompt = self.build_base_prompt()
        
        # 选择示例
        selected_demos = random.sample(self.demos, min(num_demos, len(self.demos)))
        
        if selected_demos:
            prompt += "\n示例:\n"
            for i, demo in enumerate(selected_demos, 1):
                prompt += f"\n示例 {i}:\n"
                
                # 输入
                for field, value in demo.inputs.items():
                    prompt += f"{field}: {value}\n"
                
                # 输出
                for field, value in demo.outputs.items():
                    prompt += f"{field}: {value}\n"
        
        prompt += "\n现在请处理以下输入:\n"
        return prompt
    
    def optimize(self, metric_fn: Callable, trainset: List[Example], 
                 num_iterations: int = 10) -> str:
        """优化提示词"""
        best_score = 0
        best_prompt = None
        best_demos = []
        
        for iteration in range(num_iterations):
            # 随机选择示例组合
            num_demos = random.randint(1, min(5, len(trainset)))
            selected = random.sample(trainset, num_demos)
            
            # 构建提示词
            self.demos = selected
            prompt = self.build_few_shot_prompt(num_demos)
            
            # 评估
            score = self._evaluate_prompt(prompt, metric_fn, trainset)
            
            if score > best_score:
                best_score = score
                best_prompt = prompt
                best_demos = selected.copy()
        
        self.optimized_prompt = best_prompt
        self.demos = best_demos
        
        return best_prompt
    
    def _evaluate_prompt(self, prompt: str, metric_fn: Callable, 
                        trainset: List[Example]) -> float:
        """评估提示词效果"""
        scores = []
        
        # 在验证集上测试
        for example in trainset[:10]:  # 使用前10个示例评估
            try:
                # 构建完整提示词
                full_prompt = prompt
                for field, value in example.inputs.items():
                    full_prompt += f"{field}: {value}\n"
                
                # 调用模型
                output = self.llm_client.generate(full_prompt)
                
                # 解析输出
                predicted = self._parse_output(output)
                
                # 计算分数
                score = metric_fn(predicted, example.outputs)
                scores.append(score)
            except Exception as e:
                scores.append(0)
        
        return sum(scores) / len(scores) if scores else 0
    
    def _parse_output(self, output: str) -> Dict[str, Any]:
        """解析模型输出"""
        result = {}
        lines = output.strip().split('\n')
        
        for line in lines:
            if ':' in line:
                key, value = line.split(':', 1)
                result[key.strip()] = value.strip()
        
        return result

# 使用示例
def mock_llm_client():
    """模拟LLM客户端"""
    class MockLLM:
        def generate(self, prompt: str) -> str:
            return "summary: 这是一个摘要\nkey_points: 要点1, 要点2"
    return MockLLM()

signature = Signature(
    name="text_summarization",
    input_fields=["document"],
    output_fields=["summary", "key_points"],
    instructions="请为输入文档生成摘要和关键要点。"
)

optimizer = DSPyStyleOptimizer(signature, mock_llm_client())

# 添加训练示例
train_examples = [
    Example(
        inputs={"document": "长文本1..."},
        outputs={"summary": "摘要1", "key_points": "要点1, 要点2"}
    ),
    Example(
        inputs={"document": "长文本2..."},
        outputs={"summary": "摘要2", "key_points": "要点A, 要点B"}
    )
]

for ex in train_examples:
    optimizer.add_demo(ex)

# 构建提示词
prompt = optimizer.build_few_shot_prompt()
print("优化后的提示词:")
print(prompt)

18.1.3 其他提示词开发工具

OTHER_DEV_TOOLS = """
其他提示词开发工具:

1. **PromptIDE**
   - OpenAI提供的提示词开发环境
   - 支持版本控制、协作、测试
   - 与OpenAI API深度集成

2. **Promptmetheus**
   - 可视化提示词编辑器
   - 支持变量、条件、循环
   - 实时预览和测试

3. **PromptLayer**
   - 提示词版本管理和追踪
   - 性能分析和A/B测试
   - 团队协作功能

4. **Humanloop**
   - 提示词工程和评估平台
   - 人工反馈集成
   - 模型微调支持

5. **Vellum**
   - 企业级提示词管理平台
   - 部署和监控功能
   - 多环境支持
"""

18.2 提示词测试与调试工具:LangSmith、PromptEval

18.2.1 LangSmith

LangSmith是LangChain提供的全生命周期LLM应用开发平台,包含强大的测试和调试功能。

核心功能:

LANGSMITH_FEATURES = """
LangSmith 核心功能:

1. **Tracing(追踪)**
   - 记录完整的调用链路
   - 可视化Chain执行流程
   - 查看每一步的输入输出

2. **Debugging(调试)**
   - 查看实际发送的提示词
   - 检查模型参数
   - 分析token使用情况

3. **Testing(测试)**
   - 批量测试数据集
   - 自动化评估
   - 回归测试

4. **Monitoring(监控)**
   - 生产环境监控
   - 性能指标追踪
   - 错误告警
"""

# LangSmith 使用示例
"""
import os
os.environ["LANGCHAIN_API_KEY"] = "your-api-key"
os.environ["LANGCHAIN_PROJECT"] = "my-project"

from langchain import OpenAI, LLMChain, PromptTemplate
from langchain.callbacks import LangChainTracer

# 启用追踪
tracer = LangChainTracer()

# 创建Chain
llm = OpenAI()
template = PromptTemplate(
    input_variables=["product"],
    template="为{product}写一段营销文案。"
)
chain = LLMChain(llm=llm, prompt=template, callbacks=[tracer])

# 执行(会自动记录到LangSmith)
result = chain.run("智能手表")
"""

本地调试工具实现:

import json
from datetime import datetime
from typing import Dict, List, Any, Optional
from dataclasses import dataclass, field, asdict

@dataclass
class TraceStep:
    """追踪步骤"""
    step_name: str
    step_type: str  # prompt, llm_call, tool_use, etc.
    inputs: Dict[str, Any]
    outputs: Dict[str, Any]
    start_time: datetime
    end_time: Optional[datetime] = None
    latency_ms: float = 0.0
    token_usage: Dict[str, int] = field(default_factory=dict)
    metadata: Dict[str, Any] = field(default_factory=dict)

@dataclass
class Trace:
    """追踪记录"""
    trace_id: str
    project_name: str
    start_time: datetime
    end_time: Optional[datetime] = None
    steps: List[TraceStep] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)

class LocalDebugger:
    """本地调试器"""
    
    def __init__(self, project_name: str = "default"):
        self.project_name = project_name
        self.traces: List[Trace] = []
        self.current_trace: Optional[Trace] = None
        self.current_step: Optional[TraceStep] = None
    
    def start_trace(self, trace_id: str = None, metadata: Dict = None) -> str:
        """开始追踪"""
        if trace_id is None:
            trace_id = f"trace_{datetime.now().strftime('%Y%m%d%H%M%S')}"
        
        self.current_trace = Trace(
            trace_id=trace_id,
            project_name=self.project_name,
            start_time=datetime.now(),
            metadata=metadata or {}
        )
        
        return trace_id
    
    def start_step(self, step_name: str, step_type: str, 
                  inputs: Dict[str, Any]):
        """开始步骤"""
        if self.current_trace is None:
            self.start_trace()
        
        self.current_step = TraceStep(
            step_name=step_name,
            step_type=step_type,
            inputs=inputs,
            start_time=datetime.now()
        )
    
    def end_step(self, outputs: Dict[str, Any], 
                token_usage: Dict[str, int] = None,
                metadata: Dict[str, Any] = None):
        """结束步骤"""
        if self.current_step is None:
            return
        
        self.current_step.end_time = datetime.now()
        self.current_step.outputs = outputs
        self.current_step.latency_ms = (
            self.current_step.end_time - self.current_step.start_time
        ).total_seconds() * 1000
        
        if token_usage:
            self.current_step.token_usage = token_usage
        
        if metadata:
            self.current_step.metadata = metadata
        
        self.current_trace.steps.append(self.current_step)
        self.current_step = None
    
    def end_trace(self, metadata: Dict = None):
        """结束追踪"""
        if self.current_trace is None:
            return
        
        self.current_trace.end_time = datetime.now()
        
        if metadata:
            self.current_trace.metadata.update(metadata)
        
        self.traces.append(self.current_trace)
        self.current_trace = None
    
    def get_trace(self, trace_id: str) -> Optional[Trace]:
        """获取追踪记录"""
        for trace in self.traces:
            if trace.trace_id == trace_id:
                return trace
        return None
    
    def visualize_trace(self, trace_id: str) -> str:
        """可视化追踪"""
        trace = self.get_trace(trace_id)
        if not trace:
            return "Trace not found"
        
        output = f"""
Trace ID: {trace.trace_id}
Project: {trace.project_name}
Duration: {(trace.end_time - trace.start_time).total_seconds():.2f}s

Steps:
"""
        for i, step in enumerate(trace.steps, 1):
            output += f"\n{i}. {step.step_name} ({step.step_type})\n"
            output += f"   Latency: {step.latency_ms:.2f}ms\n"
            
            if step.token_usage:
                output += f"   Tokens: {step.token_usage}\n"
            
            output += f"   Inputs: {json.dumps(step.inputs, ensure_ascii=False)[:100]}...\n"
            output += f"   Outputs: {json.dumps(step.outputs, ensure_ascii=False)[:100]}...\n"
        
        return output
    
    def export_traces(self, filepath: str):
        """导出追踪记录"""
        data = [asdict(trace) for trace in self.traces]
        with open(filepath, 'w') as f:
            json.dump(data, f, indent=2, default=str)
    
    def analyze_performance(self) -> Dict:
        """分析性能"""
        if not self.traces:
            return {}
        
        all_steps = []
        for trace in self.traces:
            all_steps.extend(trace.steps)
        
        if not all_steps:
            return {}
        
        latencies = [s.latency_ms for s in all_steps]
        
        return {
            "total_traces": len(self.traces),
            "total_steps": len(all_steps),
            "avg_latency_ms": sum(latencies) / len(latencies),
            "max_latency_ms": max(latencies),
            "min_latency_ms": min(latencies),
            "step_type_distribution": self._count_by_type(all_steps)
        }
    
    def _count_by_type(self, steps: List[TraceStep]) -> Dict[str, int]:
        """按类型统计"""
        counts = {}
        for step in steps:
            counts[step.step_type] = counts.get(step.step_type, 0) + 1
        return counts

# 使用示例
debugger = LocalDebugger(project_name="my_chatbot")

# 开始追踪
debugger.start_trace("chat_001", metadata={"user_id": "user123"})

# 记录提示词构建步骤
debugger.start_step("build_prompt", "prompt", 
                   inputs={"template": "你好,{name}", "variables": {"name": "张三"}})
debugger.end_step(outputs={"full_prompt": "你好,张三"})

# 记录LLM调用步骤
debugger.start_step("llm_call", "llm_call", 
                   inputs={"prompt": "你好,张三", "model": "gpt-4"})
debugger.end_step(
    outputs={"response": "你好!有什么可以帮助你的?"},
    token_usage={"prompt_tokens": 10, "completion_tokens": 15, "total_tokens": 25},
    metadata={"temperature": 0.7}
)

# 结束追踪
debugger.end_trace()

# 可视化
print(debugger.visualize_trace("chat_001"))

# 性能分析
perf = debugger.analyze_performance()
print(f"平均延迟: {perf.get('avg_latency_ms', 0):.2f}ms")

18.2.2 PromptEval测试框架

from typing import List, Dict, Callable, Any
from dataclasses import dataclass
from concurrent.futures import ThreadPoolExecutor, as_completed
import time

@dataclass
class TestCase:
    """测试用例"""
    name: str
    inputs: Dict[str, Any]
    expected_outputs: Dict[str, Any] = None
    expected_behavior: str = None
    tags: List[str] = None
    
@dataclass
class TestResult:
    """测试结果"""
    test_name: str
    passed: bool
    actual_output: Any
    execution_time_ms: float
    error_message: str = None
    details: Dict = None

class PromptEvalFramework:
    """提示词评估框架"""
    
    def __init__(self):
        self.test_cases: List[TestCase] = []
        self.evaluators: Dict[str, Callable] = {}
        self.results: List[TestResult] = []
    
    def add_test_case(self, test_case: TestCase):
        """添加测试用例"""
        self.test_cases.append(test_case)
    
    def register_evaluator(self, name: str, evaluator: Callable):
        """注册评估器"""
        self.evaluators[name] = evaluator
    
    def run_tests(self, prompt_executor: Callable, 
                  parallel: bool = False) -> List[TestResult]:
        """运行测试"""
        self.results = []
        
        if parallel:
            with ThreadPoolExecutor(max_workers=5) as executor:
                futures = {
                    executor.submit(self._run_single_test, tc, prompt_executor): tc
                    for tc in self.test_cases
                }
                
                for future in as_completed(futures):
                    result = future.result()
                    self.results.append(result)
        else:
            for test_case in self.test_cases:
                result = self._run_single_test(test_case, prompt_executor)
                self.results.append(result)
        
        return self.results
    
    def _run_single_test(self, test_case: TestCase, 
                        prompt_executor: Callable) -> TestResult:
        """运行单个测试"""
        start_time = time.time()
        
        try:
            # 执行提示词
            actual_output = prompt_executor(test_case.inputs)
            
            # 评估结果
            passed = self._evaluate_result(
                test_case, actual_output
            )
            
            execution_time = (time.time() - start_time) * 1000
            
            return TestResult(
                test_name=test_case.name,
                passed=passed,
                actual_output=actual_output,
                execution_time_ms=execution_time
            )
            
        except Exception as e:
            execution_time = (time.time() - start_time) * 1000
            
            return TestResult(
                test_name=test_case.name,
                passed=False,
                actual_output=None,
                execution_time_ms=execution_time,
                error_message=str(e)
            )
    
    def _evaluate_result(self, test_case: TestCase, 
                        actual_output: Any) -> bool:
        """评估结果"""
        # 如果有期望输出,进行精确匹配
        if test_case.expected_outputs:
            return actual_output == test_case.expected_outputs
        
        # 如果有期望行为描述,使用LLM评估
        if test_case.expected_behavior:
            # 这里可以调用LLM进行评估
            return True  # 简化处理
        
        # 默认通过
        return True
    
    def generate_report(self) -> Dict:
        """生成测试报告"""
        if not self.results:
            return {"error": "No test results"}
        
        total = len(self.results)
        passed = sum(1 for r in self.results if r.passed)
        failed = total - passed
        
        avg_time = sum(r.execution_time_ms for r in self.results) / total
        
        failed_tests = [
            {
                "name": r.test_name,
                "error": r.error_message,
                "output": r.actual_output
            }
            for r in self.results if not r.passed
        ]
        
        return {
            "summary": {
                "total": total,
                "passed": passed,
                "failed": failed,
                "pass_rate": passed / total if total > 0 else 0,
                "avg_execution_time_ms": avg_time
            },
            "failed_tests": failed_tests,
            "all_results": [
                {
                    "name": r.test_name,
                    "passed": r.passed,
                    "time_ms": r.execution_time_ms
                }
                for r in self.results
            ]
        }

# 使用示例
eval_framework = PromptEvalFramework()

# 添加测试用例
eval_framework.add_test_case(TestCase(
    name="basic_greeting",
    inputs={"message": "你好"},
    expected_behavior="应该礼貌地回应问候"
))

eval_framework.add_test_case(TestCase(
    name="empty_input",
    inputs={"message": ""},
    expected_behavior="应该优雅地处理空输入"
))

eval_framework.add_test_case(TestCase(
    name="long_input",
    inputs={"message": "很长的文本..." * 100},
    expected_behavior="应该能够处理长文本"
))

# 定义执行器
def mock_executor(inputs):
    return {"response": f"收到: {inputs.get('message', '')[:20]}"}

# 运行测试
results = eval_framework.run_tests(mock_executor)

# 生成报告
report = eval_framework.generate_report()
print(f"通过率: {report['summary']['pass_rate']:.2%}")
print(f"失败测试数: {len(report['failed_tests'])}")

18.3 提示词管理工具:版本控制、权限管理、知识库

18.3.1 提示词版本控制系统

import hashlib
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass, asdict
from datetime import datetime
import json
import difflib

@dataclass
class PromptVersion:
    """提示词版本"""
    version_id: str
    prompt_id: str
    content: str
    author: str
    created_at: datetime
    commit_message: str
    parent_version: Optional[str] = None
    tags: List[str] = None
    metadata: Dict = None
    
    def __post_init__(self):
        if self.tags is None:
            self.tags = []
        if self.metadata is None:
            self.metadata = {}
    
    @property
    def content_hash(self) -> str:
        """计算内容哈希"""
        return hashlib.sha256(self.content.encode()).hexdigest()[:16]

class PromptVersionControl:
    """提示词版本控制系统"""
    
    def __init__(self, storage_path: str = "./prompt_vcs"):
        self.storage_path = storage_path
        self.versions: Dict[str, List[PromptVersion]] = {}
        self.branches: Dict[str, Dict[str, str]] = {}  # prompt_id -> {branch_name: version_id}
        self._load_data()
    
    def _load_data(self):
        """加载数据"""
        import os
        versions_file = f"{self.storage_path}/versions.json"
        branches_file = f"{self.storage_path}/branches.json"
        
        if os.path.exists(versions_file):
            with open(versions_file, 'r') as f:
                data = json.load(f)
                for prompt_id, versions_data in data.items():
                    self.versions[prompt_id] = [
                        PromptVersion(**v) for v in versions_data
                    ]
        
        if os.path.exists(branches_file):
            with open(branches_file, 'r') as f:
                self.branches = json.load(f)
    
    def _save_data(self):
        """保存数据"""
        import os
        os.makedirs(self.storage_path, exist_ok=True)
        
        versions_data = {
            k: [asdict(v) for v in vs]
            for k, vs in self.versions.items()
        }
        
        with open(f"{self.storage_path}/versions.json", 'w') as f:
            json.dump(versions_data, f, indent=2, default=str)
        
        with open(f"{self.storage_path}/branches.json", 'w') as f:
            json.dump(self.branches, f, indent=2)
    
    def commit(self, prompt_id: str, content: str, author: str,
              message: str, tags: List[str] = None) -> str:
        """提交新版本"""
        # 生成版本ID
        timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
        version_id = f"{prompt_id}_v{timestamp}"
        
        # 获取父版本
        parent_version = None
        if prompt_id in self.versions and self.versions[prompt_id]:
            parent_version = self.versions[prompt_id][-1].version_id
        
        # 创建新版本
        version = PromptVersion(
            version_id=version_id,
            prompt_id=prompt_id,
            content=content,
            author=author,
            created_at=datetime.now(),
            commit_message=message,
            parent_version=parent_version,
            tags=tags or []
        )
        
        # 保存
        if prompt_id not in self.versions:
            self.versions[prompt_id] = []
        self.versions[prompt_id].append(version)
        
        # 更新主分支
        if prompt_id not in self.branches:
            self.branches[prompt_id] = {}
        self.branches[prompt_id]["main"] = version_id
        
        self._save_data()
        
        return version_id
    
    def get_version(self, prompt_id: str, 
                   version_id: str = None) -> Optional[PromptVersion]:
        """获取特定版本"""
        if prompt_id not in self.versions:
            return None
        
        if version_id is None:
            # 返回最新版本
            return self.versions[prompt_id][-1] if self.versions[prompt_id] else None
        
        for version in self.versions[prompt_id]:
            if version.version_id == version_id:
                return version
        
        return None
    
    def get_history(self, prompt_id: str) -> List[PromptVersion]:
        """获取版本历史"""
        return self.versions.get(prompt_id, [])
    
    def diff(self, prompt_id: str, version_id1: str, 
            version_id2: str) -> str:
        """比较两个版本的差异"""
        v1 = self.get_version(prompt_id, version_id1)
        v2 = self.get_version(prompt_id, version_id2)
        
        if not v1 or not v2:
            return "Version not found"
        
        diff = difflib.unified_diff(
            v1.content.splitlines(keepends=True),
            v2.content.splitlines(keepends=True),
            fromfile=version_id1,
            tofile=version_id2
        )
        
        return ''.join(diff)
    
    def checkout(self, prompt_id: str, version_id: str) -> Optional[str]:
        """检出特定版本"""
        version = self.get_version(prompt_id, version_id)
        if version:
            return version.content
        return None
    
    def create_branch(self, prompt_id: str, branch_name: str, 
                     from_version: str = None):
        """创建分支"""
        if prompt_id not in self.branches:
            self.branches[prompt_id] = {}
        
        if from_version is None:
            # 从最新版本创建
            latest = self.get_version(prompt_id)
            from_version = latest.version_id if latest else None
        
        self.branches[prompt_id][branch_name] = from_version
        self._save_data()
    
    def merge_branch(self, prompt_id: str, branch_name: str, 
                    target_branch: str = "main"):
        """合并分支"""
        # 简化实现:将分支的最新版本复制到目标分支
        if prompt_id not in self.branches:
            return False
        
        if branch_name not in self.branches[prompt_id]:
            return False
        
        branch_version_id = self.branches[prompt_id][branch_name]
        self.branches[prompt_id][target_branch] = branch_version_id
        
        self._save_data()
        return True
    
    def tag(self, prompt_id: str, version_id: str, tag: str):
        """给版本打标签"""
        version = self.get_version(prompt_id, version_id)
        if version:
            version.tags.append(tag)
            self._save_data()
    
    def find_by_tag(self, prompt_id: str, tag: str) -> List[PromptVersion]:
        """通过标签查找版本"""
        versions = self.versions.get(prompt_id, [])
        return [v for v in versions if tag in v.tags]

# 使用示例
vcs = PromptVersionControl()

# 提交版本
v1 = vcs.commit(
    prompt_id="customer_service",
    content="你是客服助手...",
    author="alice",
    message="初始版本"
)

v2 = vcs.commit(
    prompt_id="customer_service",
    content="你是专业的客服助手...",
    author="bob",
    message="增加专业性描述"
)

# 查看历史
history = vcs.get_history("customer_service")
print(f"共有 {len(history)} 个版本")

# 比较差异
diff = vcs.diff("customer_service", v1, v2)
print("版本差异:")
print(diff)

# 打标签
vcs.tag("customer_service", v2, "stable")

# 查找标签
stable_versions = vcs.find_by_tag("customer_service", "stable")
print(f"稳定版本数: {len(stable_versions)}")

18.3.2 权限管理系统

from enum import Enum
from typing import Dict, List, Set
from dataclasses import dataclass

class Permission(Enum):
    """权限枚举"""
    READ = "read"
    WRITE = "write"
    DELETE = "delete"
    EXECUTE = "execute"
    ADMIN = "admin"

@dataclass
class User:
    """用户"""
    user_id: str
    name: str
    role: str
    groups: List[str]

@dataclass
class AccessControl:
    """访问控制"""
    resource_id: str
    owner: str
    permissions: Dict[str, List[Permission]]  # user/group -> permissions

class PromptAccessManager:
    """提示词访问管理器"""
    
    def __init__(self):
        self.users: Dict[str, User] = {}
        self.access_controls: Dict[str, AccessControl] = {}
        self.role_permissions = {
            "admin": [Permission.READ, Permission.WRITE, Permission.DELETE, 
                     Permission.EXECUTE, Permission.ADMIN],
            "editor": [Permission.READ, Permission.WRITE, Permission.EXECUTE],
            "viewer": [Permission.READ, Permission.EXECUTE],
            "guest": [Permission.READ]
        }
    
    def register_user(self, user: User):
        """注册用户"""
        self.users[user.user_id] = user
    
    def create_resource(self, resource_id: str, owner_id: str):
        """创建资源"""
        self.access_controls[resource_id] = AccessControl(
            resource_id=resource_id,
            owner=owner_id,
            permissions={owner_id: self.role_permissions["admin"]}
        )
    
    def grant_permission(self, resource_id: str, user_id: str, 
                        permissions: List[Permission]):
        """授予权限"""
        if resource_id not in self.access_controls:
            return False
        
        ac = self.access_controls[resource_id]
        
        # 检查授予者是否有ADMIN权限
        # 简化处理:假设调用者有权限
        
        ac.permissions[user_id] = permissions
        return True
    
    def revoke_permission(self, resource_id: str, user_id: str):
        """撤销权限"""
        if resource_id not in self.access_controls:
            return False
        
        ac = self.access_controls[resource_id]
        
        if user_id in ac.permissions:
            del ac.permissions[user_id]
            return True
        
        return False
    
    def check_permission(self, resource_id: str, user_id: str, 
                        permission: Permission) -> bool:
        """检查权限"""
        if resource_id not in self.access_controls:
            return False
        
        ac = self.access_controls[resource_id]
        user = self.users.get(user_id)
        
        if not user:
            return False
        
        # 检查直接权限
        if user_id in ac.permissions:
            if permission in ac.permissions[user_id]:
                return True
        
        # 检查组权限
        for group in user.groups:
            if group in ac.permissions:
                if permission in ac.permissions[group]:
                    return True
        
        # 检查角色权限
        if user.role in self.role_permissions:
            if permission in self.role_permissions[user.role]:
                return True
        
        return False
    
    def get_accessible_resources(self, user_id: str, 
                                 permission: Permission = Permission.READ) -> List[str]:
        """获取用户可访问的资源"""
        resources = []
        
        for resource_id, ac in self.access_controls.items():
            if self.check_permission(resource_id, user_id, permission):
                resources.append(resource_id)
        
        return resources
    
    def audit_log(self, resource_id: str, user_id: str, action: str, 
                  success: bool):
        """审计日志"""
        log_entry = {
            "timestamp": datetime.now().isoformat(),
            "resource_id": resource_id,
            "user_id": user_id,
            "action": action,
            "success": success
        }
        # 实际应用中应写入日志系统
        print(f"[AUDIT] {log_entry}")

# 使用示例
access_manager = PromptAccessManager()

# 注册用户
access_manager.register_user(User(
    user_id="user001",
    name="Alice",
    role="editor",
    groups=["engineering"]
))

access_manager.register_user(User(
    user_id="user002",
    name="Bob",
    role="viewer",
    groups=["engineering"]
))

# 创建资源
access_manager.create_resource("prompt_customer_service", "user001")

# 授予权限
access_manager.grant_permission(
    "prompt_customer_service",
    "user002",
    [Permission.READ, Permission.EXECUTE]
)

# 检查权限
can_write = access_manager.check_permission(
    "prompt_customer_service", "user002", Permission.WRITE
)
print(f"Bob can write: {can_write}")  # False

can_read = access_manager.check_permission(
    "prompt_customer_service", "user002", Permission.READ
)
print(f"Bob can read: {can_read}")  # True

18.3.3 提示词知识库

from typing import Dict, List, Optional, Set
from dataclasses import dataclass, field
import re

@dataclass
class KnowledgeEntry:
    """知识条目"""
    entry_id: str
    title: str
    content: str
    category: str
    tags: List[str] = field(default_factory=list)
    related_prompts: List[str] = field(default_factory=list)
    created_by: str = ""
    created_at: datetime = field(default_factory=datetime.now)

class PromptKnowledgeBase:
    """提示词知识库"""
    
    def __init__(self):
        self.entries: Dict[str, KnowledgeEntry] = {}
        self.category_index: Dict[str, List[str]] = {}
        self.tag_index: Dict[str, List[str]] = {}
        self.search_index: Dict[str, Set[str]] = {}  # 倒排索引
    
    def add_entry(self, entry: KnowledgeEntry):
        """添加条目"""
        self.entries[entry.entry_id] = entry
        
        # 更新分类索引
        if entry.category not in self.category_index:
            self.category_index[entry.category] = []
        self.category_index[entry.category].append(entry.entry_id)
        
        # 更新标签索引
        for tag in entry.tags:
            if tag not in self.tag_index:
                self.tag_index[tag] = []
            self.tag_index[tag].append(entry.entry_id)
        
        # 更新搜索索引
        self._update_search_index(entry)
    
    def _update_search_index(self, entry: KnowledgeEntry):
        """更新搜索索引"""
        # 分词并建立倒排索引
        text = f"{entry.title} {entry.content}"
        words = self._tokenize(text)
        
        for word in words:
            if word not in self.search_index:
                self.search_index[word] = set()
            self.search_index[word].add(entry.entry_id)
    
    def _tokenize(self, text: str) -> List[str]:
        """分词(简化实现)"""
        # 实际应用中应使用专业的分词工具
        words = re.findall(r'\b\w+\b', text.lower())
        return words
    
    def search(self, query: str, category: str = None, 
              tags: List[str] = None) -> List[KnowledgeEntry]:
        """搜索"""
        query_words = self._tokenize(query)
        
        # 获取候选条目
        candidate_ids = None
        for word in query_words:
            if word in self.search_index:
                if candidate_ids is None:
                    candidate_ids = self.search_index[word].copy()
                else:
                    candidate_ids &= self.search_index[word]
        
        if candidate_ids is None:
            candidate_ids = set(self.entries.keys())
        
        # 过滤
        results = []
        for entry_id in candidate_ids:
            entry = self.entries[entry_id]
            
            # 分类过滤
            if category and entry.category != category:
                continue
            
            # 标签过滤
            if tags and not all(tag in entry.tags for tag in tags):
                continue
            
            results.append(entry)
        
        # 按相关性排序(简化:匹配词数)
        results.sort(key=lambda e: self._relevance_score(e, query_words), 
                    reverse=True)
        
        return results
    
    def _relevance_score(self, entry: KnowledgeEntry, 
                        query_words: List[str]) -> int:
        """计算相关性分数"""
        text = f"{entry.title} {entry.content}".lower()
        score = 0
        for word in query_words:
            score += text.count(word)
        return score
    
    def get_by_category(self, category: str) -> List[KnowledgeEntry]:
        """按分类获取"""
        entry_ids = self.category_index.get(category, [])
        return [self.entries[eid] for eid in entry_ids]
    
    def get_by_tag(self, tag: str) -> List[KnowledgeEntry]:
        """按标签获取"""
        entry_ids = self.tag_index.get(tag, [])
        return [self.entries[eid] for eid in entry_ids]
    
    def link_to_prompt(self, entry_id: str, prompt_id: str):
        """关联到提示词"""
        if entry_id in self.entries:
            if prompt_id not in self.entries[entry_id].related_prompts:
                self.entries[entry_id].related_prompts.append(prompt_id)
    
    def get_related_knowledge(self, prompt_id: str) -> List[KnowledgeEntry]:
        """获取与提示词相关的知识"""
        related = []
        for entry in self.entries.values():
            if prompt_id in entry.related_prompts:
                related.append(entry)
        return related

# 使用示例
kb = PromptKnowledgeBase()

# 添加知识条目
entry1 = KnowledgeEntry(
    entry_id="kb_001",
    title="Few-shot提示最佳实践",
    content="""
Few-shot提示是通过提供示例来指导模型完成任务的技术。

关键要点:
1. 示例要具有代表性
2. 示例数量适中(3-5个)
3. 示例格式一致
4. 涵盖边界情况
""",
    category="techniques",
    tags=["few-shot", "best-practices", "prompting"],
    created_by="expert001"
)

kb.add_entry(entry1)

# 搜索
results = kb.search("few shot examples")
print(f"找到 {len(results)} 条相关知识")

# 按分类获取
techniques = kb.get_by_category("techniques")
print(f"技术类知识: {len(techniques)} 条")

# 关联到提示词
kb.link_to_prompt("kb_001", "prompt_classification")

18.4 大模型应用开发框架:LangChain、LlamaIndex、Semantic Kernel

18.4.1 LangChain

LangChain是目前最流行的LLM应用开发框架,提供了完整的组件生态。

核心组件:

LANGCHAIN_COMPONENTS = """
LangChain 核心组件:

1. **Model I/O**
   - LLM:语言模型接口
   - Chat Model:对话模型接口
   - Prompts:提示词管理
   - Output Parsers:输出解析

2. **Chains**
   - LLMChain:基础链
   - SequentialChain:顺序链
   - RouterChain:路由链
   - TransformChain:转换链

3. **Agents**
   - Tool:工具定义
   - Agent:智能体
   - AgentExecutor:执行器

4. **Memory**
   - ConversationBufferMemory:缓冲区记忆
   - ConversationBufferWindowMemory:窗口记忆
   - VectorStoreRetrieverMemory:向量检索记忆

5. **Retrieval**
   - Document Loaders:文档加载器
   - Text Splitters:文本分割器
   - Embeddings:嵌入模型
   - Vector Stores:向量存储
   - Retrievers:检索器
"""

# LangChain 使用示例
"""
from langchain import OpenAI, LLMChain, PromptTemplate
from langchain.memory import ConversationBufferMemory
from langchain.agents import initialize_agent, Tool

# 1. 基础Chain
llm = OpenAI(temperature=0.7)
prompt = PromptTemplate(
    input_variables=["product"],
    template="为{product}写一段营销文案。"
)
chain = LLMChain(llm=llm, prompt=prompt)
result = chain.run("智能手表")

# 2. 带记忆的Chain
memory = ConversationBufferMemory()
conversation = LLMChain(
    llm=llm,
    prompt=PromptTemplate(
        input_variables=["history", "input"],
        template="历史:{history}\n用户:{input}\n助手:"
    ),
    memory=memory
)

# 3. Agent
from langchain.tools import DuckDuckGoSearchRun
search = DuckDuckGoSearchRun()
tools = [
    Tool(
        name="Search",
        func=search.run,
        description="用于搜索最新信息"
    )
]

agent = initialize_agent(tools, llm, agent="zero-shot-react-description")
agent.run("今天北京的天气如何?")
"""

LCEL(LangChain Expression Language):

LCEL_CONCEPT = """
LCEL 是 LangChain Expression Language 的缩写,是LangChain 0.1.0+版本引入的声明式链组合语法。

核心概念:
1. Runnable:可运行组件的统一接口
2. Pipes (|):组合操作符
3. 内置组件:RunnableParallel, RunnablePassthrough等

优势:
- 简洁的语法
- 自动的流式支持
- 自动的异步支持
- 优化的并行执行
"""

# LCEL 使用示例
"""
from langchain_core.runnables import RunnablePassthrough, RunnableParallel
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

# 基础链
model = ChatOpenAI()
prompt = ChatPromptTemplate.from_template("告诉我一个关于{topic}的笑话")
chain = prompt | model

# 并行执行
chain = RunnableParallel(
    joke=prompt | model,
    topic=RunnablePassthrough()
)

# 复杂链
retrieval_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | model
)
"""

18.4.2 LlamaIndex

LlamaIndex专注于数据索引和检索,是构建RAG应用的强大工具。

LLAMAINDEX_CONCEPT = """
LlamaIndex 核心概念:

1. **Indexing(索引)**
   - VectorStoreIndex:向量存储索引
   - ListIndex:列表索引
   - TreeIndex:树形索引
   - KeywordTableIndex:关键词表索引

2. **Querying(查询)**
   - Retriever:检索器
   - Query Engine:查询引擎
   - Chat Engine:对话引擎

3. **Data Connectors(数据连接器)**
   - 支持多种数据源:文件、数据库、API等
   - 自动数据加载和解析

4. **Node Parser(节点解析器)**
   - 文档分块策略
   - 元数据提取
"""

# LlamaIndex 使用示例
"""
from llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext
from llama_index.llms import OpenAI

# 加载文档
documents = SimpleDirectoryReader('data').load_data()

# 创建索引
index = VectorStoreIndex.from_documents(documents)

# 创建查询引擎
query_engine = index.as_query_engine()

# 查询
response = query_engine.query("文档的主要内容是什么?")
print(response)

# 创建对话引擎
chat_engine = index.as_chat_engine()
response = chat_engine.chat("告诉我更多关于...")
"""

LlamaIndex核心实现:

from typing import List, Dict, Any
from dataclasses import dataclass
import numpy as np

@dataclass
class Document:
    """文档"""
    text: str
    doc_id: str
    metadata: Dict[str, Any] = None

@dataclass
class Node:
    """节点(文档块)"""
    text: str
    node_id: str
    doc_id: str
    embedding: List[float] = None
    metadata: Dict[str, Any] = None

class SimpleVectorStore:
    """简单向量存储"""
    
    def __init__(self, embedding_dim: int = 1536):
        self.embedding_dim = embedding_dim
        self.nodes: Dict[str, Node] = {}
        self.embeddings: np.ndarray = None
        self.node_ids: List[str] = []
    
    def add_nodes(self, nodes: List[Node]):
        """添加节点"""
        embeddings = []
        
        for node in nodes:
            self.nodes[node.node_id] = node
            self.node_ids.append(node.node_id)
            embeddings.append(node.embedding or [0.0] * self.embedding_dim)
        
        # 更新嵌入矩阵
        if self.embeddings is None:
            self.embeddings = np.array(embeddings)
        else:
            self.embeddings = np.vstack([self.embeddings, embeddings])
    
    def similarity_search(self, query_embedding: List[float], 
                         top_k: int = 5) -> List[Node]:
        """相似度搜索"""
        if self.embeddings is None or len(self.node_ids) == 0:
            return []
        
        query_vec = np.array(query_embedding).reshape(1, -1)
        
        # 计算余弦相似度
        similarities = np.dot(self.embeddings, query_vec.T).flatten()
        
        # 获取top-k
        top_indices = np.argsort(similarities)[-top_k:][::-1]
        
        return [self.nodes[self.node_ids[i]] for i in top_indices]

class SimpleIndex:
    """简单索引"""
    
    def __init__(self, embedding_fn=None):
        self.documents: Dict[str, Document] = {}
        self.nodes: List[Node] = []
        self.vector_store = SimpleVectorStore()
        self.embedding_fn = embedding_fn or self._default_embedding
    
    def _default_embedding(self, text: str) -> List[float]:
        """默认嵌入函数(简化实现)"""
        # 实际应调用嵌入模型API
        import hashlib
        hash_val = hashlib.md5(text.encode()).hexdigest()
        # 生成伪随机向量
        np.random.seed(int(hash_val[:8], 16))
        return np.random.randn(1536).tolist()
    
    def add_documents(self, documents: List[Document]):
        """添加文档"""
        for doc in documents:
            self.documents[doc.doc_id] = doc
            
            # 分块(简化:每个文档作为一个节点)
            node = Node(
                text=doc.text,
                node_id=f"node_{doc.doc_id}",
                doc_id=doc.doc_id,
                embedding=self.embedding_fn(doc.text),
                metadata=doc.metadata
            )
            self.nodes.append(node)
        
        # 添加到向量存储
        self.vector_store.add_nodes(self.nodes)
    
    def query(self, query_text: str, top_k: int = 5) -> List[Node]:
        """查询"""
        query_embedding = self.embedding_fn(query_text)
        return self.vector_store.similarity_search(query_embedding, top_k)

# 使用示例
index = SimpleIndex()

# 添加文档
docs = [
    Document(
        doc_id="doc1",
        text="机器学习是人工智能的一个分支,它使计算机能够从数据中学习。",
        metadata={"category": "AI"}
    ),
    Document(
        doc_id="doc2",
        text="深度学习是机器学习的一种方法,使用多层神经网络。",
        metadata={"category": "AI"}
    )
]

index.add_documents(docs)

# 查询
results = index.query("什么是人工智能?", top_k=2)
for node in results:
    print(f"相关文档: {node.text[:50]}...")

18.4.3 Semantic Kernel

Semantic Kernel是微软开发的AI开发SDK,支持多种编程语言。

SEMANTIC_KERNEL_CONCEPT = """
Semantic Kernel 核心概念:

1. **Kernel(内核)**
   - 核心编排引擎
   - 管理AI服务和插件

2. **Plugins(插件)**
   - 语义函数(Semantic Functions):基于提示词
   - 原生函数(Native Functions):基于代码

3. **Planner(规划器)**
   - 自动任务分解
   - 动态计划生成

4. **Memory(记忆)**
   - 语义记忆
   - 上下文管理

5. **Connectors(连接器)**
   - AI服务连接
   - 内存存储连接
"""

# Semantic Kernel 使用示例(Python)
"""
import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion

# 创建Kernel
kernel = sk.Kernel()

# 添加AI服务
api_key = "your-api-key"
kernel.add_chat_service("gpt-4", OpenAIChatCompletion("gpt-4", api_key))

# 定义语义函数(提示词)
prompt = """将以下文本翻译成{{$target_language}}:
{{$input}}
"""

translate = kernel.create_semantic_function(
    prompt,
    function_name="Translate",
    plugin_name="Translation"
)

# 执行
result = translate("Hello, world!", target_language="中文")
print(result)

# 定义原生函数
from semantic_kernel.skill_definition import sk_function

class MathPlugin:
    @sk_function(
        description="计算两个数的和",
        name="Add"
    )
    def add(self, input: str) -> str:
        numbers = input.split(',')
        result = float(numbers[0]) + float(numbers[1])
        return str(result)

# 注册插件
math_plugin = kernel.import_skill(MathPlugin())

# 使用Planner
from semantic_kernel.planning import BasicPlanner

planner = BasicPlanner()
plan = await planner.create_plan_async("计算3+5并翻译成法语", kernel)
result = await plan.invoke_async()
"""

Semantic Kernel风格实现:

from typing import Dict, Callable, Any, List
from dataclasses import dataclass
import inspect

@dataclass
class FunctionResult:
    """函数执行结果"""
    value: str
    metadata: Dict[str, Any] = None

class SemanticFunction:
    """语义函数"""
    
    def __init__(self, name: str, plugin_name: str, 
                 template: str, llm_client):
        self.name = name
        self.plugin_name = plugin_name
        self.template = template
        self.llm_client = llm_client
        self.parameters = self._extract_parameters()
    
    def _extract_parameters(self) -> List[str]:
        """提取模板参数"""
        import re
        pattern = r'\{\{(\$\w+)\}\}'
        matches = re.findall(pattern, self.template)
        return [m[1:] for m in matches]  # 去掉$前缀
    
    def invoke(self, **kwargs) -> FunctionResult:
        """执行函数"""
        # 填充模板
        prompt = self.template
        for param in self.parameters:
            value = kwargs.get(param, "")
            prompt = prompt.replace(f"{{{{${param}}}}}", str(value))
        
        # 调用LLM
        response = self.llm_client.generate(prompt)
        
        return FunctionResult(
            value=response,
            metadata={"function": f"{self.plugin_name}.{self.name}"}
        )

class NativeFunction:
    """原生函数"""
    
    def __init__(self, name: str, plugin_name: str, 
                 func: Callable, description: str):
        self.name = name
        self.plugin_name = plugin_name
        self.func = func
        self.description = description
    
    def invoke(self, **kwargs) -> FunctionResult:
        """执行函数"""
        result = self.func(**kwargs)
        return FunctionResult(
            value=str(result),
            metadata={"function": f"{self.plugin_name}.{self.name}"}
        )

class SimpleKernel:
    """简化版Semantic Kernel"""
    
    def __init__(self):
        self.semantic_functions: Dict[str, SemanticFunction] = {}
        self.native_functions: Dict[str, NativeFunction] = {}
        self.llm_client = None
    
    def set_llm_client(self, client):
        """设置LLM客户端"""
        self.llm_client = client
    
    def register_semantic_function(self, name: str, plugin_name: str, 
                                   template: str):
        """注册语义函数"""
        func = SemanticFunction(name, plugin_name, template, self.llm_client)
        key = f"{plugin_name}.{name}"
        self.semantic_functions[key] = func
    
    def register_native_function(self, name: str, plugin_name: str, 
                                 func: Callable, description: str = ""):
        """注册原生函数"""
        native_func = NativeFunction(name, plugin_name, func, description)
        key = f"{plugin_name}.{name}"
        self.native_functions[key] = native_func
    
    def invoke(self, function_name: str, **kwargs) -> FunctionResult:
        """调用函数"""
        if function_name in self.semantic_functions:
            return self.semantic_functions[function_name].invoke(**kwargs)
        elif function_name in self.native_functions:
            return self.native_functions[function_name].invoke(**kwargs)
        else:
            raise ValueError(f"Function {function_name} not found")

# 使用示例
def mock_llm():
    class MockLLM:
        def generate(self, prompt: str) -> str:
            return f"[LLM响应] {prompt[:50]}..."
    return MockLLM()

kernel = SimpleKernel()
kernel.set_llm_client(mock_llm())

# 注册语义函数
kernel.register_semantic_function(
    name="Summarize",
    plugin_name="TextProcessing",
    template="请为以下文本生成摘要:\n{{$input}}\n\n摘要:"
)

# 注册原生函数
def calculate_length(text: str) -> int:
    return len(text)

kernel.register_native_function(
    name="Length",
    plugin_name="TextProcessing",
    func=calculate_length,
    description="计算文本长度"
)

# 调用
result = kernel.invoke("TextProcessing.Summarize", input="这是一段长文本...")
print(f"语义函数结果: {result.value}")

result = kernel.invoke("TextProcessing.Length", text="Hello")
print(f"原生函数结果: {result.value}")

18.5 提示词工程的 CI/CD 与自动化部署

18.5.1 LLMOps概述

LLMOPS_CONCEPT = """
LLMOps(Large Language Model Operations)是MLOps在LLM领域的延伸,
专注于大语言模型应用的全生命周期管理。

LLMOps 核心流程:

1. **开发(Develop)**
   - 提示词工程
   - 原型开发
   - 本地测试

2. **版本控制(Version)**
   - 提示词版本管理
   - 数据版本管理
   - 模型版本管理

3. **测试(Test)**
   - 单元测试
   - 集成测试
   - 评估测试

4. **部署(Deploy)**
   - 环境管理
   - 灰度发布
   - A/B测试

5. **监控(Monitor)**
   - 性能监控
   - 质量监控
   - 成本监控

6. **反馈(Feedback)**
   - 用户反馈收集
   - 模型改进
   - 持续优化
"""

18.5.2 CI/CD流水线设计

from typing import List, Dict, Callable, Any
from dataclasses import dataclass
from enum import Enum
import time

class PipelineStage(Enum):
    """流水线阶段"""
    LINT = "lint"
    TEST = "test"
    EVALUATE = "evaluate"
    BUILD = "build"
    DEPLOY = "deploy"

@dataclass
class PipelineResult:
    """流水线结果"""
    stage: str
    success: bool
    duration_ms: float
    output: str
    error: str = None

class PromptCIPipeline:
    """提示词CI流水线"""
    
    def __init__(self):
        self.stages: Dict[PipelineStage, List[Callable]] = {
            stage: [] for stage in PipelineStage
        }
        self.results: List[PipelineResult] = []
    
    def add_stage(self, stage: PipelineStage, func: Callable):
        """添加阶段任务"""
        self.stages[stage].append(func)
    
    def run(self, context: Dict[str, Any]) -> List[PipelineResult]:
        """运行流水线"""
        self.results = []
        
        for stage in PipelineStage:
            print(f"Running stage: {stage.value}")
            
            for func in self.stages[stage]:
                start_time = time.time()
                
                try:
                    output = func(context)
                    duration = (time.time() - start_time) * 1000
                    
                    result = PipelineResult(
                        stage=stage.value,
                        success=True,
                        duration_ms=duration,
                        output=str(output)
                    )
                except Exception as e:
                    duration = (time.time() - start_time) * 1000
                    
                    result = PipelineResult(
                        stage=stage.value,
                        success=False,
                        duration_ms=duration,
                        output="",
                        error=str(e)
                    )
                    
                    self.results.append(result)
                    print(f"Stage {stage.value} failed: {e}")
                    return self.results  # 失败时停止
                
                self.results.append(result)
        
        return self.results
    
    def generate_report(self) -> Dict:
        """生成报告"""
        total_duration = sum(r.duration_ms for r in self.results)
        success_count = sum(1 for r in self.results if r.success)
        
        return {
            "total_stages": len(self.results),
            "success_count": success_count,
            "failure_count": len(self.results) - success_count,
            "total_duration_ms": total_duration,
            "stages": [
                {
                    "stage": r.stage,
                    "success": r.success,
                    "duration_ms": r.duration_ms,
                    "error": r.error
                }
                for r in self.results
            ]
        }

# 使用示例
pipeline = PromptCIPipeline()

# 添加Lint阶段
def lint_prompts(context):
    """检查提示词格式"""
    prompt = context.get("prompt", "")
    if not prompt:
        raise ValueError("Empty prompt")
    if "{{" in prompt and "}}" not in prompt:
        raise ValueError("Unclosed template variable")
    return "Lint passed"

pipeline.add_stage(PipelineStage.LINT, lint_prompts)

# 添加测试阶段
def run_unit_tests(context):
    """运行单元测试"""
    # 模拟测试
    return "All tests passed"

pipeline.add_stage(PipelineStage.TEST, run_unit_tests)

# 添加评估阶段
def evaluate_prompt(context):
    """评估提示词质量"""
    # 模拟评估
    return "Quality score: 0.92"

pipeline.add_stage(PipelineStage.EVALUATE, evaluate_prompt)

# 运行流水线
context = {"prompt": "你好,{{name}}"}
results = pipeline.run(context)

report = pipeline.generate_report()
print(f"流水线完成: {report['success_count']}/{report['total_stages']} 阶段成功")

18.5.3 自动化部署系统

from typing import Dict, List, Optional
from dataclasses import dataclass
from datetime import datetime
import json

@dataclass
class DeploymentConfig:
    """部署配置"""
    environment: str  # dev, staging, production
    prompt_id: str
    version: str
    traffic_percentage: float = 100.0
    rollback_on_error: bool = True
    health_check_url: str = None

class PromptDeploymentManager:
    """提示词部署管理器"""
    
    def __init__(self):
        self.deployments: Dict[str, List[Dict]] = {}  # env -> deployments
        self.current_versions: Dict[str, Dict[str, str]] = {}  # env -> {prompt_id: version}
    
    def deploy(self, config: DeploymentConfig) -> Dict:
        """部署提示词"""
        deployment_id = f"deploy_{datetime.now().strftime('%Y%m%d%H%M%S')}"
        
        deployment = {
            "id": deployment_id,
            "config": config,
            "status": "in_progress",
            "started_at": datetime.now(),
            "completed_at": None,
            "error": None
        }
        
        if config.environment not in self.deployments:
            self.deployments[config.environment] = []
        
        self.deployments[config.environment].append(deployment)
        
        try:
            # 1. 验证配置
            self._validate_config(config)
            
            # 2. 健康检查
            if config.health_check_url:
                self._health_check(config.health_check_url)
            
            # 3. 执行部署
            self._execute_deployment(config)
            
            # 4. 更新当前版本
            if config.environment not in self.current_versions:
                self.current_versions[config.environment] = {}
            self.current_versions[config.environment][config.prompt_id] = config.version
            
            deployment["status"] = "completed"
            deployment["completed_at"] = datetime.now()
            
        except Exception as e:
            deployment["status"] = "failed"
            deployment["error"] = str(e)
            
            if config.rollback_on_error:
                self._rollback(config)
        
        return deployment
    
    def _validate_config(self, config: DeploymentConfig):
        """验证配置"""
        if config.traffic_percentage < 0 or config.traffic_percentage > 100:
            raise ValueError("Traffic percentage must be between 0 and 100")
        
        if config.environment not in ["dev", "staging", "production"]:
            raise ValueError("Invalid environment")
    
    def _health_check(self, url: str):
        """健康检查"""
        # 模拟健康检查
        import random
        if random.random() < 0.1:  # 10%失败率模拟
            raise Exception("Health check failed")
    
    def _execute_deployment(self, config: DeploymentConfig):
        """执行部署"""
        # 模拟部署过程
        print(f"Deploying {config.prompt_id}:{config.version} to {config.environment}")
        time.sleep(1)  # 模拟部署时间
    
    def _rollback(self, config: DeploymentConfig):
        """回滚部署"""
        print(f"Rolling back {config.prompt_id} in {config.environment}")
        # 回滚逻辑
    
    def get_deployment_history(self, environment: str) -> List[Dict]:
        """获取部署历史"""
        return self.deployments.get(environment, [])
    
    def get_current_version(self, environment: str, prompt_id: str) -> Optional[str]:
        """获取当前版本"""
        return self.current_versions.get(environment, {}).get(prompt_id)
    
    def promote(self, prompt_id: str, from_env: str, to_env: str):
        """提升部署(从低级环境到高级环境)"""
        current_version = self.get_current_version(from_env, prompt_id)
        
        if not current_version:
            raise ValueError(f"No version found in {from_env}")
        
        config = DeploymentConfig(
            environment=to_env,
            prompt_id=prompt_id,
            version=current_version
        )
        
        return self.deploy(config)

# 使用示例
deploy_manager = PromptDeploymentManager()

# 部署到开发环境
dev_config = DeploymentConfig(
    environment="dev",
    prompt_id="customer_service",
    version="v1.2.0"
)
dev_result = deploy_manager.deploy(dev_config)
print(f"开发环境部署: {dev_result['status']}")

# 部署到生产环境(灰度)
prod_config = DeploymentConfig(
    environment="production",
    prompt_id="customer_service",
    version="v1.2.0",
    traffic_percentage=10  # 10%流量
)
prod_result = deploy_manager.deploy(prod_config)
print(f"生产环境部署: {prod_result['status']}")

# 提升部署
promote_result = deploy_manager.promote("customer_service", "dev", "staging")
print(f"提升到Staging: {promote_result['status']}")

18.5.4 配置即代码

"""
提示词工程的配置即代码(Configuration as Code)实践
"""

# prompt_config.yaml 示例
PROMPT_CONFIG_YAML = """
prompts:
  customer_service:
    name: "Customer Service Assistant"
    description: "Handles customer inquiries"
    versions:
      v1.0.0:
        template: |
          You are a helpful customer service assistant.
          Customer query: {{query}}
          Please provide a helpful response.
        variables:
          - query
        parameters:
          temperature: 0.7
          max_tokens: 500
        
      v1.1.0:
        template: |
          You are a professional customer service assistant for {{company}}.
          Customer query: {{query}}
          Customer history: {{history}}
          Please provide a helpful and personalized response.
        variables:
          - company
          - query
          - history
        parameters:
          temperature: 0.5
          max_tokens: 800
    
    evaluation:
      test_cases:
        - name: "basic_query"
          inputs:
            query: "How do I reset my password?"
          expected_behavior: "Provides password reset instructions"
      
      metrics:
        - helpfulness
        - accuracy
        - response_time
    
    deployment:
      environments:
        dev:
          model: "gpt-3.5-turbo"
        staging:
          model: "gpt-4"
        production:
          model: "gpt-4"
          traffic_split:
            v1.0.0: 90
            v1.1.0: 10
"""

class PromptConfigManager:
    """提示词配置管理器"""
    
    def __init__(self, config_path: str):
        self.config_path = config_path
        self.config = self._load_config()
    
    def _load_config(self) -> Dict:
        """加载配置"""
        # 实际应解析YAML文件
        import yaml
        with open(self.config_path, 'r') as f:
            return yaml.safe_load(f)
    
    def get_prompt_config(self, prompt_id: str, version: str = None) -> Dict:
        """获取提示词配置"""
        prompt_config = self.config.get("prompts", {}).get(prompt_id)
        
        if not prompt_config:
            return None
        
        if version:
            return prompt_config.get("versions", {}).get(version)
        
        # 返回最新版本
        versions = prompt_config.get("versions", {})
        if versions:
            latest = sorted(versions.keys())[-1]
            return versions[latest]
        
        return None
    
    def get_evaluation_config(self, prompt_id: str) -> Dict:
        """获取评估配置"""
        prompt_config = self.config.get("prompts", {}).get(prompt_id)
        return prompt_config.get("evaluation", {}) if prompt_config else {}
    
    def get_deployment_config(self, prompt_id: str, environment: str) -> Dict:
        """获取部署配置"""
        prompt_config = self.config.get("prompts", {}).get(prompt_id)
        if not prompt_config:
            return None
        
        deployment = prompt_config.get("deployment", {})
        environments = deployment.get("environments", {})
        return environments.get(environment)
    
    def validate_config(self) -> List[str]:
        """验证配置"""
        errors = []
        
        prompts = self.config.get("prompts", {})
        
        for prompt_id, prompt_config in prompts.items():
            # 验证版本
            versions = prompt_config.get("versions", {})
            if not versions:
                errors.append(f"{prompt_id}: No versions defined")
            
            for version, version_config in versions.items():
                if "template" not in version_config:
                    errors.append(f"{prompt_id}:{version}: Missing template")
                
                # 验证变量
                template = version_config.get("template", "")
                declared_vars = set(version_config.get("variables", []))
                
                import re
                used_vars = set(re.findall(r'\{\{(\w+)\}\}', template))
                
                undeclared = used_vars - declared_vars
                if undeclared:
                    errors.append(f"{prompt_id}:{version}: Undeclared variables: {undeclared}")
        
        return errors

# 使用示例
# config_manager = PromptConfigManager("prompt_config.yaml")
# errors = config_manager.validate_config()
# if errors:
#     print("配置错误:")
#     for error in errors:
#         print(f"  - {error}")

本章小结

本章全面介绍了提示词工程工具链:

  1. 提示词开发工具:学习了LangChain Prompt Hub的使用方法,了解了DSPy框架的核心理念和优化方法,掌握了提示词模板化和参数化技术。

  2. 测试与调试工具:掌握了LangSmith的追踪和调试功能,学习了PromptEval测试框架的构建方法,能够建立完整的提示词测试体系。

  3. 提示词管理工具:实现了提示词版本控制系统、权限管理系统和知识库,能够支撑企业级的提示词管理需求。

  4. 大模型应用开发框架:深入了解了LangChain、LlamaIndex和Semantic Kernel三大框架的核心概念和使用方法,能够根据场景选择合适的框架。

  5. CI/CD与自动化部署:掌握了LLMOps的核心理念,能够设计提示词CI/CD流水线,实现提示词的自动化测试和部署。

通过构建完整的工具链,可以大幅提升提示词工程的效率和质量,实现提示词开发的标准化和工程化。


参考资源

  1. LangChain Documentation: python.langchain.com/
  2. LangSmith Documentation: docs.smith.langchain.com/
  3. LlamaIndex Documentation: docs.llamaindex.ai/
  4. Semantic Kernel Documentation: learn.microsoft.com/en-us/seman…
  5. DSPy Documentation: dspy-docs.vercel.app/
  6. Prompt Flow: microsoft.github.io/promptflow/
  7. Weights & Biases Prompts: docs.wandb.ai/guides/prom…
  8. MLflow LLM Tracking: mlflow.org/docs/latest…