用 FastAPI 将 LangChain Agent 封装成对外接口

🔖 系列 :FastAPI + LangChain 实战

📅 适合人群 :有 Java 基础,想了解 AI 服务架构的开发者

阅读时长:约 10 分钟


用 FastAPI 将 LangChain Agent 封装成对外接口

------ 以 Java 主服务调用 Python AI 服务为例


📌 前言

很多公司的后端是 Java(Spring Boot)技术栈,但 AI 生态(LangChain、Transformers 等)几乎都在 Python 世界。这时候,最常见也最合理的做法是:

Java 负责主业务,Python 负责 AI 推理,两者通过 HTTP 接口通信。

FastAPI 正是 Python 这侧的 HTTP 入口。本文将完整讲解这套架构的搭建方式,从 FastAPI 基础、LangChain Tool、Agent 原理,到 Java 如何调用 Python 服务。


一、整体架构

系统分为三层:

Java 和 Python 是两个独立部署的服务,通过标准 HTTP 协议连接。这样设计的好处:

  • ✅ 职责清晰,Java 管业务,Python 管 AI
  • ✅ 两侧可以独立升级、独立扩容
  • ✅ Python AI 服务可以被多个 Java 服务复用

二、完整请求流程

以用户查询「我的订单 ORD-001 到哪里了」为例:

💡 Java 对 Python 的调用,和调用任何第三方 HTTP 接口没有区别。


三、Python 侧:FastAPI 封装 Agent

3.1 FastAPI 三个核心概念

① 路由:声明"什么路径 + 什么方法 → 执行什么函数"

python 复制代码
@app.get("/")        # GET 请求
@app.post("/ask")    # POST 请求

② 请求体 :客户端发来的 JSON,用 Pydantic BaseModel 定义结构

python 复制代码
class AskRequest(BaseModel):
    question: str          # 必填
    user_id: str = ""      # 可选,有默认值

③ 响应体:规范返回给客户端的 JSON 格式

python 复制代码
class AskResponse(BaseModel):
    answer: str

3.2 LangChain Tool 定义

Tool 是 Agent 的「手」,@tool 装饰器把普通函数变成 LangChain 可识别的工具。

⚠️ 关键:函数的 docstring 就是工具描述,LLM 靠它决定什么时候调用,必须写清楚。

python 复制代码
from langchain.tools import tool

@tool
def query_order(order_id: str) -> str:
    """查询订单状态。当用户询问订单进度时调用。输入格式:ORD-XXX"""
    orders = {
        "ORD-001": {"product": "iPhone 15", "status": "已发货", "price": 5999},
        "ORD-002": {"product": "AirPods Pro", "status": "待发货", "price": 1799},
    }
    order = orders.get(order_id)
    if not order:
        return f"找不到订单 {order_id}"
    return f"订单{order_id}:{order['product']},{order['status']},¥{order['price']}"

3.3 Agent 推理机制(ReAct)

Agent 的核心是 ReAct 循环(Re ason + Act),以「ORD-001 到哪里了」为例:

复制代码
Thought:      用户想查询订单状态,调用 query_order 工具
Action:       query_order("ORD-001")
Observation:  iPhone 15,已发货,¥5999
Final Answer: 您的订单 ORD-001(iPhone 15)已发货,金额 ¥5999。

循环最多执行 max_iterations 次,直到得出 Final Answer。


3.4 完整 Python 服务代码

python 复制代码
# main.py ------ Python AI 服务

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from langchain.tools import tool
from langchain.agents import AgentExecutor, create_react_agent
from langchain_openai import ChatOpenAI
from langchain import hub

app = FastAPI()

# ── 1. 定义 Tool ──────────────────────────────────────────
@tool
def query_order(order_id: str) -> str:
    """查询订单状态。当用户询问订单进度时调用。输入格式:ORD-XXX"""
    orders = {
        "ORD-001": "iPhone 15,已发货,¥5999",
        "ORD-002": "AirPods Pro,待发货,¥1799",
    }
    return orders.get(order_id, f"找不到订单 {order_id}")

# ── 2. 初始化 Agent(应用启动时创建一次)────────────────────
llm = ChatOpenAI(model="gpt-4o", temperature=0)
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, [query_order], prompt)
executor = AgentExecutor(
    agent=agent,
    tools=[query_order],
    max_iterations=5,
    handle_parsing_errors=True,
)

