当前位置: 首页 > news >正文

大模型应用开发实战(18)——构建智能体(Agent)框架客户端

🤵‍♂️ 个人主页:小李同学_LSH的主页

✍🏻 作者简介:LLM学习者
🐋 希望大家多多支持,我们一起进步!😄
如果文章对你有帮助的话,
欢迎评论 💬点赞👍🏻 收藏 📂加关注+

目录

一、先把目标说清楚:要做什么

二、为什么 Agent 一旦上服务端,问题就变了

三、项目结构

四、先把基础 LLM 调用封装好,这是服务端的第一块地基

config.py

.env

core/llm.py

core/message.py

五、代码实践


很多人做到 Agent 这一步,前面的路都还算顺:
封装一个 LLM 类,补几个工具,写个 ReAct 循环,命令行里一跑,智能体就“活了”。

但问题也恰恰从这里开始。

一旦你不再满足于本地 Demo,而是想把它真正变成一个可访问、可复用、可扩展的服务端系统,原来那套写法就会迅速暴露问题:

  • 对话历史只存在进程内,服务一重启就没了
  • 多用户同时访问时,上下文容易串线
  • 工具调用、异常处理、接口返回格式都不统一
  • 调试靠print,排障靠猜
  • 本地能跑,一上 HTTP 服务就开始混乱

所以,从 Agent 到 Agent Server,并不是“把脚本套个 FastAPI”这么简单。
真正要做的,是把原来“面向单次运行”的代码,重构成“面向请求生命周期”的服务架构。

Hello-Agents 在第四章里对基础 LLM 调用做了一个很关键的动作:先把模型 ID、API Key、Base URL 统一放进环境变量,再封装一个专门的HelloAgentsLLM客户端类,把与模型服务交互的细节和上层 Agent 逻辑分开。这个思路非常适合作为服务端化的起点。

这篇文章就不空谈概念,直接从工程视角出发,带你把一个最小可用的 Agent 框架,继续往前推成一个服务端版本

一、先把目标说清楚:要做什么

我们最终要得到的,不再是这样一个命令行 Agent:

result = agent.run("请帮我总结这段文本") print(result)

而是一个可以通过 HTTP 调用的服务:

POST /chat POST /chat/stream GET /sessions/{session_id} POST /sessions/{session_id}/clear GET /health

也就是说,我们希望把原本的单机 Agent,升级成下面这类结构:

不是“多了 FastAPI”,而是多了三件关键事情:

  1. 请求生命周期
  2. 会话状态管理
  3. 服务化接口抽象

FastAPI 本身就是一个基于 Python 类型提示的高性能 API 框架,支持自动生成 OpenAPI 文档;它的中间件机制可以在每个请求进入路由前和响应返回前统一处理逻辑,这一点非常适合做 Agent 服务端。

二、为什么 Agent 一旦上服务端,问题就变了

在本地脚本里,Agent 的核心问题通常是:

  • 模型怎么调
  • Prompt 怎么写
  • 工具怎么接
  • 循环什么时候停

但服务端场景下,问题会变成:

  • 一个用户的多轮消息放哪
  • 多个用户同时访问怎么隔离
  • 接口怎么定义才不乱
  • 流式输出怎么返回
  • 模型异常、工具异常、参数异常怎么统一处理
  • 服务启动时怎么加载配置
  • 健康检查、日志、追踪怎么补

这两个阶段的重心完全不同。

为了更直观一点,可以先看这个对比表。

维度本地脚本版 Agent服务端版 Agent
调用方式函数调用 / CLIHTTP API / SSE
状态存储进程内变量Session / Redis / DB
用户规模单用户多用户并发
异常处理print + try/except统一异常响应
输出方式一次性打印JSON / 流式返回
可观测性手动看日志请求日志 / trace_id / metrics
扩展方式改脚本分层重构、服务抽象

三、项目结构

