Closed9

langchainでfunction calling

もぶもぶもぶもぶ

参考: Tools as OpenAI Functions

langchain==0.0.310

サンプルプログラム

from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage
from langchain.tools import MoveFileTool, format_tool_to_openai_function
from dotenv import load_dotenv

load_dotenv()

model = ChatOpenAI(model="gpt-3.5-turbo-0613")


tools = [MoveFileTool()]
functions = [format_tool_to_openai_function(t) for t in tools]

message = model.predict_messages(
    [HumanMessage(content="move file foo to bar")], functions=functions
)

print(message)

実行結果

content='' additional_kwargs={'function_call': {'name': 'move_file', 'arguments': '{\n  "source_path": "foo",\n  "destination_path": "bar"\n}'}}

Langsmithでの表示

もぶもぶもぶもぶ

積み残し

  • カスタムツールの作成方法
  • predict_messagesのfunctionsに渡せる型の調査
  • メモリの追加方法調査
もぶもぶもぶもぶ

より生のfunction callingに近いやり方

# https://python.langchain.com/docs/modules/chains/how_to/openai_functions
from typing import Optional
from langchain.chains.openai_functions import (
    create_structured_output_chain,
)
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from dotenv import load_dotenv
from langchain.pydantic_v1 import BaseModel, Field

load_dotenv()


class Person(BaseModel):
    name: str = Field(..., description="The name of the person")
    age: int = Field(..., description="The age of the person")
    fav_food: Optional[str] = Field(None, description="The favorite food of the person")


llm = ChatOpenAI(model="gpt-4", temperature=0)
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "human",
            "Use the given format to extract information from the following input: {input}",
        ),
        ("human", "Tip: Make sure to answer in the correct format"),
    ]
)

chain = create_structured_output_chain(Person, llm, prompt, verbose=True)
result: Person = chain.run("Sally is 13")
print(result)

もぶもぶもぶもぶ
from langchain.chains.openai_functions import (
    create_structured_output_chain,
)
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.pydantic_v1 import BaseModel, Field
from langchain.chains import SimpleSequentialChain
from typing import List
from dotenv import load_dotenv


load_dotenv()


llm = ChatOpenAI(model="gpt-4", temperature=1.0)


class IngredientList(BaseModel):
    ingredients: List[str] = Field(..., description="食材のリスト")


# 創作料理のクラス定義。料理名と説明文を持つ
class CreativeDish(BaseModel):
    name: str = Field(..., description="料理名")
    description: str = Field(..., description="料理の説明文")


suggest_ingedients_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "human",
            "{input}に適した食材を5つ提案してください",
        ),
    ]
)

suggest_creative_dish_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "human",
            "以下を食材として利用した創作料理を提案してください: {input}",
        ),
    ]
)


# 食材の提案チェーン
suggest_ingredients_chain = create_structured_output_chain(
    IngredientList, llm, suggest_ingedients_prompt, verbose=True
)

# 創作料理の提案チェーン
suggest_creative_dish_chain = create_structured_output_chain(
    CreativeDish, llm, suggest_creative_dish_prompt, verbose=True
)

overall_chain = SimpleSequentialChain(
    chains=[suggest_ingredients_chain, suggest_creative_dish_chain], verbose=True
)

dish = overall_chain.run("闇鍋")

# 結果の表示
print(dish)

> Entering new SimpleSequentialChain chain...


> Entering new LLMChain chain...
Prompt after formatting:
Human: 闇鍋に適した食材を5つ提案してください

> Finished chain.
ingredients=['豚肉', 'ネギ', 'キムチ', '豆腐', 'エノキ']
Prompt after formatting:
Human: 以下を食材として利用した創作料理を提案してください: ingredients=['豚肉', 'ネギ', 'キムチ', '豆腐', 'エノキ']

> Finished chain.
name='豚肉とキムチのホットポット' description='テーブル上で温まりながら楽しむ、豚肉とキムチのホットポットで温まりましょう。まずは豚肉を炒めてから、ネギ、キムチ、豆腐、エノ キを加えて煮込みます。豚肉の旨味とキムチの辛さが絶妙に絡み合い、また豆腐とエノキはその旨味を吸い上げます。ご飯と一緒に食べても、そのままスープとして楽しんでも美味しい一品です。'

> Finished chain.
name='豚肉とキムチのホットポット' description='テーブル上で温まりながら楽しむ、豚肉とキムチのホットポットで温まりましょう。まずは豚肉を炒めてから、ネギ、キムチ、豆腐、エノ キを加えて煮込みます。豚肉の旨味とキムチの辛さが絶妙に絡み合い、また豆腐とエノキはその旨味を吸い上げます。ご飯と一緒に食べても、そのままスープとして楽しんでも美味しい一品です。'


もぶもぶもぶもぶ

batchで並列で色々やれるの便利

from langchain.chains.openai_functions import (
    create_structured_output_chain,
)
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.pydantic_v1 import BaseModel, Field
from typing import List
from dotenv import load_dotenv
import random


load_dotenv()


llm = ChatOpenAI(model="gpt-4", temperature=1.0)


class IngredientList(BaseModel):
    ingredients: List[str] = Field(..., description="食材のリスト")


# 創作料理のクラス定義。料理名と説明文を持つ
class CreativeDish(BaseModel):
    name: str = Field(..., description="料理名")
    description: str = Field(..., description="料理の説明文")


suggest_ingedients_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "human",
            "創作料理に適した食材を{input}個提案してください",
        ),
    ]
)

suggest_creative_dish_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "human",
            "以下を食材として利用した創作料理を提案してください: {input}",
        ),
    ]
)


# 食材の提案チェーン
suggest_ingredients_chain = create_structured_output_chain(
    IngredientList, llm, suggest_ingedients_prompt, verbose=True
)

# 創作料理の提案チェーン
suggest_creative_dish_chain = create_structured_output_chain(
    CreativeDish, llm, suggest_creative_dish_prompt, verbose=True
)

num_of_ingredients_for_dish = 3
num_of_dish = 5
ingredients: IngredientList = suggest_ingredients_chain.run("10")
ingredients_list = [
    random.sample(ingredients.ingredients, num_of_ingredients_for_dish)
    for _ in range(num_of_dish)
]
dishes = suggest_creative_dish_chain.batch(
    [{"input": ingredients} for ingredients in ingredients_list]
)

# 結果の表示
for dish in dishes:
    print(dish["input"])
    print(dish["function"])
もぶもぶもぶもぶ

トレースをとることを考えると、suggest_ingredients_chainとsuggest_creative_dish_chainもまとめて一つのRuunableにまとめたいなー。

LangChain Expression Language (LCEL)をもうちょっと掘るか。

もぶもぶもぶもぶ

一つのチェーンにまとめられそう

from langchain.schema.runnable import RunnableLambda, RunnableMap


def length_text(text: str) -> int:
    return len(text)


def double_text(text: str) -> str:
    return text + text


chain = RunnableLambda(length_text) | RunnableLambda(double_text)
result = chain.invoke("hello")
print(result)

chain1 = RunnableLambda(length_text)
chain2 = RunnableLambda(double_text)
map_chain = RunnableMap({"length": chain1, "double": chain2})

total_chain = RunnableLambda(double_text) | map_chain
result2 = total_chain.invoke("hello")
print(result2)
10
{'length': 10, 'double': 'hellohellohellohello'}
このスクラップは2023/10/07にクローズされました