AI工程化实战:基于RAG与LangChain构建金融大模型问答系统
这次我们来看一个关于Harness Engineering的实战教程。Harness Engineering或者说“AI 工程化”是当前 AI 大模型应用开发领域最核心、也最容易被忽视的环节。它解决的核心问题不是“如何让模型回答得更好”而是“如何让模型在真实、复杂的业务系统中稳定、可靠、高效地工作”。简单来说Harness Engineering 是关于如何“驾驭”大模型将其从实验室的玩具变成生产环境中的可靠组件。这涉及到系统设计、流程编排、错误处理、性能优化等一系列工程实践。对于想从“调 API”进阶到“做项目”的开发者来说这是必须跨越的一道坎。本文将从原理出发结合一个完整的“金融大模型问答机器人”项目案例带你从零开始搞懂 Harness Engineering 的核心思想、技术栈和落地步骤。无论你是刚接触 AI 应用开发的小白还是希望提升工程化能力的开发者这篇文章都将提供一套可直接复用的实战框架。1. 核心能力速览在深入细节前我们先通过一个表格快速了解 Harness Engineering 的核心关注点和本次项目案例的关键信息。能力项说明项目类型AI 大模型应用工程化 (Harness Engineering) 实战核心目标设计并实现一个稳定、可扩展、可维护的 AI 应用系统而非仅仅调用模型 API。技术栈LLM (Qwen)、LangChain、LangIndex、FastAPI、RAG、GraphRAG、LoRA、SFT、高效微调、量化等。硬件门槛训练阶段需要 GPU显存需求视模型大小和微调方法而定从 16G 到多卡不等。推理/部署阶段可选择 GPU 加速或纯 CPU性能较低。云端 API 调用则无本地硬件要求。启动方式项目通常以代码仓库形式提供通过命令行启动后端 API 服务如uvicorn app:app和前端 Web 界面。接口能力提供标准的 RESTful API基于 FastAPI支持问答、文档检索、对话历史管理等。批量任务支持批量文档的离线处理、向量化入库以及批量问答测试。适合场景企业级知识问答、智能客服、内容分析与生成、内部知识库助手等需要高可靠性和定制化的场景。2. 适用场景与使用边界Harness Engineering 不是某个具体的工具而是一套方法论和最佳实践。它主要适用于以下场景复杂业务逻辑集成当 AI 能力需要与数据库、业务规则、外部 API、工作流引擎等现有系统深度集成时。高可靠性要求金融、医疗、法律等领域要求 AI 输出的结果必须稳定、可解释、可追溯不能出现“幻觉”或随机错误。长上下文与知识管理需要处理大量私有文档如产品手册、公司制度、研报并基于此进行精准问答RAG。成本与性能优化需要对模型调用进行缓存、限流、降级并可能涉及模型微调、量化以优化效果和推理成本。可维护与可演进系统需要易于调试、监控、升级和扩展新的 AI 能力或模型。使用边界与注意事项并非替代编程Harness Engineering 是辅助和增强核心业务逻辑和系统架构仍需开发者设计。数据安全与隐私处理企业私有数据时务必确保数据在传输、存储、处理过程中的安全谨慎使用第三方云服务。版权与合规使用开源模型或微调数据时需遵守相关许可证。生成内容需符合法律法规避免侵权和不当内容。效果验证任何 AI 系统上线前都必须经过严格的测试和人工评估不能完全依赖自动化指标。3. 环境准备与前置条件开始实战前需要准备好开发环境。以下是一个通用的环境清单具体版本可根据项目需求调整。操作系统Linux (Ubuntu 20.04/22.04 LTS 推荐) 或 macOS。Windows 建议使用 WSL2。Python版本 3.9 或 3.10。推荐使用conda或venv创建独立的虚拟环境。包管理工具pip最新版。版本控制git。硬件开发/调试至少 16GB 内存。如果进行轻量级微调如 LoRA需要一张至少 8GB 显存的 NVIDIA GPU如 RTX 3070/4060 Ti。生产推理根据模型大小和并发量选择。7B 参数模型 INT4 量化后可在 6GB 显存 GPU 或纯 CPU较慢上运行。磁盘空间预留 20GB 以上空间用于存放代码、依赖、模型文件和向量数据库。网络能顺畅访问 GitHub、Hugging Face、PyPI 等资源。基础环境配置命令示例# 1. 创建并激活虚拟环境 (以 conda 为例) conda create -n harness-env python3.10 -y conda activate harness-env # 2. 升级 pip pip install --upgrade pip # 3. 安装 PyTorch (请根据 CUDA 版本到官网获取最新命令) # 例如对于 CUDA 11.8 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu1184. 项目实战金融大模型问答机器人我们将围绕“金融大模型问答机器人”这个案例拆解 Harness Engineering 的落地过程。项目目标是构建一个能理解金融术语、基于公司内部财报和研报进行准确问答的助手。4.1 项目设计能力分层与核心抽象好的工程化始于好的设计。我们采用分层架构将系统解耦。接入层 (Presentation Layer)职责处理用户请求HTTP/WebSocket返回响应。负责身份验证、限流、日志记录。技术选型FastAPI。它异步性能好自动生成 API 文档非常适合 AI 应用。应用层 (Application Layer / Orchestration Layer)职责编排核心业务逻辑。这是 Harness 的核心决定“什么时候调用什么”。技术选型LangChain / LangGraph。用于构建链Chain或图Graph来组织对大模型、工具、记忆的调用顺序。能力层 (Capability Layer)职责提供原子能力如调用大模型、检索知识、调用计算工具。技术选型LLMQwen-7B/14B开源可商用。也可接入 OpenAI GPT 系列作为备选或对比。检索 (RAG)LangChain 的 Retriever 接口后端连接向量数据库如 Chroma, Qdrant, Weaviate。工具 (Tools)自定义 Python 函数用于查询数据库、调用计算 API 等。数据层 (Data Layer)职责存储非结构化知识向量库、对话历史、用户信息等。技术选型Chroma轻量级向量库、PostgreSQL关系型数据、Redis缓存。核心抽象Agent一个具备自主决策能力的实体根据目标、上下文和可用工具决定下一步行动。在我们的项目中可以设计一个“金融分析 Agent”。Harness可以理解为对 Agent 或 Chain 的“封装器”和“控制器”。它负责输入标准化与验证清洗用户问题处理多轮对话历史。流程控制与错误处理决定走 RAG 流程还是直接问答处理模型超时、API 错误。输出后处理与格式化将模型的原始输出转换为结构化的 JSON 或友好的自然语言。可观测性 (Observability)注入日志、指标收集和链路追踪。4.2 项目实现分步构建步骤一知识库构建 (RAG Pipeline)这是保证问答准确性的基础。我们使用 LangChain 构建一个离线处理管道。# knowledge_base_ingest.py import os from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma def build_knowledge_base(data_dir: str, persist_dir: str): 加载、切分文档生成向量库并持久化 # 1. 加载文档 loader DirectoryLoader(data_dir, glob**/*.pdf, loader_clsPyPDFLoader) documents loader.load() print(f已加载 {len(documents)} 份文档) # 2. 分割文本 text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, , , ] ) splits text_splitter.split_documents(documents) print(f分割为 {len(splits)} 个文本块) # 3. 创建嵌入模型和向量库 # 使用开源嵌入模型如 BGE 或 text2vec embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-small-zh-v1.5, model_kwargs{device: cuda}, # 或 cpu encode_kwargs{normalize_embeddings: True} ) # 4. 生成向量库并保存 vectordb Chroma.from_documents( documentssplits, embeddingembeddings, persist_directorypersist_dir ) vectordb.persist() print(f知识库已构建并保存至 {persist_dir}) if __name__ __main__: build_knowledge_base(./data/financial_reports, ./chroma_db)步骤二核心问答链与 Harness 封装这里我们实现一个简单的 Harness它集成了检索、重排序和问答。# harness.py from typing import List, Optional from langchain.chains import RetrievalQA from langchain.prompts import PromptTemplate from langchain_community.llms import HuggingFacePipeline from langchain_community.vectorstores import Chroma from langchain_community.embeddings import HuggingFaceEmbeddings from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline import logging logger logging.getLogger(__name__) class FinancialQAHarness: def __init__(self, model_path: str, vectordb_path: str): 初始化 Harness加载模型、向量库构建问答链 self.vectordb_path vectordb_path self.model_path model_path self.qa_chain None self._initialize_components() def _initialize_components(self): 初始化 LLM 和检索器 # 1. 加载本地 LLM (以 Qwen 为例) print(正在加载语言模型...) tokenizer AutoTokenizer.from_pretrained(self.model_path, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( self.model_path, device_mapauto, # 自动分配 GPU/CPU torch_dtypeauto, trust_remote_codeTrue ) pipe pipeline( text-generation, modelmodel, tokenizertokenizer, max_new_tokens512, temperature0.1, do_sampleTrue ) llm HuggingFacePipeline(pipelinepipe) # 2. 加载向量数据库 print(正在加载向量知识库...) embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-small-zh-v1.5) vectordb Chroma(persist_directoryself.vectordb_path, embedding_functionembeddings) retriever vectordb.as_retriever(search_kwargs{k: 4}) # 检索前4个相关片段 # 3. 构建提示词模板 prompt_template 你是一个专业的金融分析师助手。请严格根据以下上下文信息回答问题。如果上下文信息不足以回答问题请直接说“根据现有信息无法回答”不要编造信息。 上下文 {context} 问题{question} 请提供专业、准确、简洁的回答 PROMPT PromptTemplate( templateprompt_template, input_variables[context, question] ) # 4. 构建检索问答链 self.qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, retrieverretriever, chain_type_kwargs{prompt: PROMPT}, return_source_documentsTrue # 返回参考来源 ) print(Harness 初始化完成。) def query(self, question: str, history: Optional[List] None) - dict: 核心查询方法包含错误处理和日志 logger.info(f收到问题: {question}) try: # 这里可以加入对 history 的处理逻辑例如重写问题或丰富上下文 result self.qa_chain.invoke({query: question}) answer result[result] sources [doc.metadata.get(source, 未知) for doc in result[source_documents]] response { answer: answer, sources: list(set(sources)), # 去重 status: success } logger.info(f问题处理成功。) except Exception as e: logger.error(f处理问题时发生错误: {e}) response { answer: 系统处理您的问题时出现错误请稍后再试。, sources: [], status: error, error_detail: str(e) } return response # 初始化 Harness harness FinancialQAHarness( model_pathQwen/Qwen-7B-Chat, vectordb_path./chroma_db )步骤三构建 API 服务 (FastAPI)将 Harness 包装成 Web 服务提供 HTTP 接口。# app.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from harness import FinancialQAHarness import uvicorn app FastAPI(title金融问答机器人 API) # 全局 Harness 实例 qa_harness FinancialQAHarness( model_path./models/Qwen-7B-Chat, # 假设模型已下载到本地 vectordb_path./chroma_db ) class QueryRequest(BaseModel): question: str conversation_id: str None # 用于管理多轮对话 class QueryResponse(BaseModel): answer: str sources: list[str] status: str conversation_id: str None app.post(/query, response_modelQueryResponse) async def query_financial_bot(request: QueryRequest): 核心问答接口 if not request.question or len(request.question.strip()) 0: raise HTTPException(status_code400, detail问题不能为空) # 调用 Harness 处理 result qa_harness.query(request.question) # 构造响应 response QueryResponse( answerresult[answer], sourcesresult[sources], statusresult[status], conversation_idrequest.conversation_id ) return response app.get(/health) async def health_check(): 健康检查端点 return {status: healthy, model_loaded: True} if __name__ __main__: # 启动服务默认端口 8000 uvicorn.run(app, host0.0.0.0, port8000)步骤四高级特性集成 (GraphRAG 微调)GraphRAG对于复杂的金融关系如公司、人物、事件可以构建知识图谱增强推理能力。可以使用neo4j等图数据库并利用 LangChain 的GraphCypherQAChain。模型微调 (SFT/LoRA)如果通用金融语料效果不佳可以使用公司内部的问答对、研报摘要等数据对基座模型进行监督微调 (SFT) 或参数高效微调 (LoRA)。这能显著提升领域内任务的性能。量化部署为了降低部署成本可以使用auto-gptq,llama.cpp等工具对模型进行 INT4/INT8 量化大幅减少显存占用和提升推理速度。4.3 项目业绩与评估一个工程化项目必须有明确的评估指标准确性在预留的测试集上回答与标准答案的匹配度可采用 ROUGE, BLEU 或人工评分。响应时间P95/P99 延迟应满足业务要求如 5 秒内。系统可用性API 的可用性 SLA如 99.9%。成本单次问答的模型推理成本Token 消耗和基础设施成本。可维护性代码模块化程度、配置灵活性、日志和监控的完备性。5. 接口 API 与批量任务5.1 API 调用示例服务启动后python app.py可以通过任何 HTTP 客户端调用。使用 curl 测试curl -X POST http://127.0.0.1:8000/query \ -H Content-Type: application/json \ -d { question: 腾讯控股2023年第四季度的营收是多少, conversation_id: user_123_session_1 }使用 Python requests 调用import requests import json url http://127.0.0.1:8000/query payload { question: 请对比一下茅台和五粮液最近一年的股价走势。, conversation_id: analysis_456 } headers {Content-Type: application/json} response requests.post(url, datajson.dumps(payload), headersheaders, timeout30) if response.status_code 200: result response.json() print(f答案{result[answer]}) print(f参考来源{result[sources]}) else: print(f请求失败: {response.status_code}, {response.text})5.2 批量任务处理对于需要处理大量文档或批量测试问答对的场景可以编写脚本。批量文档入库脚本# batch_ingest.py import os import sys sys.path.append(.) from knowledge_base_ingest import build_knowledge_base import logging from pathlib import Path logging.basicConfig(levellogging.INFO) def batch_process(data_root: str): 遍历目录分批处理文档 for company_dir in Path(data_root).iterdir(): if company_dir.is_dir(): logging.info(f正在处理公司: {company_dir.name}) # 可以为每个公司创建独立的向量库或合并到一个库中 output_dir f./chroma_db_{company_dir.name} build_knowledge_base(str(company_dir), output_dir) if __name__ __main__: batch_process(./data/companies)批量问答测试脚本# batch_qa_test.py import pandas as pd import requests import time from tqdm import tqdm def run_batch_test(test_csv: str, api_url: str, output_csv: str): 读取 CSV 文件中的问题调用 API并保存结果 df pd.read_csv(test_csv) # 列id, question, expected_answer results [] for _, row in tqdm(df.iterrows(), totallen(df)): payload {question: row[question]} try: resp requests.post(api_url, jsonpayload, timeout15) if resp.status_code 200: result resp.json() results.append({ id: row[id], question: row[question], expected: row[expected_answer], actual: result[answer], sources: ;.join(result[sources]), status: result[status] }) else: results.append({...}) # 记录错误 except Exception as e: results.append({...}) # 记录异常 time.sleep(0.5) # 避免请求过载 # 保存结果 pd.DataFrame(results).to_csv(output_csv, indexFalse) print(f批量测试完成结果已保存至 {output_csv})6. 资源占用与性能观察模型加载阶段加载一个 7B 参数的 FP16 模型显存占用约为 14GB。使用量化如 GPTQ-INT4可降至 4-6GB。推理阶段单次问答的显存占用会额外增加取决于输入输出长度。通常 7B 模型处理 1024 token 的上下文显存峰值在原有基础上增加 1-2GB。向量检索检索过程主要在 CPU 内存中进行取决于向量库大小。百万级向量的检索可在百毫秒内完成。API 服务使用uvicorn配合多个 worker可以处理一定程度的并发。性能瓶颈通常在模型推理。需要监控 GPU 利用率和 API 响应延迟。监控建议使用nvidia-smi监控 GPU 显存和利用率。在 FastAPI 中集成 Prometheus 指标如prometheus-fastapi-instrumentator暴露请求次数、延迟、错误率等。记录详细的日志包括用户问题、检索到的文档、模型回答、处理耗时便于调试和优化。7. 常见问题与排查方法问题现象可能原因排查方式解决方案启动服务时ModuleNotFoundErrorPython 依赖未安装或虚拟环境未激活检查当前 Python 环境which python运行pip list查看关键包在正确的虚拟环境中运行pip install -r requirements.txt加载模型时显存不足 (OOM)模型过大或未量化运行nvidia-smi查看显存占用1. 使用量化模型 (GPTQ/AWQ)。2. 使用device_mapcpu或auto让部分层卸载到 CPU。3. 换用更小模型。知识检索返回无关内容1. 文本分割不合理。2. 嵌入模型不匹配或效果差。3. 检索 top-k 值不合适。1. 检查分割后的文本块是否完整。2. 测试嵌入模型在领域内的相似度判断能力。3. 查看检索到的源文档内容。1. 调整chunk_size和chunk_overlap。2. 更换或微调嵌入模型。3. 引入重排序 (Re-ranking) 模型。4. 调整检索参数k。API 响应速度慢1. 模型推理慢。2. 检索耗时过长。3. 网络或序列化开销。1. 使用time模块记录各阶段耗时。2. 监控 GPU 利用率是否饱和。1. 启用模型量化。2. 为向量检索建立索引。3. 使用异步处理或批处理。4. 考虑模型缓存 (如vLLM)。模型回答质量差幻觉1. 提示词设计不佳。2. 检索到的上下文不相关。3. 模型本身能力不足。1. 分析模型接收到的完整提示词。2. 检查检索到的上下文是否包含答案。1. 优化提示词加入更严格的指令。2. 改进 RAG 流程提升检索质量。3. 对模型进行领域微调 (SFT/LoRA)。服务运行一段时间后崩溃内存泄漏、GPU 显存未释放、文件句柄耗尽查看系统日志和进程监控1. 定期重启服务使用进程管理器如 systemd/supervisor。2. 检查代码中是否有资源未释放。3. 设置合理的请求超时和并发限制。8. 最佳实践与使用建议从简单开始迭代优化先构建一个最小可行产品 (MVP)例如只有基础 RAG 的问答再逐步加入 Agent、图检索、微调等复杂功能。配置化将模型路径、向量库路径、API 密钥、超参数等写入配置文件如config.yaml或.env文件避免硬编码。日志与监控先行在开发早期就集成结构化日志和基础监控这是线上排查问题的生命线。测试驱动为核心的 Harness 逻辑编写单元测试和集成测试模拟各种输入包括异常输入确保系统健壮性。版本化管理对模型文件、向量库、代码、配置进行版本控制。考虑使用 DVC (Data Version Control) 管理数据和模型。安全与合规API 接口增加认证和限流。用户输入进行必要的清洗和过滤防止提示词注入。处理敏感数据时确保符合数据安全法规。成本意识监控 Token 使用量对于高频场景考虑缓存策略、使用更小或量化模型、设置使用配额。Harness Engineering 的本质是将软件工程的最佳实践应用于 AI 系统构建。它要求开发者不仅关注模型效果更要关注整个系统的可靠性、可维护性和可扩展性。通过本文的“金融问答机器人”项目实战你应该已经掌握了从设计、实现、测试到部署的核心流程。真正的掌握始于动手建议你克隆相关代码从准备一份自己的数据开始逐步搭建和调试过程中遇到的具体问题才是最好的老师。