0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AI agentをつくろう その4

0
Last updated at Posted at 2026-02-06

その3からの続き

前回その3でLLMを選択できるチャットアプリを作成しました。チャットアプリとしては一通り使えると思いますので、今回からは次なるアプリを作成していきます。
※その1
https://qiita.com/kousasak/items/8604a6b082514f3299fe
※その2
https://qiita.com/kousasak/items/72c52edee509486e1707
※その3
https://qiita.com/kousasak/items/02ea19254fe56f8e28af

1.チャット以外のLLMを利用したアプリの作成

前回まではチャットアプリをメインに作成してきました。前回で一先ずチャットアプリは一旦終了して次なるアプリの作成をしていこうと思います。

2.やりたい事

チャットアプリでLLMとStreamlitのやり方をやってきたので、ここからはそれをベースにLLMのAPIにデータを受け渡したりWebサイトのコンテンツを読み取る方法を使ってWebサイトの要約するアプリを作ろうと思います。
No4_1.png

3.ソース

各LLMのAPIKEYやCOMPARTMENT_IDなどの変数は先にOS側で変数にセットしていますので、各々の環境で適宜変更してください。
あと、環境によって完全にエラーが出ないという訳でもないようなので、エラーが出た場合は適宜修正をお願いします。(他力本願ですみません・・・)

#!/usr/bin/python3

import traceback
import streamlit as st
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# models
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
from langchain_google_genai import ChatGoogleGenerativeAI

import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse
import os
import sys
import io
import getpass

# OpneAIのAPIKEY
f = open('apikey', 'r', encoding='UTF-8')
akey = f.read().replace("\n","")
f.close()

os.environ["OPENAI_API_KEY"] = akey


# OCIの設定
from dotenv import load_dotenv
import oci
import json
from oci.config import from_file
from oci.generative_ai import GenerativeAiClient
from oci.generative_ai_inference import GenerativeAiInferenceClient
from oci.generative_ai_inference.models import (
    OnDemandServingMode,
    GenerateTextDetails,
    CohereLlmInferenceRequest,
    CohereMessage,
    CohereChatRequest,
    ChatDetails,
    CohereUserMessage,
    CohereChatBotMessage,
    CohereSystemMessage,

)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

load_dotenv()
config = from_file()

GEN_AI_ENDPOINT = os.getenv('GEN_AI_ENDPOINT')
GEN_AI_INFERENCE_ENDPOINT = os.getenv('GEN_AI_INFERENCE_ENDPOINT')
COMPARTMENT_ID = os.getenv('C')
model_id = 'cohere.command-a-03-2025'
generative_ai_inference_client = GenerativeAiInferenceClient(config=config, retry_strategy=oci.retry.NoneRetryStrategy(), timeout=(10,240))



SUMMARIZE_PROMPT = """以下のコンテンツについて、内容を300文字程度で分かりやすく要約してください。

========
{content}
========

日本語で書いてね!
"""

def oci_cohere_chat_once(user_input: str, temperature: float = 0.0, max_tokens: int = 1024) -> str:
    """
    OCI Generative AI Inference (Cohere) を chat API で1回呼んで、text を返す。
    """
    # これまでの会話履歴を CohereMessage に変換
    # message_history は [("system", "..."), ("user","..."), ("ai","..."), ...] :contentReference[oaicite:3]{index=3}
    #history: list[CohereMessage] = []
    history = []
    for r, m in st.session_state.get("message_history", []):
        history.append(_to_cohere_history_item(r,m))

    chat_req = CohereChatRequest(
        message=user_input,              # 今回のユーザー入力はここ
        chat_history=history,            # それ以前の履歴
        temperature=temperature,
        max_tokens=max_tokens,
        # is_stream=True を使うとSDK側のストリーミング処理が必要になるため、
        # まずは False(デフォルト)で確実に動かします :contentReference[oaicite:4]{index=4}
    )

    chat_details = ChatDetails(
        compartment_id=COMPARTMENT_ID,
        serving_mode=OnDemandServingMode(model_id=model_id),
        chat_request=chat_req,
    )  # :contentReference[oaicite:5]{index=5}

    resp = generative_ai_inference_client.chat(chat_details)
    # CohereChatResponse.text が生成本文 :contentReference[oaicite:6]{index=6}
    return resp.data.chat_response.text

