大模型应用开发全家桶:LLM+RAG+Agent+MCP+工作流双技术栈实践

大模型应用从 Demo 到落地,最缺的不是模型 API,而是一套能串起 LLM、RAG、Agent、MCP、工作流和工具调用的工程骨架。最近看到很多开发者都在问同一个问题:项目里既要接大模型,又要做知识库问答,还要让模型学会调用内部工具,甚至要支持 FastAPI 和 Spring Boot 两种技术栈,到底该怎么组织代码,才不会越写越乱?

这篇文章准备把一条经过验证的 AI 应用开发主线完整拆开讲清楚。它不绑定某个特定厂商模型,也不局限于单一编程语言,而是围绕 LLM、RAG、Agent、MCP、工作流、工具调用这六个核心模块,分别给出 FastAPI 和 Spring Boot 双版本的设计思路与示例代码。读完后你可以得到一套可直接落地参考的“AI 大模型应用开发全家桶”,知道每个模块解决什么问题、放在项目的哪一层、关键接口怎么定义,以及遇到报错时先查哪里。

先说结论:这套全家桶的核心价值,是帮你把“模型能力”和“业务系统”解耦。模型负责理解和生成语言,业务系统负责权限、数据、事务和流程。如果你正在做知识库问答、智能客服、Copilot、自动化办公或内部流程助手,这篇文章值得收藏后仔细对照实践。

1. 这篇文章真正要解决的问题

过去一年里,AI 大模型应用开发最大的变化不是模型本身变强了,而是应用架构从“单次对话”快速走向“多模块协作”。早期我们写一个 LLM 调用,基本就是构造函数、传 Prompt、拿结果。但现在一个稍微完整一点的应用,往往同时包含以下需求:

  • 接一个大语言模型,并支持切换不同厂商、不同模型。
  • 把公司内部文档、数据库记录、网页内容做向量化,让模型基于私有知识回答。
  • 让模型具备“自主规划”能力,能判断一个问题是否需要调用外部工具。
  • 通过 MCP 协议统一暴露工具接口,避免每个应用单独写一套工具 SDK。
  • 把多个模型调用、工具调用、条件判断组合成一个可复用、可监控的工作流。
  • 同时服务 Java 团队和 Python 团队,让前后端或微服务能够各自以自己的语言接入。

这六个需求单独拎出来都不算难,难的是把它们放在同一个项目里,还要保证代码结构清晰、可测试、可维护。很多人一开始会把所有逻辑都写进一个 service 方法:先调 LLM,再查向量库,再根据结果调工具,最后拼 Prompt 再调一次。表面上看功能能跑,但一旦模型返回格式变化、第三方 API 超时、或者需要增加新的工具,整个方法就会迅速膨胀,最终变成无法维护的“面条代码”。

所以,这篇文章真正要解决的,不是“怎么调一次 API”,而是“如何从零搭建一套可扩展的 AI 应用工程骨架”。它强调几个原则:

  1. 每个大模型能力模块都是独立组件,可以单独测试。
  2. 对外暴露统一接口,内部实现可替换。
  3. Python 版和 Java 版保持相同的模块划分,方便团队跨语言协作。
  4. 先跑通最小闭环,再逐步接入 RAG、Agent、MCP 和工作流。

如果你是刚进入 AI 应用开发的新手,可以按文章顺序逐步实现;如果你已经做过简单 LLM 调用,可以直接跳到你最关心的模块,比如 RAG 或 MCP。无论哪种情况,这套全家桶的设计思路都能帮你避免“代码堆砌”的坑。

需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。

2. 全家桶全景:六大模块的定位与关系

在动手写代码之前,先用一张概念图理解整套系统。这里不使用复杂图例,而是用表格列出每个模块的核心职责、输入输出和典型场景。

模块 核心职责 典型输入 典型输出 对应场景
LLM 大模型接入与对话 消息列表、参数 模型回复、Token 用量 聊天、内容生成、意图识别
RAG 检索增强生成 用户问题、知识库 携带上下文的回复 企业知识库问答、客服助手
Agent 任务规划与执行 用户目标、可用工具 执行动作、最终回答 自动化任务、多步推理
MCP 工具协议标准化 工具定义、调用请求 统一格式的工具结果 工具接入、跨服务调用
工作流 流程编排与状态控制 流程定义、输入变量 流程结果 结构化业务自动化
工具调用 具体业务操作 工具参数 操作结果 查天气、查订单、发消息

它们的关系可以这样理解:LLM 是所有模块的“大脑底座”;RAG 为大脑提供外部知识;Agent 让大脑学会拆解任务;MCP 为 Agent 提供统一工具访问协议;工作流则把一组可重复的调用编排成标准化流程;工具调用是真正落地执行的动作。

很多初学者会混淆 Agent 和工作流。Agent 偏“动态规划”,每一步都可能不同,模型自己决定下一步调什么;工作流偏“静态编排”,流程是预先画好的,每一步固定执行。真实项目里经常两者结合:流程负责兜底和标准化部分,Agent 负责需要灵活判断的部分。

在架构分层上,我建议至少分四层:

  • 接口层:Controller/API 路由,暴露 HTTP 接口。
  • 应用层:编排 LLM、RAG、Agent、工作流等核心服务。
  • 领域层:定义模型请求、消息、文档、工具、Agent 状态等对象。
  • 基础层:封装 HTTP 客户端、向量存储、数据库、日志等。

FastAPI 版天然适合这种分层,Python 类型提示配合 Pydantic 让数据模型很清晰。Spring Boot 版则借助 Spring 的 IoC 容器把各个 Bean 注入到应用层。两条技术栈虽然语法不同,但模块边界可以一一对应,这也是这套全家桶能“双版本”落地的关键。

3. 环境准备与前置条件

在开始搭建全家桶前,先把环境确认好。这里不写死具体版本号,因为 AI 工具链更新非常快,你只需要保证下面这些组件在本地能用即可。

  • 操作系统:Windows/macOS/Linux 都可以。
  • Python 版本:建议 3.10 及以上,便于使用较新的类型语法。
  • Java 版本:建议 17 及以上,许多 Spring Boot 3 的依赖要求 17 起步。
  • 包管理器:Python 使用 pip 或 poetry;Java 使用 Maven 或 Gradle。
  • 模型 API:准备一个可用的 LLM API Key,可以是 OpenAI 兼容格式的,也可以是国内大模型服务商提供的 Key。
  • 向量数据库:可以选择 Chroma、Milvus、Qdrant 或 Elasticsearch,先从本地轻量方案开始。
  • 开发工具:VS Code 配合 Python 插件,或 IntelliJ IDEA 配合 Spring 插件。

这里真正容易踩坑的地方是你对“模型 API”的选择。很多初学者以为只能用一个固定厂商的 SDK,实际现在绝大多数模型服务都提供 OpenAI 兼容接口,也就是说只要你的代码实现了 OpenAI Chat Completion 格式,就能适配大部分平台。因此,建议在全家桶里封装一个 LLMClient 接口,底层用 HTTP 方式调用,而不是在业务代码里绑定某个厂商 SDK。

另外,本地要做 RAG,还需要准备一些示例文档。可以用一本开源电子书、几份 Markdown 说明文档或者数据库导出的文本。核心目的是验证“检索—增强—生成”的链路是否跑通,不必追求大规模数据集。

4. FastAPI 版基础骨架:LLM 接入与统一接口

先用 FastAPI 实现最小闭环:调用一个 OpenAI 兼容的大模型,完成一次对话。这样做的好处是先把网络层、配置层和数据模型层铺好,后续所有模块都复用这套基础。

4.1 项目结构建议

code复制ai-family-python/
├── app/
│   ├── api/
│   │   └── chat.py
│   ├── core/
│   │   └── config.py
│   ├── domain/
│   │   ├── message.py
│   │   └── response.py
│   ├── infra/
│   │   ├── llm/
│   │   │   ├── base.py
│   │   │   └── openai_client.py
│   ├── application/
│   │   └── chat_service.py
│   └── main.py
├── requirements.txt
└── .env

这个结构把领域层、基础设施层、应用层分开。domain 目录放消息和返回结构,infra 目录放模型厂商实现,application 目录放业务编排逻辑,api 目录只负责 HTTP 暴露。

4.2 配置文件

env复制# .env
OPENAI_API_KEY=sk-your-key
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4o-mini
python复制# app/core/config.py
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    openai_api_key: str
    openai_base_url: str = "https://api.openai.com/v1"
    openai_model: str = "gpt-4o-mini"
    embedding_model: str = "text-embedding-3-small"
    vector_store_path: str = "./data/chroma"

    class Config:
        env_file = ".env"

settings = Settings()

