惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 最新话题
小众软件
小众软件
C
Check Point Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
V
Visual Studio Blog
Last Week in AI
Last Week in AI
P
Proofpoint News Feed
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The GitHub Blog
The GitHub Blog
T
Tailwind CSS Blog
Recorded Future
Recorded Future
雷峰网
雷峰网
WordPress大学
WordPress大学
A
Arctic Wolf
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
T
Tor Project blog
T
Threat Research - Cisco Blogs
Scott Helme
Scott Helme
Spread Privacy
Spread Privacy
G
Google Developers Blog
Security Latest
Security Latest
MongoDB | Blog
MongoDB | Blog
T
Threatpost
I
InfoQ
T
Tenable Blog
T
The Exploit Database - CXSecurity.com
S
Security Affairs
H
Hackread – Cybersecurity News, Data Breaches, AI and More
人人都是产品经理
人人都是产品经理
C
Cisco Blogs
博客园 - 三生石上(FineUI控件)
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
aimingoo的专栏
aimingoo的专栏
C
CXSECURITY Database RSS Feed - CXSecurity.com
美团技术团队
Google DeepMind News
Google DeepMind News
The Hacker News
The Hacker News
D
Docker
博客园 - 【当耐特】
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Vercel News
Vercel News
PCI Perspectives
PCI Perspectives
Microsoft Azure Blog
Microsoft Azure Blog
C
CERT Recently Published Vulnerability Notes
月光博客
月光博客
Cloudbric
Cloudbric
A
About on SuperTechFans
F
Fortinet All Blogs

XINDOO

