Ruby版LangchainのLangchainrbをさわってみる(チャット編)
はじめに
LLM 関係のライブラリとして Langchain が有名ですが、本家(?)は Python と TypeScript のみです。
普段は Ruby をメインで書いているため Ruby でも LLM を Langchain 相当のことをしたいと思い調べていると Ruby 版 Langchain として Langchainrb が開発されていることを知りました。
本記事では Langchainrb の基本的な使い方を紹介したいと思います。
(バージョン:0.13.4)
サポートしている LLM
上記が記事執筆時点で Langchainrb がサポートしている LLM です。
私はまだ OpenAI の LLM しか触ったことがないのですが、有名な LLM は大体サポートされている感じでしょうか。
Chat
ここでは OpenAI の LLM で基本的なチャットを試してみます。
準備として Langchainrb の gem 以外に ruby-openai の gem が必要となります。
gem "ruby-openai", "~> 6.3.0"
単純に LLM からのレスポンスを受け取る場合は以下の記述だけです。
とても簡単ですね。
llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
llm.chat(messages: [{role: "user", content: "こんにちは!"}])
chat のレスポンスは次のような形式になります。
#<Langchain::LLM::OpenAIResponse:0x0000000111c33630
@model=nil,
@raw_response=
{"id"=>"chatcmpl-9bFvjKlAIXLOS9LEOJL784QwI4kVT",
"object"=>"chat.completion",
"created"=>1718666399,
"model"=>"gpt-3.5-turbo-0125",
"choices"=>
[{"index"=>0,
"message"=>{"role"=>"assistant", "content"=>"こんにちは!お元気ですか?何かお手伝いできることがありますか?"},
"logprobs"=>nil,
"finish_reason"=>"stop"}],
"usage"=>{"prompt_tokens"=>8, "completion_tokens"=>25, "total_tokens"=>33},
"system_fingerprint"=>nil}>
Langchain::LLM::OpenAIResponse クラスには completion というメソッドが実装されており、メッセージのみを取り出すことができます。
llm.chat(messages: [{role: "user", content: "こんにちは!"}]).completion
# "こんにちは!お元気ですか?何かお手伝いできることがありますか?"
上記の例では LLM のインスタンスに api_key しか渡しませんでしたが、当然モデルの指定をすることが可能です。
モデルを指定する場合は default_options の chat_completion_model_name として指定します。
その他 default_options で指定できるのは以下のとおりです。
Langchain::LLM::OpenAI.new(
api_key: ENV["OPENAI_API_KEY"],
default_options: {
chat_completion_mode_name: #{モデル名},
n: #{n},
temperature: #{temperature},
}
)
ChatGPT のようにストリーミングで表示するには一手間必要になります。
chat メソッドはブロックを受け取ることができるので、チャンクごとに出力することで滑らかに表示させることが可能です。
response = ""
llm.chat(messages: messages) do | chunk |
content = chunk.dig('delta', 'content') rescue nil
next unless content
print content
response += content
end
Prompt Management
プロンプトマネジメントはその名の通りプロンプト管理機能です。
JSON でプロンプトを管理することができるので、機能ごとにプロンプトだけを切り出すことが容易になります。
プロンプトマネジメントは LLM の API に依存していないので、プロンプトを扱うどのアプリでも取り入れやすい機能といえます。
基本の使い方。
prompt = Langchain::Prompt::PromptTemplate.new(template: "Tell me a {adjective} joke about {content}.", input_variables: ["adjective", "content"])
prompt.format(adjective: "funny", content: "chickens") # "Tell me a funny joke about chickens."
プロンプトを JSON 形式で保存したり、JSON からロード可能
# jsonで保存
prompt.save(file_path: "spec/fixtures/prompt/prompt_template.json")
# jsonからロードする
prompt = Langchain::Prompt.load_from_path(file_path: "spec/fixtures/prompt/prompt_template.json")
prompt.input_variables # ["adjective", "content"]
json 以外にも yaml または yml がサポートされているようです。
フューショットラーニングのプロンプトも簡潔に書けます。
prompt = Langchain::Prompt::FewShotPromptTemplate.new(
prefix: "足し算をしてください",
suffix: "Input: {input}\nOutput:",
example_prompt: Langchain::Prompt::PromptTemplate.new(
input_variables: ["input", "output"],
template: "Input: {input}\nOutput: {output}"
),
examples: [
{ "input": "3+5", "output": "8" },
{ "input": "4+1", "output": "5" }
],
input_variables: ["input"]
)
prompt.format(input: '100+300')
# "足し算をしてください\n\nInput: 3+5\nOutput: 8\n\nInput: 4+1\nOutput: 5\n\nInput: 3+5\nOutput:"
Output Parser
LLM のレスポンスを指定したスキーマで構造化することができる機能です。
OpenAI の API には json モードがあるので不要かもしれませんが、それ以外の LLM を使用する際に使えるかもしれません。
# プロンプト
json_schema = {
type: "object",
properties: {
name: {
type: "string",
description: "Persons name"
},
age: {
type: "number",
description: "Persons age"
},
interests: {
type: "array",
items: {
type: "object",
properties: {
interest: {
type: "string",
description: "A topic of interest"
},
levelOfInterest: {
type: "number",
description: "A value between 0 and 100 of how interested the person is in this interest"
}
},
required: ["interest", "levelOfInterest"],
additionalProperties: false
},
minItems: 1,
maxItems: 3,
description: "A list of the person's interests"
}
},
required: ["name", "age", "interests"],
additionalProperties: false
}
parser = Langchain::OutputParsers::StructuredOutputParser.from_json_schema(json_schema)
prompt = Langchain::Prompt::PromptTemplate.new(template: "Generate details of a fictional character.\n{format_instructions}\nCharacter description: {description}", input_variables: ["description", "format_instructions"])
prompt_text = prompt.format(description: "Korean chemistry student", format_instructions: parser.get_format_instructions)
# Generate details of a fictional character.
# You must format your output as a JSON value that adheres to a given "JSON Schema" instance.
# ...
# レスポンスをパースする
llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
llm_response = llm.chat(messages: [{role: "user", content: prompt_text}]).completion
parser.parse(llm_response)
# {
# "name" => "Kim Ji-hyun",
# "age" => 22,
# "interests" => [
# {
# "interest" => "Organic Chemistry",
# "levelOfInterest" => 85
# },
# ...
# ]
# }
さいごに
最後まで読んでいただきありがとうございました。
本記事では基本のチャットに使う機能のみ紹介しました。
次回記事では Assistant と RAG の機能を触ってまとめてみようと思います。
Discussion