4.3 统一消息模型

python复制# app/domain/message.py
from typing import Optional, List, Dict, Any
from pydantic import BaseModel, Field

class ChatMessage(BaseModel):
    role: str
    content: str
    name: Optional[str] = None
    function_call: Optional[Dict[str, Any]] = None

class ChatRequest(BaseModel):
    messages: List[ChatMessage]
    temperature: float = Field(default=0.7, ge=0, le=2)
    max_tokens: Optional[int] = None
    stream: bool = False

class ChatResponse(BaseModel):
    message: ChatMessage
    usage: Optional[Dict[str, int]] = None

这里定义了两个核心模型。ChatMessage 表示对话消息,ChatRequest 表示请求体,ChatResponse 统一返回给前端。把消息模型独立出来,后续 Agent 和 RAG 才能复用。

4.4 抽象 LLM 客户端

python复制# app/infra/llm/base.py
from abc import ABC, abstractmethod
from typing import List

from app.domain.message import ChatMessage, ChatResponse

class BaseLLMClient(ABC):
    @abstractmethod
    async def chat(self, messages: List[ChatMessage], temperature: float = 0.7,
                   max_tokens: int = None, stream: bool = False) -> ChatResponse:
        pass
python复制# app/infra/llm/openai_client.py
import httpx
from app.core.config import settings
from app.domain.message import ChatMessage, ChatResponse
from app.infra.llm.base import BaseLLMClient

class OpenAIClient(BaseLLMClient):
    def __init__(self):
        self.api_key = settings.openai_api_key
        self.base_url = settings.openai_base_url
        self.model = settings.openai_model

    async def chat(self, messages, temperature=0.7, max_tokens=None, stream=False):
        payload = {
            "model": self.model,
            "messages": [msg.model_dump(exclude_none=True) for msg in messages],
            "temperature": temperature,
        }
        if max_tokens:
            payload["max_tokens"] = max_tokens
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
        }
        async with httpx.AsyncClient(base_url=self.base_url) as client:
            resp = await client.post("/chat/completions", json=payload, headers=headers)
            resp.raise_for_status()
            data = resp.json()
        choice = data["choices"][0]["message"]
        return ChatResponse(
            message=ChatMessage(role=choice["role"], content=choice.get("content")),
            usage=data.get("usage"),
        )

BaseLLMClient 是一个抽象接口。以后要接入其他模型,只要实现这个接口即可,应用层完全不需要改动。httpx.AsyncClient 是异步 HTTP 客户端,适合 FastAPI 这类异步框架。

4.5 应用层服务

python复制# app/application/chat_service.py
from typing import List
from app.domain.message import ChatMessage, ChatResponse
from app.infra.llm.base import BaseLLMClient

class ChatService:
    def __init__(self, llm_client: BaseLLMClient):
        self.llm_client = llm_client

    async def chat(self, messages: List[ChatMessage], temperature: float = 0.7):
        return await self.llm_client.chat(messages, temperature=temperature)

4.6 API 路由和启动入口

python复制# app/api/chat.py
from fastapi import APIRouter, Depends

from app.domain.message import ChatRequest, ChatResponse, ChatMessage
from app.application.chat_service import ChatService
from app.infra.llm.openai_client import OpenAIClient

router = APIRouter(prefix="/chat", tags=["chat"])

def get_chat_service():
    return ChatService(OpenAIClient())

@router.post("/completions", response_model=ChatResponse)
async def chat_completions(request: ChatRequest, service: ChatService = Depends(get_chat_service)):
    response = await service.chat(request.messages, request.temperature)
    return response
python复制# app/main.py
from fastapi import FastAPI
from app.api.chat import router as chat_router

app = FastAPI(title="AI Family Python")
app.include_router(chat_router)

@app.get("/health")
async def health():
    return {"status": "ok"}
bash复制pip install fastapi uvicorn pydantic-settings httpx python-dotenv
uvicorn app.main:app --reload --port 8000

验证方式:

bash复制curl -X POST http://localhost:8000/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"用一句话介绍什么是RAG"}]}'

如果一切正常,你会得到一个包含 message.content 和 usage 的 JSON。到这里,全家桶的第一个模块已经跑通。后面所有模块都会围绕这个 ChatService 扩展。

5. Spring Boot 版基础骨架:LLM 接入与统一接口

Java 团队同样需要这套统一抽象。Spring Boot 的做法和 FastAPI 类似,只是把依赖注入交给 Spring 管理。下面是一个完整的工程骨架。

5.1 项目依赖

xml复制<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
</dependencies>

5.2 配置项

properties复制# src/main/resources/application.yml
ai:
  llm:
    api-key: sk-your-key
    base-url: https://api.openai.com/v1
    model: gpt-4o-mini
java复制// src/main/java/com/example/aifamily/config/LLMProperties.java
@Data
@ConfigurationProperties(prefix = "ai.llm")
public class LLMProperties {
    private String apiKey;
    private String baseUrl;
    private String model;
}
java复制// src/main/java/com/example/aifamily/config/AppConfig.java
@Configuration
@EnableConfigurationProperties(LLMProperties.class)
public class AppConfig {

    @Bean
    public WebClient llmWebClient(LLMProperties props) {
        return WebClient.builder()
                .baseUrl(props.getBaseUrl())
                .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/json")
                .build();
    }
}

5.3 定义消息模型和请求响应

java复制// src/main/java/com/example/aifamily/domain/ChatMessage.java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChatMessage {
    private String role;
    private String content;
}
java复制// src/main/java/com/example/aifamily/domain/ChatRequest.java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChatRequest {
    private List<ChatMessage> messages;
    private Double temperature;
    private Integer maxTokens;
}
java复制// src/main/java/com/example/aifamily/domain/ChatResponse.java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChatResponse {
    private ChatMessage message;
    private Map<String, Integer> usage;
}

5.4 定义 LLM 抽象接口和 OpenAI 实现

java复制// src/main/java/com/example/aifamily/infra/llm/BaseLLMClient.java
public interface BaseLLMClient {
    Mono<ChatResponse> chat(List<ChatMessage> messages, Double temperature, Integer maxTokens);
}
java复制// src/main/java/com/example/aifamily/infra/llm/OpenAIClient.java
@Service
@RequiredArgsConstructor
public class OpenAIClient implements BaseLLMClient {

    private final WebClient webClient;
    private final LLMProperties props;

    @Override
    public Mono<ChatResponse> chat(List<ChatMessage> messages, Double temperature, Integer maxTokens) {
        Map<String, Object> payload = new HashMap<>();
        payload.put("model", props.getModel());
        payload.put("messages", messages);
        payload.put("temperature", temperature != null ? temperature : 0.7);
        if (maxTokens != null) {
            payload.put("max_tokens", maxTokens);
        }

        return webClient.post()
                .uri("/chat/completions")
                .header(HttpHeaders.AUTHORIZATION, "Bearer " + props.getApiKey())
                .bodyValue(payload)
                .retrieve()
                .bodyToMono(JsonNode.class)
                .map(data -> {
                    JsonNode choice = data.path("choices").get(0);
                    JsonNode msg = choice.path("message");
                    ChatResponse response = new ChatResponse();
                    response.setMessage(ChatMessage.builder()
                            .role(msg.path("role").asText())
                            .content(msg.path("content").asText())
                            .build());
                    if (data.has("usage")) {
                        response.setUsage(convertUsage(data.path("usage")));
                    }
                    return response;
                });
    }

    private Map<String, Integer> convertUsage(JsonNode usageNode) {
        Map<String, Integer> usage = new HashMap<>();
        usage.put("prompt_tokens", usageNode.path("prompt_tokens").asInt());
        usage.put("completion_tokens", usageNode.path("completion_tokens").asInt());
        usage.put("total_tokens", usageNode.path("total_tokens").asInt());
        return usage;
    }
}

5.5 应用层和 Controller

java复制// src/main/java/com/example/aifamily/application/ChatService.java
@Service
@RequiredArgsConstructor
public class ChatService {

    private final BaseLLMClient llmClient;

    public Mono<ChatResponse> chat(List<ChatMessage> messages, Double temperature, Integer maxTokens) {
        return llmClient.chat(messages, temperature, maxTokens);
    }
}
java复制// src/main/java/com/example/aifamily/api/ChatController.java
@RestController
@RequestMapping("/chat")
@RequiredArgsConstructor
public class ChatController {

    private final ChatService chatService;

    @PostMapping("/completions")
    public Mono<ChatResponse> completions(@RequestBody @Valid ChatRequest request) {
        return chatService.chat(request.getMessages(), request.getTemperature(), request.getMaxTokens());
    }
}

