はじめに
⚠️ このプロジェクトは現在開発中です。Flappyの初版 をできるだけ早くリリースするために、一生懸命に作業しています。お楽しみに!ドキュメンテーションとコード例は近日公開予定です。
Flappyは、LLMベースのエージェント/アプリケーションを構築するためのプログラミング言語に依存しないSDKです。始めるには、使用している言語を指定してください。
インストール
- NodeJS (TypeScript)
- Java
- Kotlin
- C#
npm install @plesito/node-flappy@next
# or yarn add @pleisto/node-flappy@next
Plugin
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
</extension>
</extensions>
</build>
Dependency
<dependency>
<groupId>com.pleisto</groupId>
<artifactId>flappy</artifactId>
<version>0.0.8</version>
</dependency>
<dependency>
<groupId>com.pleisto</groupId>
<artifactId>flappy-java-bindings</artifactId>
<version>0.0.8</version>
</dependency>
<dependency>
<groupId>com.pleisto</groupId>
<artifactId>flappy-java-bindings</artifactId>
<version>0.0.8</version>
<classifier>${os.detected.classifier}</classifier>
</dependency>
// plugins
id("com.google.osdetector") version "1.7.3"
// dependencies
implementation("com.pleisto:flappy:0.0.8")
implementation("com.pleisto:flappy-java-bindings:0.0.8")
implementation("com.pleisto:flappy-java-bindings:0.0.8:${osdetector.classifier}")
<PackageReference Include="Pleisto.Flappy" Version="0.0.0.4-alpha" />
LLMアダプターの作成
Flappyは、OpenAIのChatGPTやhugingfaceのtransformersなど、複数のLLM実装をサポートしています。また、LLMBase
インターフェースを実装することで、自分自身のLLMアダプターを作成することも可能です。
- NodeJS (TypeScript) with ChatGPT
- NodeJS (TypeScript) with Baichuan-53B API
- Java
- Kotlin
- C#
// you need to manually install `openai` package.
import { env } from 'node:process'
import OpenAI from 'openai'
import { ChatGPT } from '@pleisto/node-flappy'
const chatGpt = new ChatGPT(
new OpenAI({
apiKey: env.OPENAI_API_KEY!,
baseURL: env.OPENAI_API_BASE!
})
)
import { env } from 'node:process'
import { Baichuan } from '@pleisto/node-flappy'
const chatGpt = new Baichuan(
{
baichuan_api_key: env.BAICHUAN_API_KEY!,
baichuan_secret_key: env.BAICHUAN_SECRET_KEY!,
}
)
Dotenv dotenv = Dotenv.load();
ChatGPT llm = new ChatGPT(new ChatGPT.ChatGPTConfig(null, dotenv.get("OPENAI_TOKEN"), dotenv.get("OPENAI_API_BASE")));
val dotenv = dotenv()
val llm = ChatGPT(
ChatGPT.ChatGPTConfig(token = dotenv["OPENAI_TOKEN"], host = dotenv["OPENAI_API_BASE"])
)
var gpt35 = new ChatGPT(new OpenAIAPI
{
Auth = new APIAuthentication(apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")),
ApiUrlFormat = Environment.GetEnvironmentVariable("OPENAI_API_URL"),
ApiVersion = "v1",
}, "gpt-3.5-turbo", null, null);
エージェントの定義
人工知能では、エージェントは環境を認識し、判断を下し、特定の目標または目標セットを達成するための行動を取るように設計されたコンピュータプログラムまたはシステムを指します。エージェントは自律的に動作し、つまり、人間のオペレーターによって直接制御されることはありません。
Flappyのエコシステムでは、エージェントはLLMの多目的な伝送路として作用します。エージェントは、データの構造化、外部APIの呼び出し、またはLLMが生成したPythonコードのサンドボックス化など、様々なタスクを必要に応じて組み合わせて処理するように設計されています 。これは硬直した歯車ではなく、効率的で安全なLLMのインタラクションを必要に応じて適応する柔軟なツールです。
主要な概念
関数
関数はFlappyフレームワークにおけるエージェントの基盤となります。このドキュメンテーションでは、定義と利用が可能な2つの主要な関数タイプ、InvokeFunction
とSythesizedFunction
を紹介します。
InvokeFunction
はエージェントが環境と対話するための機能を提供し、LangchainのAgentsのToolsに類似しています。これは入力と出力のパラメータで定義され、その構造はLanguage Learning Model (LLM)が効率的に対話するために明確である必要があります。これらのパラメータと関数がユーザーのリクエストや世界との対話において果たす役割を理解することは、効率的なタスク計画のために重要です。SythesizedFunction
はLLMが演じる関数の一種です。その説明と入力と出力の構造を定義するだけでよく、LLMはその後、定義された形式で入力を処理し、期待される形式で出力を生成しようとします。この特徴は、LLMが構造化データの抽出タスクを実行したり、LLMが構造化データを出力することが期待されるシナリオで、特に有用です。
コードインタープリタ
Flappyのコードインタープリタは、ChatGPTコードインタープリタの等価な代替品として機能し、言語モデルがPythonコードを通じて複雑なユーザー要求を満たすことを可能にします。Flappyのコードインタープリタが市場の他のオープンソース実装と異なるのは、そのサンドボックス化された安全機能です。これにより、本番環境での展開に必要な厳格なセキュリティ要件を満たすことが確保されます。
- NodeJS (TypeScript) with ChatGPT
- Java
- Kotlin
- C#
import { createFlappyAgent, createInvokeFunction, createSynthesizedFunction, createCodeInterpreter, z } from '@pleisto/node-flappy'
enum Verdict {
Innocent = 'Innocent',
Guilty = 'Guilty',
Unknown = 'Unknown'
}
const MOCK_LAWSUIT_DATA =
"As Alex Jones continues telling his Infowars audience about his money problems and pleads for them to buy his products, his own documents show life is not all that bad — his net worth is around $14 million and his personal spending topped $93,000 in July alone, including thousands of dollars on meals and entertainment. The conspiracy theorist and his lawyers file monthly financial reports in his personal bankruptcy case, and the latest one has struck a nerve with the families of victims of Sandy Hook Elementary School shooting. They're still seeking the $1.5 billion they won last year in lawsuits against Jones and his media company for repeatedly calling the 2012 massacre a hoax on his shows. “It is disturbing that Alex Jones continues to spend money on excessive household expenditures and his extravagant lifestyle when that money rightfully belongs to the families he spent years tormenting,” said Christopher Mattei, a Connecticut lawyer for the families. “The families are increasingly concerned and will continue to contest these matters in court.” In an Aug. 29 court filing, lawyers for the families said that if Jones doesn’t reduce his personal expenses to a “reasonable” level, they will ask the bankruptcy judge to bar him from “further waste of estate assets,” appoint a trustee to oversee his spending, or dismiss the bankruptcy case. On his Infowars show Tuesday, Jones said he’s not doing anything wrong."
const agent = createFlappyAgent({
llm: chatGpt,
// Define your agent's features.
features: [
createCodeInterpreter(),
createSynthesizedFunction({
name: 'getMeta',
description: 'Extract meta data from a lawsuit full text.',
args: z.object({
lawsuit: z.string().describe('Lawsuit full text.')
}),
returnType: z.object({
verdict: z.nativeEnum(Verdict),
plaintiff: z.string(),
defendant: z.string(),
judgeOptions: z.array(z.string())
})
}),
createInvokeFunction({
name: 'getLatestLawsuitsByPlaintiff',
description: 'Get the latest lawsuits by plaintiff.',
args: z.object({
plaintiff: z.string(),
arg1: z.boolean().describe('For demo purpose. set to False'),
arg2: z.array(z.string()).describe('ignore it').optional()
}),
returnType: z.string(),
resolve: async args => {
// Do something
// e.g. query SQL database
console.debug('getLatestLawsuitsByPlaintiff called', args)
return MOCK_LAWSUIT_DATA
}
})
]
})
public class Law {
public static final String LAW_EXECUTE_PLAN_PROMPT = "Find the latest case with the plaintiff being families of victims and return its metadata.";
static final String MOCK_LAWSUIT_DATA =
"As Alex Jones continues telling his Infowars audience about his money problems and pleads for them to buy his products, his own documents show life is not all that bad — his net worth is around $14 million and his personal spending topped $93,000 in July alone, including thousands of dollars on meals and entertainment. The conspiracy theorist and his lawyers file monthly financial reports in his personal bankruptcy case, and the latest one has struck a nerve with the families of victims of Sandy Hook Elementary School shooting. They're still seeking the $1.5 billion they won last year in lawsuits against Jones and his media company for repeatedly calling the 2012 massacre a hoax on his shows. “It is disturbing that Alex Jones continues to spend money on excessive household expenditures and his extravagant lifestyle when that money rightfully belongs to the families he spent years tormenting,” said Christopher Mattei, a Connecticut lawyer for the families. “The families are increasingly concerned and will continue to contest these matters in court.” In an Aug. 29 court filing, lawyers for the families said that if Jones doesn’t reduce his personal expenses to a “reasonable” level, they will ask the bankruptcy judge to bar him from “further waste of estate assets,” appoint a trustee to oversee his spending, or dismiss the bankruptcy case. On his Infowars show Tuesday, Jones said he’s not doing anything wrong.";
public static FlappyFeatureBase<?, ?> lawGetLatestLawsuitsByPlaintiff = new FlappyInvokeFunction(
"getLatestLawsuitsByPlaintiff",
"Get the latest lawsuits by plaintiff.",
GetLatestLawsuitsArguments.class,
String.class,
(a, agent, $completion) -> MOCK_LAWSUIT_DATA
);
public static FlappyFeatureBase<?, ?> lawGetMeta = new FlappySynthesizedFunction(
"getMeta",
"Extract meta data from a lawsuit full text.",
LawMetaArguments.class,
LawMetaReturn.class
);
public static void main(String[] args) throws ExecutionException, InterruptedException {
Dotenv dotenv = Dotenv.load();
ChatGPT llm = new ChatGPT(new ChatGPT.ChatGPTConfig(null, dotenv.get("OPENAI_TOKEN"), dotenv.get("OPENAI_API_BASE")));
FlappyBaseAgent agent = new FlappyBaseAgent(
llm, Arrays.asList(lawGetMeta, lawGetLatestLawsuitsByPlaintiff)
);
}
public enum Verdict {
Innocent, Guilty, Unknown
}
public static class LawMetaReturn {
@FlappyField
Verdict verdict;
@FlappyField
String plaintiff;
@FlappyField
String defendant;
@FlappyField
List<String> judgeOptions;
public Verdict getVerdict() {
return verdict;
}
public void setVerdict(Verdict verdict) {
this.verdict = verdict;
}
public String getPlaintiff() {
return plaintiff;
}
public void setPlaintiff(String plaintiff) {
this.plaintiff = plaintiff;
}
public String getDefendant() {
return defendant;
}
public void setDefendant(String defendant) {
this.defendant = defendant;
}
public List<String> getJudgeOptions() {
return judgeOptions;
}
public void setJudgeOptions(List<String> judgeOptions) {
this.judgeOptions = judgeOptions;
}
}
static class GetLatestLawsuitsArguments {
@FlappyField
String plaintiff;
@FlappyField(description = "For demo purpose. set to False")
Boolean arg1;
@FlappyField(description = "ignore it", optional = true)
List<String> arg2 = null;
public String getPlaintiff() {
return plaintiff;
}
public void setPlaintiff(String plaintiff) {
this.plaintiff = plaintiff;
}
public Boolean getArg1() {
return arg1;
}
public void setArg1(Boolean arg1) {
this.arg1 = arg1;
}
public List<String> getArg2() {
return arg2;
}
public void setArg2(List<String> arg2) {
this.arg2 = arg2;
}
}
static class LawMetaArguments {
@FlappyField(description = "Lawsuit full text.")
String lawsuit;
public String getLawsuit() {
return lawsuit;
}
public void setLawsuit(String lawsuit) {
this.lawsuit = lawsuit;
}
}
}
import flappy.FlappyBaseAgent
import flappy.annotations.FlappyField
import flappy.features.FlappyInvokeFunction
import flappy.features.FlappySynthesizedFunction
import flappy.llms.ChatGPT
import io.github.cdimascio.dotenv.dotenv
enum class Verdict {
Innocent, Guilty, Unknown
}
const val MOCK_LAWSUIT_DATA =
"""As Alex Jones continues telling his Infowars audience about his money problems and pleads for them to buy his products, his own documents show life is not all that bad — his net worth is around $14 million and his personal spending topped $93,000 in July alone, including thousands of dollars on meals and entertainment. The conspiracy theorist and his lawyers file monthly financial reports in his personal bankruptcy case, and the latest one has struck a nerve with the families of victims of Sandy Hook Elementary School shooting. They're still seeking the $1.5 billion they won last year in lawsuits against Jones and his media company for repeatedly calling the 2012 massacre a hoax on his shows. “It is disturbing that Alex Jones continues to spend money on excessive household expenditures and his extravagant lifestyle when that money rightfully belongs to the families he spent years tormenting,” said Christopher Mattei, a Connecticut lawyer for the families. “The families are increasingly concerned and will continue to contest these matters in court.” In an Aug. 29 court filing, lawyers for the families said that if Jones doesn’t reduce his personal expenses to a “reasonable” level, they will ask the bankruptcy judge to bar him from “further waste of estate assets,” appoint a trustee to oversee his spending, or dismiss the bankruptcy case. On his Infowars show Tuesday, Jones said he’s not doing anything wrong."""
class LawMetaArguments(
@FlappyField(description = "Lawsuit full text.")
val lawsuit: String
)
class LawMetaReturn(
@FlappyField
val verdict: Verdict,
@FlappyField
val plaintiff: String,
@FlappyField
val defendant: String,
@FlappyField
val judgeOptions: List<String>
)
val lawGetMeta = FlappySynthesizedFunction(
name = "getMeta",
description = "Extract meta data from a lawsuit full text.",
args = LawMetaArguments::class.java,
returnType = LawMetaReturn::class.java,
)
class GetLatestLawsuitsArguments(
@FlappyField
val plaintiff: String,
@FlappyField(description = "For demo purpose. set to False")
val arg1: Boolean,
@FlappyField(description = "ignore it", optional = true)
val arg2: List<String>?
)
val lawGetLatestLawsuitsByPlaintiff = FlappyInvokeFunction(
name = "getLatestLawsuitsByPlaintiff",
description = "Get the latest lawsuits by plaintiff.",
args = GetLatestLawsuitsArguments::class.java,
returnType = String::class.java,
invoker = { _, _ -> MOCK_LAWSUIT_DATA }
)
const val LAW_EXECUTE_PLAN_PROMPT =
"Find the latest case with the plaintiff being families of victims and return its metadata."
suspend fun main(args: Array<String>) {
val dotenv = dotenv()
val chatGPT = ChatGPT(
ChatGPT.ChatGPTConfig(token = dotenv["OPENAI_TOKEN"], host = dotenv["OPENAI_API_BASE"])
)
val agent = FlappyBaseAgent(
maxRetry = 2,
inferenceLLM = chatGPT,
features = listOf(lawGetMeta, lawGetLatestLawsuitsByPlaintiff)
)
}
internal class LawCase
{
public static void Main()
{
var gpt35 = new ChatGPT(new OpenAIAPI
{
Auth = new APIAuthentication(apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")),
ApiUrlFormat = Environment.GetEnvironmentVariable("OPENAI_API_URL"),
ApiVersion = "v1",
}, "gpt-3.5-turbo", null, Logger.CreateLogger<ChatGPT>());
var lawAgent = new FlappyAgent(new FlappyAgentConfig
{
LLM = gpt35,
Features = new IFlappyFeature[]
{
new SynthesizedFeature<getMeta_Args,getMeta_Return,FlappyFeatureOption>(new SynthesizedFeatureDefinition<getMeta_Args,getMeta_Return>
{
Name = "getMeta",
Description = "Extract meta data from a lawsuit full text.",
Args = new getMeta_Args(),
ReturnType = new getMeta_Return()
}),
new InvokeFeature<getLatestLawsuits_Args,getMeta_Args,FlappyFeatureOption>(new InvokeFeatureDefinition<getLatestLawsuits_Args, getMeta_Args>
{
Name = "getLatestLawsuitsByPlaintiff",
Description= "Get the latest lawsuits by plaintiff.",
Args = new getLatestLawsuits_Args(),
ReturnType = new getMeta_Args(),
Resolve = (args) =>
{
Console.WriteLine($"====================== getLatestLawsuitsByPlaintiff call =========================");
Console.WriteLine($"getLatestLawsuitsByPlaintiff called");
Console.WriteLine(JObject.FromObject(args).ToString());
Console.WriteLine($"====================== getLatestLawsuitsByPlaintiff call =========================");
return Task.FromResult(new getMeta_Args
{
lawsuit =MOCK_LAWSUIT_DATA
});
}
})
},
}, null, null, Logger.CreateLogger<FlappyAgent>());
}
private const string LAW_EXECUTE_PLAN_PROMPT = "Find the resume of a frontend engineer and return their metadata.";
private const string MOCK_LAWSUIT_DATA =
"As Alex Jones continues telling his Infowars audience about his money problems and pleads for them to buy his products, his own documents show life is not all that bad — his net worth is around $14 million and his personal spending topped $93,000 in July alone, including thousands of dollars on meals and entertainment. The conspiracy theorist and his lawyers file monthly financial reports in his personal bankruptcy case, and the latest one has struck a nerve with the families of victims of Sandy Hook Elementary School shooting. They're still seeking the $1.5 billion they won last year in lawsuits against Jones and his media company for repeatedly calling the 2012 massacre a hoax on his shows. “It is disturbing that Alex Jones continues to spend money on excessive household expenditures and his extravagant lifestyle when that money rightfully belongs to the families he spent years tormenting,” said Christopher Mattei, a Connecticut lawyer for the families. “The families are increasingly concerned and will continue to contest these matters in court.” In an Aug. 29 court filing, lawyers for the families said that if Jones doesn’t reduce his personal expenses to a “reasonable” level, they will ask the bankruptcy judge to bar him from “further waste of estate assets,” appoint a trustee to oversee his spending, or dismiss the bankruptcy case. On his Infowars show Tuesday, Jones said he’s not doing anything wrong.";
}
internal class getLatestLawsuits_Args
{
public string plantiff { get; set; }
[Description("For demo purpose. set to False")]
public bool arg1 { get; set; }
[Description("ignore it")]
public bool arg2 { get; set; }
public override string ToString()
{
return JObject.FromObject(this).ToString();
}
}
internal class getMeta_Args
{
[Description("Lawsuit full text.")]
public string lawsuit { get; set; }
public override string ToString()
{
return JObject.FromObject(this).ToString();
}
}
[JsonObject(ItemRequired = Required.Always)]
public class getMeta_Return
{
[JsonConverter(typeof(StringEnumConverter))]
[DefaultValue(Verdict.Unknow)]
public Verdict verdict { get; set; } = Verdict.Unknow;
public string plaintiff { get; set; } = string.Empty;
public string defendant { get; set; } = string.Empty;
public string[] judgeOptions { get; set; } = Array.Empty<string>();
public override string ToString()
{
return JObject.FromObject(this).ToString();
}
}
public enum Verdict
{
Innocent,
Guilty,
Unknow
}
Call your agent
Create and execute a action plan
Augmented Language Models (ALMs) blend the reasoning capabilities of Large Language Models (LLMs) with tools that allow for knowledge retrieval and action execution. Existing ALM systems trigger LLM thought processes while pulling observations from these tools in an interleaved fashion. Specifically, an LLM reasons to call an external tool, gets halted to fetch the tool's response, and then decides the next action based on all preceding response tokens. Such a paradigm, though straightforward and easy to implement, often leads to huge computation complexity from redundant prompts and repeated execution.
Flappy uses ReWOO instead of ReAct to minimize LLM token output, thereby reducing costs. Building on this, the agent is equipped to autonomously devise a plan based on the user's prompt. This involves determining the sequence of functions that need to be invoked to fulfill the given prompt. The execution then proceeds according to this formulated plan, further optimizing the efficiency of our system.
- NodeJS (TypeScript) with ChatGPT
- Java
- Kotlin
- C#
agent.executePlan('Find the latest case with the plaintiff being families of victims and return its metadata.')
Future<LawMetaReturn> future = lawAgent.executePlanAsync(LAW_EXECUTE_PLAN_PROMPT);
lawAgent.use {
it.executePlan<LawMetaReturn>(LAW_EXECUTE_PLAN_PROMPT)
}
var data = lawAgent.ExecutePlan(LAW_EXECUTE_PLAN_PROMPT).GetAwaiter().GetResult();
Call synthesized function directly
You can also call synthesized function directly without executing a action plan.
- NodeJS (TypeScript) with ChatGPT
- Java
- Kotlin
- C#
agent.callFeature('getMeta', {lawsuit: MOCK_LAWSUIT_DATA})
Future<String> future = agent.callFeatureAsync("getMeta", LAW_EXECUTE_PLAN_PROMPT);
agent.use {
val future = it.callFeature<String>("getMeta", LAW_EXECUTE_PLAN_PROMPT)
}
var result = await agent.CallFeature<TestObject>(callName, LAW_EXECUTE_PLAN_PROMPT);
Call Code Interpreter
Code Interpreter currently needs to be called directly. We are working on a better way to integrate it with the agent.
- NodeJS (TypeScript) with ChatGPT
- Java
- Kotlin
- C#
agent.executePlan(
'There are some rabbits and chickens in a barn. What is the number of chickens if there are 396 legs and 150 heads in the barn?'
)
// or call feature directly:
agent.callFeature('pythonSandbox', {
code: 'There are some rabbits and chickens in a barn. What is the number of chickens if there are 396 legs and 150 heads in the barn?'
})
Future<String> future = agent.executePlanAsync("There are some rabbits and chickens in a barn. What is the number of chickens if there are 396 legs and 150 heads in the barn?");
agent.use {
it.executePlan<String>("There are some rabbits and chickens in a barn. What is the number of chickens if there are 396 legs and 150 heads in the barn?")
}
var data = lawAgent.ExecutePlan("There are some rabbits and chickens in a barn. What is the number of chickens if there are 396 legs and 150 heads in the barn?").GetAwaiter().GetResult();