关于内卷,几个值得深想的洞察 当创作被 Skill 化:我用 AI 写了一部 320 章的长篇网文 AI第一剑,先斩程序员 AI 也会偷懒?这个 PUA 工具专治“摸鱼式编程” Gstack 深度解析:YC CEO 开源的 AI 工程团队 GitHub Trending霸榜!深度解析AI Coding辅助神器 Superpowers 深度探讨:从 OpenClaw 爆火,看 AI Agent 的真相与程序员的未来 我复刻了NotebookLM的信息图功能 我用AI写了部小说,这里是整个过程 [翻译]我在谷歌14年学到的21堂课 2万字吊打40万字:为什么我的“牛马Agent”比“数字分身”更聪明? 最近AI领域爆火的 Agent Skills 是什么? 从计算机科学的视角来看拖延症 一周改6个库后我悟了:AI时代,程序员正在退化成“甲方” 使用n8n做一个自动同步更新的Github项目问答机器人 Agent设计模式——第 20 章:优先级排序 Agent设计模式——第 1 章:提示词链 Agent设计模式——附录 D - 使用 AgentSpace 构建 Agent Agent设计模式——第 19 章:评估和监控 Agent设计模式——第 10 章:模型上下文协议 (MCP) Agent设计模式——第 21 章:探索和发现 Agent设计模式——智能体设计模式 Agent设计模式——第 11 章:目标设定和监控 Agent设计模式——第 9 章:学习和适应 Agent设计模式——第 16 章:资源感知优化 Agent设计模式——附录 G - 编码 Agent Agent设计模式——第 13 章:人机协同 Agent设计模式——第 17 章:推理技术 Agent设计模式——附录 F - 深入剖析:Agent 推理引擎的内部运作机制 Agent设计模式——章节目录 Agent设计模式—— Agent设计模式——第 7 章:多 Agent 协作 Agent设计模式——附录 E - 命令行界面中的 AI Agent Agent设计模式——附录 C - Agentic 框架快速概览 Agent设计模式——第 3 章:并行化 Agent设计模式——**常见问题解答:Agentic 设计模式** Agent设计模式——第 14 章:知识检索(RAG) Agent设计模式——第 18 章:Guardrails/安全模式 Agent设计模式——第 15 章:Agent 间通信(A2A) Agent设计模式——第 8 章:内存管理 Agent设计模式——第 12 章:异常处理和恢复 Agent设计模式——第 4 章:反思 Agent设计模式——附录 B - AI Agentic 交互:从图形界面到现实世界环境 Agent设计模式——第 5 章:工具使用(函数调用) Agent设计模式——结论 Agent设计模式——第 6 章:规划 Agent设计模式——第 2 章:路由 从经验主义到贝叶斯理论:如何排查线上问题 我用AI为自己造了一把安全的开发者“瑞士军刀”” 从LLM和MCP的协同过程看如何做优化 打通Dify与AI工具生态:将Workflow转为MCP工具的实践 一文了解知识库背后的技术RAG AI应用的五个级别:从入门到专家的进阶之路 一文入门AI圈最近爆火的MCP协议 HTTP/3:性能改进(第 2 部分) deepseek-r1祛魅:从过度热捧到理性认知⁠ 为什么AI智能体需要工作流 如何用GPT-4o解读视频 json命令行处理神器jq介绍 OpenAI的结构化浅析 从大模型的原理到提示词优化 从经济学原理看团队分工合作 [翻译]关于人工智能的30个思考 从马斯洛需求层次理论谈职场激励 知识与智慧 如何使用大语言模型绘制专业图表 两个开源项目打造自己的大模型聚合平台 我让gpt4o给我推荐了一千多次书 得到了这些数据 用Langchain创建一个可以总结网页内容的Agent 推荐一个好用的命令行工具ShellGPT 关于ffmpeg height not divisible by 2的错误 使用Certbot解决https证书自动更新的问题 Spring Cache简明教程 软件开发中的抓大放小vs极致细节思维 OpenAI的多函数调用(Multiple Function Calling)简介 如何使用ffmpeg制作透明背景的视频 spring-kafka中ContainerProperties.AckMode详解 如何在地图上寻找最密集点的位置? IO密集型服务提升性能的三种方法 职场中的基本归因错误和自利归因 使用javax.validation.constraints校验参数合法性 Java Optional:让你的代码更优雅 ChatGPT函数调用初体验:让ChatGPT具备抓取网页文本的能力 如何使用ChatGPT提升自己的“码”力? 使用ffmpeg拼接两张图片 ThreadPoolExecutor——高效处理并发任务的必备良器 从CPU的视角看 多线程代码为什么那么难写! 使用ffmpeg缩小视频体积的几种方式 Linux parallel 命令使用手册 为什么说过早优化是万恶之源? Linux xargs命令介绍 深入理解Spring的事件通知机制 Java高并发之CyclicBarrier简介 聊一聊过度设计! 详解Redisson分布式限流的实现原理 Java中使用HashMap时指定初始化容量性能一定会更好吗? 如何用ffmpeg截取视频片段&截取时间不准确的坑 XINDOO的2022年年终总结 使用ffmpeg将视频转成HLS(m3u8)格式 谷歌Guava LoadingCache介绍
OpenAI Assistants-API简明教程
xindoo · 2023-11-19 · via XINDOO

  OpenAI在11月6号的开发者大会上,除了公布了gpt4-v、gpt-4-turbo等新模型外,还有一个assistants-api,基于assistants-api开发者可以构建自己的AI助手,目前assistants-api有三类的工具可以用。首先就是之前大火的代码解释器(Code Interpreter),这个在chatgpt-plus会员上线的时候大火了一把。其次就是文件检索(Retrieval),利用Retrieval你可以在assistants中外挂自己的知识库。还有就是函数调用了,这个就不在多说了。assistants-api目前还处于beta版本,但从OpenAI的规划来看,后续应该是会支持DALLE3、gpt4-v甚至是plugin的,我们可以期待下。

  使用assistants-api和使用chat-api有啥区别?首先就是chat api只能使用模型的chat能力的,而且如果你之前使用过,就会发现chat对话历史都需要自行维护,很不方便。而assistants-api除了chat的能力外,它还可以调用强大的解释器(Code Interpreter),还可以调用外部函数(Functions Calling), 而且还可以外挂自己的知识库(Retrieval),主要你还不需要维护对话历史,只需要关注对话本身即可。 如果后续assistants-api支持了plugin、DALLE3和gpt4-v之后,你完全可以认为它就是一个api版本的chatGPT-Plus,当然功能可以可以完全定制,相信看到这里你肯定也蠢蠢欲动,想定制自己的AI助手了。

  在正式开始开发之前,我们先来了解下Assistants-API的几个核心对象。

对象 作用
Assistant 助手,可以使用指定模型根据的一个实体,如果把助手比作某个人的化,这里就是指具备某些能力的一个具体的人
Thread 没有合适的翻译,这里就不翻译了,可以认为这个是和助手的沟通的上下文对话信息, 就好比你和某宝客服沟通,整个对话就可以认为是一个Thread
Run 也没有合适的翻译,可以认为是你向助手发起一次对话,整个对话响应的过程及工程中的状态变化,就可以当成一个run,一个run里不仅仅可以有模型的回复,还可以有函数调用、代码解释器调用、文件召回……
Run Step Run各个步骤的详情,可以看到整个助手的运行过程,主要是方便问题排查和助手优化

  知道了这些概念,我们就可以着手实现自己的Assistant了,为了能更好理解整个Assistant的开发流程,我们还是用一个具体的示例来完成整个功能的开发。假设我们需要开发一个花店财务助手,它的主要功能是根据我们每天卖出去的花,统计成本和收入,最后将收入和成本保存到数据库里。

  这里我提前准备了一个excel表格(flower_prices.xlsx),来记录所有花的成本和售价(虚构数据、不代表真实价格)。

  下面正式开始我们花店财务助手的开发和使用。