启动后同样访问 POST /chat/completions,请求体与 FastAPI 版保持一致。这个双版本实践表明:只要模块边界定义清楚,语言只是实现手段,架构思想可以复用。

6. RAG 检索增强生成:如何让模型“知道”私有知识

LLM 接入只是第一步。真实业务场景里,模型不可能知道你公司内部的规章制度、产品文档、历史工单。RAG 解决的核心问题,就是把外部知识注入到大模型生成过程中,而不是重新训练模型。RAG 的本质是“先检索、再回答”。

RAG 流程一般分两步:

  1. 离线索引:将文档切分、嵌入向量、存入向量数据库。
  2. 在线问答:将用户问题嵌入向量,检索相似片段,拼入 Prompt,再让模型生成。

这一节先给出 FastAPI 版的关键实现,再用 Spring Boot 版做一个简单对比。两者思路一致,差异主要在 API 风格和数据模型上。

6.1 文档切分与向量化

切分是 RAG 最容易忽略却影响最大的环节。切得太粗,上下文会混入无关内容;切得太细,语义不完整。常见策略是:按章节标题切分,设定每块 300 到 500 字,块与块之间保留 50 字重叠。重叠是为了避免关键信息刚好被切断。

python复制# app/infra/rag/doc_processor.py
from typing import List
from langchain_text_splitters import RecursiveCharacterTextSplitter

def split_document(content: str, chunk_size: int = 500, chunk_overlap: int = 50) -> List[str]:
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        separators=["\n\n", "\n", "。", ";", ","]
    )
    return splitter.split_text(content)

6.2 向量存储接口

为了不绑定具体向量数据库,抽象一个 VectorStore 接口,提供 add_documents 和 similarity_search 方法。这样你可以先本地用 Chroma,后续再换 Milvus 或 Qdrant。

python复制# app/infra/vector/base.py
from abc import ABC, abstractmethod
from typing import List

class VectorStore(ABC):
    @abstractmethod
    def add_texts(self, texts: List[str], metadatas: List[dict] = None):
        pass

    @abstractmethod
    def similarity_search(self, query: str, k: int = 4) -> List[dict]:
        pass
python复制# app/infra/vector/chroma_store.py
import chromadb
from app.core.config import settings
from app.infra.vector.base import VectorStore

class ChromaStore(VectorStore):
    def __init__(self):
        self.client = chromadb.PersistentClient(path=settings.vector_store_path)
        self.collection = self.client.get_or_create_collection("docs")

    def add_texts(self, texts, metadatas=None):
        ids = [f"doc_{i}" for i in range(len(texts))]
        self.collection.upsert(
            documents=texts,
            ids=ids,
            metadatas=metadatas or [{} for _ in texts]
        )

    def similarity_search(self, query, k=4):
        result = self.collection.query(query_texts=[query], n_results=k)
        docs = result.get("documents", [[]])[0]
        metas = result.get("metadatas", [[]])[0]
        return [{"content": doc, "metadata": meta or {}} for doc, meta in zip(docs, metas)]

注意,upsert 的效果是如果 doc_{i} 已存在则覆盖。生产环境建议用文档哈希或唯一业务 ID 作为主键,否则重复索引会污染结果。

6.3 RAG 服务:检索 + 拼接 + 生成

python复制# app/application/rag_service.py
from app.domain.message import ChatMessage, ChatResponse
from app.infra.llm.base import BaseLLMClient
from app.infra.vector.base import VectorStore

class RagService:
    def __init__(self, llm_client: BaseLLMClient, vector_store: VectorStore):
        self.llm_client = llm_client
        self.vector_store = vector_store

    def build_prompt(self, query: str, contexts: List[str]) -> str:
        context_text = "\n\n".join([f"[片段{i+1}] {c}" for i, c in enumerate(contexts)])
        prompt = f"""请根据以下知识片段回答用户问题。
如果知识片段中没有答案,请直接说明“根据现有资料无法回答”,不要编造。

知识片段:
{context_text}

用户问题:{query}
"""
        return prompt

    async def ask(self, query: str, k: int = 4):
        docs = self.vector_store.similarity_search(query, k=k)
        contexts = [doc["content"] for doc in docs]
        prompt = self.build_prompt(query, contexts)
        messages = [ChatMessage(role="system", content="你是企业知识库助手。"),
                    ChatMessage(role="user", content=prompt)]
        response = await self.llm_client.chat(messages, temperature=0.3)
        return {
            "answer": response.message.content,
            "sources": contexts,
        }

这里把温度设置为 0.3,让模型更多依赖检索内容而不是自由发挥,减少幻觉。返回的 sources 可以帮助前端展示引用来源,这也是 RAG 在交付时很重要的一环。

6.4 新增 RAG 路由

python复制# app/api/rag.py
from fastapi import APIRouter, Depends
from pydantic import BaseModel

from app.application.rag_service import RagService
from app.infra.llm.openai_client import OpenAIClient
from app.infra.vector.chroma_store import ChromaStore

router = APIRouter(prefix="/rag", tags=["rag"])

class AskRequest(BaseModel):
    query: str
    k: int = 4

def get_rag_service():
    return RagService(OpenAIClient(), ChromaStore())

@router.post("/ask")
async def ask(request: AskRequest, service: RagService = Depends(get_rag_service)):
    return await service.ask(request.query, request.k)

6.5 Spring Boot 版的 RAG 关键差异

Java 端的 RAG 实现逻辑相同,但依赖不同。通常用 Spring AI 中的 VectorStore 抽象,也可以自己封装嵌入模型。下面给一个简化示例,用来演示“先检索,再生成”的编排顺序。

java复制// src/main/java/com/example/aifamily/application/RagService.java
@Service
@RequiredArgsConstructor
public class RagService {

    private final BaseLLMClient llmClient;
    private final SimpleVectorStore vectorStore;

    public Mono<RagResult> ask(String query, int k) {
        List<Document> docs = vectorStore.similaritySearch(query, k);
        String context = docs.stream()
                .map(doc -> "[片段] " + doc.getContent())
                .reduce((a, b) -> a + "\n\n" + b)
                .orElse("");
        String prompt = buildPrompt(query, context);

        List<ChatMessage> messages = List.of(
                ChatMessage.builder().role("system").content("你是企业知识库助手。").build(),
                ChatMessage.builder().role("user").content(prompt).build()
        );

        return llmClient.chat(messages, 0.3, null)
                .map(resp -> new RagResult(resp.getMessage().getContent(), docs));
    }
}

这里没有引入太重的框架,核心是让大家看到 RAG 的编排逻辑和 LLM 模块是解耦的。向量检索返回 List<Document>,应用层负责拼 Prompt,LLM 客户端负责生成。这套接口在双技术栈中保持一致。

7. Agent 与工具调用:让模型学会动手做事

如果说 RAG 解决的是“知识不足”,Agent 解决的是“能力不足”。模型本身只能生成文本,但通过工具调用,它可以查订单、发通知、更新数据库。Agent 的核心循环是:感知任务 → 决定动作 → 执行工具 → 观察结果 → 再次决定,直到任务完成。

7.1 工具定义

先定义一个统一的工具接口。工具必须有两部分:函数本体和一个描述元数据。描述元数据给模型看,函数本体给程序调用。

python复制# app/domain/tool.py
from typing import Callable, Any, Optional
from pydantic import BaseModel, Field

class ToolParameter(BaseModel):
    name: str
    type: str = "string"
    description: str = ""
    required: bool = True

class Tool(BaseModel):
    name: str
    description: str
    parameters: list[ToolParameter] = Field(default_factory=list)
    function: Optional[Callable] = None

    async def execute(self, **kwargs) -> Any:
        if self.function is None:
            raise ValueError(f"Tool {self.name} has no function bound")
        return await self.function(**kwargs)

7.2 实现两个示例工具

python复制# app/tools/weather_tool.py
async def get_weather(city: str) -> str:
    """模拟查询天气,真实场景应接入天气 API"""
    return f"{city}今日晴,气温 24℃,东南风 2 级。"
python复制# app/tools/order_tool.py
async def get_order_status(order_id: str) -> str:
    """模拟查询订单,真实场景应查询数据库"""
    return f"订单 {order_id} 已发货,预计 2025-08-01 送达。"
python复制# app/tools/registry.py
from app.domain.tool import Tool
from app.tools.weather_tool import get_weather
from app.tools.order_tool import get_order_status

def get_default_tools():
    return [
        Tool(name="get_weather",
             description="根据城市名查询天气",
             parameters=[{"name": "city", "type": "string", "description": "城市名,如北京", "required": True}],
             function=get_weather),
        Tool(name="get_order_status",
             description="根据订单号查询订单状态",
             parameters=[{"name": "order_id", "type": "string", "description": "订单号", "required": True}],
             function=get_order_status),
    ]

