首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >从 API 调用到生产系统:AI 大模型应用开发的工程化路径

从 API 调用到生产系统:AI 大模型应用开发的工程化路径

原创
作者头像
用户12339161
发布于 2026-10-30 16:57:27
发布于 2026-10-30 16:57:27
1820
举报

核心判断: 调用一次 Chat Completions API 不等于“会做大模型应用”。真正拉开差距的,是 RAG 检索质量、Agent 工具调用的稳定性、微调数据的构建能力,以及结构化输出与评估监控的闭环。本文沿“检索 → 行动 → 适配 → 部署 → 质量”五条工程主线,给出可直接运行的代码骨架。

一、RAG:让模型先看材料再回答

通用模型的两条硬约束——知识有截止日期、私有文档进不了训练集——决定了 RAG 是企业落地的最短路径:先检索 Top-K 片段,再让模型基于片段生成。

最小可运行链路如下,覆盖加载、切分、向量化、检索、生成:

代码语言:javascript
复制
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate

docs = PyPDFLoader("handbook.pdf").load()
chunks = RecursiveCharacterTextSplitter(
    chunk_size=500, chunk_overlap=80
).split_documents(docs)

vectorstore = Chroma.from_documents(
    chunks, OpenAIEmbeddings(model="text-embedding-3-small")
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

prompt = ChatPromptTemplate.from_messages([
    ("system", "只基于以下上下文作答。若上下文不含答案,直接说“未找到”。\n\n{context}"),
    ("human", "{question}"),
])
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
chain = prompt | llm

生产化至少再加两层:查询改写(低召回时用 LLM 重写 query)和兜底降级(无命中时走通用回答而非强行编造)。如果检索质量本身不过关,后面接再强的模型都是浪费 token。

二、Agent:模型只做决策,执行永远在你手里

Function Calling 的本质常被误解。模型不执行函数,只输出一段结构化调用请求;真正调函数、回填结果的是你的代码。循环是:发消息 → 模型返回 tool_calls → 你执行 → 结果塞回对话 → 模型总结

代码语言:javascript
复制
tools = [{
    "type": "function",
    "function": {
        "name": "query_order",
        "description": "当用户询问订单状态、物流进度时使用",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string", "description": "订单号"}},
            "required": ["order_id"],
        },
    },
}]

messages = [{"role": "user", "content": "帮我查订单 SN20260611001 到哪了"}]
resp = client.chat.completions.create(
    model="qwen-max", messages=messages, tools=tools
)
msg = resp.choices[0].message
if msg.tool_calls:
    tc = msg.tool_calls[0]
    result = query_order(**json.loads(tc.function.arguments))
    messages.append(msg)  # 必须回传 assistant 的 tool_calls
    messages.append({
        "role": "tool",
        "tool_call_id": tc.id,       # id 必须原样带回
        "content": json.dumps(result, ensure_ascii=False),
    })
    final = client.chat.completions.create(
        model="qwen-max", messages=messages, tools=tools
    )

高频错误是忘记回传 tool_calls 消息,导致下一轮 tool_call_id 对不上。另一个值得多花时间的细节是工具描述:它定义的是“什么时候该用”,不是“这个函数做什么”,写得模糊,模型就会在不该调用的场景瞎调。

三、LoRA:用不到 1% 的参数换领域适配

当输出风格、术语体系或格式约束无法靠 Prompt 稳定控制时,才轮到微调。LoRA 冻结基座权重,只在注意力模块旁路插入低秩矩阵,7B 模型的显存需求可从 60GB+ 降到 16GB 以内。

代码语言:javascript
复制
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained(
    "your-base-model", device_map="auto", torch_dtype="auto"
)
config = LoraConfig(
    r=8, lora_alpha=16,
    target_modules=["q_proj", "v_proj"],
    lora_dropout=0.05, task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)
model.print_trainable_parameters()
# 可训练参数通常在原模型的 0.1%–1% 量级

数据格式用 JSONL 的 instruction / output 对即可,但质量比量级更重要:格式统一、长度适中、风格一致。训练完成后,LoRA 权重可单独保存为适配器,切换任务时无需重新加载基座。

四、部署与质量:容易被跳过、但决定能不能上线的两步

推理层,vLLM 配合 FP8/NVFP4 量化能把显存占用和吞吐拉到可接受区间。服务层用 LangServe 的 add_routes 可以把任意 Runnable 挂到 FastAPI,自动生成 OpenAPI 文档。但比“能跑起来”更重要的是两个工程习惯:

结构化输出用 Pydantic 约束,不用裸 JSON。 裸 Prompt 输出的 JSON 可能在字段名、类型上四处漂移;response_format={"type":"json_object"} 只能保证合法 JSON,不保证符合 Schema。生产代码应经过 Pydantic 验证,并对拒答、截断、限流分别处理,而非静默替换成默认值。

上线前先量 RAG 质量。 RAGAS 的 faithfulness、answer_relevancy、context_recall 三个指标分别度量“有没有编”“答得切不切题”“该召回的有没有召回”,跑一轮基准测试的成本远低于线上翻车后再排查。

大模型应用的工程门槛不在“调通 API”,而在检索链路的召回质量、工具调用链的健壮性、微调数据的构建纪律,以及从第一天就把评估和结构化验证放进流水线。代码骨架搭起来只是起点,这四层做扎实了,系统才经得起真实流量。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、RAG:让模型先看材料再回答
  • 二、Agent:模型只做决策,执行永远在你手里
  • 三、LoRA:用不到 1% 的参数换领域适配
  • 四、部署与质量:容易被跳过、但决定能不能上线的两步
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档