⚙️ 模块3:性能优化与工程化处理

3.1 Token 消耗优化

优化策略具体方法预估节省
精简系统提示词移除冗余描述、合并重复规则、用缩写替代长表述15-30%
上下文压缩用摘要替代原始对话,只保留关键信息30-60%
分批处理大文档分片处理,避免一次塞入所有内容20-40%
缓存重复调用相同问题返回缓存结果,不重复调用 API视重复率而定
模型选择简单任务用 Haiku(便宜),复杂任务用 Sonnet/Opus50-80%
#!/usr/bin/env python3
"""TokenOptimizer — Token消耗监控与优化"""

import hashlib, json, time
from functools import lru_cache

class TokenMonitor:
    """Token 使用监控器"""

    def __init__(self):
        self.total_input_tokens = 0
        self.total_output_tokens = 0
        self.call_count = 0
        self.call_history: list[dict] = []

    def record(self, input_tokens: int, output_tokens: int, operation: str):
        self.total_input_tokens += input_tokens
        self.total_output_tokens += output_tokens
        self.call_count += 1
        self.call_history.append({
            "op": operation, "in": input_tokens, "out": output_tokens,
            "time": time.time()
        })

    def report(self) -> str:
        total = self.total_input_tokens + self.total_output_tokens
        avg_in = self.total_input_tokens / max(self.call_count, 1)
        avg_out = self.total_output_tokens / max(self.call_count, 1)

        # 成本估算 (Claude Sonnet 参考价格)
        cost_estimate = (self.total_input_tokens / 1_000_000) * 3 + \
                        (self.total_output_tokens / 1_000_000) * 15

        return f"""
📊 Token 使用报告
{'='*40}
总调用次数: {self.call_count}
总输入Token: {self.total_input_tokens:,}
总输出Token: {self.total_output_tokens:,}
总Token消耗: {total:,}
平均输入/次: {avg_in:.0f}
平均输出/次: {avg_out:.0f}
预估费用: ${cost_estimate:.4f} USD
"""


class ResponseCache:
    """响应缓存 — 避免重复API调用"""

    def __init__(self, max_size: int = 100):
        self.cache: dict[str, dict] = {}
        self.max_size = max_size

    def _key(self, prompt: str, model: str) -> str:
        return hashlib.md5(f"{model}:{prompt}".encode()).hexdigest()

    def get(self, prompt: str, model: str) -> str | None:
        key = self._key(prompt, model)
        entry = self.cache.get(key)
        if entry and time.time() - entry["time"] < 3600:  # 1小时过期
            return entry["response"]
        return None

    def set(self, prompt: str, model: str, response: str):
        key = self._key(prompt, model)
        if len(self.cache) >= self.max_size:
            # 淘汰最旧的
            oldest = min(self.cache.items(), key=lambda x: x[1]["time"])
            del self.cache[oldest[0]]
        self.cache[key] = {"response": response, "time": time.time()}


def compress_context(conversation: list[dict], max_tokens: int = 4000) -> list[dict]:
    """
    上下文压缩:将长对话压缩到指定 token 限制内
    粗略估算: 1个中文字符 ≈ 0.5 token
    """
    compressed = []
    token_count = 0

    # 从最近的对话开始往前取
    for turn in reversed(conversation):
        turn_tokens = (len(turn.get("user", "")) + len(turn.get("assistant", ""))) * 0.5
        if token_count + turn_tokens > max_tokens:
            # 对更早的对话做摘要
            if not compressed:
                compressed.append({
                    "role": "user",
                    "content": f"[早期对话摘要: 共{len(conversation)}轮,已省略]"
                })
            break
        compressed.insert(0, turn)
        token_count += turn_tokens

    return compressed

3.2 代码模块化与解耦

# 推荐的项目目录结构
"""
ai_agent_project/
├── agent/
│   ├── __init__.py
│   ├── core.py           # Agent 核心运行循环
│   ├── memory.py         # 记忆系统(短期/长期/会话)
│   ├── tools.py          # 工具注册与执行
│   ├── planner.py        # 任务规划器
│   └── prompts.py        # Prompt 模板集中管理
├── services/
│   ├── __init__.py
│   ├── llm_service.py    # 大模型调用封装
│   ├── vector_store.py   # 向量数据库服务
│   └── cache_service.py  # 缓存服务
├── utils/
│   ├── __init__.py
│   ├── logger.py         # 日志系统
│   ├── config.py         # 配置管理
│   └── monitor.py        # 性能监控
├── tests/
│   ├── test_memory.py
│   ├── test_tools.py
│   └── test_agent.py
├── config.yaml           # 配置文件
├── requirements.txt
└── main.py               # 入口文件
"""

3.3 日志监控与容错机制