7.3 Agent 循环核心逻辑

Agent 的难点在于如何让模型输出“工具调用指令”。主流方案有两种:

  1. 使用模型原生 function calling 能力。
  2. 让模型输出 JSON,再用代码解析 JSON 并执行工具。

本文选择第二种方案,因为它与厂商解耦,任何支持指令遵循的模型都能用。缺点是需要更严格的异常处理,但作为教学示例,它更适合看清 Agent 的本质。

python复制# app/application/agent_service.py
import json
from typing import List

from app.domain.message import ChatMessage
from app.domain.tool import Tool
from app.infra.llm.base import BaseLLMClient

class AgentService:
    def __init__(self, llm_client: BaseLLMClient, tools: List[Tool]):
        self.llm_client = llm_client
        self.tools = {tool.name: tool for tool in tools}

    def describe_tools(self) -> str:
        lines = []
        for tool in self.tools.values():
            params_desc = "; ".join(
                [f"{p.name}: {p.description}" for p in tool.parameters]
            )
            lines.append(f"- {tool.name}({params_desc}): {tool.description}")
        return "\n".join(lines)

    def build_system_prompt(self) -> str:
        tool_desc = self.describe_tools()
        return f"""你是一个智能助手,可以调用以下工具完成任务。

{tool_desc}

当需要调用工具时,必须只输出以下 JSON 格式:
{{"tool": "工具名", "arguments": {{"参数名": "参数值"}}}}

其他情况,正常回答用户。"""

    async def run(self, user_query: str, max_steps: int = 5) -> dict:
        messages = [
            ChatMessage(role="system", content=self.build_system_prompt()),
            ChatMessage(role="user", content=user_query),
        ]

        for step in range(max_steps):
            response = await self.llm_client.chat(messages, temperature=0.2)
            content = response.message.content.strip()

            if content.startswith("{"):
                try:
                    action = json.loads(content)
                except json.JSONDecodeError:
                    messages.append(ChatMessage(role="assistant", content=content))
                    messages.append(ChatMessage(role="user", content="请输出有效 JSON。"))
                    continue

                tool = self.tools.get(action["tool"])
                if not tool:
                    messages.append(ChatMessage(role="assistant", content=content))
                    messages.append(ChatMessage(role="user", content=f"工具 {action['tool']} 不存在"))
                    continue

                args = action.get("arguments", {})
                result = await tool.execute(**args)
                messages.append(ChatMessage(role="assistant", content=content))
                messages.append(ChatMessage(role="user", content=f"工具执行结果:{result}"))
                continue

            return {
                "final_answer": content,
                "steps": step + 1,
                "history": [m.model_dump() for m in messages],
            }

        return {"final_answer": "达到最大步骤数,任务未完成。", "steps": max_steps}

这段代码的核心价值是展示了 Agent 的“循环”本质。它没有直接调用一次模型就返回,而是把模型输出当作“指令”,执行完工具后再把结果喂给模型。这一步会让很多人突然理解 Agent 和普通聊天的区别:普通聊天是一次问答,Agent 是多轮问答加工具执行的推进。

7.4 Spring Boot 版工具调用示例

Java 版本同样可以做工具注册和调用,只是动态执行函数时需要借助接口而不是直接传 Callable。这里可以设计一个 ToolExecutor 接口。

java复制// src/main/java/com/example/aifamily/domain/Tool.java
@Data
@Builder
public class Tool {
    private String name;
    private String description;
    private List<ToolParameter> parameters;
    private ToolExecutor executor;
}
java复制// src/main/java/com/example/aifamily/domain/ToolExecutor.java
@FunctionalInterface
public interface ToolExecutor {
    String execute(Map<String, Object> arguments);
}
java复制// src/main/java/com/example/aifamily/application/AgentService.java
@Service
@RequiredArgsConstructor
public class AgentService {

    private final BaseLLMClient llmClient;
    private final List<Tool> tools;

    public Mono<Map<String, Object>> run(String userQuery, int maxSteps) {
        List<ChatMessage> messages = new ArrayList<>();
        messages.add(ChatMessage.builder().role("system").content(buildSystemPrompt()).build());
        messages.add(ChatMessage.builder().role("user").content(userQuery).build());

        return executeLoop(messages, maxSteps);
    }

    private Mono<Map<String, Object>> executeLoop(List<ChatMessage> messages, int remainingSteps) {
        if (remainingSteps <= 0) {
            return Mono.just(Map.of("final_answer", "达到最大步骤数"));
        }

        return llmClient.chat(messages, 0.2, null)
                .flatMap(response -> {
                    String content = response.getMessage().getContent().trim();
                    if (!content.startsWith("{")) {
                        return Mono.just(Map.of("final_answer", content));
                    }
                    try {
                        ObjectMapper mapper = new ObjectMapper();
                        JsonNode action = mapper.readTree(content);
                        String toolName = action.get("tool").asText();
                        JsonNode argsNode = action.get("arguments");
                        Map<String, Object> args = mapper.convertValue(argsNode, Map.class);

                        Tool tool = tools.stream()
                                .filter(t -> t.getName().equals(toolName))
                                .findFirst()
                                .orElse(null);

                        if (tool == null) {
                            messages.add(ChatMessage.builder().role("assistant").content(content).build());
                            messages.add(ChatMessage.builder().role("user").content("工具不存在").build());
                            return executeLoop(messages, remainingSteps - 1);
                        }

                        String result = tool.getExecutor().execute(args);
                        messages.add(ChatMessage.builder().role("assistant").content(content).build());
                        messages.add(ChatMessage.builder().role("user").content("工具执行结果:" + result).build());
                        return executeLoop(messages, remainingSteps - 1);
                    } catch (Exception e) {
                        messages.add(ChatMessage.builder().role("assistant").content(content).build());
                        messages.add(ChatMessage.builder().role("user").content("无法解析工具调用,请重新输出").build());
                        return executeLoop(messages, remainingSteps - 1);
                    }
                });
    }
}

8. MCP:工具接入的标准化协议

当系统里的工具越来越多,你会发现每个 Agent 都要自己定义工具格式,不同项目无法复用。MCP(Model Context Protocol,模型上下文协议)就是为了解决这个问题而出现的。它定义了一个标准:模型客户端如何发现可用工具、如何调用工具、如何获取工具结果。

MCP 的意义不在于引入一个新框架,而在于让工具接入变成“配置式”。你可以把一个天气服务、数据库查询服务、GitHub 操作服务分别封装成 MCP Server,然后多个 Agent 共用一套工具发现和调用机制。

8.1 MCP Python 版最小实现

这里使用 FastMCP 库,它能用装饰器快速暴露工具。

python复制# mcp_server.py
from fastmcp import FastMCP

mcp = FastMCP("CompanyTools")

@mcp.tool()
def query_employee(name: str) -> str:
    """根据员工姓名查询在职状态和部门"""
    # 实际项目中这里会查询 HR 系统
    return f"{name},研发部,在职"

@mcp.tool()
def send_message(phone: str, content: str) -> str:
    """给指定手机号发送短信通知"""
    # 实际项目中接入短信网关
    return f"短信已发送至 {phone}:{content}"

if __name__ == "__main__":
    mcp.run(transport="stdio")
bash复制pip install fastmcp
python mcp_server.py

运行后,这个 MCP Server 会通过标准输入输出等待调用请求。Agent 端通过配置即可发现这两个工具,而不需要在自己的代码里 import 具体的业务函数。

8.2 Agent 接入 MCP 工具

在实际 AGI 应用中,Agent 启动时需加载配置并生成可用工具列表。以 Python 为例:

python复制# app/application/mcp_agent_service.py
import asyncio
from typing import List

from app.domain.tool import Tool

async def load_mcp_tools(mcp_config: dict) -> List[Tool]:
    # 简化实现:实际应建立连接并拉取工具列表
    tools = []
    # 假设通过 MCP 客户端发现了一个 query_employee 工具
    tools.append(Tool(
        name="query_employee",
        description="根据员工姓名查询在职状态和部门",
        parameters=[{"name": "name", "type": "string", "description": "员工姓名"}],
        function=lambda name: f"模拟:{name},研发部,在职",
    ))
    return tools

更完整的做法是启动 MCP Server 并调用工具发现接口,但这里的主旨是理解 MCP 对工具调用的标准化作用:工具不再绑定在代码里,而是运行在独立的进程中。

8.3 Spring Boot 版 MCP 注意事项

