Screaming Loud

日々是精進

2024年振り返り

去年の振り返りはこちら

yuutookun.hatenablog.com

仕事

去年LLM部門として新規事業を始めたんですが、LLMに限らない新規事業を進めるということで新規事業室に変わりました。 新規事業の難しさをひしひしと感じながらも、なんとかやっていてます。 顧客の課題を解決するためには、ただコードを書いてるわけにもいかないので、友人に頼んでインタビューをさせてもらったり、課題の抽出や解決法を考えたり、あまり経験してこなかった動きをしていて難しい日々です。

そしてまだPMFはできていないので、来年PMFできると嬉しいです。

あとはこんなブログを書いたりしてました。

tech.gunosy.io

社内での人の動きが多くて、結構今年はなかなか大変だったのですが、そのおかげと言ってはなんですが、チームのことや自分のことを改めて考える時間にもなったのでプラスになったかなと思っています。

読書

読書メーター で記録している読書量を見ると、本を読む量はそこまで増えてないんですが、本を理解する密度は上がった気がしています。

この点についてはコーチングを受ける中で本を読む際に、そこから何を得たか、それを基に次にどのようなアクションを取るか、を意識しながら読むようにというアドバイスを受け、それを意識しているからだと思います。 もともとアウトプットは必須だと思っていて読書メーターに引き続き感想を書いてます。 また、紙だけでなく電子書籍でも読むようになって気軽にマーカーを引けられるので、「ここが気になった部分」というのを振り返れるのが良かったです。

プライベート

家の周りにめちゃくちゃ飲食店があるので、引っ越してから昼飯に困らず快適な日々を過ごしてます。

引き続きテニスもやっていますが、試合に勝てるかというと。。。

猫たちも元気。

来年は色々変化が多そうなので、やっていき!

「解像度を上げる」を読んだまとめ

いつも読んだ本は読書メーターにまとめているけど、そもそも本読んでどういうアクションするのっていうのをまとめることをしていなかった。 とりあえず流し読みでも知識を入れるという意味では重要だけど、もっと深い学びを得るには内容をまとめておいた方が良いかなと。

読書の解像度を上げるための「解像度を上げる」のまとめです。

何がささったか

解像度を上げるには 情報x思考x行動 の3つの掛け合わせ。 ただ、その起点はほぼ行動から始まる。 行動を増やすことによって、質の高い情報と思考を獲得するサイクルが回り始める。

解像度を上げるには以下4つの視点で理解していく必要がある。

  • 深さ、広さ、構造、時間

何かしらものを作ったり、仕事で問題解決をしたり、新規事業を立ち上げたりするため解像度を上げようとするはず。 その際、課題と解決策のどちらの解像度を上げようとしているのかを意識する。

課題の大きさ

何か価値を生み出そうとしたとき、そこに課題がないと価値は生まれない。 そして、さらには課題以上の価値は生まれない。 課題が小さければ価値も小さくなるし、課題が大きければ生まれる価値も大きい。

じゃあ課題の大きさはどうやって定義するかというと、

課題の大きさ=課題の強度x課題の頻度

行動量

解像度が低い原因は圧倒的に知識不足と情報不足。

サーベイの量が圧倒的に足りていない、事例を100個は知らないと情報量として足りていない。

深さ

じゃあ深さを出すには?

実際に顧客と同じ体験をする。

「なぜそうなのか?」を繰り返すことで深ぼっていく。 ただ、深掘りできない場合も多々あってそれは結局情報不足や知識不足なので、詰まったら行動して情報を集める。

言語化:書く、話す

Amazonがやっているようなプレスリリースを書いてみる

全体としてとにかくどれだけ行動量を増やすかが「解像度」を上げられるかどうかの分かれ目だなと感じました。 行動量増やしましょう。

fastAPIのbackgroundTasks内での同期処理にご注意

最近Pythonを書いており、Goの雰囲気でfastAPIのbackgroundTasks内で重い同期処理を実行するコードを書いたら、他のレスポンスが止まる実装になってしまって困ったので、その際の備忘録として残しておきます。

結論

  • backgroundTasksは async def を渡すか def を渡すかで変わる
  • そもそもasyncはシングルスレッド

backgroundTasksに def を渡すか async def を渡すか

