跳转至

ospp

约 1 个字 预计阅读时间不到 1 分钟

OSPP2025 经验分享

约 6079 个字 1 张图片 预计阅读时间 20 分钟

了解到 OSPP 的契机是通过学校工作室,工作室的学长姐在去年的 OSPP 申请中成果颇丰,看着学长姐中选的项目一个个都非常的高大上,让我非常眼红,但是当时的我并不知道应该怎么去参加开源,怎么去找 issue 去做,可以说在今年正式参加 OSPP 之前,我是一位完全零经验的开源小白,只有 git 和 github 的基础使用经验。

在今年的 5 月份,OSPP 的项目开始逐步放出,我选项目的主题是跟 AI Agent 有关的项目,主要是从功利的角度去选的,因为今年 AI Agent 非常的火爆,初步锚定的是海立老师的项目,项目目标是用 langgraph 构建 k8s 的问答智能体,但是在沟通了两周之后,我觉得这个项目其实不太适合我,首先因为项目主力语言是 python 去搭 agent,同时还要写前端页面,我一直以来都是写 java 的,去从零学习两个新的领域并在一个月内写出方案对于我来说难度有点太大了,而且在写申请书的同时还在找实习,实在顶不住压力,就决定换项目了。

大概是 5 月中旬的时候,打算将目标项目更换为 java 中的 ai 项目,因为我本身并没有开源项目的贡献经验,所以并不考虑 Apache 相关的项目,卷度太高,最终选择了 LiteFlow 这个社区作为我的申请项目。

5 月可以说是我相对最忙的一月,一边忙着找实习,一边撰写 OSPP 的开源申请书,同时还要兼顾学校的课程。不过最终的结果是比较喜人的,在 5 月中下旬拿到了一家 AI 初创的 offer,在拿到 offer 后就不打算继续找了,一方面是这家初创给的实在太多了,比大厂后端最高档的还要高一档,另一方面是可以全身心投入到 OSPP 申请书的撰写中,事实证明我的选择还挺正确的。

接下来一起看看我的申请全过程

5.09 - 6.16 套磁与申请书撰写

首先必须要明确的一点是,套磁很重要!必须积极的和导师进行邮件沟通,如果能加到微信就更好了。

申请书撰写我使用的是飞书文档,在飞书文档上书写完毕后,使用飞书自带的导出成 pdf 功能导出即可。

今年 OSPP 的项目申请只能选择一个了,很多项目到后期甚至没人选,导致主办方将项目申请时间延长了两周,当时看着我申请的项目热度一点一点提高,给我吓得不轻。

下面是我的套磁邮件与导师回复:

  1. 2025-05-19
Text Only
我看了你的实现思路文档。


对于LiteFlow应该是看过一些源代码,并且对Dify是有过自己的实践和思考的。这个是正确的方向。


我说下我的想法和一些重点,你可以自行看下你的申请书还缺失了哪些


1. LF的核心就四大块, 规则如何解析,脚本如何解析,如何执行的,存储插件如何做到热更新规则/脚本(存储插件只看SQL即可),这部分需要细看,原理要阐述。这相当于LF的整个骨架。阐述要精炼,不用贴大量代码,关键部分指出来即可。这也是考量学生对LF源代码理解的能力。

2. LF和大模型结合,其实更多的还是如何调用大模型的能力,来更方便的提供开发者开箱即用的能力,不用开发者自己去写大模型组件。LF官方就为之封装好一些现成的插件可以使用。

3. 关于LF如何调用大模型,其实无非就是LF封装一些与大模型交互的插件。如何封装,涉及如何选型大模型的Client,选型是一个很重要的方面。比如说spring-ai, langchain4j等等,你可以详细了解下后者。选型定了之后,其实实现无非就是调用这些工具去和大模型平台进行交互。很多能力都是大模型平台直接提供的,我们先直接对接大模型平台的能力。不考虑自己本地去向量化,对接本地的向量数据库,也不考虑自己去存储记忆体。