Java 生态中也有 Spring AI 对 MCP 的支持,但如果你不想引入重型依赖,也可以直接用 HTTP 方式实现一个简单的 MCP HTTP Server。用 POST /mcp/tools/call 接收工具名和参数,内部路由到对应 Service。这本质上就是“工具网关”。这种实现的好处是:Java 服务不需要理解 Python 的内部实现,只需遵守同一套 HTTP 协议,非常符合微服务场景。

设计的核心是收敛工具入口。所有工具都通过一个 Controller 暴露,并加上调用日志、权限校验和限流。这样即使工具注册在 Python 端,Java 侧也能通过 HTTP 调用同一个 MCP Server,达成双技术栈共存。

9. 工作流:从单次调用到流程编排

Agent 擅长动态决策,但真实业务里的很多流程是固定的,比如“收到工单→判断分类→调用知识库→生成草稿→人工审核→发送回复”。如果每次都让 Agent 自由发挥,结果不可控。工作流就是把固定的步骤定义成模板,让系统按顺序或条件执行。

9.1 工作流节点设计

工作流可以抽象成三种节点:

  • 开始节点:接收输入变量。
  • 处理节点:调用 LLM、RAG、工具或代码。
  • 条件节点:根据前一步结果决定走哪个分支。
  • 结束节点:返回最终结果。

先用 Python 实现一个极简工作流引擎,支持按顺序执行节点。

python复制# app/infra/workflow/engine.py
from typing import Any, Dict, Callable

class WorkflowNode:
    def __init__(self, name: str, func: Callable, depends: list[str] = None):
        self.name = name
        self.func = func
        self.depends = depends or []

class WorkflowEngine:
    def __init__(self):
        self.nodes = {}

    def add_node(self, node: WorkflowNode):
        self.nodes[node.name] = node

    async def run(self, start_node: str, input_data: Dict[str, Any]) -> Dict[str, Any]:
        results = {}
        visited = set()

        async def execute(node_name: str):
            if node_name in visited:
                return
            visited.add(node_name)
            node = self.nodes[node_name]
            kwargs = {"input_data": input_data, "results": results}
            result = await node.func(**kwargs)
            results[node.name] = result

        await execute(start_node)
        return results

9.2 用工作流串联 RAG 和 Agent

让流程体现实际价值,可以构建一个“智能客服工作流”:先查询知识库,再决定是否需要调用 Agent。这个工作流既包含固定步骤,也在中间引入条件判断,更贴近真实场景。

python复制# app/application/workflow_service.py
from app.application.rag_service import RagService
from app.application.agent_service import AgentService
from app.infra.workflow.engine import WorkflowEngine, WorkflowNode

async def create_customer_service_flow(rag: RagService, agent: AgentService):
    async def step_extract( input_data, results):
        query = input_data["query"]
        # 这里可以接意图识别模型,简化处理为直接输出
        return {"intent": input_data.get("intent", "general")}

    async def step_rag( input_data, results):
        query = input_data["query"]
        result = await rag.ask(query, k=3)
        return result

    async def step_agent( input_data, results):
        query = input_data["query"]
        result = await agent.run(query, max_steps=3)
        return result

    async def step_decide( input_data, results):
        rag_answer = results.get("step_rag", {}).get("answer", "")
        # 简单规则:如果 RAG 回答中包含“无法回答”,转 Agent
        if "无法回答" in rag_answer:
            agent_result = await step_agent(input_data, results)
            return {"final_answer": agent_result["final_answer"]}
        return {"final_answer": rag_answer}

    engine = WorkflowEngine()
    engine.add_node(WorkflowNode("step_extract", step_extract))
    engine.add_node(WorkflowNode("step_rag", step_rag))
    engine.add_node(WorkflowNode("step_decide", step_decide))
    return engine

这种方式的好处是:业务逻辑可预测,每一步都有结果记录,便于日志追踪。相比纯 Agent 的自由发挥,带固定工作流的场景更受甲方欢迎,因为可解释、可审计。

9.3 Spring Boot 工作流实现要点

Java 版本不需要全部自己写,可以引入流程引擎,比如 Flowable、Camunda。但如果只想在内存中做简单编排,完全可以用 Map<String, Function> + CompletableFuture 实现。建议先定义节点接口:

java复制public interface FlowNode {
    Mono<Map<String, Object>> execute(Map<String, Object> inputData, Map<String, Object> results);
}

然后把 RAG 和 Agent 的 Service 包成节点,放入 FlowEngine。这套思想和 Python 版完全一致。重点是:不要在业务 Controller 里直接写一大堆 if-else 调用,将流程节点化之后,扩展新步骤只需要新增节点,不再修改原方法。

10. 运行验证与效果检查清单

写到这里,你已经拥有了全家桶的全部核心模块。但代码写出来不等于能上线,接下来给出一套可操作的验证路径。

验证级别 检查项 预期结果
基础 启动 FastAPI / Spring Boot 服务 控制台无报错,/health 返回 ok
LLM 模块 调用 /chat/completions 返回正常文字回复和 usage
RAG 模块 先执行索引脚本,再调用 /rag/ask 回答中包含知识库内容,sources 非空
Agent 模块 发起“帮我查北京天气” 回复中包含工具执行结果(模拟天气)
MCP 启动 MCP Server,用 MCP 客户端调用工具 能发现并调用 query_employee 等工具
工作流 执行智能客服流程 条件判断生效,返回结果含来源记录

如果某一步失败,优先检查以下方向:

  1. 配置是否正确,尤其是 API_KEY、BASE_URL 和 MODEL。
  2. 模型是否支持当前使用的方式,比如工具调用 JSON 输出要求模型有较强的指令遵循能力。
  3. 依赖版本是否冲突,Python 使用 pip freeze 检查,Java 使用 Maven/Gradle 依赖树检查。
  4. 向量数据库是否真的写入了数据,很多 RAG 失败是因为索引阶段没有调用成功。

下面用一个表格汇总常见问题:

问题现象 可能原因 排查方式 解决方案
请求模型报 401 API Key 错误或未配置 检查 .env / application.yml 中 Key 重新生成 Key,确认服务商支持的 Base URL
模型返回 429 请求频率超限或余额不足 查看响应头和账户余额 降低并发,增加重试,或切换模型
RAG 回答不包含知识库内容 未执行索引,或检索不到相关片段 打印 sources 查看检索结果 执行索引脚本,调整分块大小,减少 k 值
Agent 无法进入工具调用 模型输出不是 JSON 在调试日志中打印模型原始输出 更换更强模型,或优化 System Prompt 示例
MCP Server 启动报错 transport 参数不支持 查看 FastMCP 文档确认版本 改用 sse 或 http 方式
工作流执行顺序错误 节点依赖配置不对 检查 depends 字段 显式声明依赖,或改用顺序执行

11. 全家桶落地的工程建议与安全注意

把 Demo 变成生产系统,光有功能远远不够。从工程角度看,下面几条值得认真对待。

11.1 配置中心化和密钥隔离

一定不要把 API Key 直接写在代码里。FastAPI 项目可以使用 .env 和 Pydantic Settings;Spring Boot 项目可以使用配置文件加环境变量覆盖。生产环境建议接入配置中心,比如 Nacos、Apollo 等,或者在 CI/CD 中注入环境变量。密钥要遵循最小权限原则,不同服务使用不同 Key,便于在模型平台上审计调用来源。

11.2 统一的结构化日志

大模型应用的排查难度比普通后端高很多,因为你无法确定问题是出现在 Prompt 环节、模型环节还是工具环节。建议在所有关键入口打印结构化日志,记录:

  • 请求 ID。
  • 用户 ID。
  • 模型名称和参数。
  • Prompt 内容。
  • 模型返回内容。
  • 工具调用参数和结果。
  • 消耗 Token 数和耗时。

FastAPI 可以通过中间件实现,Spring Boot 则可以用拦截器或 AOP。日志只记录业务必要信息,不得泄露用户隐私和敏感业务数据。这里要特别强调,在生产环境一定要遵守数据安全合规,对任意数据库查询、文件读取、消息发送类工具,都要先做权限校验并保留审计记录。

11.3 请求级超时与重试策略

模型服务经常出现网络抖动。不要无限制等待,也不要无脑重试。推荐方案:

  • HTTP 请求设置连接超时和读取超时,比如连接 5 秒,读取 60 秒。
  • 对 429、5xx 错误使用指数退避重试,最多 3 次。
  • 对超时类错误不重试,直接返回降级结果,避免重复消耗 Token。

11.4 用评测集做回归控制

AI 应用最怕“这周调好,下周模型升级就坏了”。建议建立一个小规模回归测试集,比如 50 条常见问题,定期跑一遍全家桶,人工或 LLM 评估结果质量。每次修改 Prompt、调整分块参数或换模型后都执行评测,确保整体效果没有退化。

