ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从石头汤到AI应用:模块化大模型开发实战指南

从石头汤到AI应用:模块化大模型开发实战指南 最近在做大模型应用落地时一直觉得“把 AI 集成到业务里”这件事很玄学。模型选型、Prompt 编写、Agent 编排、上下文管理、成本控制……每一个环节都有无数细节网上的资料又碎又散很难直接串成一套能跑的流程。直到我重新翻到 “Stone Soup AI (2024)” 这个思路才意识到一个问题我们总觉得 AI 应用开发需要从零写一个大而全的框架但事实上真正稳定可靠的产品大多是用“模块化拼装 社区协作 增量迭代”的方式做出来的——就像那个经典的《石头汤》故事一样每个人都有贡献一点最后煮出一锅所有人都能享用的汤。这篇文章我想从一个工程实践者的角度聊聊 Stone Soup AI 到底是什么、它解决了什么问题并给出一个可运行的最小实战案例帮助你把“AI 能力集成”这件事从模糊概念落到具体代码。无论你是刚接触大模型应用开发的新手还是已经在做 AI 工程化的后端开发者这套思路都值得参考。1. 背景与核心概念1.1 什么是 Stone Soup AIStone Soup石头汤是欧洲一个很古老的民间故事一位旅人来到一个村庄锅里只放了一块石头和水村民们很好奇于是纷纷拿出一颗胡萝卜、几个土豆、一小块肉……最终大家一起煮出了一锅香气四溢的汤。故事的核心不是“石头能煮汤”而是“协作让每个人都能贡献一点最终所有人都受益”。Stone Soup AI 借用了这个隐喻指的是用“社区协作 模块化复用 渐进式构建”的方式来做 AI 应用开发。在 2024 年这个大模型爆发的时间节点上Stone Soup AI 这个概念被越来越多地提起因为它精准地描述了大模型应用开发的一种现实状态你不需要从零训练一个大模型你不需要自己搭建完整的机器学习基础设施你只需要把已有的模型能力、工具链、开源组件、数据资源、社区配方“拼装”起来形成一套满足业务需求的 AI 系统。换句话说Stone Soup AI 是一种工程方法论而不是某一个具体的开源框架或商业产品。1.2 它解决什么问题过去我们写一个传统软件逻辑是高度确定性的输入什么参数走什么分支输出什么结果全部由代码定义。但到了 AI 应用开发情况完全变了大模型的输出是概率性的同样的输入可能产生不同的结果模型能力边界不清晰有时候表现很好有时候突然“智商掉线”业务需要的不只是“调用一次模型”而是多个步骤的推理、工具调用、记忆管理你在本地跑通的流程到了生产环境可能因为网络、限流、上下文长度、成本等问题直接崩掉。这些问题靠单个人、单个组件很难解决。Stone Soup AI 的思路是不要试图自己造轮子解决所有问题而是把社区中已经验证过的解决方案组合起来边用边补最终形成一个适合自己业务的 AI 应用系统。1.3 常见应用场景Stone Soup AI 适合以下场景企业内部知识库问答RAG 应用自动化客服助手代码辅助与自动化测试数据分析与报表生成Agent 自动化流程如自动收集信息、调用工具完成任务多模型协同的复杂任务编排。这些场景的共同特点是不追求单一的“模型有多强”而是追求“整个系统能不能稳定、可靠、成本可控地完成任务”。2. 环境准备与版本说明2.1 开发环境说明在开始写代码之前我们需要准备一套可用的开发环境。本文的示例以常见的 Python 环境为例不会绑定特定的操作系统Windows、macOS、Linux 都可以运行。需要准备的组件如下组件说明Python建议 3.10 及以上版本部分依赖库对低版本 Python 支持不够友好OpenAI SDK用于调用大模型接口示例中会使用openaiPython 库大模型 API需要准备一个可用的模型服务可以是 OpenAI 兼容接口的任意服务也可以是本地部署的模型服务dotenv用于读取环境变量文件管理 API Key 等敏感配置开发工具VS Code、PyCharm 等任一编辑器即可注意不同大模型服务的接口规范可能略有差异本文示例以 OpenAI 兼容接口为主如果你使用的是其他服务需要根据服务商的文档调整base_url、model_name等参数。2.2 安装依赖库建议先创建一个独立的 Python 虚拟环境避免污染系统全局环境# 创建虚拟环境 python -m venv stonesoup_env # 激活虚拟环境 # Windows: stonesoup_env\Scripts\activate # macOS / Linux: source stonesoup_env/bin/activate激活虚拟环境后安装项目依赖pip install openai python-dotenv这里安装了两个库openaiOpenAI Python 官方 SDK虽然名字叫 OpenAI但它也支持调用其他兼容 OpenAI 接口的模型服务是目前大模型开发最常用的 SDK 之一python-dotenv用于加载.env文件中的环境变量配置方便管理 API Key避免把密钥写死在代码里。2.3 准备模型服务如果你没有可用的模型服务有几种选择使用国内外主流的云服务厂商提供的大模型 API它们大多数支持 OpenAI 兼容接口使用本地方案部署开源模型例如通过 Ollama、vLLM 等工具加载 Qwen、Llama 等开源模型使用个人开发时常用的“中转服务”但需要自己确认服务的稳定性和合规性。在.env文件中配置你的模型服务信息OPENAI_API_KEY你的_API_Key OPENAI_BASE_URLhttps://你的模型服务地址/v1 OPENAI_MODEL_NAME你的模型名称配置完成后我们就可以进入核心内容理解 Stone Soup AI 的工程原理并动手实现一个最小可运行案例。3. 核心原理拆解如何构建一个“可拼装”的 AI 应用3.1 模块化设计不要写“一坨”代码在传统业务开发里我们习惯把流程拆成函数、类、服务但在 AI 应用开发中很多人的代码却变成了一锅粥一段 Prompt 写在字符串里一次模型调用放在 try-except 里工具函数全部堆在同一个文件里……Stone Soup AI 的第一个核心原则是模块化。从工程实践来看一个稳定可维护的 AI 应用至少应该拆分出以下几个模块模块职责示例模型接入层封装大模型 API 调用统一处理请求、重试、超时LLMClientPrompt 管理层管理所有 Prompt 模板支持版本迭代PromptManager工具层定义 Agent 可以调用的外部工具ToolRegistry记忆层管理多轮对话的上下文MemoryStore编排层控制 Agent 的推理与工具调用循环Agent这样拆分的好处很明显当你想换一个模型服务时只需要修改模型接入层当你想调整 Prompt 时不需要动核心逻辑代码当你想新增一个工具时只需要在工具层注册一个新的函数。3.2 Agent 的基本组成工具、记忆与编排2024 年AI Agent 是绝对的技术热点。但很多人对 Agent 的理解停留在“调用一个大模型让它自己思考”的层面。实际上一个可用的 Agent 至少需要三个部分第一个是工具Tools。Agent 不能只停留在“聊天”它需要具备调用外部系统的能力比如查数据库、调接口、发邮件、执行代码等。这些能力以工具函数的形式提供给模型。第二个是记忆Memory。多轮对话场景下模型本身是无状态的。你需要把历史对话内容、关键信息、中间推理过程保存下来在每次请求时拼接进上下文。第三个是编排Orchestration。Agent 不能只是一次调用它需要循环执行“思考 - 调用工具 - 观察结果 - 再思考”的过程直到得出最终结论。这个循环的结束条件、最大步数、异常处理都需要精心设计否则 Agent 很容易陷入无限循环。3.3 ReAct 模式最简单的 Agent 实现思路ReActReasoning Acting是目前最经典、最容易落地的 Agent 实现模式思路是把用户的请求作为输入调用大模型让它输出 Reasoning思考过程根据思考结果决定是调用工具Action还是直接给出最终回答Answer如果调用了工具把工具返回结果反馈给模型继续下一轮思考直到模型给出最终回答或者达到最大轮次上限。这个模式实现简单、行为可解释很适合作为 AI 应用开发的第一步。后面的实战案例会按这个模式来实现一个极简 Agent。4. 完整实战案例搭建一个“石头汤”式的 AI 助手4.1 项目结构我们来实现一个带工具调用能力的 AI 助手它能回答普通问题也能查询天气和计算表达式。核心思路就是“像煮石头汤一样”先把基础模块打好再逐步加入新工具。项目结构如下stone_soup_ai/ ├── .env # 环境变量配置 ├── requirements.txt # 依赖清单 ├── config.py # 配置读取 ├── llm_client.py # 模型接入层 ├── tools.py # 工具层定义 ├── agent.py # Agent 编排层 └── main.py # 入口文件4.2 配置文件与环境变量先创建requirements.txtopenai python-dotenv创建.env文件OPENAI_API_KEYsk-xxxxxx OPENAI_BASE_URLhttps://api.openai.com/v1 OPENAI_MODEL_NAMEgpt-4o-mini如果你用的是国内大模型服务或本地方案把OPENAI_BASE_URL和OPENAI_MODEL_NAME改成对应的值即可。4.3 配置读取模块文件config.pyimport os from dotenv import load_dotenv load_dotenv() class Config: 全局配置读取类 OPENAI_API_KEY os.getenv(OPENAI_API_KEY, ) OPENAI_BASE_URL os.getenv(OPENAI_BASE_URL, https://api.openai.com/v1) OPENAI_MODEL_NAME os.getenv(OPENAI_MODEL_NAME, gpt-4o-mini) MAX_AGENT_STEPS int(os.getenv(MAX_AGENT_STEPS, 5))这里把配置统一封装成一个Config类方便其他模块引用。MAX_AGENT_STEPS是 Agent 最大执行步数用来避免死循环。4.4 模型接入层文件llm_client.pyfrom openai import OpenAI from config import Config class LLMClient: 封装大模型 API 调用 def __init__(self): self.client OpenAI( api_keyConfig.OPENAI_API_KEY, base_urlConfig.OPENAI_BASE_URL, ) self.model Config.OPENAI_MODEL_NAME def chat(self, messages, temperature0.7): 调用模型对话接口 messages: [{role: system, content: ...}, {role: user, content: ...}] try: response self.client.chat.completions.create( modelself.model, messagesmessages, temperaturetemperature, ) return response.choices[0].message.content except Exception as e: # 在实际项目中建议记录完整的日志 raise RuntimeError(f模型调用失败: {e})这段代码做了几件事在构造函数中初始化 OpenAI 客户端读取配置提供chat方法接收消息列表并返回模型回复文本对异常做了一层包装方便上层统一处理错误。4.5 工具层文件tools.pyimport json import random class ToolRegistry: 工具注册中心 def __init__(self): self.tools {} # 注册内置工具 self.register(get_weather, self.get_weather, 查询指定城市的天气情况) self.register(calculate, self.calculate, 计算数学表达式例如: calculate(1 2 * 3)) def register(self, name, func, description): self.tools[name] { func: func, description: description } def get_weather(self, city: str) - str: 模拟查询天气实际项目中应调用真实天气服务 weather_data { 北京: 晴25°C, 上海: 多云28°C, 广州: 小雨30°C, } result weather_data.get(city, 暂不支持该城市查询) return json.dumps({city: city, weather: result}, ensure_asciiFalse) def calculate(self, expression: str) - str: 计算数学表达式注意这里仅用于示例生产环境需做安全校验 try: # 安全实现请使用 ast 模块或第三方计算库 result eval(expression) return json.dumps({expression: expression, result: result}) except Exception as e: return json.dumps({expression: expression, error: str(e)}) def list_tools(self): 列出所有可用的工具描述 tool_descriptions [] for name, info in self.tools.items(): tool_descriptions.append(f- {name}: {info[description]}) return \n.join(tool_descriptions) def call(self, name: str, args: dict): 调用指定工具 if name not in self.tools: return json.dumps({error: f工具 {name} 不存在}) tool_func self.tools[name][func] try: return tool_func(**args) except TypeError as e: return json.dumps({error: f工具参数错误: {e}})工具层是整个 AI 助手的“食材库”。你只需要在ToolRegistry中注册一个新的函数Agent 就能调用它。这就是 Stone Soup AI 的协作思路在代码层面的体现每个人贡献一个工具Agent 把这些工具串起来完成任务。需要注意的是示例中的calculate使用了 Python 内置eval这在实际生产环境中是非常危险的。如果你要做一个真实可用的计算工具推荐使用ast模块解析表达式或者使用simpleeval这类安全的计算库。安全边界永远要放在第一位。4.6 Agent 编排层文件agent.pyimport json from llm_client import LLMClient from tools import ToolRegistry class Agent: 一个极简的 ReAct 风格 Agent def __init__(self): self.llm LLMClient() self.tools ToolRegistry() self.messages [] self.max_steps 5 # 最大执行步数防止死循环 def run(self, user_input: str) - str: 运行 Agent处理用户输入并返回最终结果 # 初始化系统提示词 system_prompt f你是一个 AI 助手。你可以使用以下工具 {self.tools.list_tools()} 当用户的问题需要使用工具时请严格按以下 JSON 格式输出 {{action: 工具名, args: {{参数名: 参数值}}}} 当你可以直接回答时请直接输出你的答案不要携带任何额外内容。 self.messages [ {role: system, content: system_prompt}, {role: user, content: user_input} ] for step in range(self.max_steps): # 第一步调用模型获取响应 response self.llm.chat(self.messages) self.messages.append({role: assistant, content: response}) # 尝试将模型输出解析为 JSON判断是否需要调用工具 try: parsed json.loads(response) action parsed.get(action) args parsed.get(args, {}) if action: # 第二步调用工具 tool_result self.tools.call(action, args) print(f → 调用工具: {action}, 参数: {args}) print(f → 工具结果: {tool_result}) # 把工具结果加入消息列表 self.messages.append({ role: user, content: f工具返回结果: {tool_result}。请根据这个结果回答用户问题。 }) continue except json.JSONDecodeError: # 如果模型输出不是合法 JSON说明它在直接回答用户问题 return response # 如果达到最大步数仍未结束返回最后一次响应 return response这个 Agent 是整个案例的核心逻辑很简单每次循环先调用模型如果模型输出是 JSON 格式的工具调用指令就执行工具把结果反馈给模型继续下一轮如果模型输出不是 JSON就认为它给出了最终答案直接返回。需要说明的是这个实现是教学用的极简版。在生产环境中你应该使用结构化输出例如 function calling 机制来保证模型输出的规范性而不是依赖字符串 JSON 解析。字符串解析的不确定性很高一旦模型输出带有多余字符解析就会失败。4.7 入口文件文件main.pyfrom agent import Agent def main(): print( Stone Soup AI 极简助手 ) print(输入 exit 退出程序) agent Agent() while True: user_input input(\n你: ) if user_input.lower() in (exit, quit): print(再见) break print(AI: , end, flushTrue) try: result agent.run(user_input) print(result) except Exception as e: print(f出错了: {e}) if __name__ __main__: main()入口文件提供了一个交互式命令行界面方便测试。在生产项目中你可以把Agent.run()封装成一个 HTTP 接口接入 Web 前端或者通过消息队列接入业务系统。4.8 运行与验证在项目根目录下运行python main.py假设我们依次输入以下内容你: 今天北京天气怎么样 AI: → 调用工具: get_weather, 参数: {city: 北京} → 工具结果: {city: 北京, weather: 晴25°C} 北京今天晴天气温 25°C是个适合出门的好天气。 你: 计算一下 (12 34) * 2 的结果 AI: → 调用工具: calculate, 参数: {expression: (12 34) * 2} → 工具结果: {expression: (12 34) * 2, result: 92} 计算公式 (12 34) * 2 的结果是 92。 你: 你好介绍一下你自己 AI: 你好我是一个 AI 助手可以查询天气、进行数学计算以及回答各种问题。从运行结果可以看出这个极简 Agent 已经具备了“理解用户意图 - 调用工具 - 总结回答”的基本能力。当你需要给它添加新能力时只需要在ToolRegistry中新增一个注册函数不需要修改 Agent 的任何逻辑。5. 常见问题与排查思路在实际开发 AI 应用的过程中会遇到各种各样的问题。下面整理了一份高频问题排查表供你参考。问题现象常见原因解决思路调用模型 API 返回 401API Key 错误或未配置检查.env文件是否被正确加载检查环境变量名是否拼写正确模型返回内容频繁截断上下文窗口超限或max_tokens设置过小合理压缩历史消息或调大max_tokens参数Agent 不断调用同一个工具Prompt 指令不够明确模型陷入循环在系统提示词中加入“如果需要重复调用工具请先分析上一次结果是否有意义”并设置最大工具调用次数JSON 解析失败模型输出带有多余字符提示词约束不严格改用 function calling 或结构化输出尽量避免依赖纯文本 JSON 解析工具调用报错但 Agent 不感知异常信息没有正确返回给模型将工具异常信息以 JSON 格式返回给模型让模型能根据异常调整下一步行为本地开发正常但生产环境频繁超时网络环境、限流策略不同为模型客户端配置超时与重试机制并使用异步调用或连接池多轮对话后记忆混乱没有正确管理上下文消息引入消息裁剪、摘要记忆等机制限制单轮上下文长度成本突然大幅上升上下文消息不断累计大模型输入 token 过多对历史消息做摘要压缩或定期清理会话历史5.1 JSON 输出不稳定怎么办这是目前调用大模型时最常见的痛点之一。使用纯文本 Prompt 要求模型输出 JSON即使写了很多细则模型仍然可能输出带解释、带 Markdown 代码块的内容。推荐解决方案使用平台的 function calling 能力让模型直接输出结构化工具调用参数而不是要求它输出 JSON 字符串使用 JSON Mode如果模型服务支持在后端做一层“清洗”例如提取字符串中最长的 JSON 片段再解析。5.2 工具调用死循环怎么处理Agent 死循环在工程实践中非常常见。比如你让它查询一个不存在的城市天气它可能反复调用天气工具每次结果都一样。解决办法是设置最大步数上限超过即强制终止让 Agent 在工具执行失败后学会“放弃”和“告知用户”而不是重试相同的调用引入“重复结果检测”如果连续两次工具返回结果相同让模型停止调用工具直接生成回答。6. 最佳实践与工程建议当你的 AI 应用从“能跑”走向“好用”有一些工程实践值得从一开始就注意。6.1 配置管理密钥绝不进代码库这是最基本也最容易被忽视的一条。API Key 一旦被提交到 Git 仓库就相当于泄露了。无论你的仓库是私有的还是公开的都应该遵守这个规则。建议做法所有密钥放在.env文件中并在.gitignore中加入.env生产环境使用配置中心或环境变量注入不要把配置写在业务代码里定期轮换 API Key尤其是当团队成员变动时。6.2 Prompt 也是代码需要版本管理在我的经验里几乎所有 AI 应用都会频繁修改 Prompt。今天换一个措辞明天加一段限定后天发现效果不如以前……如果没有版本管理很快就会陷入混乱。建议做法将 Prompt 模板存为独立文件例如 YAML 或 JSON而不是硬编码在代码里使用 Git 管理 Prompt 文件的每次变更这样当线上效果退化时可以快速回滚自动化测试时准备一组固定的测试用例每次修改 Prompt 后跑一遍回归测试。6.3 日志与可观测性AI 应用的调试比传统应用难很多因为大模型的行为是概率性的同样的输入可能得到不同的输出。如果日志不完善当线上出现“用户说答错了”的情况时你很难定位是 Prompt 问题、工具问题还是上下文问题。建议做法记录每次模型请求的完整消息列表、模型参数、返回结果、耗时对 Agent 的每一步循环加上步骤编号日志在生产环境建议引入链路追踪系统把用户请求 ID 和所有日志串起来方便排查问题。6.4 安全与合规边界AI 应用涉及的安全问题远比传统 Web 应用复杂建议优先关注以下几点工具调用安全不要轻信大模型生成的工具参数。例如如果工具的入参是 SQL必须做参数化处理如果工具的入参是代码必须做沙箱隔离。大模型本身不具备安全意识它会按照用户的指示去构造任何参数内容安全在应用层接入内容审核能力对模型的输入和输出都做检测数据合规用户数据进入大模型 API 时必须确认服务商的隐私政策避免敏感数据裸奔权限最小化Agent 调用外部系统时只授予完成任务所需的最小权限避免 Agent 拥有过高权限后被恶意利用。6.5 成本控制大模型 API 的成本主要来自输入 token 和输出 token。在实际项目中上下文会随着多轮对话不断膨胀成本上升得很快。建议做法对历史消息做截断或摘要控制输入 token 数量在 Agent 工具返回结果时对长文本做裁剪只保留最关键的信息在非核心场景使用更小、更便宜的模型在核心推理环节再使用更强的大模型为每个用户请求设置 token 上限异常请求自动熔断。7. 总结与学习路线回到 Stone Soup AI 这个名字本身它不是一个需要安装的框架也不是一个可以直接下载的代码库而是一种解决问题的视角——不要试图一个人造一个轮子而是学会把社区里的好材料、好方案、好工具组合起来快速搭建出可靠可用的 AI 系统。这篇文章中我们完成了一个极简的 ReAct 风格 Agent 案例它能够调用工具、处理多轮对话并给出了模块化、可扩展的项目结构。这个案例虽然简单但它包含的模型接入层、工具注册层、Agent 编排层的设计思路完全可以沿用到更复杂的生产项目中。如果你想继续深入下一步可以按这个路线走学习 RAG检索增强生成给你的 AI 助手接入私有知识库这是企业落地 AI 最刚需的能力研究 function calling在真实项目里用结构化工具调用替代 JSON 字符串解析提升稳定性探索多 Agent 协作把一个复杂的任务拆分成多个子 Agent各自负责一个领域再通过主 Agent 调度关注模型部署与工程化如果数据不能出内网你需要学习本地方案结合 vLLM、Ollama 等工具做模型部署建立评估体系用一套固定数据集定期测试 Agent 的行为变化防止“改了一个 Prompt之前能过的用例全挂了”。Stone Soup AI 的核心就是“让每个参与者都能贡献一点”。在 AI 应用开发中也是如此你不需要掌握所有技术只需要理解整体架构知道每个模块应该怎么接然后不断往汤里加你自己的“食材”就可以了。如果这篇文章对你有帮助建议收藏备用。后续我还会继续写关于 RAG、Agent 编排、模型部署与调试的实际案例欢迎在评论区交流你的落地经验。
返回列表