一、写 config:让配置有个统一的家

config 是什么、为什么要写

之前项目里每个脚本都要重复写 load_dotenv() 再一个个 os.getenv() 去读 API Key、模型名。文件一多,同样的代码抄好几遍,换个配置要满项目搜,而且 os.getenv() 拿到的东西没有类型约束、也没有默认值。

所以我写了 app/core/config.py,用 pydantic-settingsBaseSettings 定义一个 Settings 类,把所有配置集中在一处:

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", extra="ignore")

    DEEPSEEK_API_KEY: str
    DEEPSEEK_BASE_URL: str
    LLM_MODEL: str = "deepseek-chat"
    APP_NAME: str = "AI Intern Agent"
    DEBUG: bool = False

settings = Settings()

config 的作用:全项目只需 from app.core.config import settings 一处导入,就能拿到所有配置;每个字段有类型注解和默认值,配置项一目了然;以后改配置只改这一个文件。

踩的坑:没有显式声明 env_file

第一次写完启动服务直接报错,说 DEEPSEEK_BASE_URL 字段缺失。原因是 BaseSettings 默认不会自动读取 .env 文件,必须显式声明它去哪读:

model_config = SettingsConfigDict(env_file=".env", extra="ignore")

补上这一行后,配置才真正从 .env 加载进来。以后写配置类,第一件事就是加 model_config

二、重构 main:从函数堆变成真正的 Web 应用

为什么要重构

原来的 main.py 里只有一个普通函数,根本不是一个能对外提供服务的 Web 应用——没有应用实例,也没有任何接口。要让项目真正跑成一个后端服务,必须重构。

重构后的变化

from fastapi import FastAPI
from app.core.config import settings
from app.schemas.common import HealthResponse

app = FastAPI()

@app.get("/health", response_model=HealthResponse)
def health():
    return HealthResponse(
        status="ok",
        service=settings.APP_NAME,
        version="0.1.0",
        app_name=settings.APP_NAME,
        debug=settings.DEBUG,
    )

重构后:

  • 创建了真正的 FastAPI 应用实例 app,项目可以用 uvicorn 启动成服务。
  • 有了第一个接口 GET /health,用来检查服务是否正常运行。
  • 用 Pydantic 模型 HealthResponse 约定返回数据的结构(response_model),FastAPI 会自动校验响应并生成接口文档。
  • 启动后访问 /health 能返回 JSON,访问 /docs 能看到自动生成的 Swagger 文档页。

项目从"一堆脚本"正式变成了"能启动、有接口、有文档"的 Web 服务。

三、Prompt 设计:给 LLM 定好规矩

为什么要设计 Prompt

同样的问题,问法不同,LLM 给的答案质量天差地别。要让模型稳定输出我们想要的结果,就得设计好 Prompt,而不是随口一问。这里的关键是把 Prompt 分成两层:

  • System Prompt:设定角色和规则,整个任务里不变。
  • User Prompt:每次具体的任务输入,会变化。

我为 JD 分析写了一个 System Prompt,设定"资深招聘专家"角色;又写了一个模板函数把招聘文案拼成 User Prompt:

SYSTEM_PROMPT_JD = """你是一位资深招聘专家,熟悉当前就业市场以及各类岗位的职责与技能需求。"""

def build_jd_user_prompt(jd_text: str) -> str:
    return f"""请分析下面这段文本,提取出关键信息,并且输出包含职位名称,技能要求,职责,关键词四个部分
{jd_text}
"""

跑通后,输入一段 JD,模型稳定返回了"职位名称 / 技能要求 / 职责 / 关键词"四个字段的结构化结果。

对后续项目的作用

这套 Prompt 结构是整个项目的核心地基。后面无论是把 JD 分析做成 POST 接口,还是让模型输出规范的 JSON,都建立在"System 定角色、User 传输入、模板函数负责拼装"这个模式之上。设计好 Prompt,就是给后续所有 LLM 功能打好了统一的基础。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