agent_server/ ├── app/ │ ├── main.py │ ├── config.py │ ├── schemas.py │ ├── middleware.py │ ├── exceptions.py │ │ │ ├── core/ │ │ ├── llm.py │ │ ├── message.py │ │ └── agent.py │ │ │ ├── services/ │ │ ├── agent_service.py │ │ └── session_service.py │ │ │ ├── tools/ │ │ ├── base.py │ │ ├── registry.py │ │ └── calculator.py │ │ │ └── api/ │ └── chat.py │ ├── .env ├── requirements.txt └── run.py

这个结构对应的是很典型的三层思路:

  • core:模型调用、消息对象、Agent 主循环
  • services:服务编排、会话管理
  • api:HTTP 路由层

四、先把基础 LLM 调用封装好,这是服务端的第一块地基

先用.env管模型 ID、API Key、Base URL,再封装一个统一的 LLM 客户端,让业务逻辑不直接碰底层 SDK。

这个动作看起来普通,但服务端一旦起来,它的价值会立刻放大:

  • 不同环境切模型更方便
  • 测试环境、生产环境可切换
  • 更容易替换成 OpenAI 兼容服务
  • 不会把 SDK 调用散落在各个路由里

config.py

from pydantic_settings import BaseSettings class Settings(BaseSettings): app_name: str = "Agent Server" debug: bool = True llm_model: str = "gpt-4.1-mini" llm_api_key: str llm_base_url: str llm_timeout: int = 60 llm_temperature: float = 0.2 max_steps: int = 5 max_history_length: int = 20 class Config: env_file = ".env" settings = Settings()

.env

LLM_API_KEY=your_api_key LLM_BASE_URL=https://your-openai-compatible-endpoint/v1 LLM_MODEL=gpt-4.1-mini

core/llm.py

from openai import OpenAI from app.config import settings class HelloAgentsLLM: def __init__(self): self.model = settings.llm_model self.client = OpenAI( api_key=settings.llm_api_key, base_url=settings.llm_base_url, timeout=settings.llm_timeout, ) def chat(self, messages, tools=None): payload = { "model": self.model, "messages": [m.to_dict() for m in messages], "temperature": settings.llm_temperature, } if tools: payload["tools"] = tools payload["tool_choice"] = "auto" return self.client.chat.completions.create(**payload)

core/message.py

from dataclasses import dataclass, field from typing import Optional, Dict, Any from datetime import datetime @dataclass class Message: role: str content: str name: Optional[str] = None metadata: Dict[str, Any] = field(default_factory=dict) timestamp: datetime = field(default_factory=datetime.utcnow) def to_dict(self): data = { "role": self.role, "content": self.content, } if self.name: data["name"] = self.name return data

五、代码实践

import os from openai import OpenAI from dotenv import load_dotenv from typing import List, Dict # 加载 .env 文件中的环境变量 load_dotenv() class HelloAgentsLLM: """ 为本书 "Hello Agents" 定制的LLM客户端。 它用于调用任何兼容OpenAI接口的服务,并默认使用流式响应。 """ def __init__(self, model: str = None, apiKey: str = None, baseUrl: str = None, timeout: int = None): """ 初始化客户端。优先使用传入参数,如果未提供,则从环境变量加载。 """ self.model = model or os.getenv("LLM_MODEL_ID") apiKey = apiKey or os.getenv("LLM_API_KEY") baseUrl = baseUrl or os.getenv("LLM_BASE_URL") timeout = timeout or int(os.getenv("LLM_TIMEOUT", 60)) if not all([self.model, apiKey, baseUrl]): raise ValueError("模型ID、API密钥和服务地址必须被提供或在.env文件中定义。") self.client = OpenAI(api_key=apiKey, base_url=baseUrl, timeout=timeout) def think(self, messages: List[Dict[str, str]], temperature: float = 0) -> str: """ 调用大语言模型进行思考,并返回其响应。 """ print(f"🧠 正在调用 {self.model} 模型...") try: response = self.client.chat.completions.create( model=self.model, messages=messages, temperature=temperature, stream=True, ) # 处理流式响应 print("✅ 大语言模型响应成功:") collected_content = [] for chunk in response: content = chunk.choices[0].delta.content or "" print(content, end="", flush=True) collected_content.append(content) print() # 在流式输出结束后换行 return "".join(collected_content) except Exception as e: print(f"❌ 调用LLM API时发生错误: {e}") return None # --- 客户端使用示例 --- if __name__ == '__main__': try: llmClient = HelloAgentsLLM() exampleMessages = [ {"role": "system", "content": "You are a helpful assistant that writes Python code."}, {"role": "user", "content": "写一个快速排序算法"} ] print("--- 调用LLM ---") responseText = llmClient.think(exampleMessages) if responseText: print("\n\n--- 完整模型响应 ---") print(responseText) except ValueError as e: print(e) >>> --- 调用LLM --- 🧠 正在调用 xxxxxx 模型... ✅ 大语言模型响应成功: 快速排序是一种非常高效的排序算法...
http://www.jsqmd.com/news/661599/

