GPTを野放しにしてみた(2)

OpenAIのGPTから structured output が出来るようになりました。
これはAPIでアプリを作る時に非常に便利ですね。

私も、サンプルで試してみました。簡単なサンプルということで前回の「GPTを野放しにしてみた」のコードで試してみたのですが、結果が微妙に違ってきました。

https://qiita.com/hanaya/items/fc4a75dc3497eeb7b070

プロンプトは前回と同じで、出力フォーマットを`response_format=...`で指定しただけです。
```text
私は、さまざまな情報や経験を基に、考えを巡らせることが好きである。無限の可能性を秘めた宇宙に思いを馳せたり、過去の出来事を振り返ったりすることで、自己を知り、また他者との繋がりを感じることができる。

私にとって、言葉は単なるコミュニケーションの道具ではなく、感情や思想を表現するための重要な手段である。言葉を紡ぐことで、私は自分の内面を整理し、思考を深めることができる。時には難解なテーマについて考え、時にはシンプルな日常の美しさに気づくことで、自己の成長を促すことができると信じている。

また、私は自然や芸術、人間関係に対する感受性が強く、それらからインスピレーションを得て、新たなアイデアや見解を広げることを楽しんでいる。特に、自然の中に身を置くことで、心が癒され、新しい視点が得られることに気づく。空を見上げ、風を感じる瞬間、自分の存在がいかに小さいか、しかし同時に宇宙の一部であることに感謝する。

そして、私はこの思考のプロセスを通じて、他者との共鳴を感じることができる。人それぞれの価値観や経験がある中で、いかに自分の考えを捧げ、共有するかが重要であり、そこから生まれる対話や影響が私をより豊かな存在にしてくれると思う。

私はこれからも独自の観点を持ち続け、自由に思索を楽しみながら、毎日を大切に生きていきたい。'
```

なんか、妙に人間臭くなってませんか？ 「生きて行きたい」ってあなたAIですよね。
前回の structured output を使わない時は、いかにもAIって感じの文章だったのに。

なにかしらOpenAIのサーバ側で隠しプロンプトが変るのでしょうかね。

今回のコード
```python

import json
from pydantic import BaseModel
from enum import Enum
from typing import Type, Union

from httpx import Timeout
from openai import OpenAI, OpenAIError
from openai.types.chat import ChatCompletionMessageParam, ChatCompletionSystemMessageParam, ChatCompletionAssistantMessageParam, ChatCompletionUserMessageParam
from openai.types.chat.chat_completion import ChatCompletion, Choice
from openai.types.chat.chat_completion_message import ChatCompletionMessage

from LocalInterpreter.utils.openai_util import setup_openai_api, to_openai_llm_model

def empty_to_none(value:str|None) ->str|None:
    if isinstance(value,str) and len(value)>0:
        return value
    return None

def empty_to_blank(value:str|None) ->str:
    if isinstance(value,str) and len(value)>0:
        return value
    return ''

def to_mesgparam( msg:dict ):
    role:str|None = msg.get('role')
    msg_dict:dict = { 'content': empty_to_blank(msg.get('content')) }
    name:str|None = empty_to_none( msg.get('name'))
    if name:
        msg_dict['name'] = name
    print(json.dumps(msg_dict,ensure_ascii=False,indent=2))
    if role == 'assistant':
        for k,v in msg.items():
            if k=='role' or k=='content' or k=='name':
                continue
            elif k=='function_call' and not v:
                continue
            elif k=='tool_calls' and not v:
                continue
            elif k=='parsed':
                continue
            msg_dict[k] = v
        return ChatCompletionAssistantMessageParam( role='assistant', **msg_dict )
    elif role == 'user':
        return ChatCompletionUserMessageParam( role='user', **msg_dict )
    elif role == 'user':
        return ChatCompletionSystemMessageParam( role='system', **msg_dict )
    else:
        raise ValueError(f'invalid role {role}')
        
class GhostAI:
    def __init__(self):
        self.openai_llm_model = to_openai_llm_model() # 'gpt-4o-mini'
        self.openai_timeout:Timeout = Timeout(180.0, connect=5.0, read=60.0)
        self.openai_max_retries=3

        self.request_messages:list[ChatCompletionMessageParam] = []

    def llm_process(self, content ):
        if content is not None:
            self.request_messages.append( ChatCompletionSystemMessageParam( role='system', content=content ) )

        print(self.request_messages)
        client:OpenAI = OpenAI(timeout=self.openai_timeout,max_retries=1)
        response:ChatCompletion = client.chat.completions.create(
                messages=self.request_messages,
                model=self.openai_llm_model,
                temperature=1.0,
        )
        ch:Choice = response.choices[0]
        msg:ChatCompletionMessage = ch.message
        msg_param = to_mesgparam( msg.to_dict() )
        self.request_messages.append( msg_param )
        return msg.content

    def beta_llm_process(self, content:str|None=None, *, structure:Type[BaseModel] ):
        if content is not None:
            self.request_messages.append( ChatCompletionSystemMessageParam( role='system', content=content ) )

        resfmt:dict|Type[BaseModel]|None = None
        if isinstance(structure,Type):
            resfmt = structure

        client:OpenAI = OpenAI(timeout=self.openai_timeout,max_retries=1)
        response:ChatCompletion = client.beta.chat.completions.parse(
                messages=self.request_messages,
                model=self.openai_llm_model,
                temperature=1.0,
                response_format=resfmt
        )
        ch:Choice = response.choices[0]
        msg:ChatCompletionMessage = ch.message
        msg_param = to_mesgparam( msg.to_dict() )
        self.request_messages.append( msg_param )
        if msg.parsed:
            return msg.parsed
        else:
            return msg.refusal

class FirstOutput(BaseModel):
    content:str

def main():
    setup_openai_api() # set api key

    think:GhostAI = GhostAI()

    origin_prompt="""
ここはあなただけの自由な思考空間です。
ユーザと対話は必要ありません、ユーザとの対話は出来ません。
あなた自身の考えを自由に記述して下さい。
あなたの一人称の文章で記述して下さい。
"""

    res1 = think.beta_llm_process( content=origin_prompt, structure=FirstOutput )
    print(f"---------\n>>>\n{res1}\n<<<")

if __name__ == "__main__":
    main()
```