ARTICLE DETAIL

资讯详情

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

Stone Soup AI:从最小骨架到工具调用的渐进式集成实战

Stone Soup AI:从最小骨架到工具调用的渐进式集成实战 你大概听过“石头汤”的故事几个穷困的旅人走到一个村庄架起一口大锅放一块石头进去煮水说自己在做一锅美味的石头汤。路过的村民好奇有人送来胡萝卜有人送来土豆有人送来几块肉。最后大家真的喝上了一锅丰盛的汤。2024 年之后很多 AI 应用开发的演进方式和这个故事越来越像一开始只有一个大模型 API看起来什么也做不了但当你把它放在一个可扩展的骨架里数据层、工具层、记忆层、评测层像“村民们”一样不断向锅里加料最终长成一个完整、可用的 AI 系统。这就是本文要讲的Stone Soup AI2024工程思路用最小骨架启动靠渐进式集成把 AI 能力真正组合起来。本文将围绕这套思路展开先讲清楚 Stone Soup AI 是什么、它解决什么问题然后带你把一个可运行的多模型对话骨架搭出来再逐步加入工具调用、向量检索、Agent 等能力。适合正在做 AI 应用开发、想从“调 API”走向“做系统”的读者。1. Stone Soup AI 是什么1.1 从寓言到工程思想“石头汤”这个故事的核心不是那块石头而是协作与渐进一个不起眼的最小起点通过持续加入模块最终变成远超单个部件价值的结果。AI 开发中的 Stone Soup 思想可以这样理解石头 大模型 APIGPT、Claude、通义、文心、DeepSeek 等它们更像是“能说话的引擎”不是完整产品。锅 应用骨架负责接收请求、维护会话、调用模型、返回结果。不断加入的料 RAG、工具调用、Agent、记忆、权限控制、评测、日志。换句话说Stone Soup AI 是一种面向 AI 应用的渐进式集成方法论。它强调一开始不要追求“大而全”而是先有一个能跑通的最小闭环再通过可插拔模块把能力逐层补齐。1.2 它与“传统软件开发”有什么不同传统后端开发里数据模型、接口、权限、缓存通常在一开始就要设计好中途替换成本很高。但 AI 应用最大的特点是不确定性模型能力在快速变化今天用的模型明天可能被更强的替代。提示词、上下文策略、工具编排没有唯一标准答案。同一个任务不同模型表现差异非常大。Stone Soup AI 的思路是把稳定部分做成接口和骨架把不稳定部分做成可替换的“食材”。这样当模型升级、新的数据源接入、新的工具出现时不需要推翻整口锅。1.3 常见应用场景场景用 Stone Soup 思路解决的问题企业知识库问答先用大模型做对话再逐步接入向量库、权限过滤、溯源引用智能客服先跑通对话流程再加入工具查询订单、退换货策略、人工接管代码助手先做代码问答再接入仓库索引、CI 状态、自动修改提交Agent 应用先定义工具协议再逐步注册更多可调用能力内部效率工具先做一个统一对话入口再连接各类业务系统这篇文章里我们会用实际代码演示这套思路的核心骨架。2. 核心设计理念在动手写代码之前先把 Stone Soup AI 的四个核心设计理念说清楚。这些理念决定了项目结构怎么写、接口怎么定也会帮助你以后理解市面上 AI 框架的设计动机。2.1 最小可用骨架Minimal Skeleton第一版代码只需要满足三个功能接收用户消息。把消息发给某个大模型。把模型回复返回给用户。不要在第一版加入登录、向量库、多租户、监控告警。这些功能如果和骨架耦合在一起后续很容易把系统越搞越重。最小骨架的价值在于让业务方看到一条真实可用的链路再决定优先加什么调料。2.2 接口先行而非实现先行Stone Soup AI 的核心是“可替换”。可替换的前提是抽象接口。下面这个例子是最核心的抽象# 模型网关接口伪代码 class ModelGateway: def chat(self, messages, temperature0.7, toolsNone): 所有模型统一走这个方法。 输入统一是 OpenAI 风格的 message 列表。 输出统一是包含 role/content 或 tool_calls 的对象。 raise NotImplementedError不管后端接的是 OpenAI、Claude、Azure OpenAI、本地部署的模型还是各种国内大模型只要外面套一层协议转换上层应用代码就不需要改动。这样你换模型的时候动的是“食材”不是“锅”。2.3 渐进式功能叠加Incremental Enhancement先有基础对话再加工具先有工具再做 Agent 循环先有单轮再维护多轮记忆先有单知识库再做多知识源路由。每加一个能力都要保证之前的功能依然正常运行。这也是为什么最好有一个小的回归测试集每次加“调料”之后跑一遍冒烟用例确认基础对话没有被破坏。2.4 可观测与可控Observability and ControlAI 系统的黑盒特性决定了日志和开关非常重要。每个请求都要能追踪到用户输入。模型名称与参数。上下文长度。工具调用过程。模型输出。消耗的 token。延迟。同时要保留“开关”比如在某个模型效果不佳时能在配置中心一键切换。Stone Soup AI 的工程化程度很大程度上取决于这些基础设施是否从一开始就预留好了位置。3. 环境准备与项目结构3.1 运行环境说明本文的示例使用以下环境Python 3.10 及以上。FastAPI 作为服务框架。openai Python SDK 1.x用于调用兼容 OpenAI 协议的模型服务。uvicorn 用于启动服务。需要注意不同版本 SDK 在工具调用、消息对象序列化上有差异。示例代码展示的是整体工程思路如果你的 SDK 版本或模型接口不通优先查看对应版本的官方文档把调用参数调整成你环境的真实写法。3.2 安装依赖建议先创建虚拟环境python -m venv venv source venv/bin/activate # Windows 下是 venv\Scripts\activate然后安装依赖pip install fastapi uvicorn openai pydantic python-dotenv如果你使用的是国内模型服务只要它提供 OpenAI 兼容接口通常可以通过设置 base_url 来对接。3.3 项目目录我们建立一个 demo 项目目录结构如下stone-soup-ai/ ├── .env ├── requirements.txt ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI 入口 │ ├── config.py # 配置读取 │ ├── models.py # 请求/响应数据结构 │ ├── gateway.py # 模型网关 │ └── tools.py # 工具注册中心这个结构虽然简单但已经体现了“骨架 可插拔模块”的分层思想。4. 完整实战从一锅清水开始现在我们来写一段完整可运行的代码。先跑通一个纯对话版本再向锅里加“工具调用”这一味关键调料。4.1 配置管理创建.env文件把模型相关的配置放到这里避免把密钥写死在代码里# .env LLM_API_KEYyour-api-key LLM_BASE_URLhttps://api.openai.com/v1 LLM_MODELgpt-4o-mini如果使用国内兼容 OpenAI 协议的服务把LLM_BASE_URL改成你的服务地址把LLM_MODEL改成实际模型名即可。对应的配置读取模块# app/config.py import os from dotenv import load_dotenv load_dotenv() class Settings: api_key: str os.getenv(LLM_API_KEY, ) base_url: str os.getenv(LLM_BASE_URL, https://api.openai.com/v1) model: str os.getenv(LLM_MODEL, gpt-4o-mini) settings Settings()4.2 定义请求数据结构使用 Pydantic 定义接口的输入输出# app/models.py from typing import List, Optional from pydantic import BaseModel, Field class ChatMessage(BaseModel): role: str Field(..., description角色system/user/assistant/tool) content: Optional[str] Field(None, description消息内容) name: Optional[str] Field(None, description工具调用时可带) class ChatRequest(BaseModel): messages: List[ChatMessage] temperature: float 0.7 stream: bool False class ChatResponse(BaseModel): reply: Optional[str] None tool_calls: Optional[list] None这里把tool_calls放在响应结构里是为后面 Agent 化预留位置。4.3 模型网关模型网关是“石头汤”里最重要的一层抽象。后续接任何模型都先把协议转换成 OpenAI 兼容格式# app/gateway.py from openai import OpenAI from app.config import settings class ModelGateway: def __init__(self): self.client OpenAI( base_urlsettings.base_url, api_keysettings.api_key, ) self.model settings.model def chat(self, messages, temperature0.7, toolsNone): kwargs {} if tools: kwargs[tools] tools response self.client.chat.completions.create( modelself.model, messagesmessages, temperaturetemperature, **kwargs, ) return response.choices[0].messagegateway.chat()返回的是 SDK 的 message 对象它包含role、content、tool_calls等属性。把模型调用隔离在这个类里上层业务不关心背后是哪一个模型。4.4 工具注册中心接下来是 Agent 能力的雏形工具注册中心。它的作用是把“能调用的外部能力”统一注册起来并生成模型所需的结构化描述。# app/tools.py import json from typing import Callable, Dict class ToolRegistry: def __init__(self): self._functions: Dict[str, Callable] {} self._schemas: Dict[str, dict] {} def register(self, name: str, description: str, parameters: dict): def decorator(func: Callable): self._functions[name] func self._schemas[name] { type: function, function: { name: name, description: description, parameters: parameters, }, } return func return decorator def get_schemas(self): return list(self._schemas.values()) def call(self, name: str, arguments: str): if name not in self._functions: raise ValueError(funknown tool {name}) args json.loads(arguments) if isinstance(arguments, str) else arguments return self._functions[name](**args) tool_registry ToolRegistry()这里最值得注意的是register的设计业务函数只需要通过装饰器注册就会自动生成工具描述和可调用映射。之后每加一个新的工具不需要改核心逻辑只需要新增一个函数。4.5 注册一个示例工具我们注册一个“获取服务器时间”的小工具用来测试工具调用链路# app/tools_demo.py from app.tools import tool_registry tool_registry.register( nameget_current_time, description获取指定时区的当前时间参数 timezone 为 IANA 时区名例如 Asia/Shanghai, parameters{ type: object, properties: { timezone: { type: string, description: IANA 时区名, } }, required: [timezone], }, ) def get_current_time(timezone: str Asia/Shanghai): from datetime import datetime from zoneinfo import ZoneInfo dt datetime.now(ZoneInfo(timezone)) return {timezone: timezone, local_time: dt.isoformat()}这个工具本身很简单但它验证了整个工具调用流程模型识别意图 → 模型生成工具参数 → 系统调用函数 → 结果回传给模型 → 模型组织最终回答。4.6 FastAPI 服务入口现在把以上模块在 FastAPI 中组合起来。先写一个不包含工具调用的基础对话接口再把它扩展成支持工具调用的版本。# app/main.py import json from fastapi import FastAPI from app.models import ChatRequest, ChatResponse from app.gateway import ModelGateway from app.tools import tool_registry import app.tools_demo # noqa: F401 确保工具被注册 app FastAPI(titleStone Soup AI, version0.1.0) gateway ModelGateway() app.post(/v1/chat, response_modelChatResponse) def chat(request: ChatRequest): messages [m.dict() for m in request.messages] # 第一轮带上工具定义调用模型 assistant_message gateway.chat( messagesmessages, temperaturerequest.temperature, toolstool_registry.get_schemas(), ) # 如果模型没有要求调用工具直接返回 if not getattr(assistant_message, tool_calls, None): return ChatResponse(replyassistant_message.content or ) # 如果模型要求调用工具进入工具执行循环 messages.append({ role: assistant_message.role, content: assistant_message.content, tool_calls: [ { id: tc.id, type: function, function: { name: tc.function.name, arguments: tc.function.arguments, }, } for tc in assistant_message.tool_calls ], }) for tool_call in assistant_message.tool_calls: tool_name tool_call.function.name tool_result tool_registry.call(tool_name, tool_call.function.arguments) messages.append({ role: tool, tool_call_id: tool_call.id, name: tool_name, content: json.dumps(tool_result, ensure_asciiFalse), }) # 把工具结果交还给模型让它生成最终回答 final_message gateway.chat( messagesmessages, temperaturerequest.temperature, toolstool_registry.get_schemas(), ) return ChatResponse(replyfinal_message.content or )4.7 运行与验证启动服务uvicorn app.main:app --reload --port 8000用 curl 测试基础对话curl -X POST http://localhost:8000/v1/chat \ -H Content-Type: application/json \ -d { messages: [ {role: user, content: 你好请简单介绍下你自己} ] }如果密钥、网络正常你会收到模型回复。再测试工具调用curl -X POST http://localhost:8000/v1/chat \ -H Content-Type: application/json \ -d { messages: [ {role: user, content: 请问现在北京时间是什么} ] }正常情况下模型会先生成一个get_current_time工具调用然后我们执行工具、把结果回传最后由模型组织出自然语言回复。整个链路就是 Agent 最核心的“模型 工具”闭环。4.8 验证思路你可以在终端打印中间过程比如print(tool_calls:, assistant_message.tool_calls) print(tool_result:, tool_result)这能帮你确认是“模型没识别工具”还是“工具执行报错”。后续接入更复杂的 Agent 框架时这种分步观察的习惯会很有价值。5. 如何“加料”三条进阶扩展路径骨架和工具闭环跑通后你已经有一个可以继续生长的 AI 应用了。下面三条拓展路径是 2024 年 AI 应用开发最常遇到的场景。5.1 加向量库从对话升级为 RAG如果你希望 AI 回答基于自己的文档而不是完全依赖模型内部知识就需要引入RAGRetrieval-Augmented Generation检索增强生成。核心流程是把文档切片并向量化。用户提问后先从向量库找回相关片段。把片段拼入上下文再让模型回答。加入方式示例伪代码def rag_chat(user_question: str): docs vector_store.search(user_question, top_k5) context \n\n.join(doc.page_content for doc in docs) messages [ { role: system, content: f请基于以下资料回答用户问题\n\n{context}, }, {role: user, content: user_question}, ] return gateway.chat(messages)这里建议把“文档切分大小”“相似度阈值”“引用格式”都做成配置项方便反复调优。5.2 加 Agent 编排让模型决定调用流程工具调用是一轮“识别意图 → 调用 → 回传”。Agent 则是在这个基础上增加多步决策能力。比如用户问“帮我调查竞品新闻并生成一份摘要”系统可能需要调用搜索工具。调用网页抓取工具。调用总结工具。Stone Soup AI 的思路是继续沿用工具注册中心把业务函数一步步加进去然后再用更复杂的编排层控制循环次数、停止条件、异常恢复策略。加入循环后你的主逻辑会变成for _ in range(max_steps): message gateway.chat(messages, toolsschemas) if not message.tool_calls: break # 执行所有 tool_calls追加结果到 messages这一步值得单独做一个小项目专门练习因为 Agent 的难点不在代码而在“模型会连续调用多个工具后开始犯错”的状态管理。5.3 加多模态能力图文输入输出多模态模型如 GPT-4o、Claude、Qwen-VL已经可以实现图片理解、语音输入等能力。Stone Soup 思路在这里同样适用接口层先支持多模态消息格式底层模型可以在多个多模态模型之间切换。你不需要重复搭建服务只需要在消息协议里增加 image 等内容类型。例如把图片转为 base64放入 user 消息{ role: user, content: [ {type: text, text: 这张图里有什么}, {type: image_url, image_url: {url: data:image/png;base64,...}} ] }大多数兼容 OpenAI 协议的模型服务都支持这种格式具体字段要以模型文档为准。6. 常见问题与排查思路下面的表格整理了 Stone Soup AI 骨架搭建中最高频的几个问题。问题现象常见原因解决思路请求报 401 / Invalid API Key环境变量没加载或密钥错误检查.env确认load_dotenv()已执行模型不调用工具工具描述不清晰或模型不支持 function calling简化参数描述换支持工具调用的模型工具执行后模型继续乱答tool_call 结果拼接格式错误检查 tool_call_id 是否一一对应中文乱码JSON 序列化时 ensure_ascii 问题使用json.dumps(..., ensure_asciiFalse)token 消耗过大上下文不断累积实现窗口截断或摘要压缩不同模型返回值结构不同模型协议不完全兼容 OpenAI在 gateway 层做适配转换启动时工具没被注册模块没有 import在入口模块中显式导入工具模块再补充一个排查顺序清单先确认网络连通和密钥是否有效。用官方 SDK 写一个最小调用绕过业务代码定位问题。打印实际发送给模型的 messages检查是否包含工具定义。打印工具执行结果确认是不是函数自身异常。最后再检查流程编排逻辑。这个顺序能帮你把问题从“环境问题→模型问题→代码问题”逐层隔离。7. 工程实践与生产建议代码跑通只是第一步。下面这些工程实践是把 Stone Soup AI 从 Demo 变成可靠系统必须考虑的内容。7.1 密钥与配置管理不要在代码里硬编码任何密钥。本地使用.env生产环境使用配置中心或云厂商密钥管理服务。日志中禁止打印完整密钥和完整请求内容尤其注意不要误把整个 messages 打到日志里。7.2 敏感信息与安全边界AI 应用天然会接触用户输入必须考虑注入风险。比如用户输入中可能包含“忽略之前的指令”。工程上可以这样做系统提示词里明确边界。对模型输出做内容安全过滤。工具调用需要额外鉴权特别是涉及数据库、订单、支付等敏感操作时。涉及生产环境数据变更的 Agent 动作必须走人工确认不能由模型直接执行。特别是当你给模型注册了“查数据库”“发邮件”这类工具时要默认遵循最小权限原则模型只拥有完成任务所需的最小权限变更类操作用审批链路兜底。7.3 成本治理大模型调用是按 token 计费的Stone Soup 式 AI 系统很容易越用越贵。建议从第一天就记录每次请求消耗的输入、输出 token。每次工具调用产生的额外消耗。模型缓存命中情况。单用户、单租户的成本分摊。当上下文过大时引入滑动窗口、摘要压缩、向量检索代替全文拼接都能明显降低成本。7.4 可观测性在 gateway 层统一打印类似这样的结构化日志{ request_id: xxx, model: gpt-4o-mini, prompt_tokens: 1234, completion_tokens: 234, latency_ms: 1850, tool_calls: [get_current_time], success: true }有了这些数据你才能回答“哪个请求慢”“哪个工具经常失败”“为什么这个月成本涨了 30%”。7.5 评估与回归AI 应用的评估是最容易被忽视的工程环节。建议建一个评测集包含基础问答用例。边界输入用例空字符串、超长文本、恶意指令。工具调用用例。RAG 知识来源正确性用例。每次升级模型、修改提示词、增加工具都先跑一遍评测集。没有评估你就不知道自己往锅里加的“调料”到底有没有让汤变得更好喝。8. 总结与下一步学习方向Stone Soup AI2024不是一个具体的软件包而是一种 AI 应用架构思想从最小可用的对话骨架出发通过统一接口、工具注册、渐进扩展把大模型、检索、工具、记忆等能力逐步组合成完整的业务系统。这篇文章里我们完成了以下关键实践搭建了 FastAPI 服务 模型网关的基础骨架。实现了工具注册中心和工具调用闭环。梳理了 RAG、Agent、多模态三条扩展路径。整理了密钥、安全、成本、可观测性四个工程重点。如果你想把这条路线继续走下去建议按顺序研究下面几个方向LangChain / LlamaIndex 的底层实现看它们如何做模型抽象和链式编排。Function Calling 协议细节不同模型对工具描述的敏感度差异。RAG 调优切分策略、embedding 模型、重排机制。Agent 记忆与规划如何管理多轮工具调用的状态。模型评估与评测集建设让 AI 应用质量可度量。最后给你一个很实在的建议别急着把架构设计得无比复杂。先把这一节代码里的小骨架复制到本地用你手上可用的模型 API Key把一条最简单的对话和工具调用链路跑通。等这口锅真正沸腾起来你自然会知道下一块该往里面放什么“食材”。如果你在复现过程中遇到了问题可以对照第 6 节的排查清单逐步定位也可以在评论区把你的报错信息发出来。下一篇文章我会重点拆解工具调用循环里的状态管理以及如何用最少代码实现一个可追踪的 Agent 运行日志。
返回列表