
AI 改变一切的时刻是什么样子很多人会想到 2016 年 3 月AlphaGo 与李世石的第二局比赛。第 37 手AlphaGo 在棋盘中央落下一颗人类棋手几乎不会考虑的棋子当时的解说员甚至认为它是失误。最后 AlphaGo 赢了。这手棋后来被反复提起因为它不像模仿更像一种人类没有预料到的方案。今天再来看AI 正在从围棋棋盘扩散到代码、客服、营销、内容生成、智能体、编程助手、模型部署等各个领域。关键已经不是模型能不能做到而是工程团队能不能把这种能力稳定接入业务系统。下面按这条链路展开先理解核心概念再准备开发环境然后实现一个带检索增强的 AI 问答服务最后处理常见问题、完成生产化改造并延伸到 Agent 开发。1. 为什么“第 37 手”是 AI 应用开发的分水岭1.1 第 37 手不是神迹而是“跳出训练数据的一步”2016 年 AlphaGo 与李世石的比赛第 37 手之所以被反复讨论不是因为它来自某种神秘力量而是因为它明显跳出了人类棋谱常见的选点。按当时棋手的判断这手棋放在棋盘中央后没有立即形成局部收益更像一次冒险。可后续战斗证明这手棋改变了全局节奏。从工程角度看AlphaGo 能下出这一步依赖的并不是单一的神经网络而是蒙特卡洛树搜索、策略网络、价值网络、训练数据、算力调度和比赛工程支持共同组成的系统。很多分析认为第 37 手代表的是模型在约束条件下探索新方案的能力而不是单纯背诵已有棋谱。这对 AI 应用开发有直接启发使用大模型时真正有价值的不是让模型复述知识而是让它在给定的业务约束、工具条件和数据范围内生成可执行的解决方案。能做到这一点的基础是工程上先建立好上下文、检索、工具调用和结果校验的链路。1.2 从“模型会做什么”到“系统能做什么”很多团队拿到大模型 API 后的第一个误区是把模型当成了完整产品。实际情况是模型 API 决策的是“给定一段文本下一段文本是什么”而业务系统要解决的往往是“用户带着一个真实问题进来系统能不能稳定回答并承担责任”。两者的差异可以这样理解能力层关键词典型内容模型能力生成、推理、理解把用户问题转成自然语言回答生成摘要改写文案系统能力数据、流程、控制接入权限、检索知识库、调用订单接口、记录日志、回调告警同一家公司里哪怕两个团队使用同一个模型一个团队可能做出聊天玩具另一个团队却做出了人工客服辅助系统。差别往往不在模型而在工程能力。具体到问答场景直接调用模型的回答可能“听起来很流畅但内容不对”。接入内部知识库、加上检索、设定来源引用、校验回答质量之后系统才会从“会说话”变成“可用”。1.3 “AI 无处不在”背后的工程底座现在经常看到 AI 编程、AI Agent、AI 视频生成、AI 营销、AI 建站等新名词。这些方向看起来千差万别但落到工程实现上背后几乎都是同一套底座大模型 API 或本地模型服务上下文管理和提示词组织向量检索或外部知识接入工具调用和动作执行日志、评测、限流、降级也就是说AI 技术从模型能力走向业务能力靠的不是某个神秘算法而是把上述环节串起来的工程体系。理解了这一点就不会被一个个新概念带走而是能循着同一套链路去拆解问题。2. AI 应用工程化的核心概念与整体架构2.1 大模型应用的最小组成模型、上下文、工具、记忆一个可用的大模型应用至少包含四个组成要素。组成作用常见实现模型负责语言理解、生成、推理大模型聊天接口、文本生成接口上下文决定模型基于哪些信息回答System Prompt、用户输入、检索片段、历史记录工具让模型触发外部动作Function Calling、HTTP API、数据库查询、代码执行器记忆保存短期或长期信息会话缓存、向量数据库、业务表模型负责“思考”上下文负责“依据”工具负责“行动”记忆负责“积累经验”。实际项目中很多人只关注模型选型忽略了上下文质量结果模型再强回答依旧不准确。2.2 从 Prompt 工程到 RAG再到 Agent大模型应用的能力升级通常会走三条路径它们不是互相替代而是逐层叠加。Prompt 工程是最基础的一层。开发者把问题背景、回答格式、约束条件直接写进提示词让模型在给定范围内作答。它适合一次性任务比如翻译、改写、提取结构化字段。局限也很明显模型知识有截止日期无法访问私有业务数据提示词写得太长还会占用窗口。RAG检索增强生成解决的是知识问题。简单说系统先从文档或数据库里检索出与问题相关的片段再把片段拼接到提示词中让模型基于这些资料回答。它适合企业知识库、产品文档问答、客服辅助。RAG 的前提是检索质量足够高否则模型拿到无关片段回答依旧会偏。Agent 解决的是执行问题。模型不只回答问题而是根据任务目标规划步骤调用工具查看工具返回结果再决定下一步动作。比如“帮我查一下订单状态并生成催发货邮件”Agent 需要先调用订单查询工具再调用邮件生成工具。这已经接近 AlphaGo 下棋时的“搜索-评估-决策”循环。2.3 一套可复用的 AI 应用参考架构参考架构可以按职责分成五层每一层只解决一类问题。分层职责关键点接入层接收用户请求Web API、IM 消息、工单系统回调应用服务层执行业务流程问答服务、Agent 编排、内容生成流程模型层调用语言模型和向量模型模型路由、超时控制、上下文组装数据层提供知识、业务数据和历史记录业务库、向量库、对象存储、缓存基础设施层保障稳定、安全和可观测日志、监控、限流、权限、审校使用这套架构时遇到“回答不准”先查数据层和应用服务层遇到“接口超时”先查模型层遇到“没有权限”先查接入层。思路清晰排错就会快很多。3. 环境准备与依赖配置3.1 技术栈选型下面的最小案例使用 Python 3.10 以上版本、FastAPI 提供 HTTP 接口、OpenAI SDK 调用大模型 API使用 NumPy 实现一个简化版的向量检索。这套组合适合学习环境快速验证也方便后续替换模型服务商。选择 FastAPI 的原因是它足够轻量自带请求校验和 Swagger 文档适合快速写一个可测试的接口。选择 OpenAI SDK 不代表必须使用某一个固定厂商目前很多大模型服务商都提供兼容接口只需要修改 API Key、Base URL 和模型名称即可。Java 团队可以保留后端技术栈使用 Spring AI 或自己封装 HTTP 调用。核心原理相同下面案例中的代码结构可以作为理解链路的参考。3.2 获取模型 API Key 与配置环境变量实际项目里API Key 不应该硬编码到代码中。推荐使用环境变量保存本地开发时用.env文件管理生产环境使用密钥管理系统或配置中心。在项目根目录创建.env文件OPENAI_API_KEYsk-your-key OPENAI_BASE_URLhttps://api.example.com/v1 CHAT_MODELgpt-4o-mini EMBEDDING_MODELtext-embedding-3-small说明OPENAI_API_KEY模型服务商的访问凭据。OPENAI_BASE_URL兼容 OpenAI 接口的服务地址。如果使用本地部署的模型服务这里改成本地服务的地址。CHAT_MODEL实际使用的对话模型名称。EMBEDDING_MODEL生成向量时使用的模型名称。检查环境变量是否加载成功可以执行python -c from dotenv import load_dotenv; load_dotenv(); import os; print(os.getenv(OPENAI_API_KEY))输出非空说明加载成功。注意不要把这个命令的结果提交到仓库。3.3 项目目录与依赖清单为了后续扩展方便建议保持目录简单ai-qa-demo/ ├── app/ │ ├── __init__.py │ └── main.py ├── .env.example ├── requirements.txt └── README.mdrequirements.txt示例fastapi0.111.0 uvicorn[standard]0.30.1 openai1.30.1 python-dotenv1.0.1 numpy1.26.4 pydantic2.7.1安装依赖pip install -r requirements.txt这里给出的是学习环境示例版本进入生产环境前要根据项目实际依赖情况锁定版本并做兼容性测试。如果本地已经有其他 Python 项目推荐先用虚拟环境隔离依赖。4. 实现一个最小可运行的 AI 问答服务4.1 需求与功能边界初始版本不追求复杂目标是跑通一个最小闭环用户提交问题系统从预设知识片段中检索相关内容再把内容交给大模型生成回答最后返回答案和参考来源。输入输出定义输入输出用户问题文本回答文本、参考知识片段列表示例金卡用户有什么权益根据知识片段生成回答并返回命中的片段不要在一开始加入多轮对话、复杂权限、工作流编排。先把链路跑通后续再扩展。4.2 实现后端接口在app/main.py中实现基础服务import os from typing import List from dotenv import load_dotenv from fastapi import FastAPI, HTTPException from openai import OpenAI from pydantic import BaseModel, Field load_dotenv() app FastAPI(titleAI QA Service) client OpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL), ) class QARequest(BaseModel): question: str Field(..., min_length1, max_length2000) class QAResponse(BaseModel): answer: str sources: List[str] # 演示用知识片段实际项目应从配置中心或数据库中读取 knowledge_docs [ 公司产品支持通过API批量创建订单每次最多提交100条。, 退款申请需要在订单完成后30天内提交审批通常需要1-2个工作日。, 客户等级分为普通、银卡、金卡金卡用户享受专属客服通道。, ] app.post(/qa, response_modelQAResponse) def qa(request: QARequest): try: contexts search_knowledge(request.question) except Exception as e: raise HTTPException(status_code502, detailfknowledge search failed: {e}) if not contexts: raise HTTPException(status_code404, detailno related knowledge found) system_prompt 你是企业知识库助手。请只基于提供的知识片段回答问题不要编造内容。如果知识不足请明确说明。 user_prompt 知识片段\n \n.join(contexts) \n\n用户问题 request.question try: resp client.chat.completions.create( modelos.getenv(CHAT_MODEL, gpt-4o-mini), temperature0.2, max_tokens500, messages[ {role: system, content: system_prompt}, {role: user, content: user_prompt}, ], ) answer resp.choices[0].message.content return QAResponse(answeranswer, sourcescontexts) except Exception as e: raise HTTPException(status_code502, detailfmodel call failed: {e})这段代码的关键点在提示词约束。system_prompt明确要求模型“只基于提供的知识片段回答”这能明显降低编造答案的概率。temperature0.2表示保留少量随机性但整体偏向稳定输出。4.3 加入简单的向量检索增强上面的代码里缺少search_knowledge函数。为了让检索不依赖额外服务这里先用 NumPy 实现一个简单的余弦相似度版本。import numpy as np def embed_text(text: str) - List[float]: resp client.embeddings.create( modelos.getenv(EMBEDDING_MODEL, text-embedding-3-small), input[text], ) return resp.data[0].embedding def search_knowledge(question: str, top_k: int 2) - List[str]: if not question.strip(): return [] q_vec np.array(embed_text(question)) scored [] for doc in knowledge_docs: d_vec np.array(embed_text(doc)) score float( np.dot(q_vec, d_vec) / (np.linalg.norm(q_vec) * np.linalg.norm(d_vec) 1e-9) ) scored.append((score, doc)) scored.sort(keylambda x: x[0], reverseTrue) return [doc for _, doc in scored[:top_k]]这段代码有明确的演示边界每次检索都会重新调用 Embedding 接口没有缓存也没有索引。知识片段少的时候没问题知识量变大之后每次遍历所有文档会产生明显延迟和费用。生产环境建议在启动阶段完成文档向量化并把向量写入向量数据库检索时只查询 TopN 结果。4.4 关键参数说明参数含义推荐值错误配置的表现temperature控制随机性0 越稳定2 越发散知识问答 0.2 以下同一问题多次回答不一致max_tokens限制模型最多生成多少 Token500回答被截断结果不完整top_p核采样参数控制候选词范围0.8-0.9可搭配 temperature配合不当会导致输出质量不稳定embedding model文本转向量使用的模型与业务文本类型匹配检索片段相关性差答非所问温度越高模型越容易“发挥”但在企业知识问答场景里这通常意味着不可控。建议所有需要事实依据的回答都使用低温度把“创造性”留给文案生成类场景。5. 运行验证与结果检查5.1 启动服务在项目根目录执行uvicorn app.main:app --reload --port 8000启动成功后访问http://127.0.0.1:8000/docs可以看到 FastAPI 自带的 Swagger 文档。到这里接口定义、请求参数、响应结构已经在文档中自动生成了。如果端口被占用可以换成其他端口uvicorn app.main:app --reload --port 8001注意app.main:app的含义是从app包下的main.py中导入名为app的 FastAPI 实例。目录结构一旦调整这里的导入路径也要同步修改。5.2 发送请求并验证使用 curl 发送一个请求curl -X POST http://127.0.0.1:8000/qa \ -H Content-Type: application/json \ -d {question: 金卡用户有什么权益}预期返回类似{ answer: 根据知识库金卡用户享受专属客服通道。, sources: [ 客户等级分为普通、银卡、金卡金卡用户享受专属客服通道。 ] }验证时不要只看接口是否返回 200还要看三点回答内容是否基于知识片段产生。sources是否包含了正确的参考片段。如果问题不在知识库中系统是否明确返回“知识库中没有相关内容”。5.3 日志与观测点实际开发中AI 接口是典型的不稳定依赖没有日志很难定位问题。至少要在调用前后记录这些信息用户问题检索到的知识片段使用的对话模型名称模型响应耗时Token 用量最终返回内容的前 200 字示例日志片段question金卡用户有什么权益 contexts客户等级分为普通、银卡、金卡金卡用户享受专属客服通道 modelgpt-4o-mini duration_ms1234 total_tokens256生产环境建议给每次请求生成trace_id把日志、调用链和模型响应串起来。否则出现一次“用户说回答不对”的反馈时排查会非常困难。6. 常见问题排查与生产化改造6.1 高频问题排查表问题现象常见原因检查方式处理建议返回 401API Key 错误或已过期检查.env和密钥管理平台重新生成 Key确认环境变量已刷新返回 403服务未开通、欠费、区域限制查看服务商控制台开通对应模型权限确认账号状态502 model call failed模型服务异常或超时查看服务商返回的完整错误码增加重试和超时时间排查网络回答与知识库无关检索片段为空或相似度阈值过低打印search_knowledge结果调整top_k检查知识库切分方式回答被截断max_tokens设置过小查看返回内容是否以停顿结尾调大max_tokens或做自动拼接相同问题结果不稳定temperature过高检查接口参数知识问答降到 0.2 以下增加评测集回归上下文超长检索片段和历史记录过多查看请求 Token 数减少片段数量控制历史轮数换更大窗口模型6.2 三条典型排查路径第一条路径API 调用报错。先确认.env文件是否在正确目录再确认环境变量名是否拼写一致。打印时不要打印完整 Key只打印前四位和后四位防止泄漏。接着确认服务商控制台中的模型是否已被开通。第二条路径回答明显答非所问。不要把问题直接归结为“模型不行”先在日志里看检索结果。如果检索片段为空问题出在知识库不在模型。如果检索片段正确但回答仍然跑偏问题出在提示词约束或温度设置。必要时把完整 Prompt 打印出来手动检查上下文是否清楚。第三条路径生产环境流量一高就超时。要先分清楚瓶颈在模型层还是应用层。模型层看模型调用耗时和服务商限流应用层看 Python 进程线程数和数据库连接池。最简单的优化是先启用流式响应再给模型调用加缓存最后用消息队列削峰。6.3 从学习环境到生产环境还要补什么学习环境只需要能跑通生产环境至少要补齐以下内容配置外置API Key、模型名称、超时时间全部走环境变量或配置中心。权限控制接口要标识用户身份限制单用户调用频率。内容安全增加输入输出审核避免生成违规内容高风险场景加入人工复核。日志与追踪使用结构化日志和trace_id串联检索结果、模型参数和返回内容。异常兜底模型调用失败时返回友好降级文案而不是直接把异常堆栈暴露给用户。代价控制记录每次调用的 Token 消耗设置每日预算和告警。评测集沉淀一批典型问题每次修改 Prompt 或模型后跑一遍回归测试。版本管理Prompt 和模型配置都要版本化方便回滚。不要试图一次性实现所有能力。按“先跑通再加固”的顺序先保证功能可用再逐步补充安全和稳定性。7. 从 Demo 到 Agent更接近“第 37 手”的实践路径7.1 给模型增加工具调用问答服务跑通后下一步通常是让模型执行动作。工具调用Function Calling是 Agent 的基础能力。模型本身不会执行函数它只负责“决定调用哪个工具、传入什么参数”真正执行由应用代码完成。一个简化工具定义如下{ name: get_order_status, description: 根据订单号查询订单状态, parameters: { type: object, properties: { order_id: { type: string } }, required: [order_id] } }完整的 Agent 工作循环大致如下用户输入目标。模型判断需要调用工具并输出函数名和参数。应用代码执行工具得到结果。把工具结果作为新消息追加到对话中。模型再次生成回答决定是完成还是继续调用下一个工具。这就是最小形态的 Agent。注意每一步动作都要有权限校验、超时控制和审计日志。不要以为 Agent 会自动完成所有事缺少约束的 Agent 在生产环境会引发严重事故。7.2 用 Spring AI 或 LangChain 减少重复工作LangChain 在 Python 生态中比较流行它封装了 Prompt 管理、文档加载、向量检索、工具调用等模块适合快速做原型验证。如果你已经使用 Python可以用它减少胶水代码。Java 技术栈可以关注 Spring AI。它提供了类似的抽象把 ChatModel、EmbeddingModel、Tool Calling 等能力统一封装适合已有 Spring Boot 项目的团队集成。和 LangChain 一样Spring AI 也在快速迭代接入前要确认当前版本对应的接口变化。框架能减少重复劳动但不要忽略底层原理。框架解决的是“调用方式”模型输出质量仍由数据、检索、提示词和评测决定。7.3 AI 应用开发学习路线与工程清单学习路径建议按下面顺序推进大模型基础Token、Context 窗口、温度、采样。提示词工程系统提示词、少样本示例、格式约束。RAG文档切分、向量检索、相似度阈值、重排。Agent工具调用、规划策略、持久记忆、安全边界。工程化部署、监控、评测、限流、安全审校。每一步都可以沉淀成工程清单。例如阶段检查项Prompt 修改是否跑过评测集是否能回滚RAG 上线片段切分大小是否合理检索失败是否有兜底Agent 上线工具权限是否最小化每一步动作是否有日志模型切换是否对比同一批问题的新旧输出发布上线是否配置了限流、降级、监控告警把这些清单写进项目的 CI 或发布流程AI 应用才不容易偏离可控范围。8. 实践建议先跑通最小闭环再追逐新概念8.1 最小闭环比复杂框架更有用面对蜂拥而至的新概念最容易陷入的误区是先学框架、先搭复杂架构结果代码没跑通概念倒是懂了不少。更有效的做法是先写出一个最小服务一个 HTTP 接口一组知识片段一次模型调用一次结果返回。这个闭环虽然简单但它能让你切身体会几个核心问题Prompt 如何组织检索结果如何影响回答模型参数如何影响输出稳定性日志如何帮助排查问题。把这些体验积累起来再切换到 Agent 或多模态应用思路会顺很多。8.2 建议的练习项目清单想巩固 AI 应用能力可以从下面四个项目中选择一两个动手完成项目一基于文档的客服问答机器人。练习文档切分、向量检索、来源引用。项目二代码评审助手。输入 Git Diff输出风险点和修改建议。练习结构化提示词和流式输出。项目三SQL 生成助手。根据表结构生成 SQL并通过工具执行查询。练习工具调用和结果校验。项目四会议纪要助手。上传会议记录检索历史相关内容生成待办清单。练习长文本处理和记忆管理。每个项目都不要停留在“能跑”这一步。跑通之后加一个异常分支加一个日志字段加一个评测用例才算真正掌握。8.3 自我评估清单判断自己是否真正掌握这条链路可以对照以下问题如果检索结果为空你的系统会返回什么如果模型 API 超时用户会看到什么如果同一问题连续问十次回答是否一致如果知识库更新了系统是否能读到新内容如果出现一次用户投诉你是否能通过日志还原完整请求过程能回答清楚这些问题说明你已经从“调用 API”进入“构建 AI 应用”的阶段。AlphaGo 的第 37 手是 AI 在棋盘上创造的意外而你需要在业务系统里做的是用工程手段把这种“意外”变成稳定、可验证、能落地的能力。