4. 需要对接哪些大模型,以及哪些大模型平台。这个需要思考下,当然也要和第3点结合起来思考。当然不用大而全,大而全不一定是好的,骨架打结实,后面扩展也是非常容易的 。毕竟我们是一个开源框架,开源就意味着需要不断迭代。

5. 假设我们已经定义好了各种AI组件,那么使用者如何使用。我希望的是开箱即用,这意味着开发者无需自己再去写类去集成。就如同dify一样,dify是拖一个组件到画布上,然后在画布上选择模型,填写参数。LF是后端版本的dify,虽然没有界面。但是使用方面应该和dify应该如出一辙,直接在编排中写已经定义好的组件。那最关键的一点来了:如何去定义各个组件上的参数,以及如何标准化这些参数,哪些参数的scope为公共参数,哪些参数的scope为每一个组件的参数?现有LF体系中的规则参数设定有哪些?满不满足这次课题中的ai组件的需求?如果不满足,应该在LF原有体系中做出什么样的改变?(这一个大点才是这次课题中最重要最重要的设计,直接关系着使用者该如何使用,以及最终和现有LF体系结合的形态)

6. 考虑完了第5点之后,其实也就是入参设计,那么就要考虑每个ai组件的出参设计。LF天然的有上下文,而且上下文功能特别强大。如何加以运用?并且如何在表达式上实现后一个ai组件的入参引用前一个ai组件的出参。这就是出入参绑定设计。

7. 最后则需要考虑,现有的编排方式是否满足这次课题,需不需要增加新的表达式,我举例说明:有些大模型平台有种ai组件叫做意图分类的。那么意图分类在LF上用什么表达式来表达?第一想到的肯定是SWITCH,但是SWITCH是单选,而很多大模型的意图是多选,那么是不是要支持SWITCH的多选特性呢?这只是一个例子,需要自己思考这次课题有没有其他需要扩展表达式的场景。



其实这次课题需要考虑的点非常多,要很好的完成这次课题需要前期投入大量的思考和设计。很多功能需要和LF本身结合。所以需要花大量的精力去研读源码。

LF本身设计的非常饱满,而且也在不停的迭代。


既然是和AI有关,你也可以结合AI来辅助你分析源码,去弄懂它。但是得确保,你是真的理解了源码并拥有对于本次课题的设计大致思想。


加油~~源码中碰到不懂的也可以直接来问我。邮件一般24小时内会回。周末除外


---


> 导师您好,我已将自己对该项目的初步实现思路以及过程中遇到的一些疑问整理在了一份飞书文档中,方便您查阅。如您有空,还请帮忙看看,并给予一些指导意见,万分感谢!

> 飞书链接:
  1. 2025-05-28
Text Only
我仔细看了你的文档,能看出来是花了时间思考的。

但是对于这个课题的思考,需要抓住重点。以下是一些建议:



1. 关于三级参数配置体系,从参数的scope来说,有些需要全局配置,比如用对接什么平台,也就是base-url,api-key这些。但是有些参数只是组件层面的,比如这个组件用什么模型,组件分类,温度等参数。其实并没有三层,甚至于都算不上层的概念。只是不同的参数配在不同地方而已。在chain中配置完全没必要,因为有可能出现,一个chain中2个ai模型是不同的,参数也不一样。

2. AI组件设计,你不能完全照搬Dify,要结合LF,比如说start和end,在LF里没有这回事。还有什么变量赋值,发送http请求,现有LF就能完成这些。还是一个结合的问题。

3. AI组件设计,方案一,预先开发AI节点,的确会引起组件id冲突问题,如果去映射。这方式太死板。方案二,用户还要自己实现方法,那基本上违背了开箱即用这个诉求,至少在使用层面上不是太方便。提示:是否能用Annotation的声明式方式让用户去自定义ai组件呢?这个不需要类,只需要定义接口即可,接口如何变成AI组件,这需要靠动态代理,动态代理如何做?这样一来,同时解决了组件上的入参问题和NodeId冲突问题,那么prompt如何定义,动态化的入参出参怎么做?

    1. 当然我只是提出个概念,你也可以不按照我说的来设计,设计讲究自圆其说,相对优点最多,同时设计的缺陷控制在可以容忍的范围内。

