====== 第十五章:生产实践 - 测试、评估与部署 ====== 一个 LangChain 项目能否长期稳定运行,决定因素往往不在 Demo 阶段,而在生产化阶段。很多系统原型很好看,一上线就暴露问题:结果不稳定、成本失控、日志缺失、无法回归、没人敢放权给业务使用。因此,真正的收官能力是:**测试、评估、监控、部署与运营闭环。** 本章会围绕“上线前必须具备什么”展开,覆盖单元测试、集成测试、回归评测、日志字段、成本治理、服务部署、灰度发布和线上运营。 ===== 15.1 为什么 LLM 项目更需要测试 ===== 传统系统很多结果是确定性的,而 LLM 系统天然带有概率性。也就是说,同一个问题、同一个模型、同一份 prompt,在不同时间都可能有细微差异。因此,一个 LangChain 项目如果没有评测集和回归流程,就很难放心迭代。 ===== 15.2 至少要有的测试层次 ===== 建议把测试分成 4 层: * **单元测试**:工具函数、数据清洗函数、路由器、格式化函数; * **集成测试**:检索链、Agent 链、结构化输出解析; * **回归评测**:固定问题集对比新旧版本表现; * **人工抽检**:高价值或高风险案例人工复核。 ===== 15.3 单元测试:先测确定性逻辑 ===== ==== 15.3.1 一个路由器测试 ==== def route_request(question: str) -> str: if "报销" in question: return "knowledge" if "订单" in question: return "agent" return "agent" 对应测试: def test_route_request(): assert route_request("差旅报销流程怎么走?") == "knowledge" assert route_request("帮我查一下订单状态") == "agent" ==== 15.3.2 一个工具函数测试 ==== def normalize_document_text(text: str) -> str: return " ".join(line.strip() for line in text.splitlines() if line.strip()) def test_normalize_document_text(): raw = " 第一行 第二行 " assert normalize_document_text(raw) == "第一行 第二行" 这些测试虽然“没有 LLM 感”,但对工程稳定性非常重要。 ===== 15.4 集成测试:测关键链路 ===== 对于 LangChain 项目,集成测试重点不是“每个 token 是否一致”,而是: * 链路是否能跑通; * 输出结构是否符合要求; * 是否命中正确工具或检索器; * 出错时是否能优雅失败。 ==== 15.4.1 一个结构化输出测试示意 ==== def test_ticket_analysis_has_required_fields(ticket_agent): result = ticket_agent.invoke({ "messages": [{"role": "user", "content": "VPN 无法连接,影响远程办公。"}] }) structured = result["structured_response"] assert structured.priority in {"low", "medium", "high"} assert isinstance(structured.need_human, bool) ===== 15.5 回归评测:给系统一套固定题库 ===== 回归评测是上线后最关键的能力之一。每次: * 修改 Prompt; * 更换模型; * 调整工具描述; * 调整切分策略; * 更换向量模型; 都应该跑回归集。 ==== 15.5.1 一个最小评测集示例 ==== eval_set = [ { "question": "差旅报销多久到账?", "must_include": ["3 个工作日"], "type": "rag" }, { "question": "帮我查一下订单状态", "expected_behavior": "ask_for_order_id", "type": "agent" }, { "question": "VPN 无法连接,影响我今天办公。", "expected_priority": "high", "type": "structured" }, ] ===== 15.6 LLM 项目的评估指标 ===== 按场景不同,指标也不同: **RAG 项目**: * 命中率; * 回答正确率; * 引用准确率; * 无依据拒答率。 **Agent 项目**: * 工具选择正确率; * 参数填写正确率; * 高风险动作拦截率; * 任务完成率。 **结构化输出项目**: * 字段合法率; * 缺失率; * 重试率; * 最终可落库率。 **整体系统**: * 平均延迟; * 单次成本; * 错误率; * 用户满意度。 ===== 15.7 日志与可观测性 ===== 一个可上线的 LangChain 项目,至少应记录这些字段: * `trace_id` / `request_id`; * 用户输入摘要; * 使用的模型名称与版本; * prompt token / completion token; * 工具调用列表; * 检索命中文档; * 总耗时; * 最终状态与错误类型。 log_record = { "trace_id": "trace-20260403-001", "request_type": "rag", "model": "gpt-4o-mini", "prompt_tokens": 1350, "completion_tokens": 268, "tool_calls": [], "retrieved_sources": ["finance_policy_v2.md"], "latency_ms": 1820, "status": "success", } 没有这些日志,你在生产环境里几乎无法定位问题。 ===== 15.8 成本治理 ===== LLM 项目的另一个常见痛点是:能跑,但太贵。建议至少做 4 件事: * 为不同任务选择不同模型; * 限制最大输出长度; * 对重复问题加缓存; * 为高成本任务设置预算或熔断。 ==== 15.8.1 一个简单成本预算示意 ==== monthly_budget = 3000 current_cost = 1820.5 if current_cost > monthly_budget: print("预算已超额,请切换低成本模型或降级部分功能") ===== 15.9 部署建议 ===== 上线时建议把系统拆成几个清晰模块: * API 服务层; * 检索与 Agent 服务层; * 向量库与缓存层; * 日志与监控层。 如果是 Web 服务,推荐统一对外暴露 API,而不是把全部逻辑写死在前端。 ===== 15.10 一个简化 FastAPI 服务示例 ===== from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class AskRequest(BaseModel): question: str @app.post("/ask") def ask(req: AskRequest): answer = rag_chain.invoke(req.question) return { "answer": answer, "trace_id": "demo-trace-001" } 实际项目中还应补充: * 认证鉴权; * 超时控制; * 请求日志; * 错误捕获; * 流式接口。 ===== 15.11 灰度发布与回滚 ===== Prompt、模型、切分策略、工具描述都可能引发回归问题。因此建议: * 保留上一个稳定版本; * 新版本先小流量灰度; * 比较关键指标; * 一旦异常,支持快速回滚。 ===== 15.12 上线后的运营闭环 ===== LLM 项目上线不是结束,而是开始。你需要持续做: * 收集失败案例; * 扩充评测集; * 优化 Prompt 和工具描述; * 调整检索策略; * 追踪成本变化; * 对高频问题沉淀 FAQ。 ===== 15.13 一份上线前检查清单 ===== 上线前至少确认: * 是否有评测集? * 是否有 trace_id 和日志字段? * 是否能统计 token 和成本? * 是否有高风险动作审批? * 是否能优雅处理模型或工具错误? * 是否支持版本回滚? ===== 15.14 一个最小“上线前自检脚本”思路 ===== checks = { "has_eval_set": True, "has_logging": True, "has_trace_id": True, "has_budget_guard": True, "has_rollback_plan": False, } for key, value in checks.items(): print(key, "OK" if value else "MISSING") 这种脚本虽简单,但很适合作为上线前 checklist 的自动化入口。 ===== 15.15 全书总结 ===== 到这里,这套教程已经从入门、核心组件、Agent、RAG、Memory、Streaming、LangGraph,一直讲到项目实战和生产落地。真正希望你带走的,不是某几个 API,而是三件事: * 用工程视角看待 LangChain; * 用系统拆解方法构建 LLM 应用; * 用评估和治理保证系统长期可用。 ===== 练习 ===== 1. 为你的 LangChain 项目建立一份上线前检查清单。 2. 设计一个最小评测集,至少包含 20 个真实问题和预期结果。 3. 写出你的生产日志字段规范,说明哪些字段用于排错,哪些字段用于运营分析。 4. 选择教程中的一个项目实战,补充部署架构图和回归测试方案。 5. 将你的一个 Prompt 改动纳入灰度发布和回滚流程设计中。 ===== 后续建议 ===== * 把教程中的每个章节都落成可运行样例; * 把失败案例收集进评测集,而不是只改一次 Prompt; * 在下一轮项目中优先考虑“稳定、可测、可维护”,而不是只追求炫技式 Agent。 ===== 15.16 补充案例:Docker 部署思路 ===== 如果你准备把项目容器化,可以先从一个很简单的 Dockerfile 开始: FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] ===== 15.17 补充案例:CI 中执行最小回归测试 ===== name: rag-regression on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' - run: pip install -r requirements.txt - run: pytest -q 即使最初只是把单元测试和基础集成测试跑起来,也已经能显著提升迭代信心。