如果你在 2024 年关注过 AI 工程圈,一定会反复听到一个词:Stone Soup AI。它字面意思是“石头汤 AI”,这个名字本身来自一个经典寓言——一个陌生人带着一口空锅走进村庄,说要煮一锅石头汤。村民们好奇,你带一根胡萝卜,我带一把洋葱,他拿几块土豆,最后真的煮出了一锅浓汤。2024 年的 AI 应用开发,越来越像这个故事:真正撑起一个产品的,往往不是某个“神秘大模型”,而是把已有的组件一件件放进锅里的人。
这里想给出一个明确判断:Stone Soup AI 并不是某个具体的开源仓库,也不应该被理解成“用 AI 生成一切”的口号。它更准确地说,是一种面向 2024 年 AI 工程现实的开发范式——模型能力已经由大厂 API 或开源模型提供,普通开发团队真正要做的,不是从零训练模型,而是像煮石头汤一样,把 LLM、Embedding、向量数据库、工具调用、服务编排这些“食材”组装成一个可运行、可维护、可评估的业务系统。
这篇文章会从概念到代码,把 Stone Soup AI 这套思路完整落地。你会看到:为什么 2024 年做 AI 应用的核心矛盾不是模型不够强,而是工程化不够顺手;一个最小 AI Agent 长什么样;一个不依赖重型中间件的 RAG 示例怎么写;以及把它封装成 FastAPI 服务后,如何验证、如何排错、如何在生产环境避开那些“看起来不大,炸起来很疼”的坑。无论你是后端工程师、算法工程师,还是刚准备进入 AI 应用开发的初学者,这篇文章都值得收藏备用。
1. Stone Soup AI:这篇文章真正要解决的问题
先说结论:2024 年普通团队做 AI 应用,最大的痛点不是“没有模型可用”,而是“不知道从哪开始”。你打开技术社区,看到的是 Agent、RAG、微调、多模态、向量数据库、LangChain、LlamaIndex、模型部署、AI 应用开发学习路线……概念一个接一个,但回到自己的项目里,你还是不知道第一行代码该写在哪里。
Stone Soup AI 要解决的,正是这个“第一行代码”的问题。它不要求你从零训练一个 ChatGLM 或 LLaMA,不要求你先维护几个 GPU 集群,也不要求你一开始就设计一个复杂的多智能体协同系统。它建议你先搭一口空锅——也就是一个最简可运行的系统骨架,然后根据业务的真实反馈,把需要的“食材”一件一件加进去。
从技术分工看,2024 年开发团队其实只需要聚焦四件事:一是明确业务问题到底是什么类型,是文本分类、信息抽取、开放问答,还是需要多步工具调用的 Agent 任务;二是选好模型入口,是通过 API 调用,还是基于开源模型做私有化部署;三是设计好上下文,也就是如何把用户输入、检索资料、工具返回结果组合成模型能理解的 messages;四是做好评估与控制,保证模型输出在业务上是可接受的。
换句话说,2024 年做 AI 产品,难点已经从“模型训练”转移到了“系统设计”。如果你还在纠结“我要不要训练一个自己的大模型”,大概率是方向搞错了。更现实的路径是:把别人的“石头”拿过来,煮自己的“汤”。
1.1 适合认真读这篇文章的人
- 后端工程师:想给自己的系统加入 AI 能力,但不想被概念淹没,需要一条可执行的接入路径。
- 算法工程师:熟悉模型原理,但不熟悉工程侧的服务封装、向量检索、接口验证,需要补齐工程短板。
- 技术管理者 / 产品经理:需要判断一个 AI 项目该用什么技术栈、需要多少成本、哪些功能应该先用现成方案快速验证。
- AI 初学者:已经会调用 ChatGPT,但不知道“AI 应用开发”和“单纯的 API 调用”到底差在哪里。
如果只是打算“玩一玩 AI 工具”,这篇文章可能偏重了;但如果你要做一个真正给用户用的 AI 功能,下面这些内容是绕不开的。
2. 核心概念:Stone Soup AI 的三种理解方式
要先理解 Stone Soup AI,不能只看字面。在 2024 年的技术语境里,它至少可以拆成三层意思。
第一层,是“组装优于训练”。这个理念最早在开源社区很常见:不需要每个人都从零写一遍基础组件,把别人已验证过的组件拿过来,按自己的业务场景拼装,比自己“闭门造车”靠谱得多。大模型时代这一层被进一步放大了——你不能自己从头训练一个 GPT-5 级别的大模型,但你可以把模型 API、向量检索、业务数据库、工具函数组装成一个外人看起来“很 AI”的系统。
第二层,是“渐进式构建”。石头汤的故事里,那口锅最开始只有水和石头,味道寡淡,但随着越来越多村民贡献食材,汤越来越好喝。AI 项目的正确启动方式也应该是这样:第一天只需要一个最小的可运行 Demo,哪怕只调用一次模型 API 并打印结果;第二周再接入真实数据,做检索增强;第一个月再加评估、监控、灰度。不要一开始就追求“宏大架构”,否则大概率会在第二周因为复杂度膨胀而放弃。
第三层,是“开放协作”。石头汤能煮成,靠的是每个村民都愿意贡献一点。今天 AI 工程的可用组件非常丰富:开源模型、RAG 框架、向量数据库、可观测工具、评测集,几乎每一层都有成熟方案。团队真正要做的,是定好接口标准,让这些组件能互相配合,而不是所有事情都自己做。
2.1 与“从零训练模型”的差异
很多人一谈到“AI 项目”,第一反应是“我要训练模型”。这个反应在 2018 年合理,在 2024 年已经过时了。下面用一张表看清差异:
| 维度 | 从零训练 / 微调大模型 | Stone Soup AI 组装式开发 |
|---|---|---|
| 核心成本 | 数据清洗、GPU 算力、训练调参 | 系统集成、上下文设计、工程交付 |
| 团队要求 | 算法团队为主,工程团队配合 | 工程团队为主,算法聚焦评测 |
| 启动速度 | 数周到数月 | 数小时到数天 |
| 迭代方式 | 重新训练或微调 | 替换模型 / 增加工具 / 优化检索 |
| 风险点 | 算力成本高、数据合规风险大 | 模型输出不稳定、组件之间兼容性 |
| 适合情况 | 特定领域效果要求极高、数据敏感 | 大多数业务场景的技术验证和快速上线 |
这里并不是说微调和训练没有价值,而是提醒团队先判断自己到底处于哪个阶段。绝大多数业务场景,在用户量和数据量都没有被验证之前,贸然投入训练是一笔高风险投资。更稳妥的判断是:先组装,跑通业务闭环,再评估哪些环节真正需要定制模型。
2.2 Stone Soup AI 与 AI Agent、RAG 的关系
AI Agent(智能体)和 RAG(检索增强生成)是 2024 年两个高频概念,它们恰好都属于 Stone Soup AI 这种组装式开发范式里的“组件”。
- RAG 解决的是“模型不知道业务知识”的问题。模型是通用能力,你的业务数据是私人资产,RAG 通过先检索再生成的方式,把相关资料塞进上下文,让模型基于资料回答。
- AI Agent 解决的是“模型只会聊天、不会做事”的问题。它让模型可以调用外部工具,比如查天气、查数据库、创建工单,从而从“聊天机器人”升级为“业务助理”。
- 编排层解决的是“多个组件如何协作”的问题。一个完整的 AI 应用,往往同时需要 RAG 和 Agent,还需要流程控制、权限校验、超时重试。
在后面的示例里,你会看到这三个层面如何落到代码中。你会发现,它们并没有想象中那么神秘。
3. 开始之前的架构设计:先画锅,再放食材
“先把锅架起来”听起来很朴素,但它是 Stone Soup AI 中最重要的一步。很多项目出问题,不是因为某个组件不行,而是因为锅里放的食材互相不兼容。
在实际项目中,我更推荐把 AI 应用按下面这个分层结构来规划:
- 接入层:面向用户或上游系统的接口,常见形态是 REST API、WebSocket、消息队列消费者。这一层负责参数校验、鉴权、限流。
- 编排层:核心业务逻辑所在,决定“先做什么、后做什么”。它可以是简单的 if-else,也可以是基于 Agent 的循环调用,重点是把 LLM 的输入输出和业务状态管理好。
- 模型层:LLM 对话、Embedding 向量化、重排序模型。这一层要先定义好统一接口,方便以后切换模型服务。
- 存储层:业务数据库、向量数据库、缓存。向量数据库负责存文档切片和 Embedding,业务数据库负责存用户状态和交互记录。
- 可观测层:日志、链路追踪、模型输出评估。没有这一层,你在生产环境里根本没法回答“模型答得好不好”这个问题。
从调用链上看,一个典型的请求是:用户请求进来后,编排层判断需要哪些上下文,先通过 Embedding 做向量检索,把相关资料拼到 prompt 里,再调用 LLM 生成回答,最后把回答返回给用户,同时记录日志。如果需要工具调用,就在 LLM 返回 tool_calls 后执行本地函数,并把执行结果回传给模型,让模型生成最终回答。
设计阶段还有三个原则值得记住:
第一,最小依赖原则。能用标准库和轻量库解决的问题,就不要引入重量级编排框架。很多团队一上来就引入 Agent 框架,结果被抽象层里隐藏的 bug 折腾得焦头烂额。起步阶段,先自己写 50 行代码,把链路走通,再考虑是否引入框架。
第二,接口可替换原则。所有外部依赖都要收敛到一个薄薄的封装层后面,尤其是模型 API。这样将来换模型、换 Embedding 服务,不需要改业务代码。
第三,成本显性化原则。2024 年很多 AI 项目的失败,不是技术达不到,而是账单先到了。设计时就该考虑每个环节的 token 消耗,包括 prompt 里的系统提示词、检索回来的上下文、模型多次调用造成的费用累积。
4. 环境准备与基础配置
下面开始实际操作。本机环境假设是 macOS 或 Linux,Windows 也可以运行,只是激活虚拟环境的命令稍有不同。需要提前安装 Python 3.10 及以上版本,以及 pip。
先创建一个项目目录,并初始化虚拟环境:
mkdir stone-soup-ai-demo cd stone-soup-ai-demo python3 -m venv venv source venv/bin/activateWindows 环境下的激活命令是:
venv\Scripts\activate4.1 安装依赖
为了让示例保持轻量,只安装下面这些库:
# 文件路径:requirements.txt python-dotenv>=1.0 openai>=1.30 numpy>=1.26 fastapi>=0.110 uvicorn[standard]>=0.29安装命令:
pip install -r requirements.txt这里用 openai 库,是因为它已经是事实上的“模型 API 客户端标准接口”。即使你使用的是其他提供 OpenAI 兼容接口的模型服务,也可以用同一个 SDK 接入,只需要修改环境变量。
4.2 配置 API 密钥与模型参数
在项目根目录创建.env文件,内容如下:
# 文件路径:.env OPENAI_API_KEY=sk-your-key-here OPENAI_BASE_URL=https://api.openai.com/v1 LLM_MODEL=gpt-4o-mini EMBEDDING_MODEL=text-embedding-3-small需要特别提醒:.env文件一定不能提交到 Git 仓库。建议把.env加入.gitignore:
# 文件路径:.gitignore .env venv/ __pycache__/如果你的模型服务不是 OpenAI 官方,只要它提供 OpenAI 兼容的/chat/completions和/embeddings接口,就把OPENAI_BASE_URL改成服务方提供的地址。这是 2024 年做 AI 工程最值得养成的习惯:把模型服务当成可替换的“外部资源”,而不是把代码写死到某一家厂商。
5. 从零拼装第一个 AI Agent
这一节的目的是用最少的代码,跑通一个带工具调用能力的 AI Agent。它会演示整个 Stone Soup AI 最核心的流程:用户输入 → 模型判断需要工具 → 执行工具 → 把工具结果回传给模型 → 生成最终回答。
5.1 核心流程拆解
2024 年,主流大模型已经原生支持 Function Calling(工具调用)。这意味着模型不再只输出文本,而是可以输出一个结构化的“调用请求”,比如:我想调用get_weather,参数是{"city": "北京"}。你的服务端拿到这个请求,真正去执行本地函数,再把函数的返回结果作为一条工具消息发给模型。
一次完整的 Agent 交互如下:
- 用户提问:“北京今天天气怎么样?”
- 系统把用户问题发送给模型,并告诉模型可用的工具列表。
- 模型返回一个 tool_calls,内容是调用
get_weather(city="北京")。 - 系统执行本地函数
get_weather("北京"),得到结果。 - 系统把函数结果添加到消息序列里,再次发送给模型。
- 模型基于工具结果,生成最终的自然语言回答。
注意,模型本身不会真正调用任何外部系统,它只是“决定调用哪个工具,并生成参数”。真正执行工具的是你的代码。这个边界想清楚,就不会对 Agent 产生“什么都能干”的误解。
5.2 完整示例代码
创建weather_agent.py:
# 文件路径:weather_agent.py import json import os from dotenv import load_dotenv from openai import OpenAI load_dotenv() client = OpenAI( api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_BASE_URL"), ) def get_weather(city: str) -> str: """模拟查询天气的工具。真实项目中这里应该调用第三方天气服务。""" fake_data = { "北京": "晴,25℃", "上海": "多云,28℃", "广州": "小雨,30℃", } return fake_data.get(city, f"暂未收录 {city} 的实时天气") def call_agent(user_input: str) -> str: tools = [ { "type": "function", "function": { "name": "get_weather", "description": "查询指定城市当前的天气情况", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如:北京", } }, "required": ["city"], }, }, } ] messages = [{"role": "user", "content": user_input}] response = client.chat.completions.create( model=os.getenv("LLM_MODEL", "gpt-4o-mini"), messages=messages, tools=tools, tool_choice="auto", ) message = response.choices[0].message messages.append(message) if message.tool_calls: for tool_call in message.tool_calls: args = json.loads(tool_call.function.arguments) result = get_weather(**args) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result, }) second_response = client.chat.completions.create( model=os.getenv("LLM_MODEL", "gpt-4o-mini"), messages=messages, tools=tools, tool_choice="auto", ) return second_response.choices[0].message.content return message.content if __name__ == "__main__": print(call_agent("北京今天天气怎么样?"))这段代码虽然只有 60 行左右,但它已经具备了 Agent 的最基本结构:工具定义、模型调用、工具执行、结果回传。后面无论你接入多少个工具,核心骨架都不会变。
5.3 运行与预期效果
运行命令:
python weather_agent.py正常情况下,你会在终端看到类似下面的输出:
北京今天晴,25℃,体感温度很舒适。这里真正容易踩坑的地方是:如果模型返回的tool_calls参数为空,说明模型认为不需要调用工具,此时要直接返回message.content。另外,如果tool_call.id回传错误,服务端会报错“tool call id mismatch”,这也是新手最容易遇见的 Agent 调试问题。
6. 第二步:用 Embedding 搭建知识库问答服务
普通对话能力只解决了“会说话”的问题,业务场景更常需要的是“懂业务”。比如你的系统里有一批内部手册,你希望用户提问时,AI 能基于手册内容回答,而不是自己编造。这就用到了 RAG:Retrieval-Augmented Generation,检索增强生成。
6.1 为什么需要 RAG
大模型的参数里学不到你公司的私有知识。即使你喂了很长一段文档,模型也受限于上下文窗口,而且每次请求把全部文档塞进去,成本不现实。RAG 的思路是:先把文档切成小块,用 Embedding 模型把每一块转成向量;用户提问时,把问题也转成向量,然后找出语义上最相似的几个文档块,只把这几块拼到 prompt 里,让模型基于这些材料回答。
为了体现 Stone Soup AI 的“渐进式构建”,这里先用 NumPy 实现一个最小的本地向量检索,不依赖任何重型向量数据库。这个示例用来理解原理完全够用。
6.2 一个不依赖向量数据库的最小实现
创建simple_rag.py:
# 文件路径:simple_rag.py import os from dotenv import load_dotenv from openai import OpenAI import numpy as np load_dotenv() client = OpenAI( api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_BASE_URL"), ) EMBEDDING_MODEL = os.getenv("EMBEDDING_MODEL", "text-embedding-3-small") def split_text(text: str, chunk_size: int = 200, overlap: int = 40) -> list[str]: """按字符做基础切分,生产环境建议换成语义切分。""" chunks = [] start = 0 while start < len(text): end = min(start + chunk_size, len(text)) chunks.append(text[start:end]) if end == len(text): break start = end - overlap return chunks def build_index(text: str): """把文本切片并生成向量索引。""" chunks = split_text(text) vectors = client.embeddings.create(model=EMBEDDING_MODEL, input=chunks) return chunks, [item.embedding for item in vectors.data] def cosine_similarity(a, b): a = np.array(a) b = np.array(b) return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))) def search(query: str, chunks, vectors, top_k: int = 3): query_vec = client.embeddings.create( model=EMBEDDING_MODEL, input=[query], ).data[0].embedding scored = [ (cosine_similarity(query_vec, vec), chunk) for vec, chunk in zip(vectors, chunks) ] scored.sort(key=lambda x: x[0], reverse=True) return scored[:top_k] if __name__ == "__main__": docs = [ "Stone Soup AI 是一种把现成模型、工具和数据组件组装成 AI 应用的方法论。", "RAG 是指检索增强生成,先检索相关资料,再让模型基于资料生成回答。", "AI Agent 的核心是让模型可以调用外部工具,并自主完成多步任务。", ] chunks, vectors = build_index("\n".join(docs)) for score, chunk in search("什么是 RAG?", chunks, vectors): print(round(score, 4), chunk)运行命令:
python simple_rag.py预期输出会显示:问题“什么是 RAG?”和第二条文档的相似度最高,另外两条文档相似度明显更低。这种“先检索再生成”的能力,是让 AI 系统基于你私有知识回答问题的地基。
6.3 什么时候升级到正式向量数据库
上面的示例只适合理解和原型验证。当你的文档量超过几千条、查询并发上来了、需要按标签过滤或者做增量更新时,建议升级到专门的向量数据库组件。2024 年常见的可选方案包括:
| 方案 | 特点 | 推荐场景 |
|---|---|---|
| Chroma | 轻量,Python 原生,适合原型 | 快速验证、单机测试 |
| Qdrant | Rust 实现,性能好,支持过滤 | 中等规模生产环境 |
| Milvus | 分布式能力较强,组件较多 | 大规模生产环境 |
| pgvector | 基于 PostgreSQL 的扩展 | 团队已有 PG 技术栈时优先考虑 |
从 Stone Soup AI 的角度看,原型阶段的 NumPy 实现和正式阶段的向量数据库,本质是同一个接口:传入向量、返回最相似的 Top K。提前封装好这个接口,后面替换存储层时,你的业务代码基本不用改动。
7. 完整示例:用 FastAPI 封装一个可调用的 AI 接口
前面的 Agent 和 RAG 都是命令行脚本,真实业务还需要暴露成接口给前端或上游服务调用。这一节用 FastAPI 把 RAG 功能封装成一个POST /ask接口,并告诉你如何验证它真的可用。
7.1 完整服务端代码
创建app.py:
# 文件路径:app.py import os from dotenv import load_dotenv from fastapi import FastAPI from pydantic import BaseModel from openai import OpenAI from simple_rag import cosine_similarity load_dotenv() client = OpenAI( api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_BASE_URL"), ) EMBEDDING_MODEL = os.getenv("EMBEDDING_MODEL", "text-embedding-3-small") LLM_MODEL = os.getenv("LLM_MODEL", "gpt-4o-mini") app = FastAPI(title="Stone Soup AI Demo") def embed(text: str): return client.embeddings.create( model=EMBEDDING_MODEL, input=[text], ).data[0].embedding class AskRequest(BaseModel): question: str @app.post("/ask") def ask(req: AskRequest): # 这里使用一个最小知识库,实际项目中应该从向量数据库读取。 docs = [ "Stone Soup AI 是一种把现成模型、工具和数据组件组装成 AI 应用的方法论。", "RAG 是指检索增强生成,先检索相关资料,再让模型基于资料生成回答。", "AI Agent 的核心是让模型可以调用外部工具,并自主完成多步任务。", ] query_embedding = embed(req.question) scored = [] for doc in docs: doc_embedding = embed(doc) scored.append((cosine_similarity(query_embedding, doc_embedding), doc)) scored.sort(key=lambda x: x[0], reverse=True) context = "\n".join([doc for _, doc in scored[:2]]) messages = [ { "role": "system", "content": "你是一个知识库助手,只能根据提供的上下文回答问题。如果上下文没有依据,请明确告知,不要自行编造。", }, { "role": "user", "content": f"上下文:\n{context}\n\n问题:{req.question}", }, ] answer = client.chat.completions.create( model=LLM_MODEL, messages=messages, ) return { "question": req.question, "answer": answer.choices[0].message.content, }这段代码演示了一个非常典型的 RAG 接口:将问题向量化 → 与文档向量比较 → 取最相关的上下文 → 拼进 prompt → 让 LLM 生成回答。注意在真实项目中,文档向量不应该每次请求都重新生成,而应该在服务启动时构建一次,或直接存入向量数据库。
7.2 启动服务
在项目根目录执行:
uvicorn app:app --reload --host 0.0.0.0 --port 8000看到如下日志说明启动成功:
INFO: Uvicorn running on http://0.0.0.0:8000 INFO: Application startup complete.7.3 验证接口
打开另一个终端,用 curl 发送一个测试请求:
curl -X POST http://127.0.0.1:8000/ask \ -H "Content-Type: application/json" \ -d '{"question": "Stone Soup AI 是什么?"}'预期会返回一段 JSON,结构类似:
{ "question": "Stone Soup AI 是什么?", "answer": "Stone Soup AI 是一种把现成模型、工具和数据组件组装成 AI 应用的方法论。" }如果请求失败,第一步应该看 Uvicorn 所在终端输出的运行日志。常见错误是 API Key 无效、网络不通、模型名不存在。日志里一般会给出明确的 HTTP 状态码和错误信息,比盲目猜测模型效果更有效。
8. 常见问题与排查思路
从命令行 Demo 到生产环境,你会遇到一批规律性的问题。下面这些场景来自 2024 年 AI 工程实践的常见反馈,值得提前对照检查:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 模型返回空内容 | 提示词触发了内容过滤,或返回内容未正确解析 | 打印原始 response,检查 finish_reason | 调整提示词,检查代码是否读取了正确的字段 |
| Tool call 死循环 | 工具结果没有改变对话状态,模型反复调用同一个工具 | 设置最大迭代次数,打印每次 messages 变化 | 在 Agent 循环中加入 turn_limit 限制 |
| 提示 tool_call_id mismatch | 回传工具消息时 tool_call_id 写错或漏写 | 打印完整 messages,检查每个 tool 消息 | 严格从 tool_calls 中取 id 回传 |
| 向量检索结果不相关 | 文本切分过碎或过长,Embedding 模型与业务语言不匹配 | 单独测试 search 函数,观察相似度分数 | 调整 chunk_size 和 overlap,必要时换重排序模型 |
| 接口启动报错 pydantic 版本冲突 | FastAPI 和 pydantic 版本兼容性问题 | 查看完整堆栈,检查依赖树 | 升级相关依赖,保持 requirements 中版本一致 |
| 上下文超限 | 检索回来的文档过多,prompt 太长 | 打印 token 数量,参考模型的 context length | 限制 top_k、缩短文档切片、使用摘要优化 |
| 模型输出不稳定,JSON 解析失败 | 模型没有严格遵循输出格式 | 检查原始输出,确认是否被截断 | 使用 response_format 或更严格的 few-shot 示例 |
| 每次请求都很慢 | Embedding 和 LLM 调用串行,且没有缓存 | 增加耗时日志,分析瓶颈 | 对向量和热门回答做缓存,必要时并发调用 |
| 密钥被提交到 Git 仓库 | .gitignore 未配置,团队协作不规范 | 在仓库历史中搜索密钥 | 吊销密钥并轮换,配置 .gitignore 和密钥扫描 |
排查这些问题时,最重要的原则是“先看日志,再看数据,最后猜模型”。很多模型表现问题,根因其实是上游传进去的上下文不对,而不是模型变笨了。
9. 最佳实践与工程建议
到这里,你已经跑通了一个带 Agent 和 RAG 的最小 AI 服务。但“能运行”和“能上线”之间,还有一段路要走。下面这些工程建议来自真实项目的通用经验,按重要程度排列。
9.1 提示词与上下文管理
提示词会被反复修改,所以它应该作为代码资产来管理。建议把系统提示词、few-shot 示例、检索 prompt 模板单独拆成配置或 Python 常量,而不是散落在业务代码里。对每一类提示词,至少准备一个自动化用例,避免改动一次 prompt 导致另一个场景回归。
更重要的一点是:不要试图把全部业务背景写进一个超长 system prompt。系统提示词越长,模型越容易忽略关键信息,token 成本也越高。更好的做法是尽量让业务信息通过 RAG 或工具结果进入上下文,系统提示词只负责定原则和约束输出格式。
9.2 安全、权限与合规边界
2024 年 AI 应用的安全问题,重点不是“防止 AI 反抗人类”,而是“防止模型被利用产生业务风险”。首先,任何 AI 服务都必须做鉴权,不能让一个模型接口裸奔在公网上,否则很容易被刷爆账单。其次,要对用户输入做基本的注入防护,模型应该被明确告知不能执行与当前任务无关的指令。最后,涉及个人信息或商业敏感数据时,尽量优先选择私有化部署或支持数据隔离的模型服务,避免敏感信息进入第三方日志。
所有需要模型做“决策”的高风险操作,都必须有人工确认环节。比如模型判断“应该删除这条数据”,系统不能直接执行,而是应该生成一个待确认工单,由业务人员审核后操作。并且,所有带写操作的功能必须先走测试环境验证,生产操作要有备份和回滚方案,使用最小权限账号,不要使用管理员权限。
9.3 成本控制与性能优化
大模型 API 的成本不是按调用次数算,而是按 token 算,这是新手最容易忽略的。一个 RAG 请求,如果检索回 10 段文档,prompt 可能膨胀到几千 token,看起来只是“多了一点点”,乘以每日几万次调用,成本立刻变得可观。
建议从第一天就做好三个动作:一是缓存,对重复提问和热门问题的回答做 Redis 或内存缓存,这是成本优化效果最明显的手段;二是限流,对每个用户、每个 IP 设置调用频率限制,防止异常流量和恶意刷接口;三是模型分级,简单任务用便宜的轻量模型,复杂任务才用强模型,而不是所有请求都打同一个模型。
9.4 可观测性与模型评估
模型输出不是确定性的,所以上线前必须建立评估机制。最简单的方式是准备一份“评测集”,包含几十条典型业务问题和对应的可接受答案标准。每次修改 prompt 或更换模型,都跑一遍评测集,人工或半自动判断回答质量是否下降。这是 2024 年 AI 工程实践里最能体现工程水平的部分。
在日志方面,除了普通的应用日志,至少要记录:模型名、prompt 版本、输入输出 token 数、响应延迟、返回内容摘要。有条件的话,接入链路追踪工具,把一次用户请求对应的检索、模型调用、工具调用串起来,出问题时能快速定位。
9.5 灰度发布与回滚
模型服务的一大特点是“外部升级不受你控制”,同一个模型可能过一段时间行为就变了。所以发布流程要设计成可回滚的:模型名、prompt 版本、参数配置都要作为配置项,而不是写死在代码里。生产环境可以先用 5% 流量做灰度,对比新老版本的评估指标,确认没问题再全量切换。
9.6 团队协作与分工
一个完整的 AI 项目至少需要三类角色:工程侧负责接口、数据流、部署;算法或 AI 侧负责模型选型、提示词策略、评测;业务侧负责提供真实场景和验收标准。这三类角色之间的沟通界面,应该是一份“评估集 + 通过标准”。谁改了 prompt,谁更新了评测集,都应该通过代码评审和文档记录沉淀下来。
10. 总结与后续学习方向
Stone Soup AI 这篇“石头汤”,现在你应该知道该怎么煮了:先搭一个能跑通的最小工程骨架,再按业务需求逐步加入模型、工具、检索、服务封装、评测回归。它不强调一开始就追求大而全,而是强调每一步都有可验证的产出,让团队能够持续迭代。
如果你打算继续深入,建议按下面的路线推进:
- 第一阶段:熟练封装模型调用。你会写统一的 LLM 调用函数,理解 messages 结构、温度参数、输出格式控制。
- 第二阶段:掌握结构化输出和工具调用。让模型按照 JSON Schema 返回结果,并能在 Agent 中正确执行工具。
- 第三阶段:搭建完整的 RAG 链路。从文本切分、Embedding、向量存储到检索重排,理解每一步如何影响最终回答质量。
- 第四阶段:建设评估与可观测体系。建立评测集、维度评分、日志追踪,这是从“能用”到“好用”的分水岭。
- 第五阶段:解决部署与运维。理解模型私有化部署、弹性伸缩、成本优化、并发控制,逐步形成团队级的 AI 应用开发学习路线。
如果你是第一次接触 AI 应用开发,不要急着学完所有内容。把文章里的weather_agent.py和simple_rag.py复制到自己的项目里,先跑通一次,感受一下“模型输出”和“代码控制”之间的配合方式。跑通之后,再对照最佳实践逐条优化。
这篇文章更像一个起点,而不是终点。2024 年的 AI 工程变化很快,但面向业务的组装式开发思路不会过时:明确问题,选好组件,控制成本,持续评估。建议收藏备用,项目里用到 AI 能力时,再回来对照一遍。