趋近智
随着您的大语言模型应用从简单的脚本演变为更复杂的系统,代码的组织方式变得越来越重要。就像在传统软件开发中一样,结构良好的代码库更易于理解、维护、测试和扩展。对于大语言模型应用而言尤其如此,它们通常涉及提示模板、与外部API的交互逻辑以及模型输入输出的特定数据处理等特有组件。及早采纳良好的结构实践将大大节省后期精力,尤其是在与他人协作或准备部署时。
软件设计中的一个基本原则是关注点分离。这意味着应用程序的不同部分应负责独立的功能。将此应用于大语言模型应用有助于管理复杂性。请考虑分离以下常见关注点:
合理的目录结构使项目浏览和理解变得容易得多。理想的结构取决于应用程序的复杂程度,但这里有几种常见模式:
简单应用结构:
对于小型项目,扁平结构可能足够,将关注点清晰分离到不同的Python模块中:
my_llm_app/
├── app.py # 主应用逻辑或Web服务器(例如Flask)
├── config.py # 加载配置(通过环境变量获取API密钥,模型名称)
├── llm_client.py # 用于LLM API交互的函数/类
├── prompt_utils.py # 用于加载/格式化提示的辅助函数
├── prompts/ # 存储提示模板文件的目录
│ ├── summarize.txt
│ └── qa_cot.txt
├── utils.py # 通用工具函数(例如,输出解析)
├── .env # 环境变量(添加到.gitignore!)
└── requirements.txt # 项目依赖
更复杂的应用结构:
随着应用程序的增长,特别是在包含LangChain等框架或涉及多个独立功能(如问答、摘要、RAG)时,更分层或基于功能的结构更有益:
advanced_llm_app/
├── main.py # 主入口点(例如,启动Web服务器或CLI)
├── core/ # 核心共享组件
│ ├── __init__.py
│ ├── config.py # 配置加载(环境变量,文件)
│ ├── llm_interface.py # 抽象的大语言模型交互逻辑
│ ├── prompt_manager.py # 集中式提示加载/模板处理
│ └── output_parser.py # 共享输出解析工具
├── modules/ # 应用功能/模块
│ ├── __init__.py
│ ├── qa/ # 问答模块
│ │ ├── __init__.py
│ │ ├── chain.py # 问答特有逻辑(例如,LangChain链)
│ │ └── prompts/ # 问答特有提示
│ │ └── retrieval_qa.yaml
│ ├── summarization/ # 摘要模块
│ │ ├── __init__.py
│ │ ├── service.py # 摘要特有逻辑
│ │ └── prompts/ # 摘要特有提示
│ │ └── condense_document.txt
│ └── rag/ # RAG组件(如果使用)
│ ├── __init__.py
│ ├── retriever.py
│ └── vector_store.py
├── shared/ # 不属于LLM交互核心的共享工具
│ └── data_models.py # 用于验证的Pydantic模型
├── tests/ # 单元测试和集成测试
│ ├── core/
│ └── modules/
├── .env # 环境变量
└── requirements.txt
结构化大语言模型应用的依赖关系流。功能模块使用核心服务,这些服务处理配置和直接的大语言模型交互。
思考如何设计可复用的代码组件(函数、类或模块)。
llm_client.py或core/llm_interface.py)。此组件可以封装以下细节:
PromptManager类或实用函数,其可以:
.txt、.json、.yaml)。LLM包装器、PromptTemplate、OutputParser、Chain、Agent和Retriever类本身就鼓励模块化设计。围绕这些组件组织您的代码。将API密钥、模型名称或文件路径直接硬编码到应用程序代码中是脆弱且不安全的。请使用以下方式外部化配置:
python-dotenv等库从.env文件加载变量(确保将.env添加到您的.gitignore中)。在部署环境中,这些变量通常通过托管平台设置。
# config.py
import os
from dotenv import load_dotenv
load_dotenv() # 从.env文件加载变量
API_KEY = os.getenv("OPENAI_API_KEY")
MODEL_NAME = os.getenv("DEFAULT_MODEL", "gpt-3.5-turbo")
config.yaml、settings.toml)通常更清晰。PyYAML、tomli或hydra-core等库可以帮助加载和管理这些文件。
# config.yaml
llm:
default_model: "claude-3-sonnet-20240229"
temperature: 0.7
max_tokens: 500
paths:
prompts_dir: "./prompts"
features:
enable_rag: true
# config.py(使用PyYAML)
import yaml
import os
def load_config(path="config.yaml"):
with open(path, 'r') as f:
config = yaml.safe_load(f)
# 如果需要,允许使用环境变量覆盖
config["llm"]["api_key"] = os.getenv("ANTHROPIC_API_KEY")
return config
CONFIG = load_config()
MODEL_NAME = CONFIG.get("llm", {}).get("default_model", "unknown-model")
结合这些方法(例如,从文件加载默认值并使用环境变量覆盖)可提供灵活性。
提示定义您大语言模型的行为。在您的项目结构中,将它们视为首要元素:
/prompts目录中)。使用清晰的命名约定。PromptTemplate)都是不错的选择。
# prompts/summarize_template.j2
Summarize the following text in {{ target_sentences }} sentences:
{{ text_to_summarize }}
Summary:
# prompt_utils.py
from jinja2 import Environment, FileSystemLoader
import os
PROMPTS_DIR = os.path.join(os.path.dirname(__file__), 'prompts')
env = Environment(loader=FileSystemLoader(PROMPTS_DIR))
def get_prompt(template_name, **kwargs):
template = env.get_template(template_name)
return template.render(**kwargs)
# 在其他地方使用
# from prompt_utils import get_prompt
# my_prompt = get_prompt("summarize_template.j2", target_sentences=3, text_to_summarize=user_input)
结构良好的应用程序更容易添加日志和错误处理:
logging模块。记录重要事件、决策、输入/输出(可能已净化)和错误。结构化日志记录(例如JSON格式)对后续分析有益。try...except块,特别是在外部调用(LLM API、数据库查找)和数据解析/验证周围。如果需要,定义自定义异常类以区分应用程序特有错误。从一开始就周全地组织您的大语言模型应用代码,可以奠定坚实基础。它提高了清晰度,并使本章后续考量点(例如保护API密钥、监控成本、有效测试和最终部署)的实施成为一个更易于管理的过程。
简洁的语法。内置调试功能。从第一天起就可投入生产。
为 ApX 背后的 AI 系统而构建
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造