FlowKit 是一套为 AI 编程助手设计的结构化任务编排工具集。本文是系列教程的第一篇,从设计动机出发,讲清楚”为什么要造这个轮子”以及整体架构。
GitHub: FrizzleFur/flowkit
FlowKit 是一套为 AI 编程助手设计的结构化任务编排工具集。本文是系列教程的第一篇,从设计动机出发,讲清楚”为什么要造这个轮子”以及整体架构。
GitHub: FrizzleFur/flowkit
本文基于官方文档、社区资料与实际体验,系统梳理RooCode插件的架构、功能、技术实现与典型工作流,并对比其前身Cline插件,帮助开发者快速了解和上手这一AI驱动的VSCode开发助手。
RooCode(前身为Roo Cline)是一个强大的VS Code插件,提供AI驱动的自主编码代理功能,能够在编辑器中直接与用户交互,帮助完成各种开发任务。
主要功能包括:

AI-Agent(智能代理)是一种能够自主感知环境、做出决策并采取行动以实现特定目标的智能系统。Agent的核心决策逻辑是让大语言模型(LLM)根据动态变化的环境信息选择执行具体的行动或者对结果作出判断,并影响环境,通过多轮迭代重复执行上述步骤,直到完成目标。
一个典型的Agent决策流程可以精简为:
其中:
在工程实现上,Agent通常拆分为四大核心模块:
目前Agent主流的决策模型是ReAct框架(Reasoning + Acting)及其变种,这种框架让Agent能够在思考和行动之间交替进行,从而更有效地完成复杂任务。
工作流(Workflow)简单来说就是”完成一件事的完整步骤”。它像一份”说明书”,告诉你为了达成目标,需要做什么、按什么顺序做、谁来做。工作流包含几个关键要素:
如果没有工作流,我们的工作可能会变得效率低下、错误频发、进度难以跟踪。但有了工作流,一切都会变得井然有序:重复工作自动化完成,流程规范化减少错误,工作进度清晰可见。
随着人工智能技术的飞速发展,AI产品的形态也在不断演进。从简单的推荐系统到复杂的自主决策代理,AI产品正在重塑我们与技术交互的方式。本文将探讨当前主流的AI产品形态及其应用场景,帮助读者了解这一快速发展的领域。
目前的模型体验,by 2025年04月30日
| 模型名称 | 主要优势 | 特点描述 |
|---|---|---|
| Claude 3.7 | 编程能力 | • 代码生成和调试能力强 • 上下文理解准确 • 技术文档编写专业 |
| GPT-4 | 综合能力 | • 推理能力强 • 创意写作出色 • 多领域知识丰富 |
| DeepSeek | 中文理解 | • 中文语境理解准确 • 文学创作能力好 • 中国文化认知深入 |
| 豆包 | 对话能力 | • 对话风格自然流畅 • 情感交互优秀 • 人设特征鲜明 |
| 通义千问 | 知识整合 | • 知识图谱完整 • 逻辑推理严谨 • 专业领域表现稳定 |
类,接口和实现类,多个类实现一个接口的 UML 图。

继承是指在根据已有类创建新类的能力。继承最主要的好处是代码复用。如果你想要创建的类与已有的类差异不大,那也没必要重复编写相同的代码。你只需扩展已有的类并将额外功能放入生成的子类(它会继承父类的成员变量和方法)中即可。

绝大部分 动物 Animals 可以发出声音。我们需要所有子类都重写基类的 makeSound发出声音 方法, 让每个子类都发出正确的声音, 因此我们可以马上将其声明为抽象。这让我们得以忽略父类中该方法的所有默认实现,从而强制要求所有子类自行提供该方法的实现。

依赖是类之间最基础的、也是最微弱的关系类型。如果修改一个类的定义可能会造成另一个类的变化,那么这两个类之间就存在依赖关系。当你在代码中使用具体类的名称时,通常意味着存在依赖关系。
依赖是最弱、最宽泛的关系,表示一个类在某个方法中使用了另一个类(例如,作为参数、局部变量、或调用其静态方法)。
1 | 教授 ----> 课程 |
关联是一个对象使用另一对象或与另一对象进行交互的关系, 关联比依赖关系更强,
1 | 教授 ————> 课程 |
关联体现为两个类或类与接口之间语义级别上的一种强依赖关系,这种关系不是偶然性与临时性的,而是长期的,关联双方通常是平等的,
关联则意味着一个类持有另一个类的引用(作为成员变量),这种持有本身就构成了一种使用关系,因此必然存在依赖。
换句话说,关联是一种特殊的、更强的依赖。当你在类A中声明一个类B的成员变量时,类A不仅依赖于类B(因为编译时就需要知道B),而且与B建立了长期的关联。
因此,存在关联关系,就一定存在依赖关系;但存在依赖关系,不一定存在关联关系。
聚合是一种特殊类型的关联,用于表示多个对象之间的“一对多”、“多对多”或“整体对部分”的关系。普通关联仅用于描述两个对象之间的关系。通常在聚合关系中,一个对象“拥有”一组其他对象,并扮演着容器或集合的角色。组件可以独立于容器存在, 也可以同时连接多个容器。
特点:部分(Part)可以独立于整体(Whole)存在。
生命周期:整体和部分的生命周期不绑定。即使整体被销毁,部分仍然可以存在。
UML表示:用空心菱形 + 实线箭头表示,菱形指向整体。
示例:班级-学生、汽车-轮胎
组合是一种特殊类型的聚合,其中一个对象由一个或多个其他对象实例构成。组合与其他关系的区别在于组件仅能作为容器的一部分存在。
特点:部分不能独立于整体存在,整体对部分有完全的控制权。
生命周期:部分的生命周期完全依赖于整体。整体创建时创建部分,整体销毁时销毁部分
。
UML表示:用实心菱形 + 实线箭头表示,菱形指向整体。
示例:公司-部门、订单-订单项
组合优于继承继承可能是类之间最明显、最简便的代码复用方式。如果你有两个代码相同的类, 就可以为它们创建一个通用的基类,然后将相似的代码移动到其中。轻而易举!不过,继承这件事通常只有在程序中已包含大量类,且修改任何东西都非常困难时才会引起关注。下面就是此类问题的清单。子类不能减少超类的接口。你必须实现父类中所有的抽象方•法,即使它们没什么用。
继承打破了超类的封装,因为子类拥有访问父类内部详细内•容的权限。此外还可能会有相反的情况出现,那就是程序员为了进一步扩展的方便而让超类知晓子类的内部详细内容。子类与超类紧密耦合。超类中的任何修改都可能会破坏子类•的功能。
组合是代替继承的一种方法。继承代表类之间的“是”关系(汽车是交通工具),而组合则代表“有”关系(汽车有一个引擎)。
比如这个汽车的例子