# ── 3. 请求体 / 响应体 ───────────────────────────────────────
class AskRequest(BaseModel):
    question: str

class AskResponse(BaseModel):
    answer: str

# ── 4. 对外接口 ──────────────────────────────────────────────
@app.post("/ask", response_model=AskResponse)
def ask(req: AskRequest):
    try:
        res = executor.invoke({"input": req.question})
        return AskResponse(answer=res["output"])
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

启动命令:

bash 复制代码
uvicorn main:app --host 0.0.0.0 --port 8000

--host 0.0.0.0 让 Java 服务可以通过网络访问到它,不只限于本机。


四、Java 侧:调用 Python 服务

4.1 RestTemplate(同步调用)

java 复制代码
@Service
public class AiService {

    private final RestTemplate restTemplate = new RestTemplate();
    private static final String AI_URL = "http://ai-service:8000/ask";

    public String queryWithAI(String question) {
        Map<String, String> body = new HashMap<>();
        body.put("question", question);

        ResponseEntity<Map> resp = restTemplate.postForEntity(AI_URL, body, Map.class);
        return (String) resp.getBody().get("answer");
    }
}

4.2 WebClient(推荐,非阻塞)

java 复制代码
private final WebClient webClient = WebClient.builder()
    .baseUrl("http://ai-service:8000")
    .build();

public Mono<String> queryWithAI(String question) {
    return webClient.post()
        .uri("/ask")
        .bodyValue(Map.of("question", question))
        .retrieve()
        .bodyToMono(Map.class)
        .map(b -> (String) b.get("answer"));
}

五、三个关键细节

① Agent 为什么要在函数外初始化?

python 复制代码
# ✅ 正确:应用启动时创建一次
executor = AgentExecutor(...)

@app.post("/ask")
def ask(req):
    executor.invoke(...)
python 复制代码
# ❌ 错误:每次请求都创建,每次都拉 prompt,性能极差
@app.post("/ask")
def ask(req):
    executor = AgentExecutor(...)
    executor.invoke(...)

② 为什么 temperature=0?

ReAct Agent 的输出必须严格遵守 Action / Action Input 格式,LLM 才能正确解析。temperature=0 让输出更稳定,避免格式混乱导致解析失败。

③ Java 侧一定要做错误处理

java 复制代码
try {
    String answer = aiService.queryWithAI(question);
    return ResponseEntity.ok(answer);
} catch (HttpServerErrorException e) {
    // AI 服务出错时降级,不能让用户看到 500
    return ResponseEntity.ok("AI 服务暂时不可用,请稍后重试");
}

六、总结

层次 语言 职责
展示层 前端 用户界面
业务层 Java Spring Boot 主业务 · 数据库 · 权限 · 路由
AI 接口层 Python FastAPI 接收 HTTP 请求,调用 Agent
AI 推理层 LangChain Agent ReAct 推理,调用 Tool
工具层 Python @tool 真正执行查询、计算等操作

核心思路:Java 和 Python 不直接调用彼此的代码,而是通过 HTTP 接口协作。FastAPI 是 Python 侧的 HTTP 出口,Java 只需要知道接口地址和 JSON 格式,不需要了解 Python 内部实现。


如果这篇文章对你有帮助,欢迎点赞 👍 收藏 ⭐ 关注 🔔
有问题欢迎在评论区交流!

相关推荐
Irissgwe4 小时前
LangChain之聊天模型核心能力
人工智能·langchain·大模型·llm
FrontAI4 小时前
深入浅出 LangGraph —— 第5章:条件边与动态路由
人工智能·langchain·ai agent·langgraph
草莓熊Lotso5 小时前
从 LLM 底层原理到 LangChain 全链路打通:大模型应用开发新征程
linux·运维·服务器·人工智能·langchain
花千树-0105 小时前
ReAct 思考-行动-观察循环的底层实现机制
langchain·agent·react·ai编程·ai agent·langgraph·mcp
老刘说AI5 小时前
Text2SQL到数据智能
人工智能·python·低代码·语言模型·langchain
hjxu20165 小时前
【LangGraph入门 1】第一个LangGraph-HelloWorld
langchain
BU摆烂会噶6 小时前
【工作流的常见模式】LangGraph 常用模式:路由模式(条件分支)
数据库·人工智能·python·langchain
deephub14 小时前
LangChain 还是 LangGraph?一个是编排一个是工具包
人工智能·langchain·大语言模型·langgraph