相关文章:

  • 为什么92%的AGI医疗POC项目死在第6个月?——来自梅奥诊所、华西医院联合复盘的11个断点修复模型
  • Python的函数使用详解
  • OpenMemories-Tweak:索尼相机隐藏功能解锁完整指南 - 终极破解工具详解
  • 别再乱用PCA了!盘点主成分分析在业务数据分析中的3个常见误区和避坑指南
  • 抖音批量下载神器:三分钟掌握高效素材获取技巧
  • 别再手动一张张导PDF了!用C#和.NET搞定AutoCAD批量打印的完整流程(附1:1比例设置代码)
  • VS Code + Keil Assistant插件实战:从创建STM32工程到编译下载的完整避坑指南
  • AI大模型知识图谱问答系统
  • VCE客户忠诚度如何,生产工艺先进吗,市场前景预测怎样解读 - 工业设备
  • 如何快速掌握PCILeech:面向初学者的完整内存取证工具指南
  • GNN实战:用PyTorch Geometric搞定社交网络节点分类(附Cora数据集完整代码)
  • Mac Mouse Fix深度解析:如何让普通鼠标在macOS上超越苹果触控板
  • 探讨有技术研发实力的泄氮阀品牌,哪家值得选 - 工业品网
  • 市面上质量好的钢结构防火涂料产品推荐榜 - 品牌排行榜
  • GLM-4.7-Flash步骤详解:supervisorctl管理glm_vllm与glm_ui服务全命令
  • 别再手动配网了!用安信可PB-02模组+PHY Mesh App,5分钟搞定蓝牙Mesh智能灯群控
  • 新160个crackMe算法分析-41-crackme.2.exe
  • 终极Hubot-Slack适配器指南:5步快速构建智能Slack机器人
  • 揭秘PPTAgent:AI如何用反思式智能重构演示文稿制作范式
  • 为什么TinyEditor是学习前端开发的完美入门工具?
  • 2026年北京电子助视器/视障电脑/盲人电脑公司推荐:北京视源科技有限公司,为盲人和低视力人群提供全方位解决方案 - 品牌推荐官
  • 终极图像转3D神器:用ImageToSTL轻松实现照片立体化
  • 告别杂乱!Win10文件资源管理器‘快速访问’与‘最近浏览’的保姆级清理与优化指南
  • Dubbo-samples高级特性:服务分组、版本控制与负载均衡
  • Cursor Free VIP:如何免费解锁Cursor AI的Pro功能?
  • 梳理安装快的列车接近预警系统选购要点和推荐品牌 - 工业推荐榜
  • OpenTelemetry Rust与Prometheus集成:实现高效指标监控的10个技巧
  • 大模型训练实战(5)——Ollama 为什么突然成了本地大模型部署的“万能入口”?一篇讲透原理、实战和边界
  • Everything Claude Code:Anthropic Hackathon 冠军的 AI 代理优化之道
  • 2026年4月安徽护坡网厂家**评测:五大服务商深度对比与选购指南 - 2026年企业推荐榜