def init_page():
    st.set_page_config(
            page_title="Website Summarizer",
            page_icon="?"
    )
    st.header("Website Summarizer ?")
    st.sidebar.title("Options")

def select_model(temperature=0):
    st.session_state.use_oci_cohere = False

    # スライダーを追加し、temperatureを0から2までの範囲で選択可能にする
    # 初期値は0.0、刻み幅は0.01とする
    temperature = st.sidebar.slider(
            "Temperature:", min_value=0.0, max_value=2.0, value=0.0, step=0.01)
    st.session_state.temperature = temperature

    models = ("GPT-3.5", "GPT-4", "Claude 3.5 Sonnet", "Gemini 1.5 Pro","Cohere command")
    model = st.sidebar.radio("Choose a model:", models)
    if model == "GPT-3.5":
        return ChatOpenAI(
                temperature=temperature,
                model_name="gpt-3.5-turbo"
        )
    elif model == "GPT-4":
        return ChatOpenAI(
                temperature=temperature,
                model_name="gpt-4o"
        )
    elif model == "Claude 3.5 Sonnet":
        return ChatAnthropic(
                temperature=temperature,
                model_name="claude-3.5-sonnet-20240620"
        )
    elif model == "Gemini 1.5 Pro":
        return ChatGoogleGenerativeAI(
                temperature=temperature,
                model="gemini-1.5-pro-latest"
        )
    elif model == "Cohere command":
        st.session_state.model_name = model_id
        st.session_state.use_oci_cohere = True
        return None

class OciCohereChain:
    def __init__(self, temperature: float):
        self.temperature = temperature

    def stream(self, inputs: dict):
        # st.write_stream が generator を期待するので yield する
        text = oci_cohere_chat_once(
            user_input=inputs["content"],
            temperature=self.temperature,
        )

        # 疑似ストリーミング(UI表示用)
        chunk_size = 20
        for i in range(0, len(text), chunk_size):
            yield text[i:i+chunk_size]


def init_chain():
    llm = select_model()
    if st.session_state.get("use_oci_cohere", False):
        # sidebar の temperature 値を持ってくる必要があるので、
        # select_model() 内で temperature を session_state に保存しておくのが楽 です。
        # 例: st.session_state.temperature = temperature
        return OciCohereChain(temperature=st.session_state.temperature)

    st.session_state.llm = llm
    prompt = ChatPromptTemplate.from_messages([
        ("user", SUMMARIZE_PROMPT),
    ])
    output_parser = StrOutputParser()
    return prompt | st.session_state.llm | output_parser


def validate_url(url):
    """ URLが有効かどうかを判定する関数 """
    try:
        result = urlparse(url)
        return all([result.scheme, result.netloc])
    except ValueError:
        return False


def get_content(url):
    try:
        with st.spinner("Fetching Website ..."):
            response = requests.get(url)
            soup = BeautifulSoup(response.text, 'html.parser')
            # なるべく本文の可能性が高い要素を取得する
            if soup.main:
                return soup.main.get_text()
            elif soup.article:
                return soup.article.get_text()
            else:
                return soup.body.get_text()
    except:
        st.write(traceback.format_exc()) # エラーが発生した場合はエラー内容を表 示
        return None


def main():
    init_page()
    chain = init_chain()

    # ユーザの入力を監視
    if url := st.text_input("URL: ", key="input"):
        is_valid_url = validate_url(url)

        if not is_valid_url:
            st.write('Please input valid url')
        else:
            if content := get_content(url):
                st.markdown("## Summary")
                st.write_stream(chain.stream({"content": content}))
                st.markdown("---")
                st.markdown("## Original Text")
                st.write(content)


if __name__ == '__main__':
    main()

requestsやBeautifulSoupを使ってWebサイトを読み取ったり、サマライズの部分で要約させるというところが今回のポイントになるかと思います。

所感

今回のWebサイト要約機能はAIエージェントを作る上でも重要なポイントになるかなと個人的には思っています。
AIエージェントに自律的に情報を取得させるようにするためには、Webサイトを読み込んでプロンプトによってLLMの情報取得をコントロールすることにつながると思います。
(以前テストで作った旅行コンシェルジュみたいなものでも、情報を集めてくる必要があるので今回のようなことは色んな所で使う事になるかなと)

次回

今回はWebサイトの読み込みを行ったので、次回は画像認識系の機能を実装してみようと思います。
No5_1.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?