4. 出入参的绑定问题,你文档上也有提到。你是采用定义<ai-nodes>这种方式来做的。

    1. 首先你的组件定义和出入参是分开来的,开发者需要定义两块内容。这种设计太割裂

    2. 其次出入参完全采用了xml的方式来定义,需要热更吗?如果需要,那热更怎么做?如果开发者想使用存储插件,比如sql,redis,etcd等,这整个的一套东西又该如何适配。适配需要有多大的复杂度,需要花多少时间?

5. Streaming怎么做,你完全没提到

6. Function Calling和MCP大概的方向是什么,你也没提到。



其实AI组件类型定义有几个是不重要的,你可以先考虑最基础的几个,大模型组件,知识库搜索组件,分类组件。主要就这3种,大模型组件同时还有function calling和mcp的变种,加上一个streaming。



设计才是最重要的,尤其是组件定义,出入参设计。



如果你要针对于原来的解析做改动,需要考虑的非常周全(这当然也需要对源码更深层次的理解),以及对各个其他大功能块的影响评估。以及复杂度的评估。



---



> 导师您好,我根据您上次的回复,写了一份初版申请书。
>
> 有一些问题想请教一下,首先我发现要是想要实现 AI 组件的一些扩展功能,需要对原有框架代码增加解析逻辑,这样做是否合理呢?
>
> 还有就是导师您对 dify 节点看法如何?需要全部实现吗,还是只要实现部分重要的节点即可?
>
> 最后,如果申请书有任何不足之处,还望导师指出,我会改正。
>
>
>
> 申请书飞书文档:
  1. 2025-06-3
Text Only
以上是你组件设计,包括出入参的一个大致的示意。



1. 对于一个AI组件来说,需要最基本的参数,比如模型选择,systemPrompt,userPrompt,history,temperature和maxTokens,这些你用一个@AIChat标签去聚合,没太大问题(可能少了几项)。

    1. prompt当中引用的变量用双花括号表示,这没啥问题。但是考虑过prompt这种需要输入长文本,尤其是带markdown格式的文本的情况吗?这种怎么解决?现实场景中很多prompt都是非常长的。

2. 输入参数这种结构也没啥问题,从context中绑定一些数据作为ai模型的输入参数,这部分其实是一个表达式,在LF有相应的解析公共方法。

3. 对于一个大模型返回,无非就两种,文本或者对象(对象对应着JSON),那你的@AIOutPut是否也应该提供相应选项?

    1. 你选择了输出参数也绑定到原有上下文中。这部分没啥问题

4. 看起来以上所有的定义和你定义的方法没有任何关系,你返回ChatAIResult是什么意思呢,是想说明绑定的对象是ChatAIResult类型吗?那你做到@AIOutPut标签里去不就好了么。


这个接口只是用来定义大模型,最后反正要被动态代理,所以你所有的标注定义在类上看起来也完全成立。这个接口就是一个标识接口。




那知识检索组件和意图分类组件呢?只在你的文档中见到了标注的定义,但是没有见到最终的呈现方式

该如何设计,如何又和上面达成统一,或者不统一也行,你只要能自圆其说就ok




流式输出最大的点在于LF的FlowExecutor是同步返回,流式输出怎么在一个同步返回的里面持续向外streaming呢。注解层面其实提一下就可以了,不是太重要。



---



> 导师您好,我已按照您的反馈对项目申请书进行了修改,完成了第二版。麻烦您在百忙之中审阅一下
>
>
>
> 飞书文档链接:
  1. 2025-06-4- 12:33
Text Only
给你举个例子,以上是coze官方的其中一个示例。



{{}}在prompt中的确是占位的作用,表示动态参数,而整个prompt模版是静态的。整个prompt模版就是一个长文本。



在你的使用模式中,红框部分本身就是一个prompt模版。如果这部分太长了,怎么办。而且写在这里面会丢失格式信息。如果这部分直接用{{}}来占位。每次调用的时候让用户自己拼成大prompt自己传吗?