とりあえず重い処理はあとに回して、レスポンスを返したいときに使えるbackgroundTasksです

バックグラウンドタスク - FastAPI

実はbackgroundTasksは async def を渡すか def を渡すかで処理が変わります。 以下のコードはas_asyncパラメータによってどちらかを渡すかの処理になっています。

import threading
from fastapi import BackgroundTasks, Depends

router = FastAPI()
@router.get("/")
async def heavy(as_async: int, background_tasks: BackgroundTasks):
    print(f"main thread: {threading.get_ident()}")
    if as_async:
        background_tasks.add_task(async_heavy_task)
    else:
        background_tasks.add_task(heavy_task)
    return "ok"


async def async_heavy_task():
    print(f"async bg thread: {threading.get_ident()}")
    time.sleep(10)

def heavy_task():
    print(f"sync bg thread: {threading.get_ident()}")
    time.sleep(10)

実行してみると以下のようにasyncで関数を渡しているほうがレスポンスに10sかかっています。

$ time curl "http://localhost:80/?as_async=0"
"ok"curl "http://localhost:80/?as_async=0"  0.00s user 0.00s system 41% cpu 0.019 total

$ time curl "http://localhost:80/?as_async=1"
"ok"curl "http://localhost:80/?as_async=1"  0.00s user 0.01s system 0% cpu 10.047 total

これをprintしたログを見てみると以下のように def で渡した場合は別スレッドで実行していますが、 async def で渡した場合はmainスレッドで実行しています。

## async defの場合
 main thread: 281473547505696
 async bg thread: 281473547505696
## defの場合
 main thread: 281473547505696
 sync bg thread: 281473494217088

以上のことからasyncでない重い処理を async def としてメインスレッドが固まってレスポンスが遅くなります。 またメインスレッドを止めているので、他の処理も詰まってしまうのでAPI全体がブロックされてしまうので、非常に危険です。

こちらの記事が参考になりました。

qiita.com

zenn.dev

余談

上記のことがわかったあと、同期関数にするよう実装を変更しようとしました。 SQLAlchemyの AsyncSession をつかっていたので、同期の Session を作成しようと新しくコネクションを作成する実装を書いたのですが、以下のエラーが出てどうしても繋がりませんでした。

MissingGreenlet("greenlet_spawn has not been called; can't call await_only() here. Was IO attempted in an unexpected place?"

この文言で検索しても、大体eager load周りの話ばかりで、そもそもjoinしてないクエリも通らないからおかしいと思っていました。

が、結論は dsnが間違っていただけでした。

async_sessionは postgresql+asyncpg であるのに対し、 同期の session は postgresql+psycopg2 です。(他にもコネクタはいくつかありますが)

参考: PostgreSQL — SQLAlchemy 2.0 Documentation

これを気づくのに結構時間がかかってしまったので誰かの助けになれば。

sqlalchemyで sqlがprintできない

表題の通り

sqlalchemyでsqlがちゃんと想定のSQLが吐き出されているか確認したかったが、以下のようなコンパイルエラーが発生してしまった。

sqlalchemy.exc.UnsupportedCompilationError: Compiler <sqlalchemy.sql.compiler.StrSQLCompiler object at 0x107a3cb90> can't render element of type <class 'models.utils.utcnow'>: <class 'models.utils.utcnow'> construct has no default compilation handler. (Background on this error at: https://sqlalche.me/e/20/l7de)

utcnowは以下のように設定していました。

class utcnow(expression.FunctionElement):
    type = DateTime()
    inherit_cache = True


@compiles(utcnow, "postgresql")
def pg_utcnow(element, compiler, **kw) -> str:
    return "TIMEZONE('utc', CURRENT_TIMESTAMP)"

実際のprintしている部分のコードはこれ

from sqlalchemy import select, update


## session は async_sessionmaker経由で作成

stmt = update(User).where(id = 1).values(name="name")
print(stmt)
await session.execute(stmt)
await session.commit()

この場合、compilesのアノテーションがついているので以下のようにcompileを使ったprintをさせる必要がありました。

from sqlalchemy.dialects import postgresql

print(stmt.compile(dialect=postgresql.dialect()))

参考リンク

Custom SQL Constructs and Compilation Extension — SQLAlchemy 2.0 Documentation

SQL Expressions — SQLAlchemy 2.0 Documentation