#!/usr/bin/env python3
"""生产级日志、监控与容错系统"""

import logging
import traceback
from datetime import datetime
from pathlib import Path
from typing import Callable

# --- 日志系统 ---
def setup_logger(name: str = "agent", log_dir: str = "./logs") -> logging.Logger:
    """配置生产级日志系统"""
    Path(log_dir).mkdir(parents=True, exist_ok=True)

    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)

    # 文件处理器(详细日志)
    file_handler = logging.FileHandler(
        f"{log_dir}/{name}_{datetime.now():%Y%m%d}.log",
        encoding="utf-8"
    )
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(logging.Formatter(
        '%(asctime)s [%(levelname)s] %(name)s:%(funcName)s:%(lineno)d - %(message)s'
    ))

    # 控制台处理器(简要日志)
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(logging.Formatter(
        '%(asctime)s [%(levelname)s] %(message)s', '%H:%M:%S'
    ))

    logger.addHandler(file_handler)
    logger.addHandler(console_handler)
    return logger


# --- 容错装饰器 ---
def retry_on_failure(max_retries: int = 3, delay: float = 1.0,
                     backoff: float = 2.0, exceptions: tuple = (Exception,)):
    """
    自动重试装饰器
    用法: @retry_on_failure(max_retries=3, delay=1.0)
    """
    import time
    def decorator(func: Callable) -> Callable:
        def wrapper(*args, **kwargs):
            last_exception = None
            current_delay = delay

            for attempt in range(max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exception = e
                    if attempt < max_retries:
                        logging.warning(
                            f"{func.__name__} 失败 (尝试 {attempt+1}/{max_retries+1}): {e}"
                        )
                        time.sleep(current_delay)
                        current_delay *= backoff
                    else:
                        logging.error(
                            f"{func.__name__} 最终失败: {e}\n{traceback.format_exc()}"
                        )

            raise last_exception
        return wrapper
    return decorator


def safe_execute(func: Callable, *args, default=None, **kwargs):
    """
    安全执行函数,失败返回默认值
    用法: result = safe_execute(risky_function, arg1, default="备用值")
    """
    try:
        return func(*args, **kwargs)
    except Exception as e:
        logging.error(f"safe_execute 捕获异常: {e}")
        return default


# --- 健康检查 ---
class HealthChecker:
    """Agent 健康检查器"""

    def __init__(self, agent):
        self.agent = agent
        self.checks: dict[str, Callable] = {}

    def register_check(self, name: str, check_fn: Callable):
        self.checks[name] = check_fn

    def run_all(self) -> dict:
        results = {}
        for name, check_fn in self.checks.items():
            try:
                ok, detail = check_fn()
                results[name] = {"healthy": ok, "detail": detail}
            except Exception as e:
                results[name] = {"healthy": False, "detail": str(e)}
        return results

    def is_healthy(self) -> bool:
        return all(r["healthy"] for r in self.run_all().values())


# 使用示例
@retry_on_failure(max_retries=2, delay=0.5, exceptions=(Exception,))
def call_llm_with_retry(prompt: str, max_tokens: int = 500) -> str:
    """带自动重试的 LLM 调用"""
    # ... 实际的 API 调用
    pass

3.4 本地轻量化部署与简易云端部署

本地部署方案

# 1. 使用 Flask 将 Agent 包装为本地 API 服务
# pip install flask

from flask import Flask, request, jsonify
app = Flask(__name__)
agent = None  # 全局 Agent 实例

@app.route('/ask', methods=['POST'])
def ask():
    data = request.get_json()
    question = data.get('question', '')
    result = agent.ask(question)
    return jsonify({"answer": result, "timestamp": datetime.now().isoformat()})

@app.route('/health')
def health():
    return jsonify({"status": "ok"})

if __name__ == '__main__':
    agent = YourAgent()  # 初始化你的Agent
    app.run(host='0.0.0.0', port=5000, debug=False)
    # 访问: http://localhost:5000/ask

简易云端部署方案(Docker)

# Dockerfile
FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
EXPOSE 5000

CMD ["python", "main.py"]

# 构建 & 运行
# docker build -t my-agent .
# docker run -p 5000:5000 --env-file .env my-agent

✏️ 课后练习

  1. Token 审计:在之前写的 Agent 中集成 TokenMonitor,统计一次完整对话的 Token 消耗和费用。
  2. 缓存实战:为 Agent 添加 ResponseCache,测试缓存前后的 API 调用次数对比。
  3. 模块化重构:选择一个之前写的 Agent,按推荐的目录结构重构代码。
  4. 部署实践:将你的 Agent 用 Flask 包装为 API 服务,用 curl 或 Postman 测试。
← 高阶能力 进入第五阶段 →