11.5 生产环境的路由与灰度

如果内部系统中有多套模型、多个向量库,建议在架构中加入路由层。全局配置一个默认路由,不同业务域走不同模型组合。新版本模型先灰度一小部分流量,观察效果再全量切换,规避模型服务不可控带来的业务风险。

12. 双版本统一架构的核心套路总结

走到这里,你会发现 FastAPI 版和 Spring Boot 版长的其实非常像。它们都采用了相同的思想:

  • 用接口隔离模型厂商。
  • 用独立模块封装 RAG、Agent、MCP 和工作流。
  • 用统一数据模型标准化消息、工具、流程。
  • 把业务编排放在应用层,避免 Controller 膨胀。

这四种套路完全可以复制到其他语言。即使将来要用 Go 或 Node.js 实现,也可以沿用这套分模块 + 接口抽象 + 依赖注入的思路。团队在协作时,可以让 Java 组负责流程和工具网关,Python 组负责模型编排和知识库索引,两端通过 MCP 协议互通。

如果你现在正在做一个真正的 AI 应用项目,建议从最小闭环开始:先打通 LLM 接入,再逐步扩展 RAG 和 Agent。只有当你意识到工具调用会越来越多、流程越来越固定时,再引入 MCP 和工作流也不迟。这套全家桶不是一个必须一次装完的重量级框架,而是一组可以按需选用的工程组件。

今天写到的代码示例都是教学级的最小实现,离生产仍有距离,但主干路径已经清晰了。建议你把每个模块单独跑一遍,然后再尝试将两个模块串联起来。比如先让 Agent 调用 RAG,再让工作流调度 Agent,你会发现所有模块之间的耦合点都非常少,这正是这套设计的价值所在。

内容推荐

