#Configuration filefrom mistralai import Mistral, UserMessage, SystemMessage
from dotenv import load_dotenv
import os
load_dotenv()# Mistral model
mistral_model ="mistral-large-latest"
client = Mistral(api_key=os.environ.get("MISTRAL_API_KEY"))# Agent IDs
web_search_id ="web_search_agent"
food_logger_id ="food_logger_agent"
user_assistant_id ="user_assistant_agent"
image_generator_id ="image_generator_agent"# Tool definitions
tools ={"log_meal":{"name":"log_meal","description":"Log a user's meal with calorie count","input_schema":{"type":"object","properties":{"username":{"type":"string"},"meal":{"type":"string"},"calories":{"type":"number"},"timestamp":{"type":"string","format":"date-time"}},"required":["username","meal","calories","timestamp"]}},"web_search":{"name":"web_search","description":"Search the web for information about a meal","input_schema":{"type":"object","properties":{"query":{"type":"string"}},"required":["query"]}}}技术支持
我们先导入 dotenv,将本地 .env 文件中的敏感变量(如 Mistral API 密钥)加载至 Python 环境,确保凭据安全且不出现在源代码中。随后选择默认模型 mistral-large-latest,并使用 API 密钥实例化 Mistral 客户端。
接着,我们用 JSON Schema 格式定义内部工具规范(schema),它们有助于:
模拟 MCP 工具行为(尽管并非通过 McpTool 库注册)
描述预期输入,如“username”“meal”“calories”
帮助在各模块间保持一致的输入格式
现在,加入用于创建聊天补全、注册工具与代理、以及初始化客户端的辅助方法。
defcreate_chat_completion(messages, model=mistral_model):# Convert messages to new format
formatted_messages =[]for msg in messages:if msg["role"]=="system":
formatted_messages.append(SystemMessage(content=msg["content"]))else:
formatted_messages.append(UserMessage(content=msg["content"]))return client.chat.complete(
model=model,
messages=formatted_messages
)# Register toolsdefregister_tools():for tool_name, tool in tools.items():print(f"Registered tool: {tool_name}")# Register agentsdefregister_agents():print("Registered User Assistant Agent")print("Registered Web Search Agent")print("Registered Food Logger Agent")# Initialize everythingdefinitialize():
register_tools()
register_agents()
check_client()# Check if the client is initializeddefis_client_initialized():return client isnotNoneand os.environ.get("MISTRAL_API_KEY")isnotNonedefcheck_client():ifnot is_client_initialized():print("Mistral client is not initialized. Please check your API key.")returnFalseprint("Mistral client is initialized.")returnTrue
initialize()技术支持
# Web search toolimport re
from mistralai import Mistral, UserMessage, SystemMessage
from tools.configs import client
defsearch_calories(meal_desc):try:# Web search agent
websearch_agent = client.beta.agents.create(
model="mistral-medium-latest",
description="Agent able to search for nutritional information and calorie content of meals",
name="Nutrition Search Agent",
instructions="You have the ability to perform web searches with web_search to find accurate calorie information. Return ONLY a single number representing total calories.",
tools=[{"type":"web_search"}],
completion_args={"temperature":0.3,"top_p":0.95,})
response = client.beta.conversations.start(
agent_id=websearch_agent.id,
inputs=f"What are the total calories in {meal_desc}?")print("Raw response:", response)# Extract the number from the responseifhasattr(response,'outputs'):for output in response.outputs:ifhasattr(output,'content'):ifisinstance(output.content,str):
numbers = re.findall(r'\d+', output.content)if numbers:returnint(numbers[0])elifisinstance(output.content,list):for chunk in output.content:ifhasattr(chunk,'text'):
numbers = re.findall(r'\d+', chunk.text)if numbers:returnint(numbers[0])print("No calorie information found in web search response")return0except Exception as e:print(f"Error during web search: {str(e)}")return0技术支持
在上述代码中,search_calories() 使用 Mistral Agents API 的内置网页搜索连接器,使代理能够执行实时网页查询(如热量估算),克服静态模型训练的局限。其工作流程如下:
#Meal suggestionsfrom datetime import datetime
from tools.configs import client, create_chat_completion
from mistralai import SystemMessage, UserMessage
import re
defestimate_calories(meal_desc):try:# Messages for calorie estimation
messages =[
SystemMessage(content="You are a nutrition expert. Estimate calories in meals. Return ONLY a single number representing total calories."),
UserMessage(content=f"Estimate calories in: {meal_desc}")]
response = client.chat.complete(
model="mistral-small-latest",# Using smaller model to avoid rate limits
messages=messages,
temperature=0.1,
max_tokens=50)
content = response.choices[0].message.content
numbers = re.findall(r'\d+', content)if numbers:returnint(numbers[0])return0except Exception as e:print(f"Error during calorie estimation: {str(e)}")return0deflog_meal(username, meal, calories):try:# Messages for meal logging
messages =[
SystemMessage(content="You are a meal logging assistant. Log the user's meal with calories."),
UserMessage(content=f"Log this meal: {meal} with {calories} calories for user {username} at {datetime.utcnow().isoformat()}")]
response = client.chat.complete(
model="mistral-small-latest",# Using smaller model to avoid rate limits
messages=messages,
temperature=0.1,
max_tokens=100)return response.choices[0].message.content.strip()except Exception as e:print(f"Error during meal logging: {str(e)}")returnf"Logged {meal} ({calories} calories) for {username}"defsuggest_next_meal(calories, dietary_preference):try:# Messages for meal suggestion
messages =[
SystemMessage(content="You are a nutrition expert. Suggest healthy meals based on calorie intake and dietary preferences."),
UserMessage(content=f"Suggest a {dietary_preference} meal that would be a good next meal after consuming {calories} calories. Make it specific and appetizing.")]
response = client.chat.complete(
model="mistral-small-latest",
messages=messages,
temperature=0.7)return response.choices[0].message.content.strip()except Exception as e:print(f"Error during meal suggestion: {str(e)}")return"Unable to suggest next meal at this time."技术支持
#Image generation toolimport os
import re
from mistralai import Mistral, UserMessage, SystemMessage
from mistralai.models import ToolFileChunk
from tools.configs import client
from datetime import datetime
defgenerate_food_image(meal_description):try:print(f"Starting image generation for: {meal_description}")# Image generation agent
image_agent = client.beta.agents.create(
model="mistral-medium-latest",
name="Food Image Generation Agent",
description="Agent used to generate food images.",
instructions="Use the image generation tool to create appetizing food images. Generate realistic and appetizing images of meals.",
tools=[{"type":"image_generation"}],
completion_args={"temperature":0.3,"top_p":0.95,})print("Created image generation agent")
response = client.beta.conversations.start(
agent_id=image_agent.id,
inputs=f"Generate an appetizing image of: {meal_description}",
stream=False)print("Got response from image generation")
os.makedirs("generated_images", exist_ok=True)# Process the response and save images
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
image_paths =[]ifhasattr(response,'outputs'):print(f"Processing {len(response.outputs)} outputs")for output in response.outputs:ifhasattr(output,'content'):print(f"Processing content of type: {type(output.content)}")ifisinstance(output.content,list):for i, chunk inenumerate(output.content):print(f"Processing chunk {i} of type: {type(chunk)}")ifisinstance(chunk, ToolFileChunk):print(f"Found ToolFileChunk with file_id: {chunk.file_id}")try:# Download the image
file_bytes = client.files.download(file_id=chunk.file_id).read()# Save the image
image_path =f"generated_images/meal_{timestamp}_{i}.png"withopen(image_path,"wb")asfile:file.write(file_bytes)
image_paths.append(image_path)print(f"Successfully saved image to: {image_path}")except Exception as e:print(f"Error processing image chunk: {str(e)}")else:print(f"Content is not a list: {output.content}")if image_paths:print(f"Successfully generated {len(image_paths)} images")return image_paths[0]# Return the first image pathelse:print("No images were generated")return "No image was generated”
except Exception as e:print(f"Error during image generation: {str(e)}")ifhasattr(e,'__dict__'):print(f"Error details: {e.__dict__}")returnf"Error generating image: {str(e)}"技术支持
Mistral 发布了Agents API,这是一个用于构建智能、自治 AI 代理的框架,能够执行复杂的多步骤任务。它通过集成工具使用、持久化记忆和编排能力,扩展了传统语言模型的能力边界。
在本教程中,我将带您使用 Mistral Agents API 构建能够以下列方式工作的智能助手:
您将学习 Mistral 代理框架背后的核心概念,探索内置连接器与编排功能,并构建一个可运行的多代理助手。
我们通过每周五的免费通讯 The Median 为读者带来最新的 AI 动态,梳理一周重点新闻。订阅它,每周用几分钟保持敏锐:
什么是 Mistral 的 Agents API?
Mistral Agents API 是一个让大型语言模型执行多步骤动作、使用外部工具并在交互间保持记忆的框架。不同于简单的聊天 API,它使 AI 代理能够:
目前仅支持
mistral-medium-latest(亦称mistral-medium-2505)和mistral-large-latest,后续可能会支持新模型。核心概念解析
在动手之前,先澄清一下 Agents API 的核心概念。
来源:Mistral AI
代理(Agents)
代理在 Mistral 中是由模型驱动的人设,叠加了预设指令、工具访问和会话状态:
web_search或image_generation等工具让代理执行现实世界的动作。这种抽象将基础的聊天模型转化为能参与复杂流程的多功能助手。
会话
用户与代理之间的每次交互都属于一个会话,会话会存储:
这些持久会话让代理能够执行多轮任务,如规划、修订或从早前输入中学习。
连接器与自定义工具支持
Mistral 通过以下方式简化工具集成:
web_search、code_execution、image_generation、document_library,开箱即用。可以将连接器视为内置应用,而将MCP 工具视为您编写的插件。两者结合,让代理既能使用内置能力,也能访问开发者定义的端点以实现个性化逻辑。
来源:Mistral AI
条目与交接
Mistral 将会话中的所有动作记录为结构化的“条目(Entries)”,从而实现对消息交换与工具执行的可追溯与可控。
代理交接(handoffs)指一个代理将控制权委派给另一个代理,用以模块化工作流。
来源:Mistral AI
这种编排架构便于组合与管理复杂的多代理系统,同时不丢失透明度与控制力。
设置 Mistral API
让我们开始配置 Mistral。我们将先为项目创建一个 API 密钥。请按以下步骤操作:
现在,您已经拥有可与 Mistral 模型交互并构建智能代理的有效 API 密钥。
项目概览:用 Mistral Agents 打造营养教练
在本项目中,我们将使用 Mistral 的 Agents API 构建一个由 AI 驱动的营养教练。用户流程如下:
为提供一个用户可与营养师互动的网页界面,我们将使用 Gradio。
步骤 1:先决条件
开始之前,请确保已安装以下内容:
requirements.txt中列出的所有必要包另外,创建包含您 Mistral API 密钥的
.env文件。此做法能保证 API 密钥安全,并使不同团队成员或部署环境在无需修改源代码的情况下使用各自的凭据。注意:建议在 conda、venv 或 uv 等隔离环境中安装这些依赖,以避免与其他 Python 项目产生版本冲突。
以下是设置不同环境的命令:
使用 venv
使用 Conda
使用 uv
如果您尚未安装 uv:
然后:
要安装上述所有依赖,请在终端运行以下命令:
注意: 如果在使用任意代理或连接器时遇到与
mistralai相关的错误,请运行以下命令:这会将
mistralai库升级到最新版本。步骤 2:设置 Mistral 工具
本节将为与 Mistral Agents API 的交互准备工具与客户端设置。我们将定义工具模式、初始化客户端并验证认证。这些工具稍后将在配置具体代理(如热量估算或图像生成)时被引用。
步骤 2.1:工具注册与初始化
首先,我们定义工具模式、模型选择与客户端实例化。然后,构建
create_chat_completion()与check_client()等辅助方法。以下代码放在 tools 文件夹内的configs.py文件中:我们先导入
dotenv,将本地.env文件中的敏感变量(如 Mistral API 密钥)加载至 Python 环境,确保凭据安全且不出现在源代码中。随后选择默认模型mistral-large-latest,并使用 API 密钥实例化 Mistral 客户端。接着,我们用 JSON Schema 格式定义内部工具规范(schema),它们有助于:
现在,加入用于创建聊天补全、注册工具与代理、以及初始化客户端的辅助方法。
create_chat_completion()封装了对 Mistral 客户端 SDK 的client.chat.complete()调用,用于向模型发送消息。UserMessage与SystemMessage对象。register_tools()与register_agents()在本地开发时用于日志记录,标明系统中哪些工具与代理处于激活状态。与此同时,check_client()与is_client_initialized()确保 Mistral 客户端正确实例化且 API 密钥可用,帮助在代理执行时避免运行时错误。步骤 2.2:使用网页搜索代理估算热量
完成基础工具与客户端初始化后,让我们构建一个专用的营养网页搜索代理。该代理使用 Mistral 的
web_search连接器,实时查询餐食的热量估算。以下代码位于 tools 文件夹内的web_search.py文件。在上述代码中,
search_calories()使用 Mistral Agents API 的内置网页搜索连接器,使代理能够执行实时网页查询(如热量估算),克服静态模型训练的局限。其工作流程如下:mistral-medium-latest模型创建“Nutrition Search Agent”,配备web_search工具,并指示其仅返回 热量数值。没有
web_search时,大多数模型无法回答近期事件或训练数据中未见的特定餐食问题;有了它,我们可以实时查询营养信息、餐厅菜单或新闻文章。网页搜索有两个变体:
web_search:轻量、通用的搜索引擎连接器(本项目使用)。web_search_premium:增强版,包含新闻源验证与更广的上下文收集。接下来,我们将探索仅基于提示词逻辑的回退热量估算。
步骤 2.3:估算、记录与餐食建议
本节实现三个关键函数,作为回退或增强功能。以下代码位于 tools 文件夹中的
next.py文件。该脚本定义了支撑营养工作流的核心后端函数(从热量估算到后续餐食规划)。这些函数使用轻量级 Mistral 模型与标准聊天补全,当网页搜索不可用或为避免速率限制错误时,无需创建任何代理或使用连接器。各函数作用如下:
estimate_calories():当web_search连接器失败或不可用时作为回退。temperature=0.1以获得确定性输出。也就是说,对于热量估算与记录等偏重稳定与事实的任务,模型在相同输入下会给出一致、可复现的响应,而非追求创意多样性。log_meal():记录用户的餐食,并包含时间戳。suggest_next_meal():根据先前摄入热量与饮食偏好推荐下一餐。temperature=0.7调用模型,鼓励更有创意的建议。这些函数结合在一起,无需外部 API 即可完成热量估算、记录确认与健康推荐。下一步,我们将使用 Mistral 的图像生成连接器可视化食物建议。
步骤 2.4:使用图像生成连接器生成可视化
现在为助手增加视觉层。本步骤使用 Mistral 的内置
image_generation连接器,生成下一份推荐餐食的诱人图像。generate_food_image()函数使用 Mistral 的内置image_generation工具生成餐食的可视化。各部分作用如下:meal_description的图像。files.download()方法下载,并以时间戳文件名本地保存到generated_images/目录。该工具为营养教练增添了视觉层,使餐食建议更具吸引力与真实感。
步骤 3:完整营养师流水线
至此,我们已构建了各个工具与代理能力,现在将它们整合为一个流水线。此步骤把以下环节粘合为一个可工作的助手:
以上各步通过按正确顺序调用前面文件中的函数来处理。该流水线代码位于 tools 文件夹外的
agent.py文件。上述代码通过将多个代理工具串联为单个流水线,来协调完整的营养助手工作流。让我们更细致地拆解:
run_nutritionist_pipeline()接收username、meal_desc与dietary_preference作为输入,驱动推荐流程。search_calories()获取餐食热量;若失败,则回退到使用轻量模型的estimate_calories()进行估算。log_meal()记录热量值,并附上时间戳与用户。suggest_next_meal()使用基于提示的推理推荐后续餐食。generate_food_image()使用 Mistral 的图像生成连接器生成该推荐菜肴的可视化。time.sleep(1)在 API 调用间加入短暂停顿,以避免触发速率限制。该流水线将搜索、估算、记录、建议与可视化等多种能力结合为一次由 AI 驱动的营养体验。
步骤 4:用 Gradio 创建友好的用户界面
后端逻辑已完成,现在用 Gradio 打包为一个简洁、友好的前端。
在上述代码片段中,我们为与营养师代理流水线交互搭建了一个完整的 Gradio 界面。其工作方式如下:
agent.py导入驱动核心代理逻辑的run_nutritionist_pipeline()函数。process_meal()函数作为流水线的包装器,接收username、meal_desc与dietary_preference作为用户输入,触发此前定义的多代理逻辑。process_meal()内,会解析结果字典以提取餐食摘要、热量、下一餐建议与所用连接器。gr.Blocks()构建,布局分为两侧:一侧为输入字段与“Process Meal”按钮,另一侧为结果输出区域与下一餐图像预览。submit_btn.click()回调将前端按钮与process_meal()连接,传入输入并接收格式化的 Markdown 与图像输出。demo.launch()启动应用。您可设置debug = True以启用调试模式。要运行此演示,请在项目根目录打开终端并输入:
项目的整体结构如下:
您可以在这个 GitHub 仓库找到该项目的完整代码。
结语
在本教程中,我们使用 Mistral Agents API 构建了一个功能完整的 AI 营养教练。期间我们探索了:
web_search与image_generation等连接器的作用该项目展示了代理式工作流的力量:LLM 不再只是被动响应者,而是能推理、采取行动并协调任务的交互式主体。从记录餐食到提出健康替代方案,甚至生成图像,我们展示了如何将简单工具组合成强大的 AI 体验。
想进一步学习 AI 代理的使用,推荐以下动手教程: