1. 项目概述:一个开箱即用的AI智能体API
如果你正在寻找一个能替代OpenAI官方Assistants API,但又希望拥有完全自主控制权、能连接更多模型、并且可以本地部署的开源方案,那么你找对地方了。今天要聊的这个项目,正是为了解决这个痛点而生的。它是一个与OpenAI官方接口完全兼容的智能体API,这意味着你可以无缝使用官方的openaiPython库,甚至是你现有的、基于Assistants API构建的应用代码,只需修改一个base_url,就能立刻接入你自己的服务。这听起来可能有点“换汤不换药”,但关键在于,这碗“汤”的配方完全由你掌控。
这个项目的核心价值在于“开放”与“集成”。它不是一个从零开始重造轮子的AI框架,而是一个精巧的“适配器”和“增强器”。它默认集成了像One API这样的多模型管理工具,让你可以轻松地将后端从单一的GPT模型,切换到包括Claude、Gemini、国内大模型甚至本地私有模型在内的数十种选择。同时,它还预置了对RAG(检索增强生成)引擎R2R的支持,为智能体提供了强大的知识库检索能力。更吸引人的是,它允许你自定义工具(Tools),让AI助手不仅能聊天,还能执行代码、查询数据库、调用外部API,真正成为一个能与你业务系统交互的智能体。对于开发者、中小团队或是任何对数据隐私、成本控制有要求的场景来说,这样一个能自托管、可扩展的AI助手基础设施,无疑提供了极大的灵活性和安全感。
2. 核心架构与设计思路拆解
要理解这个项目为何这样设计,我们需要先拆解一下一个完整的“AI智能体服务”需要哪些核心组件。OpenAI的Assistants API本身提供了一个相对完整的范式:它定义了助手(Assistant)、线程(Thread)、消息(Message)和运行(Run)这几个核心对象,并提供了文件上传、代码解释器(Code Interpreter)和函数调用(Function Calling)等工具。这个开源项目的目标,就是在本地复现并增强这套范式。
2.1 兼容性优先的设计哲学
项目的第一要务是保持与OpenAI官方API的高度兼容。这不是简单的接口模仿,而是为了最大化生态价值。开发者积累的代码、熟悉的SDK、第三方库(如LangChain)都能平滑迁移。为了实现这一点,项目在接口层面严格遵循OpenAI的OpenAPI规范。这意味着,无论是请求的URL路径(如/v1/assistants)、请求/响应的JSON结构,还是错误码,都力求与官方一致。在代码中,你会看到大量对标官方参数名的模型定义和路由处理。这种设计极大地降低了用户的接入成本和心理门槛,你几乎不需要学习新的概念。
2.2 模块化与可插拔的后端服务
兼容性解决了“前端”问题,而项目的强大之处在于其“后端”的模块化设计。它没有将逻辑与某个特定的LLM(大语言模型)或向量数据库紧耦合,而是通过抽象层和配置,将核心能力拆分为可替换的模块:
- LLM提供商抽象:项目本身不直接调用GPT的API,而是通过集成One API这个中间层。One API本身是一个支持众多模型(如GPT、Claude、智谱、通义千问等)的统一网关。在本项目中,你只需要在配置中填入One API的地址和密钥,你的智能体就能通过它来调用背后配置的任何模型。这种设计让模型选型变得异常灵活,你可以根据成本、性能、场景随时切换,甚至同时为不同助手分配不同的模型。
- 文件与RAG服务抽象:智能体需要处理用户上传的文件并进行检索。项目提供了默认的简单文件存储和检索实现,但更推荐使用专业的R2R引擎。通过配置,你可以将文件服务模块指向R2R。R2R会负责文件的解析、分块、向量化、存储和检索,提供生产级的RAG能力。这种“专业的事交给专业的工具”的思路,保证了核心项目的轻量和专注。
- 工具(Tools)系统:这是智能体能力的扩展点。项目不仅支持OpenAI定义的
function工具,还内置了web_search(联网搜索)工具,并预留了扩展接口。工具的实现被设计为独立的服务,通过标准的OpenAPI Schema进行描述和注册。这意味着你可以将公司内部的任何一个HTTP API(如查询订单、发送邮件、触发工作流)封装成工具,让AI助手去调用,从而实现业务流程的自动化。
2.3 数据流转与状态管理
一个智能体对话通常涉及多轮交互和复杂的状态维护。项目需要管理助手配置、会话线程、消息历史、文件关联以及每次“运行”的状态(如等待工具调用结果)。其内部数据流转大致如下:
- 用户创建或选择一个助手(包含指令、模型、工具列表等元数据)。
- 用户创建一个线程,并在此线程中发送消息。
- 系统在后台创建一个“运行”任务。这个任务会从线程中获取消息历史,结合助手的指令和工具定义,通过配置的LLM提供商(如One API)发起调用。
- LLM返回的响应可能包含需要调用工具的指令。项目会解析这个指令,调用相应的工具服务,并将工具执行结果返回给LLM进行下一步推理,直到LLM返回最终面向用户的文本内容。
- 整个“运行”过程中的所有中间状态(用户消息、AI回复、工具调用及结果)都会被持久化到数据库(项目使用SQLite或PostgreSQL),从而支持异步操作和状态恢复。
这种设计确保了服务的健壮性和可追溯性,也是它能支持复杂、长周期对话任务的基础。
3. 从零开始的完整部署与配置实操
理论讲得再多,不如亲手搭起来看看。下面我将以一个典型的本地开发环境为例,带你一步步完成Open Assistant API的部署和基础配置。我假设你已经具备了基本的Docker和命令行操作知识。
3.1 环境准备与项目获取
首先,确保你的机器上已经安装了Docker和Docker Compose。这是最快捷的部署方式。你可以通过以下命令检查:
docker --version docker-compose --version如果未安装,请根据你的操作系统(Windows/macOS/Linux)去Docker官网下载安装Docker Desktop,它通常包含了Docker Compose。
接下来,获取项目代码。打开终端,使用git克隆仓库:
git clone https://github.com/MLT-OSS/open-assistant-api.git cd open-assistant-api现在,你已经进入了项目的根目录。这里最重要的配置文件就是docker-compose.yml,它定义了这个服务所需的所有容器(API服务、数据库等)及其依赖关系。
3.2 关键配置详解与填写
在启动之前,我们必须先配置好关键参数。用你喜欢的文本编辑器打开docker-compose.yml文件,找到environment部分。这里有几个必须配置的项:
OPENAI_API_KEY:这是项目的核心配置。注意,这里并非一定要填OpenAI官方的Key。正如前文所述,项目通过One API调用模型,所以这里应该填写你的One API服务的API Key。One API的管理员可以创建一个密钥,并为其分配访问特定模型的权限。如果你还没有搭建One API,需要先参照其文档进行部署。假设你的One API地址是http://localhost:3000,对应的密钥是sk-xxx-oneapi,那么这里就填它。BING_SUBSCRIPTION_KEY(可选):如果你想使用内置的联网搜索工具,需要去微软Azure门户申请Bing搜索服务的订阅密钥并填入。如果暂时不需要搜索功能,可以留空或注释掉。RAG增强配置(强烈推荐):为了获得更好的文件处理和检索能力,建议配置R2R。你需要先部署好R2R服务(参考R2R官方文档),然后修改以下配置:
environment: # 将默认的文件服务切换为R2R文件服务 - FILE_SERVICE_MODULE=app.services.file.impl.r2r_file.R2RFileService # 填写你的R2R服务地址和认证信息 - R2R_BASE_URL=http://<你的R2R服务器IP>:端口 - R2R_USERNAME=<R2R用户名> - R2R_PASSWORD=<R2R密码>如果不配置R2R,项目会使用一个简单的内置文件服务,功能有限,不适合生产环境。
认证与多租户配置:如果你希望提供SaaS服务或进行简单的用户隔离,可以开启认证。
environment: - APP_AUTH_ENABLE=true - APP_AUTH_ADMIN_TOKEN=your-super-secret-admin-token-here开启后,所有API请求都需要在Header中携带
Authorization: Bearer <token>。初始的管理员Token就是上面设置的APP_AUTH_ADMIN_TOKEN,用于管理其他用户Token。
一个完整的配置示例如下:
environment: - OPENAI_API_KEY=sk-xxx-oneapi - BING_SUBSCRIPTION_KEY= - FILE_SERVICE_MODULE=app.services.file.impl.r2r_file.R2RFileService - R2R_BASE_URL=http://192.168.1.100:8000 - R2R_USERNAME=admin - R2R_PASSWORD=password123 - APP_AUTH_ENABLE=true - APP_AUTH_ADMIN_TOKEN=my-admin-token-2024注意:所有敏感信息(如密钥、密码)直接写在
docker-compose.yml中并不安全,这只是为了演示。在生产环境中,你应该使用Docker的secrets功能或通过.env文件来管理环境变量,并确保.env文件被加入.gitignore。
3.3 启动服务与验证
配置完成后,在项目根目录下执行一条命令即可启动所有服务:
docker-compose up -d-d参数表示在后台运行。Docker会拉取必要的镜像(如Python基础镜像、PostgreSQL等),并按照定义启动容器。
启动完成后,你可以通过以下命令查看容器运行状态:
docker-compose ps如果一切正常,你应该能看到名为open-assistant-api的容器状态为Up。
现在,打开浏览器,访问以下两个地址进行验证:
- API基础地址:
http://127.0.0.1:8086/api/v1。访问这个地址,如果返回一个JSON,例如{"message":"Welcome to Open Assistant API"},说明API服务已成功运行。 - 交互式API文档:
http://127.0.0.1:8086/docs。这是自动生成的Swagger UI界面,在这里你可以看到所有可用的API端点,并可以直接在页面上进行测试调用,这对于开发和调试非常方便。
4. 核心功能使用与代码实战
服务跑起来后,我们来看看如何真正用它来创建一个能干的AI助手。我们将使用官方OpenAI Python库进行调用,体验从创建助手、对话到使用工具的全流程。
4.1 基础对话助手的创建与运行
首先,确保你已安装OpenAI官方库:pip install openai。
下面的代码演示了如何创建一个简单的问答助手,并进行一次对话。关键点在于base_url指向了我们本地部署的服务。
import openai import os # 配置客户端,指向本地部署的Open Assistant API client = openai.OpenAI( base_url="http://127.0.0.1:8086/api/v1", # 你的服务地址 api_key="your-api-key-here" # 如果未开启认证,这里可以填任意非空字符串;若开启,则填有效的Token ) # 1. 创建一个助手 # 这里的`model`参数非常重要,它需要与你One API中配置的模型名称对应。 # 例如,你在One API里将某个渠道命名为“gpt-4-turbo”,这里就填“gpt-4-turbo”。 assistant = client.beta.assistants.create( name="知识库问答专家", instructions="你是一个专业的问答助手,擅长根据提供的资料回答问题。请用中文回复。", model="gpt-4-turbo", # 此模型名必须在One API中可用 tools=[{"type": "retrieval"}] # 启用检索工具,这样助手才能处理上传的文件 ) print(f"助手创建成功,ID: {assistant.id}") # 2. 创建一个线程(可以理解为一次独立的对话会话) thread = client.beta.threads.create() print(f"线程创建成功,ID: {thread.id}") # 3. 向线程中添加用户消息 message = client.beta.threads.messages.create( thread_id=thread.id, role="user", content="请介绍一下Open Assistant API这个项目的主要特点。" ) # 4. 在线程上运行这个助手 run = client.beta.threads.runs.create( thread_id=thread.id, assistant_id=assistant.id ) print(f"运行已创建,ID: {run.id},状态: {run.status}") # 5. 轮询检查运行状态,直到完成 while run.status not in ["completed", "failed", "cancelled", "expired"]: run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id) print(f"当前运行状态: {run.status}") # 如果状态是“requires_action”,说明助手需要调用工具,这里先做简单处理 if run.status == "requires_action": print("助手需要执行工具调用,此处需处理工具提交,示例中暂设为完成。") # 实际应用中,这里需要解析run.required_action,调用对应工具,并提交结果。 # 为了演示,我们假设工具调用已完成,直接让运行进入完成状态(实际不可行,此处仅为流程示意)。 break import time time.sleep(1) # 每秒检查一次 # 6. 运行完成后,获取线程中的所有消息 if run.status == "completed": messages = client.beta.threads.messages.list(thread_id=thread.id) # 消息列表是按时间升序排列的,最新的消息在最后 for msg in messages.data: print(f"{msg.role}: {msg.content[0].text.value}") else: print(f"运行未成功完成,最终状态: {run.status}")这段代码涵盖了最核心的流程。但请注意,其中省略了对于requires_action状态(工具调用)的完整处理,我们将在下一节详细展开。
4.2 为助手赋能:自定义工具集成
智能体的强大之处在于能使用工具。项目支持两种主要工具类型:function(自定义函数)和web_search(联网搜索)。这里我们重点看如何创建一个function工具。
假设我们有一个内部系统,可以查询用户订单状态。我们将其封装成一个工具。
首先,你需要通过API(或直接在/docs页面操作)创建这个工具。这里用Python代码演示:
# 继续使用上面的client # 首先,我们需要管理员token来创建工具(如果开启了认证) admin_client = openai.OpenAI( base_url="http://127.0.0.1:8086/api/v1", api_key="my-admin-token-2024" # 使用在docker-compose中配置的管理员token ) # 定义工具的OpenAPI Schema tool_schema = { "openapi": "3.1.0", "info": {"title": "订单查询工具", "version": "v1.0.0"}, "servers": [{"url": "http://your-internal-service.com/api"}], # 你的内部服务地址 "paths": { "/order/{order_id}": { "get": { "operationId": "getOrderStatus", "summary": "根据订单ID查询订单状态", "parameters": [ { "name": "order_id", "in": "path", "required": True, "schema": {"type": "string"}, "description": "订单的唯一标识符" } ], "responses": { "200": { "description": "成功返回订单信息", "content": { "application/json": { "schema": { "type": "object", "properties": { "order_id": {"type": "string"}, "status": {"type": "string", "enum": ["pending", "shipped", "delivered", "cancelled"]}, "amount": {"type": "number"} } } } } } } } } } } # 创建工具 try: created_tool = admin_client.beta.assistants.tools.create( type="function", name="query_order_status", description="查询指定订单ID的当前状态和金额。", schema=tool_schema ) print(f"工具创建成功,ID: {created_tool.id}") except Exception as e: print(f"创建工具失败: {e}")工具创建好后,你就可以在创建或更新助手时,将其添加到助手的tools列表中:
assistant_with_tool = client.beta.assistants.create( name="订单客服助手", instructions="你是一个订单客服助手,可以帮助用户查询订单状态。当用户询问订单时,使用工具进行查询。", model="gpt-4-turbo", tools=[{"type": "function", "function": {"name": "query_order_status"}}] # 引用创建的工具 )当用户问“我的订单12345到哪里了?”时,助手会尝试调用query_order_status工具,并传入order_id: “12345”。你的后端服务需要根据之前tool_schema里定义的servers.url和路径,去实际调用你的内部订单查询API,然后将结果返回给助手,由助手组织语言回复给用户。
4.3 文件处理与RAG应用实战
让助手“读懂”你提供的文档,是另一个高频需求。结合配置的R2R服务,这个过程变得很简单。
# 1. 上传文件到助手(实际上是上传到R2R服务) file_path = "./项目说明书.pdf" with open(file_path, "rb") as f: uploaded_file = client.files.create(file=f, purpose="assistants") print(f"文件上传成功,ID: {uploaded_file.id}") # 2. 创建一个启用检索功能的助手,并将文件与之关联 assistant = client.beta.assistants.create( name="文档分析专家", instructions="你是一个文档分析助手,请严格根据我提供的文件内容来回答问题。如果问题超出文档范围,请如实告知。", model="gpt-4-turbo", tools=[{"type": "retrieval"}], # 启用检索工具 file_ids=[uploaded_file.id] # 关联上传的文件 ) # 3. 在对话中,助手会自动从关联的文件中检索相关信息来生成回答 thread = client.beta.threads.create() client.beta.threads.messages.create( thread_id=thread.id, role="user", content="根据项目说明书,第三章提到的核心技术架构是什么?" ) run = client.beta.threads.runs.create(thread_id=thread.id, assistant_id=assistant.id) # ... 轮询等待运行完成在这个过程中,当你关联file_ids时,后端服务会通知R2R引擎对该文件进行异步处理(解析、向量化)。当用户提问时,助手触发retrieval工具,服务会将问题向量化,并在R2R中搜索最相关的文档片段,将这些片段作为上下文提供给LLM,从而生成基于文档的精准回答。
5. 生产环境部署考量与高级配置
将项目用于本地开发测试和用于生产环境,需要考虑的方面大不相同。以下是几个关键的进阶议题。
5.1 性能、扩展性与高可用
默认的docker-compose.yml配置适合开发。在生产环境中,你需要考虑:
- 数据库:将默认的SQLite或示例中的PostgreSQL配置,改为使用外部的高可用PostgreSQL或MySQL集群。需要修改
docker-compose.yml中的数据库连接字符串,并确保数据库有定期备份。 - API服务:Python服务本身是无状态的,可以方便地水平扩展。你可以使用Docker Swarm或Kubernetes部署多个副本,前面用Nginx或HAProxy做负载均衡。注意,如果使用了本地内存缓存,需要替换为Redis等分布式缓存以保证状态一致。
- 文件存储:如果使用R2R,其向量数据库(如Qdrant, Weaviate)和文件存储也需要独立部署和规划扩展性。如果使用内置文件服务,务必配置一个持久化、可扩展的存储后端(如S3、MinIO)。
- 网络与安全:务必通过HTTPS提供服务。可以使用Traefik或Nginx作为反向代理,处理SSL证书。严格管理环境变量和密钥,使用密钥管理服务。
5.2 监控、日志与调试
一个稳定的服务离不开可观测性。
- 日志:确保Docker容器的日志被收集到集中式日志系统(如ELK Stack, Loki)。在
docker-compose.yml中,可以配置日志驱动和轮转策略。 - 监控:为API服务添加Prometheus指标暴露端点(如果项目未内置,可以考虑添加中间件),监控请求延迟、错误率、LLM调用耗时等关键指标。使用Grafana进行可视化。
- 调试:在生产环境,详细的LLM请求/响应日志可能包含敏感数据且体积庞大。建议仅在调试特定问题时开启DEBUG级别日志,平时使用INFO或WARNING级别。可以利用OpenAI SDK的
logging模块或HTTP请求拦截器来记录关键信息。
5.3 多租户与权限深度管理
项目自带的基于Token的简单隔离能满足基本需求。但对于复杂的SaaS场景,你可能需要:
- 租户数据隔离:确保不同用户/租户的助手、线程、文件数据在数据库层面严格隔离。这可能需要修改数据模型,在所有相关表中加入
tenant_id字段,并在所有查询中强制过滤。 - 细粒度权限控制:不仅是访问API的权限,还包括对模型使用的配额限制(如每天最多调用多少次GPT-4)、文件存储空间限制、可创建助手数量限制等。这部分逻辑需要你在业务层自行实现,并与用户管理系统集成。
- 审计日志:记录所有用户的敏感操作,如创建助手、上传文件、执行工具调用等,以备审计。
6. 常见问题排查与实战经验分享
在实际部署和使用中,你肯定会遇到各种问题。下面我整理了一些典型问题及其排查思路,这些都是我在类似项目中踩过的坑。
6.1 服务启动与连接问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
docker-compose up失败,提示端口冲突。 | 本地8086端口已被其他程序占用。 | 1.netstat -ano | findstr :8086(Windows) 或lsof -i :8086(macOS/Linux) 查看占用进程。2. 修改 docker-compose.yml中服务的端口映射,如改为"8087:8086"。 |
访问http://127.0.0.1:8086/docs超时或连接拒绝。 | 1. 容器启动失败。 2. 服务内部崩溃。 3. 防火墙/安全组规则阻止。 | 1.docker-compose logs open-assistant-api查看服务日志,通常错误信息会直接打印出来。2. 检查 OPENAI_API_KEY等环境变量是否配置正确,格式是否有误(如多余空格)。3. 对于云服务器,检查安全组是否开放了8086端口。 |
调用API创建助手时,返回401 Unauthorized。 | 认证未通过。 | 1. 确认服务端已设置APP_AUTH_ENABLE=true。2. 确认请求Header中包含了 Authorization: Bearer <your-token>。3. 确认使用的Token是有效的(通过管理员Token创建的,或就是管理员Token本身)。 |
调用API时,返回503或连接One API失败。 | One API服务不可达或配置错误。 | 1. 确认One API服务正在运行 (docker-compose ps查看one-api容器)。2. 进入Open Assistant API容器内部,尝试 curl http://one-api:3000/api/v1/models(注意容器内网络),看One API是否正常响应。3. 检查 OPENAI_API_KEY是否正确对应了One API中一个可用的密钥。 |
6.2 助手运行与工具调用问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
创建助手或运行线程时,返回404 Model not found。 | 指定的model参数在One API中不存在或不可用。 | 1. 登录One API管理界面,检查模型渠道是否配置正确且状态为“正常”。 2. 在One API中,查看你使用的API Key是否有权限访问你指定的模型名称。 3.关键点:Open Assistant API传递的 model参数,必须是One API里配置的模型名称(在渠道配置中自定义的),而不是原始的模型ID(如gpt-4-turbo-preview)。 |
助手运行状态长时间卡在queued或in_progress。 | 1. LLM响应慢。 2. 工具调用超时或阻塞。 3. 内部任务队列堆积。 | 1. 查看服务日志,是否有错误输出。 2. 检查One API后台,该模型调用是否成功,是否有报错(如余额不足、速率限制)。 3. 如果是自定义工具调用卡住,检查你的工具服务是否健康,响应是否超时。工具调用应有合理的超时设置。 |
启用了retrieval工具,但助手回复“未找到相关信息”。 | 1. 文件未成功上传或处理。 2. R2R服务未正确配置或运行。 3. 文件内容与问题相关性太低。 | 1. 确认文件已成功上传(API返回file.id)。2. 查看R2R服务日志,确认文件解析和向量化过程没有报错。 3. 在R2R的管理界面(如果有)或通过其API,查询该文件是否已成功录入知识库,并尝试用相同问题直接测试R2R的检索效果。 |
| 自定义工具被成功调用,但助手无法理解返回结果。 | 工具返回的数据格式与Schema定义不符,或过于复杂。 | 1.严格遵循Schema:确保你的工具服务返回的JSON结构与创建工具时定义的responses.200.schema完全一致。LLM对格式很敏感。2.简化数据结构:尽量返回扁平、简单的键值对。避免嵌套过深的对象或数组。 3.添加清晰描述:在Schema的 description字段中,详细描述每个返回字段的含义,这能极大帮助LLM理解数据。 |
6.3 配置与依赖问题
- 关于Python版本:项目Dockerfile中通常指定了Python版本(如
python:3.11-slim)。如果你需要在本地非Docker环境运行,务必使用相同或兼容的Python版本,否则可能遇到依赖包冲突。 - 关于依赖更新:项目依赖在
requirements.txt中定义。在自行部署时,如果遇到奇怪的运行时错误,可以尝试在构建Docker镜像前,先运行pip install -r requirements.txt --upgrade更新依赖,但要注意版本兼容性。 - 内存与磁盘占用:RAG和LLM调用比较消耗资源。确保部署的服务器有足够的内存(建议4GB以上)。如果处理大量文件,需要关注R2R向量数据库的磁盘占用。
我个人在实际部署中的几点深刻体会:
- 从“能用”到“好用”的关键是R2R:初期我尝试用项目自带的简单文件服务,效果很不理想,检索不准。切换到R2R后,不仅支持了更多文件格式(PDF、PPT、Word),检索质量也因使用了更优的分块和向量化策略而大幅提升。花时间部署和调试R2R是绝对值得的投资。
- 工具设计的“契约精神”:自定义工具时,LLM就像一个严格的“合同执行者”。你定义的OpenAPI Schema就是合同。任何偏差(比如返回了合同里没定义的字段,或者字段类型不对)都可能导致LLM解析失败,给出“我无法处理这个结果”之类的回复。务必先用Postman等工具调试好你的工具端点,确保其输入输出完全符合Schema。
- 模型名称的“映射”陷阱:这是最常踩的坑。在OpenAI官方,你写
model=”gpt-4-turbo”。但在Open Assistant API -> One API -> 实际模型这个链条里,model参数对应的是One API里你给某个渠道起的“名字”。我建议在One API里使用清晰且一致的名字,比如gpt-4o-mini、claude-3-5-sonnet,然后在代码里就用这些名字。建立一个命名对照表会很有帮助。 - 异步与轮询的取舍:OpenAI的Assistants API设计是异步的(创建运行后需要轮询状态)。在构建自己的前端或应用时,你需要处理好这个异步流程。对于Web应用,可以考虑使用Server-Sent Events (SSE) 或WebSocket来向客户端推送运行状态更新,而不是让前端频繁轮询,这能提供更流畅的用户体验。