CordisBench:用状态机评测大模型对组件生命周期的动态推理能力
CordisBench · 组件生命周期 · 状态机
在动态智能体系统中,组件常于对话过程、工具调度或异步事件流中被动态注册、初始化和销毁,其生命周期比传统后端服务更难跟踪。这要求大模型具备对运行时状态的持续推理能力,而不仅仅是静态代码理解。CordisBench 正是针对这一能力缺口设计的评估方向,它借助组件快照、事件日志与依赖关系构造评测样本,用状态机模拟生命周期迁移,考察模型能否判断当前状态并预测下一事件。本文从状态机与事件驱动原理入手,介绍如何搭建最小生命周期评测脚手架,包括样本生成、Prompt 设计、答案归一化与准确率计算。该方法适用于智能体可观测性、自主运维和 Agent Harness 评估等场景,帮助开发者定位大模型在状态追踪上的真实短板。
Hermes Skill 实战指南:智能体技能注册、编排与安装详解
Hermes Skill · 技能注册 · 技能编排
在智能体与自动化系统开发中,技能模块化是降低维护成本、提升复用性的关键思路。传统直接写死函数调用的方式,在面对多业务流程时会陷入反复修改主流程的困境。基于统一技能抽象层,Hermes Skill 为技能注册、路由、执行与编排提供了标准化的解决方案。开发者只需按规范定义技能描述文件并实现执行器,即可通过扫描或配置完成注册,由框架统一处理鉴权、限流及调用链管理。无论是企业内部自动化流程、多系统集成,还是大模型驱动的智能体平台,这种设计都能让能力管理更规范、替换更灵活。本文从环境准备、核心组件、配置原理到 Spring Boot 项目中的完整安装与验证流程,逐步拆解落地要点,并针对扫描不到技能、接口 404、线程池耗尽等常见问题给出排查建议,帮助开发者在工程实践中快速上手并建立可演进的技能管理体系。
大模型应用开发全家桶:LLM+RAG+Agent+MCP+工作流双技术栈实践
大模型应用开发 · LLM · RAG
大型语言模型(LLM)的工程化落地已从单次对话转向多模块协作,开发者普遍面临如何组织代码以集成RAG、Agent、MCP、工作流和工具调用的难题。本文从工程架构角度,剖析一套不绑定特定厂商、不局限于单一语言的大模型应用全家桶设计思路。通过接口隔离和分层抽象,将模型能力与业务系统解耦,使知识库问答、智能客服、自动化流程等场景快速接入。结合FastAPI与Spring Boot双版本示例,展示LLM接入、检索增强生成、Agent决策循环、工具标准化协议及流程编排的具体实现方法,帮助团队构建可测试、可维护、可跨语言协作的AI应用骨架。
AI大模型应用实战全家桶:从0带敲RAG、Agent与MCP双版本
大模型应用开发 · LLM · RAG
大模型应用开发正在成为后端工程师的新技能栈。除了直接调用LLM接口,还需要理解检索增强生成(RAG)、智能体(Agent)、模型上下文协议(MCP)等核心概念。RAG通过外部知识库检索辅助模型生成,解决私有领域问答;Agent借助工具调用完成多步决策;MCP则统一了外部能力接入方式。这些技术在实际工程中常组合使用,形成知识库问答、智能助手等典型场景。然而,不同技术栈的落地方式差异较大,Python和Java开发者往往需要各自的参考实现。本文介绍一套从0带敲的AI大模型应用实战全家桶,完整覆盖LLM接入、RAG、Agent、MCP、工作流与工具调用,并提供FastAPI和Spring Boot双版本代码,帮助读者在真实业务中快速搭建可复用的大模型应用骨架。
TimesFM:零样本时间序列预测基础模型怎么用?
TimesFM · 时间序列基础模型 · 零样本预测
时间序列预测是数据分析中的经典难题,传统方法往往需要针对每个数据集单独调参,成本高昂。以Transformer为代表的基础模型架构,通过在大规模时序数据上预训练,学到了通用的趋势与周期规律,从而支持零样本预测:直接输入原始序列即可得到合理基线,无需重新训练。TimesFM正是这一技术路线的开源实现,在冷启动、批量基线生成等场景中,能显著降低建模门槛,帮助工程师快速判断数据潜力。理解其patch切分与decoder-only设计,能更好地在实际预测任务中落地。
陌生开源仓库评估实战:从环境配置到批量任务的全流程指南
开源仓库 · 环境配置 · 批量任务
开源软件生态中,面对信息不全的GitHub仓库,如何快速判断其真实用途与可运行性是开发者普遍面临的难题。合理评估一个项目,不能依赖名称或star数量,而应从仓库结构、依赖文件与语言构成反推其类型,再通过环境隔离与硬件检查确保可复现性。最小样例测试是验证输入输出链路的关键手段,参数调整需先理解默认值的边界,而批量任务则考验资源管理与错误隔离能力。本文以Sequoia-X为切入点,系统拆解一套从项目类型判断到生产化评估的完整流程,覆盖依赖管理、硬件适配、性能边界等高频问题,帮助开发者降低试错成本,同时规避文档缺失与维护停滞等潜在风险。
Hermes Skill实战:从安装到编写可复用的AI智能体技能包
Hermes · Skill · AI智能体
在AI智能体开发中,如何让模型从“能用”变成“好用”,关键在于构建一套可复用、可编排的能力单元——技能(Skill)。技能机制将原子工具、Prompt指令与业务规则封装为轻量模块,主程序通过约定协议自动发现并加载,从而提升系统的扩展性与可维护性。基于开放原子基金会的开源项目Hermes Agent,企业可在私有化部署环境下搭建完整的智能体服务,通过安装辅助系统Skill、配置模型接口、编写指令文件等步骤,快速沉淀团队知识资产。本文从技能设计原理出发,结合工程实践演示最小技能包与执行脚本的编写,并梳理环境配置、路径兼容、权限隔离等高频问题的排查思路,帮助开发者从零跑通一套可落地的智能体技能管理体系。
AI大模型应用开发全家桶:LLM、RAG、Agent与MCP实战
大模型应用开发 · LLM · RAG
大模型应用开发正从单一API调用走向系统化工程。理解LLM(大语言模型)的推理能力、RAG(检索增强生成)的知识外挂、Agent的自主决策、MCP(模型上下文协议)的工具标准化,以及工作流的任务编排,是构建生产级AI应用的基础。本文以FastAPI和Spring Boot双技术栈为例,从环境搭建到代码实现,演示如何将模型调用、知识检索、工具调用与任务编排串成完整链路,帮助开发者建立从概念到落地的工程化地图,避开常见工程陷阱。
从0到实战:AI大模型应用全家桶双版本落地指南
LLM · RAG · Agent
AI大模型应用正从单一的对话接口走向复杂的工程链路。理解LLM作为认知底座、RAG补充外部知识、Agent驱动多步决策、MCP标准化工具接入、工作流固化稳定路径,以及工具调用作为执行手段,是构建可落地系统的关键。这些技术协同工作,能帮助企业将大模型能力嵌入真实业务场景,如私有知识问答、自动化流程处理和跨系统协同。本文以FastAPI与SpringBoot双版本为主线,从环境搭建、最小实现到验证与排错,完整拆解一条能跑通的AI应用开发链路,为需要从接口调用进阶到完整应用开发的工程师提供实战参考。
原生统一多模态模型:理解与生成的协同实现指南
原生统一多模态模型 · 理解与生成协同 · 多模态大模型
多模态大模型正从模块拼接走向原生统一架构,核心挑战在于让理解与生成在同一套参数内协同工作。理解-生成协同(Understanding-Generation Synergy)要求模型在表征层、任务层、系统层统一设计:通过VQGAN等图像离散化技术将视觉信息转化为统一token序列,让图像与文本在同一个序列空间自回归建模;将视觉问答、文生图等任务统一为序列生成目标,简化多任务训练;同时兼顾训练与推理的系统工程优化。这种架构能显著降低部署成本,并提升模型对跨模态内容的理解与创作能力。本文面向多模态模型开发者和研究者,拆解原生统一模型的设计逻辑,并给出从表征到任务的代码示例和系统部署建议,帮助读者快速建立多模态理解与生成一体化的工程落地思路。
SpringBoot + AI大模型:在线考试系统智能化改造实战指南
SpringBoot · AI大模型 · 在线考试系统
在线考试系统正从传统客观题判分向智能化方向演进,其核心在于将AI大模型能力与业务场景深度融合。基于SpringBoot构建的考试主链路,通过统一封装模型调用接口、设计异步批改队列和完整的评分记录表,可以实现自动组卷、智能阅卷与考情分析。这类系统广泛应用于校园测评、企业培训、认证考试等标准化考试场景,能够有效降低教师重复劳动、缩短成绩反馈周期。工程落地的关键在于异步化处理、Prompt版本管理、人工复核机制以及token成本控制,同时需关注数据脱敏与合规要求。本文从系统架构、数据库设计、模型接入、组卷算法到智能阅卷全流程,给出可落地的工程实现方案。
AI大模型应用开发实战:LLM+RAG+Agent+MCP+工作流全家桶搭建
LLM · RAG · Agent
在大模型应用开发中,LLM、RAG、Agent、MCP与工作流常被割裂理解,导致落地困难。实际上,这些能力是分层协同的整体:LLM负责生成,RAG提供外部知识,工具调用赋予行动力,Agent驾驭多步推理,MCP统一工具接入,工作流固化业务路径。本文以一次真实请求为例,演示如何通过统一消息格式将这些模块串成一套可运行的后端服务骨架,并同时提供FastAPI与Spring Boot双版本实现。从最小闭环出发,逐步替换生产组件,最终覆盖智能问答、知识检索、自动化工具调用与固定流程编排等场景,帮助企业开发者从“调用模型接口”迈向“搭建完整AI应用服务”。
SpringBoot+AI大模型在线考试系统:智能出题、批改与学情分析实战
SpringBoot · AI大模型 · 在线考试系统
在线考试系统是后台开发中的常见项目,但如何让AI大模型真正参与业务,而不只是简单调用接口?从技术架构看,SpringBoot提供了稳定的业务骨架,AI大模型则负责主观题批改、智能出题与学情分析等核心环节。这类系统在设计上需遵循“AI辅助而非依赖”的原则,通过结构化提示词、异步任务和降级策略保证考试流程可靠。其典型应用场景包括教师批量生成题目、学生交卷后的自动评分以及基于考试数据生成个性化学习报告。对于毕业设计或企业培训平台,掌握AI接口封装、任务状态管理和异常处理,比堆砌功能更有价值。本文围绕系统边界、表结构设计、AI接入位置和性能优化,完整拆解一个生产可用的在线考试系统落地过程。
LLM-as-a-Judge摘要评估实战:机制、提示词设计与避坑指南
LLM-as-a-Judge · 摘要评估 · ROUGE
大语言模型的推理能力为文本质量评估提供了新思路。传统ROUGE指标仅统计词面重叠,难以衡量摘要的语义忠实度与信息覆盖。LLM-as-a-Judge让模型像人一样阅读原文与摘要,按指定维度给出可解释评分,支持相关性、连贯性、忠实度等细粒度诊断。该方案在摘要评测、RAG答案质量验证、模型对比等场景中逐渐成为主流。但评估效果高度依赖提示词设计、评分锚点、采样配置与位置偏差处理。系统拆解了直接打分、成对比较、多维度评估三种机制,并提供最小可行验证流程和常见陷阱排查思路,帮助开发者搭建可靠、可复现的自动评估体系。
轻量快速模型实战:Gemini 3.8 Flash 如何优化成本与批量工作流
Gemini 3.8 Flash · 轻量模型 · 批量处理
在大模型应用逐渐普及的今天,如何平衡推理质量、响应速度与调用成本,成为开发者落地 AI 能力时最现实的考量。轻量快速模型正是为了应对这一挑战而出现:它并非简单弱化版,而是针对低复杂度、高重复度任务重新设计的精度与成本平衡方案。通过降低单次请求延迟、提升并发吞吐能力,这类模型让批量文本抽取、格式化输出、内容预分类等场景得以低成本嵌入现有流程。工程实践中,合理的输入清洗、结构化提示词、输出校验与日志观测,能显著提升模型的稳定性和可维护性。以 Gemini 3.8 Flash 为代表的小模型,尤其适合作为工作流中的“第一层过滤器”,帮助开发者将重复劳动从人工中解放出来,实现更快、更可控的自动化处理。
Claude一小时实战:小应用、Skills、浏览器操作与业务自动化闭环
Claude · 大模型 · 业务自动化
在人工智能与自动化技术加速落地的今天,RPA(机器人流程自动化)与大模型辅助开发已成为企业提升效率的重要抓手。其核心逻辑是:先由模型理解自然语言需求,再通过脚本、标准技能包和浏览器控件将重复动作固化为可复用流程。这种模式显著降低内部工具的开发门槛,让运营与开发人员只需描述输入、处理逻辑、输出形式,即可获得可运行的小应用和业务流程闭环。常见于报表整理、网页填表、摘要生成等高频场景。围绕 Claude 的一小时实战,从环境准备到小应用、Skills、浏览器操作与业务自动化连穿的链路,正是验证这套方法的最短路径。
Gemini 3.8升级指南:多模态与长上下文下的模型评估与接入实践
Gemini 3.8 · 多模态 · 长上下文
大模型版本迭代日益频繁,开发者如何理性应对?从多模态与长上下文两大技术主线出发,解析Gemini系列模型的能力演进与性价比变化。通过建立业务测试集、平行对比输出质量与格式稳定性,以及灰度接入与回滚机制,帮助团队在新模型发布后快速完成技术选型。同时给出Python调用Gemini API的完整示例,覆盖文本、图像及批量回归场景,并提供隐患排查与工程实践建议。无论你正在构建Agent应用还是优化既有AI流程,这套方法都能让你在模型升级中掌握主动权。
Claude新模型升级实践:从API迁移评估到Token成本优化全指南
Claude模型升级 · API迁移 · Token成本控制
在LLM应用开发中,模型迭代频繁,新版本往往宣称“更强、更便宜”,但真实落地时,能力提升和成本下降并不必然转化为业务收益。开发者需要理解大模型API的Token计费结构、上下文缓存机制、输出长度控制等基础原理,才能科学评估迁移价值。模型评测是迁移的第一关,通过建立业务场景基线、对比新旧模型输出质量,才能判断是否值得切换。工程实践中,将模型ID配置化、灰度发布、提示词版本管理、异常排查等环节环环相扣。同时,成本优化不能只看单价,还要关注输出Token膨胀、重复调用、缓存命中率等现实因素。本文梳理了一套从模型评测、API接入、迁移评估到成本控制的实操方法,帮助使用Claude API或做LLM选型的开发者,在模型升级浪潮中稳妥获利。
TimesFM:Google开源时间序列基础模型,零样本预测与工程实践指南
时间序列预测 · 基础模型 · 零样本
时间序列预测是业务决策中的高频需求,但传统建模流程复杂,且每个场景都需要重新调参与训练。TimesFM是Google Research开源的时序基础模型,基于Transformer架构与patching机制,将时间序列切分为patch并预训练在真实世界海量数据上,从而实现对任意新序列的零样本预测。其核心价值在于压缩了从数据到预测的链路,无需特征工程与逐序列训练,即可快速获得可靠的基线结果,显著降低工程交付成本。该模型适用于销量预估、流量监控、库存管理等多序列批量预测场景,尤其适合缺少历史数据或数据质量参差不齐的业务。围绕TimesFM,可以了解其架构原理、输入数据规范、零样本推理流程及反归一化等关键细节,为实际项目选型和工程接入提供直接参考。
AI训练数据退出机制配置指南:防止输入输出用于训练
数据隐私 · AI训练数据 · opt out
在人工智能与数据隐私的交汇点,训练数据合规已成为企业与开发者必须掌握的基础能力。许多AI平台默认将用户输入与输出数据纳入模型优化流程,由此催生了所谓的数据退出(opt out)机制——一种用于阻断服务数据向训练数据流转的设置。理解其原理,即区分“用于服务”与“用于训练”,是应用数据治理与保护敏感信息的关键。该机制广泛应用于个人隐私设置、企业数据管控、API请求声明及本地部署等场景。系统梳理通用退出配置流程、请求级标记方法、验证技巧与兜底策略,为应对外部AI服务中的训练数据采集风险提供了可落地的排查体系。
已经到底了哦
精选内容
热门内容
最新内容
SpringBoot集成AI大模型的在线考试系统:从组卷到学情分析
在企业级应用开发中,如何将大模型能力嵌入传统业务系统是当前工程实践的热点。在线考试系统作为典型的管理信息系统,其核心流程包含题库管理、权限控制、组卷、答题与判分等环节。传统实现往往依赖人工选题与手工评阅,难以高效支撑主观题评分和个性化学情分析。引入AI大模型后,系统能够自动生成候选题目、提供主观题评分参考、并基于考试数据生成学习建议。但工程落地的关键在于将这些能力封装为可靠的服务,与SpringBoot后端、数据库设计和权限体系有机结合。本文基于SpringBoot 3.x,结合Spring Security与MyBatis-Plus,演示一套涵盖AI组卷、AI辅助评分、学情分析的最小可运行系统,并重点讲解接口抽象、结构化输出、异步调用与降级策略,帮助开发者构建可扩展、可追溯的智能考试平台。
Hermes Skill:让大模型 Agent 实现技能化扩展的工程指南
大模型Agent在业务落地中常面临能力固化、扩展困难的问题。技能化扩展机制将模型能力与业务逻辑解耦,把工具调用、规则执行封装为可插拔的技能单元,使Agent能动态适配查询、调度等实际场景。基于清单描述与运行时调度的架构,可显著提升开发效率与系统稳定性。以Hermes Skill为例,展示技能化扩展的概念原理、安装流程、调试方法与最佳实践,为企业构建灵活可控的智能体应用提供工程参考。
Claude工作流实战:提示词、Projects、Skills与连接器组合用法
在AI应用开发与自动化脚本场景中,仅仅将对话式AI视为“智能聊天框”会极大限制其工程价值。真正提升效率的关键,在于构建一套可复用、可维护的AI工作流。核心原理包括四个层面:提示词工程作为交互底座,负责明确任务目标与输出约束;Projects提供跨对话的长期上下文管理,解决模型冷启动问题;Skills将专业处理流程结构化封装,固化代码审查、接口生成等标准动作;连接器则打通外部系统数据,实现实时数据闭环。四者协同可支撑文档生成、团队知识管理等真实业务场景,让模型从被动应答转为主动执行。本文完整拆解这套组合用法,并提供可直接落地的中英双语实践模板,帮助开发者建立稳定高效的Claude使用方式。
千问3.8本地部署Agent:用DeepSeek Harness零成本编排任务
大模型本地部署是当前开发者摆脱付费API依赖的可行路径,通过开源权重模型搭建私有推理环境,能让数据全程不出本机。在此基础上,Agent作为一种具备决策与工具调用能力的运行模式,可自动拆解目标、调用外部工具并循环推进;而任务编排框架(如DeepSeek Harness)则负责将模型、工具与流程串联成稳定的自动化流水线。这类方案对预算有限的开发者、中低端显卡用户以及有数据隔离需求的工程场景尤其有价值,既能完成文件处理、信息抽取等本地Agent任务,又不必承担按Token计费的云端成本。本文以千问3.8本地部署为例,解读如何借助DeepSeek Harness构建可控的多模型协作工作流,同时提醒零成本口号背后的硬件要求、环境调试与维护投入,帮助读者更理性地把控落地节奏。
Codex实战:从自动补全到AI编程代理的完整指南
AI编程工具正从代码补全走向代理式智能体,核心差异在于前者补全单行代码,后者能自主执行命令、读写文件、运行测试并迭代修复。这类工具将一次完整的开发循环从小时级压缩到分钟级,尤其适合重构、补写测试和排查报错等可验证场景。但使用上限取决于需求描述能力,目标、上下文、约束与验收条件缺一不可。Codex作为其中代表性CLI工具,其安全边界与权限控制同样关键。本文从工程实践角度,拆解Codex的安装、任务描述、进阶用法、踩坑排查及适用边界,帮助开发者从“会用”走向“敢用”。
Claude一小时实战:从聊天到小应用、Skills与业务自动化
在AI工具普及的今天,很多人把大模型对话当作搜索引擎使用,但真正的价值在于构建可重复运行的自动化链路。从自然语言交互到工程化落地,核心是利用Claude的对话能力生成小应用、固化可复用的Skills、通过浏览器操作代替人工执行,最终串联成完整的业务自动化流程。理解输入定义、输出验证和过程重跑这三大原理,是技术落地的基础。这种能力不仅适用于程序员,也适合运营、产品等非技术角色处理周报汇总、数据清洗、表单填报等高频重复任务。本文以一小时实践为主线,拆解从临时对话到可复用工具的关键步骤,帮助你在真实业务场景中验证AI的工程价值,避免陷入“聊天式实战”的误区。
Gemini 3.8 Flash API接入实战:从最小请求到生产级配置
大模型应用开发中,API接入往往比模型选型更考验工程细节。理解HTTP请求结构、上下文窗口和生成参数,是构建稳定AI应用的基础。Gemini 3.8 Flash及Flash Cyber作为轻量级大模型,在文本摘要、信息抽取、客服问答等高频低延迟场景中具有实用价值。本文从最小请求出发,逐步讲解多轮会话的contents维护、结构化输出的JSON校验与兜底、403与429等错误码的区分和排查,以及生产环境下的成本控制、安全防护与灰度切换,为后端开发者提供一份可落地的接入清单。
开源项目分析实战:从Sequoia-X看环境配置与源码排错
开源项目是技术选型和工程实践中的重要资源,但面对一个陌生GitHub仓库,初学者往往止步于“能跑起来”。真正高效的分析方法,应建立在对仓库元信息、环境依赖、启动方式、源码链路和排错思路的系统拆解之上。通过掌握README、构建文件、配置项与外部依赖的识别技巧,可以快速判断项目定位;结合最小化启动、日志验证与分支覆盖阅读法,又能从“运行成功”走向“理解原理”。这套方法论适用于任何开源项目,尤其适合需要二次开发或集成到自己系统中的场景。本文以Sequoia-X为例,完整演示从克隆、环境准备到问题排查与PR提交的分析流程,帮助读者建立可复用的开源项目分析模板。
DeepSeek Harness:让AI记住历史决策的工程实践
大语言模型本身不具备持久记忆,每次调用只能依赖当前上下文。在AI客服、智能助手和自动化决策系统中,历史决策的丢失常导致建议反复、结论漂移。解决这一问题的关键在于将AI决策转化为可查询、可回放的显式工程资产。通过结构化的决策日志记录,结合场景化的历史摘要注入,模型能快速复用此前结论。在需求评审、技术选型、故障处理等典型场景中,这种机制不仅提升了决策一致性,还能为后续复盘和优化提供依据。DeepSeek Harness方法正是这样一套实践思路:它不依赖特定框架,而是通过外部存储、检索与提示词构造,让AI真正‘记住’历史决策。本文结合可运行代码示例,拆解其实现原理与常见坑位。
从Demo到生产:完整Agent体系的工具调用、记忆、协同与安全护栏
随着大模型能力的普及,AI Agent 正在成为连接语言模型与真实业务的关键载体。一个可用的 Agent 并非简单的模型循环调用,而是需要构建可控的执行体系。工具调用通过结构化协议让模型安全接入外部能力;长短记忆分层管理对话连续性与跨会话事实;人机协同将高风险决策分包给人工,平衡效率与风险;安全护栏以最小权限、执行边界和全程审计兜底。LangChain 与 LangGraph 等框架为这四层提供了工程化支撑,使 Agent 能真正从 Demo 走向生产。本文结合实战经验,系统拆解这四大核心维度,并为开发者提供从简单循环到完整体系的演进路径与排查思路,帮助你在真实业务中快速定位短板、补齐边界。
已经到底了哦