所以需要考虑到实际使用便捷度。



FlowExecutor本身就有返回CompletableFuture的接口。不需要额外加方法,多从异步和SseEmitter,以及langChain4J的流式处理方向去结合思考。



申请书按照ospp要求上传,具体看官网。排版没太大要求,章节清晰就可以了。等所有同学都提交了,我们会综合评定来确定人选



---



> 导师您好,看了您的邮件回复后有一些疑惑想跟您交流一下。
>
>
>
> 首先是长文本问题,这个我认为直接使用占位符解析似乎没什么问题?将文本直接解析到占位符就行吧
>
>
>
> 流式输出的问题,我看到FlowExecutor有一个方法是execute2Future,那么用户在需要流式输出的时候应该调用这个异步方法,这样主线程就不会阻塞了,但是Future 接口的结果获取是同步且阻塞的,如果我们用户想要直接返回一个 SseEmitter 到前端同时需要在流程结束的时候处理相关的业务逻辑,那么因为 Future 接口会阻塞主线程,就导致流式输出已经输出完了,这个请求才结束,所以这样做似乎不太合理。
>
>
>
> 那么,能不能在FlowExecutor加一个执行方法,这个方法返回的是 CompletableFuture,那么用户就可以通过编写回调函数去处理流程完成之后的业务逻辑,然后主线程直接返回 SseEmitter 就可以。
>
> 然后,我突然意识到我之前的流式输出方案是不合理的,因为如果是在注解层面设置对应的 Spring Bean ID 的话,那么用户的处理逻辑在进程启动前就是定死的,如果说需要在流式传输中用到一些当前请求的局部变量的话,那么是获取不到的。所以,我觉得应该将 StreamHandler 放在上下文中,那么用户在执行一个流程之前,都可以对处理逻辑进行自定义,相对是更加灵活的。
>
>
>
> 申请书的话,最后是上传 pdf 就行吗?然后排版上是否有特殊的要求?
  1. 2025-06-5
Text Only
关于prompt,你的思考是正确的。
但是介于你的三个主要的标注参数比较多,需要从开发者的角度多去想想,怎么样简单开发我们就怎么去设计。
所以prompt不用把Resource的类型和直接写的分开。就systemPrompt和userPrompt两个可以包含所有情况,用户怎么定义,我们去适配,智能的识别。

关于sse的思考,我看了你的方案,的确CompletableFuture返回可能更好。传入回调函数可以解决streaming。

凡是用户需要直接定义的东西,希望要站在用户侧去思考。尤其这个课题,需要用户侧定义的东西还挺多,需要格外注意。

总体不错,看来是花了时间思考的。加油


---

导师您好,又修改了一版申请书,主要修改和添加的部分是 4.4,4.5,4.7 章节

然后申请书的 pdf 版本在附件中


长文本 prompt 我应该理解导师您的意思了。

但是流式输出的问题,我真没找到FlowExecutor有返回 CompletableFuture 对象的方法啊,我只看到 execute2Future 是返回 Future 接口的。

这是一个异步的执行流程,那么肯定是不会阻塞主线程的,但是要是主线程调用 furture.get()方法,这个获取结果的方法是同步且阻塞的,所以后续的操作不会执行,那么比方说有这样一个场景。

1. 请求进入
2. 在上下文设置流式输出处理器,调用 AI 相关节点
3. 调用execute2Future方法,获取 future 对象
4. future.get()获取执行结果,并对结果进行业务处理
5. return 流式输出处理器给前端

问题就在 4 -> 5 这个阶段,后端要是想要使用 SSE 协议对前端进行流式输出,那么应当直接返回相关的处理器,但是阶段 4 阻塞住了,所以就会导致 LLM 节点流式输出已经关闭了才返回相关的处理器,那么前端是收不到任何消息的。

当然解决上述问题可以靠用户自行完成,比如说我不调用 future.get()方法,那么就不会阻塞。比如说用户自己起一个 CompletableFuture 然后在里面写 future.get() ,再用回调逻辑做相关的业务处理。