正如你所看到的,每个额外参数都将使子类数量倍增。子类中将有大量的重复代码,因为子类不能同时继承两个类

组合的还有一个好处是你可以在运行时对行为进行替换。例如,你可以通过重新为汽车对象分配一个不同的引擎对象来替换已连接至汽车的引擎。
多个来源都确认了关系的强弱顺序为:组合 > 聚合 > 关联 > 依赖

面向接口进行开发, 而不是面向实现; 依赖于抽象类型,而不是具体类。
确定一个对象对另一对象的确切需求:它需执行哪些方法(其实就是对原子能力的理解和抽象)?

抽取是有成本的:抽取接口前后的对比。右侧的代码要比左侧更加灵活,但也更加复杂。
改造前

改造后
当程序规模不断扩大、变更不断增加后,真实问题才会逐渐显现出来。到了某个时候,类会变得过于庞大,以至于你无法记住其细节。查找代码将变得非常缓慢,你必须浏览整个类,甚至整个程序才能找到需要的东西。程序中实体的数量会让你的大脑堆栈过载,你会感觉自己对代码失去了控制。
还有一点:如果类负责的东西太多,那么当其中任何一件事发生改变时,你都必须对类进行修改。而在进行修改时,你就有可能改动类中自己并不希望改动的部分。
如果你开始感觉在同时关注程序特定方面的内容时有些困难的话,请回忆单一职责原则并考虑现在是否应将某些类分割为几个部分。
对于扩展,类应该是“开放”的;对于修改,类则应是“封闭”的。本原则的主要理念是在实现新功能时能保持已有代码不变。
如果一个类已经完成开发、测试和审核工作,而且属于某个框架或者可被其他类的代码直接使用的话,对其代码进行修改就是有风险的。你可以创建一个子类并重写原始类的部分内容以完成不同的行为,而不是直接对原始类的代码进行修改。这样你既可以达成自己的目标,但同时又无需修改已有的原始类客户端。
子类不应该对其父类的问题负责。
当你扩展一个类时, 记住你应该要能在不修改客户端代码的情况下将子类的对象作为父类对象进行传递。
替换原则是用于预测子类是否与代码兼容,以及是否能与其超类对象协作的一组检查。这一概念在开发程序库和框架时非常重要,因为其中的类将会在他人的代码中使用——你是无法直接访问和修改这些代码的。
尽量缩小接口的范围,使得客户端的类不必实现其不需要的行为。根据接口隔离原则,你必须将“臃肿”的方法拆分为多个颗粒度更小的具体方法。客户端必须仅实现其实际需要的方法。
高层次的类不应该依赖于低层次的类。 两者都应该依赖于抽象接口。抽象接口不应依赖于具体实现。 具体实现应该依赖于抽象接口。
在本例中,高层次的预算报告类(BudgetReport)使用低层次的数据库类(MySQLDatabase)来读取和保存其数据。这意味着低层次类中的任何改变(例如当数据库服务器发布新版本时)都可能会影响到高层次的类,但高层次的类不应关注数据存储的细节。

修改前:高层次的类依赖于低层次的类。
要解决这个问题,你可以创建一个描述读写操作的高层接口,并让报告类使用该接口代替低层次的类。然后你可以修改或扩展低层次的原始类来实现业务逻辑声明的读写接口。

修改后:低层次的类依赖于高层次的抽象。
其结果是原始的依赖关系被倒置:现在低层次的类依赖于高层次的抽象。
RunLoop是线程基础架构的一部分。RunLoop存在的目的是让线程在没有任务处理的时候进入休眠,在有任务处理的时候运行。
RunLoop不是完全自管理的,需要你在适当的时候启动。
Cocoa和Core Foundation框架都提供了RunLoop相关的API。
你不需要自己创建RunLoop对象。每个线程,包括主线程都有一个对应的RunLoop对象。
只有子线程的RunLoop需要手动启动,主线程的RunLoop在App启动调用Main函数时就已运行。


初学者总是容易被一堆概念搞得晕头转向,我想其实无非是这几种:
1 | RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber){ |
1、createSignal好难啊;
2、subscriber是什么?
3、这个block什么时候调用?