创建助手

  这里首先需要将我们的flower_prices.csv转成Assistant能使用的file,使用如下代码即可:

from openai import OpenAI
client = OpenAI(base_url='https://thales.xindoo.xyz/openai/v1/')
# 将文件上传至openAI保存
file = client.files.create(
  file=open("flower_prices.csv", "rb"),
  purpose='assistants'
)

  接下来我们定义保存账单信息的function,具体可以参考下我上篇博客OpenAI的多函数调用

# 定义保存账单的方法
def save_bill(totalCost, totalIncome):
    '''保存总成本和总的收入'''
    print(totalCost, totalIncome)
    return "success"

function = {
        "type": "function",
        "function": {
            "name": "save_bill",
            "description": "保存总成本和总的收入",
            "parameters": {
                "type": "object",
                "properties": {
                    "totalCost": {
                        "type": "number",
                        "description": "总成本",
                    },
                    "totalIncome": {
                        "type": "number",
                        "description": "总收入",
                    }
                },
                "required": ["totalCost", "totalIncome"],
            },
        }
    }
available_functions = { "save_bill": save_bill}  

创建助手(assistant)

  这里需要调用API将所有的开关、文件和函数调用信息都传给OpenAI,创建一个属于我们自己的assistant。

# 创建助手,将code_interpreter,retrieval,function都开启
assistant = client.beta.assistants.create(
  name="花店财务助手",
  description="按照每种花的售出量,统计成本和收入,计算出总利润",
  model="gpt-4-1106-preview",
  tools=[{"type": "code_interpreter"}, {"type": "retrieval"}, function],
  file_ids=[file.id]
)

创建Thread

  如上文讲到Thread是用户和Assistant对话的上下文信息,用户和Assistant初次对话肯定是需要创建上下文的,代码和很简单,如下:

# 创建对话Thread
thread = client.beta.threads.create(
  messages=[
    {
      "role": "user",
      "content": "我卖出去了红玫瑰3支、郁金香2支、百合6支,计算下总成本和总收入,给出具体的计算过程"
    }
  ]
)

  这里看到Thead并没有和Assistant关联到一起,猜测这里只是在本地代码里创建了一个Thread对象,实际上在OpenAI那边还没有任何操作,这个可能是OpenAI利用懒加载来减轻对服务端的压力。

创建Run

# 创建Run
run = client.beta.threads.runs.create(
  thread_id=thread.id,
  assistant_id=assistant.id
)

  创建Run的方法也很简单,可以看到只需要传thread_id和assistant_id两个参数即可,而这两个id都是字符串,尤其是assistant_id 你都是可以在OpenAI网站后台看到的,相信这里大家已经猜到了,Assistant和Thread不用每次都创建新的。

# 从Run中获取结果
run = client.beta.threads.runs.retrieve(
  thread_id=thread.id,
  run_id=run.id
)

获取run的状态

  Run创建好之后,需要让OpenAI运行起来,这里就需要调用Retrieve方法,来获取Run的运行结果,这里如果你打印出run的话,你可能会看到类似的信息。

Run(id='run_A9phobcoIOG3euibElksTu8a', assistant_id='asst_hW7NrPZP8q8KvE9oiuceg5mM', cancelled_at=None, completed_at=None, created_at=1700400089, expires_at=1700400689, failed_at=None, file_ids=['file-uhMIBtm4BPXlJlY1UzGIPlGn'], instructions=None, last_error=None, metadata={}, model='gpt-4-1106-preview', object='thread.run', required_action=None, started_at=1700400089, status='in_progress', thread_id='thread_nvsTyK6DQdmKoVxOseSSKZF4', tools=[ToolAssistantToolsCode(type='code_interpreter'), ToolAssistantToolsRetrieval(type='retrieval'), ToolAssistantToolsFunction(function=FunctionDefinition(name='save_bill', parameters={'type': 'object', 'properties': {'totalCost': {'type': 'number', 'description': '总成本'}, 'totalIncome': {'type': 'number', 'description': '总收入'}}, 'required': ['totalCost', 'totalIncome']}, description='保存总成本和总的收入'), type='function')])

  这里获取到的是run的最新状态,有可能run还没有执行完,所以可能需要一直循环调取,等待run的状态发生变化。Run有以下的一些状态。