但是我们为何不直接返回一个 CompletableFuture 对象呢?我觉得这样做是合理的。相关的讨论我放在申请书的 4.7 章节。

飞书链接:
  1. 2025-06-19- 12:20 导师微信通知中选

image.png

最终申请书 pdf 有 36 页,在申请书提交截止的两天后成功中选!!!

7.01 - 9.30 项目开发

在中选后的那股兴奋劲消退之后,我开始思考应该如何实现这个项目,此时的我并不知道写这鬼项目能这么折磨。

首先,申请书是我和 AI 一起写的,光是写申请书就要将我燃尽了,所以我并没有写什么 mvp 去验证我的方案可行性。

然后,中选之后我发现我的方案怎么这么像一坨 shit 呢,欸,要不不按方案写的来,我按我自己的想法写一个新的方案实现吧🤓

在 7 月初到 7 月中旬,我因为我的灵机一动一直在按我自己的想法写,我的想法大概就是底层调用 AI 模型不用开源的实现,而是自己尝试去写一套,那么在一顿自认为惊为天人的设计并用代码实现了一版之后,我信心满满地发微信跟导师说:“看看我写的怎么样🤓”

不出意外,那么要出意外了

导师把我狠狠喷了一顿,说,你自己写的再好能有开源的 langchain4j 写的好吗,不要把精力花在没必要的地方,能用开源的就用开源的

给我直接整抑郁了,不过也没办法,只能顺从了,那就用 langchain4j 去写吧!

在 7 月中旬到 7 月底,我用 langchain4j 的方案实现了一版,并且发给了导师,不出意外的话,这大概就是最终的版本了

然后出意外了= =

导师说,langchain4j 的方案也不行,首先 langchain4j 要求 jdk17 及以上,而 LiteFlow 需要支持 jdk8 及以上,这依赖就没法用,再者 langchain4j 的 api 封装的过于高层了,这个项目只需要一个非常基础的调用大模型的能力即可

又给我整抑郁了,我真是日了

但是项目还是要写的,毕竟是我自己选的,就算是史我也吃了

最后和导师沟通之后,最终敲定按我原来的方案实现,那么也就是自己去写一套底层实现。

好在这两版方案是可以互通的,总之也不算白写,那么在 8 月份一直在慢慢去实现相关的代码。

这里没有说导师不好的意思,相反我非常感谢导师对我的指导,我觉得自己真是运气爆棚,能遇到一个这么好的导师,每一次去问问题,导师都是一大段一大段的回复我,去讨论相关的方案实现,确实受益很大。

到 8 月下半的时候,感觉导师也算认可我了,也就开始讨论结项相关的事宜。

截止 8 月底,核心代码的代码量共计 1w5 行左右(不包含测试),但是还没有完全完善,需要慢慢的进行迭代。

导师这边非常的善解人意,允许直接合入我的 pr,并且告知我说,不需要担心 ospp 的时间节点,可以直接合入,但是后续需要进行相关的迭代工作

所以不出意外的话我这边其实已经可以开香槟了,后续只要跟着 ospp 的流程走就能顺利结项。

然后踏马又出意外了

ospp 的结项审核,因为我的 PR 的 commit 太多报错了,导致直接访问不了,其实也不多,就 87 个 commit,问客服之后,情况是这样的: "后台反馈是因为PR中包含的commit数量太多导致限制访问,被第三方平台解析禁止。建议精简commit数量,具体操作方法可与导师讨论决定。"

不是你告诉我怎么精简 commit?都已经合进去,还有这不是你们 OSPP 后台服务的设计问题吗?

不过跟导师反馈后,导师说能够帮我解决,实在不行就走人工审核,总之先这样吧

最后的最后,很感谢 OSPP 和 LiteFlow 社区能够给我一次参与开源的机会。我在一开始申请的时候,从来没想过自己也能够参与开源,甚至一度怀疑自己的能力是否能够胜任,不过真真正正投入进去之后,会发现曾经认为很难的事情或许并不可怕,重要的是让自己行动起来。