具体的状态和含义如下表: 状态 定义
queued 当Runs首次创建或者调用了retrive获取状态后,就会变成queued等待运行。正常情况下,很快就会变成in_progress状态。
in_progress 说明run正在执行中,这时候可以调用run step来查看具体的执行过程
completed 执行完成,可以获取Assistant返回的消息了,也可以继续想Assistant提问了
requires_action 如果Assistant需要执行函数调用,就会转到这个状态,然后你必须按给定的参数调用指定的方法,之后run才可以继续运行
expired 当没有在expires_at之前提交函数调用输出,run将会过期。另外,如果在expires_at之前没获取输出,run也会变成expired状态
cancelling 当你调用client.beta.threads.runs.cancel(run_id=run.id, thread_id=thread.id)方法后,run就会变成cancelling,取消成功后就会变成callcelled状态
cancelled Run已成功取消。
failed 运行失败,你可以通过查看Run中的last_error对象来查看失败的原因。

  这里需要特别注意requires_action状态,这个是需要要求代码本地去执行一些函数的,执行完成后将结果返回给Assistant,之后run才能继续运行下去。

run触发函数调用

  如果run.status是requires_action,我们需要调用本地工具,当然现在只有函数调用,然后将函数调用的结果返给Assistant,方便它继续执行,代码如下:

if run.status == 'requires_action':
    tool_outputs=[]
    # 调用并保存所有函数调用的结果
    for call in run.required_action.submit_tool_outputs.tool_calls:
        if call.type != "function":
            continue
        # 获取真实函数
        function = available_functions[call.function.name]
        output = {
            "tool_call_id": call.id,
            "output": function(**call.function.arguments),
        }
        tool_outputs.append(output)
    # 将函数调用的结果回传给Assistant
    run = client.beta.threads.runs.submit_tool_outputs(
        thread_id=thread.id,
        run_id=run.id,
        tool_outputs=tool_outputs
    )

获取Assistant的消息

  接下来我只需要轮询retrive接口,获取run的最新状态,如果状态是completed,就可以读取Assistant的返回结果了。

# 获取run的最新状态。 
run = client.beta.threads.runs.retrieve(
  thread_id=thread.id,
  run_id=run.id
)
if run.status == 'completed':
    messages = client.beta.threads.messages.list(
      thread_id=thread.id
    )
    print(messages)

  这里注意下messages是倒序排列的,所以最新的消息是在最上面的。

发起新信息

  上面的流程是从Assistant创建到发起首次消息的流程,如果我们需要紧接着之前的流程继续对话,只需要在thread中添加新的消息,然后然后创建并执行run即可,代码如下:

# 添加新消息
message = client.beta.threads.messages.create(
  thread_id=thread.id,
  role="user",
  content="另外还有2支向日葵,补充下这份账单"
)
# 创建run
run = client.beta.threads.runs.create(
  thread_id=thread.id,
  assistant_id=assistant.id
)
# 获取执行结果
run = client.beta.threads.runs.retrieve(
  thread_id=thread.id,
  run_id=run.id
)

结语

  以上就是Assistants-API整体的开发流程,了解了这些流程后,大家可以很容易构建出像ChatGPT-Plus的私人助理。当然Assistants-API目前还是在beta阶段,有很多功能不完善,比如不支持流式返回、不支持图片生成、不支持插件调用……,甚至run的状态还需要轮询来获取……。另外我在写本文demo的时候,发现Retrivel的文本内容召回成功率非常低,导致账单计算成功率很低(也可能是我给的文本格式的问题)。还有就是code_interpreter运行成功率也很低,经常出现运行不起来的情况,难怪还是beta版本,只能期待后续官方能优化下。

  另外有些像assistant、thread、run、run step的查看和管理的接口我这里没有讲到,具体大家可自行查阅下官网文档。 如果大家需要试用Assistants-API的话,也可以先到官方https://platform.openai.com/assistants 先行体验,试用完成后可以再将页面配置完整翻译成代码,然后再嵌入到自己的应用中。

完整的代码我已上传至Github上https://github.com/xindoo/openai-examples/blob/main/flower_assistant.ipynb,后续OpenAI其他API的使用示例我也会上传到这个仓库,有兴趣可以关注下。