Cachy:ノートパソコンの処理速度を60倍に高速化した方法
Answer.AI は、LLM SDK の通信ライブラリである httpx をパッチしてレスポンスを自動保存・再利用する「Cachy」というオープンソースツールを開発し、テスト実行時間を 60 倍短縮した。
キーポイント
LLM 開発の課題と解決策
LLM の非決定的な応答や API キー管理の問題に対し、手動でモックを作成する従来の手法に代わり、httpx ライブラリをパッチして自動キャッシュ化するアプローチを採用した。
Cachy の仕組みと効果
リクエストを一意の ID に変換し、初回実行時のレスポンスをローカルファイルに保存、以降はキャッシュから即座に返すことで、テスト実行時間を 2 分から約 2 秒に短縮した。
コード変更なしでの実装
既存のプロジェクトやノートブックに `enable_cachy()` を追加するだけで動作し、モックやフィクスチャの記述を一切不要とし、コードレビューの負荷も軽減した。
オープンソース化と機能拡張
単一プロジェクトでの成功を受け、非同期処理やストリーミング対応を追加し、独立したパッケージとして公開された。
追加コード不要の自動キャッシュ
AnthropicやOpenAIのPython SDKを使用する際、同じLLM呼び出しに対してレスポンスが自動的にキャッシュされ再利用されます。追加の実装コードは不要で、バックグラウンドで動作します。
簡易な初期化手順
Cachyの機能は `from cachy import enable_cachy` と `enable_cachy()` を呼び出すだけで有効化され、既存のOpenAIクライアントコードにそのまま適用可能です。
汎用キャッシング機能
Cachy は LLM の応答だけでなく、httpx を使用したあらゆる呼び出しをキャッシュするために利用可能です。
影響分析・編集コメントを表示
影響分析
このアプローチは、LLM を多用する開発現場におけるテストのボトルネックを根本から解決する実用的なソリューションであり、開発者の生産性を劇的に向上させる可能性があります。特に、非決定的な挙動によるコードレビューの困難さや CI/CD の複雑さを解消するため、AI アプリケーションの開発ワークフローの標準化に寄与する重要な技術です。
編集コメント
既存のライブラリを巧みにパッチするだけで劇的な効果を得られる、開発者目線の非常に賢い工夫です。LLM を扱うプロジェクトにおけるテスト自動化の障壁を下げる、即戦力となるツールと言えます。

イントロダクション。
AnswerAI では、A.I. を扱う作業を少しだけ楽にするソフトウェアを開発しています。例えば、過去 1 年間で、Anthropic や OpenAI といった LLM(大規模言語モデル)プロバイダーとの連携をよりシンプルにする一連のオープンソース Python パッケージ(Claudette, Cosette)を開発しました。
これらのパッケージは多数の LLM 呼び出しを実行しますが、これにより開発が大幅に遅くなるようないくつかの課題が生じます。
テストスイートの実行が遅い。各 LLM 呼び出しの実行に数百ミリ秒かかるためです。
LLM の応答は非決定的であり、アサーション(検証)が困難になります。
CI/CD パイプライン(Github Actions など)では、テストを実行するために API キーへのアクセス権限が必要です。
私たちがソフトウェアの多くをノートブックで開発しているため、非決定的な応答は追加の問題を引き起こします。これによりノートブックの差分に著しい肥大化が生じ、コードレビューがより困難になります 😢。
なぜ Cachy なのか?
LLM は比較的新しい技術ですが、これらの課題自体は新しいものではなく、すでに確立された解決策が存在します。各 LLM 呼び出しをモック(模擬)し、実際の LLM プロバイダーに呼び出すのではなく特定の応答を返すようにするだけです。確かにこのアプローチは非常にうまく機能しますが、少し手間がかかります。私たちの場合、手動で LLM を呼び出して応答を取得し、プロジェクトに保存した上で、それを利用するモックを作成する必要があります。プロジェクト全体で数百回行われる LLM 呼び出しに対して、このプロセスを繰り返さなければなりません 😢。
私たちは、もっと良い方法はないか、手動での介入をゼロにして自動的に背景で動作する何かを作れないかと自問しました。その「より良いもの」は非常にシンプルであることがわかりました。最も人気のある LLM SDK のソースコードを確認したところ、すべてがそれぞれの API を呼び出すために httpx ライブラリを使用していることが判明しました。私たちが行う必要があったのは、httpx の send メソッドを修正して、すべての呼び出しのレスポンスをローカルファイル(つまりキャッシュ)に保存し、将来のリクエストで再利用することだけでした。これを実現する疑似コードは以下の通りです。
@patch
def send(self:httpx._client.Client, r, **kwargs):
id_ = req2id(r) # リクエストを一意の識別子に変換
if id_ in cache: return httpx.Response(content=cache[id_])
res = self._orig_send(r, **kwargs)
update_cache(id_, res)
return res
このシンプルなパッチを私たちのプロジェクトの一つに適用したところ、その効果は即座に現れました。
テストの実行時間が約 2 分ではなく、約 2 秒でできるようになりました 🔥
ついに CI/CD パイプラインにテストスイートを追加できました
ノートブックの差分がクリーンかつ焦点を絞ったものになりました
最も素晴らしい点は、モックやフィクスチャでプロジェクトを肥大化させることなく、コードを一行も書かずにこれらの恩恵すべてを得られたことです。
それ以来、非同期処理とストリーミングに対応し、今日オープンソースとして公開する別パッケージ「cachy」へと発展させました 🎉
使用法
cachy のセットアップは非常に簡単です。
pip でインストールします pip install pycachy
ノートブックまたはスクリプトで cachy をインポートします from cachy import enable_cachy
ノートブックまたはスクリプトの先頭に enable_cachy() を追加することで、cachy を有効化できます。
これで Anthropic または OpenAI の Python SDK を使用すると、同じ LLM 呼び出しを再度行った際にレスポンスがキャッシュされ再利用されます。追加のコードを書く必要はありません。cachy は自動的にバックグラウンドで動作します。
以下に例を示します。
from cachy import enable_cachy
enable_cachy()
次に、OpenAI から completion をリクエストしてみましょう。
from openai import OpenAI
cli = OpenAI()
r = cli.responses.create(model="gpt-4.1", input="Hey!")
r
Hey! How can I help you today? 😊
id: resp_05b1a0c3eca9e1450068dbb5ff4a74819e8bc3099532846ea1
created_at: 1759229439.0
error: None
incomplete_details: None
instructions: None
metadata: {}
model: gpt-4.1-2025-04-14
object: response
output: [ResponseOutputMessage(id='msg_05b1a0c3eca9e1450068dbb600147c819e8684cbe7fe3adc40', content=[ResponseOutputText(annotations=[], text='Hey! How can I help you today? 😊', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')]
parallel_tool_calls: True
temperature: 1.0
tool_choice: auto
tools: []
top_p: 1.0
background: False
conversation: None
max_output_tokens: None
max_tool_calls: None
previous_response_id: None
prompt: None
prompt_cache_key: None
reasoning: Reasoning(effort=None, generate_summary=None, summary=None)
safety_identifier: None
service_tier: default
status: completed
text: ResponseTextConfig(format=ResponseFormatText(type='text'), verbosity='medium')
top_logprobs: 0
truncation: disabled
usage: ResponseUsage(input_tokens=9, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=11, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=20)
user: None
billing: {'payer': 'developer'}
store: True
同じリクエストを再度実行すると、応答はキャッシュから読み込まれます。
r = cli.responses.create(model="gpt-4.1", input="Hey!")
r
Hey! How can I help you today? 😊
id: resp_05b1a0c3eca9e1450068dbb5ff4a74819e8bc3099532846ea1
created_at: 1759229439.0
error: None
incomplete_details: None
instructions: None
metadata: {}
model: gpt-4.1-2025-04-14
object: response
output: [ResponseOutputMessage(id='msg_05b1a0c3eca9e1450068dbb600147c819e8684cbe7fe3adc40', content=[ResponseOutputText(annotations=[], text='Hey! How can I help you today? 😊', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')]
parallel_tool_calls: True
temperature: 1.0
tool_choice: auto
tools: []
top_p: 1.0
background: False
conversation: None
max_output_tokens: None
max_tool_calls: None
previous_response_id: None
prompt: None
prompt_cache_key: None
reasoning: Reasoning(effort=None, generate_summary=None, summary=None)
safety_identifier: None
service_tier: default
status: completed
text: ResponseTextConfig(format=ResponseFormatText(type='text'), verbosity='medium')
top_logprobs: 0
truncation: disabled
使用例:ResponseUsage(input_tokens=9, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=11, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=20)
ユーザー:なし
請求情報:{'payer': 'developer'}
保存:True
汎用キャッシング
本記事は LLM(大規模言語モデル)の応答をキャッシュすることに焦点を当てていますが、cachy を使用すれば httpx で行われるあらゆる呼び出しをキャッシュすることができます。必要なことは、cachy にキャッシュしたい URL を指定するだけです。
enable_cachy(doms=["api.example.com", "api.demo.com"])
結論
cachy は、フロー状態をより長く維持し、わずかながらもスピードアップを可能にする、そんな生活の質を向上させる小さな改善の一つです。皆様にとって有益なツールとして活用していただければ幸いです。
原文を表示

Intro.
At AnswerAI we build software that makes working with A.I. that little bit easier. For example, in the past year we built a series of open source python packages (Claudette, Cosette) that make it much simpler to work with LLM providers like Anthropic and OpenAI.
These packages make many LLM calls which pose a bunch of challenges that can really slow down development.
running the test suite is slow as each LLM call take 100’s of ms to run
llm responses are non deterministic which makes assertions difficult
ci/cd pipelines (like Github Actions) need access to API keys to run tests
As we build most of our software in notebooks non-deterministic responses create an additional problem. They add significant bloat to notebook diffs which makes code review more difficult 😢.
Why cachy?
Although LLMs are relatively new these challenges are not, and an established solution already exists. You simply mock each LLM call so that it returns a specific response instead of calling the LLM provider. Indeed this approach works pretty well but it is a little cumbersome. In our case, we would need to call the LLM manually, capture the response, save it to our project, and write a mock that uses it. We would need to repeat this process for hundreds of LLM calls across our projects 😢.
We asked ourselves if we could do better and create something that just worked automatically in the background with zero manual intervention. That something better turned out to be very simple. We looked at the source code of the most popular LLM SDKs and found that they all use the httpx library to call their respective APIs. All we needed to do was modify httpx’s send method to save the response of every call to a local file (a.k.a a cache) and re-use it on future requests. Here’s some pseudo-code that implements just that.
@patch
def send(self:httpx._client.Client, r, **kwargs):
id_ = req2id(r) # convert request to a unique identifier
if id_ in cache: return httpx.Response(content=cache[id_])
res = self._orig_send(r, **kwargs)
update_cache(id_, res)
return res
We added this simple patch to one of our projects and the payoff was immediate.
we could now run our tests in ~2 seconds instead of 2 minutes 🔥
we could finally add a test suite to our ci/cd pipeline
our notebook diffs were clean and focused
The best part is that we got all of these benefits without having to write a single line of code and bloating our project with mocks and fixtures.
Since then we’ve added support for async, streaming, and turned it into into a separate package called cachy which we’re open sourcing today 🎉.
Usage
Setting up cachy is pretty straightforward.
install it with pip pip install pycachy
import cachy in your notebook or script from cachy import enable_cachy
enable cachy by adding enable_cachy() to the top of your notebook or script
Now when you use Anthropic or OpenAI’s python SDK the response will be cached and re-used whenever you make the same LLM call again. You don’t need to write any additional code. cachy just works automatically in the background.
Here’s an example.
from cachy import enable_cachy
enable_cachy()
Now, let’s request a completion from OpenAI.
from openai import OpenAI
cli = OpenAI()
r = cli.responses.create(model="gpt-4.1", input="Hey!")
r
Hey! How can I help you today? 😊
id: resp_05b1a0c3eca9e1450068dbb5ff4a74819e8bc3099532846ea1
created_at: 1759229439.0
error: None
incomplete_details: None
instructions: None
metadata: {}
model: gpt-4.1-2025-04-14
object: response
output: [ResponseOutputMessage(id=‘msg_05b1a0c3eca9e1450068dbb600147c819e8684cbe7fe3adc40’, content=[ResponseOutputText(annotations=[], text=‘Hey! How can I help you today? 😊’, type=‘output_text’, logprobs=[])], role=‘assistant’, status=‘completed’, type=‘message’)]
parallel_tool_calls: True
temperature: 1.0
tool_choice: auto
tools: []
top_p: 1.0
background: False
conversation: None
max_output_tokens: None
max_tool_calls: None
previous_response_id: None
prompt: None
prompt_cache_key: None
reasoning: Reasoning(effort=None, generate_summary=None, summary=None)
safety_identifier: None
service_tier: default
status: completed
text: ResponseTextConfig(format=ResponseFormatText(type=‘text’), verbosity=‘medium’)
top_logprobs: 0
truncation: disabled
usage: ResponseUsage(input_tokens=9, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=11, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=20)
user: None
billing: {‘payer’: ‘developer’}
store: True
If we run the same request again, the response is now read from the cache.
r = cli.responses.create(model="gpt-4.1", input="Hey!")
r
Hey! How can I help you today? 😊
id: resp_05b1a0c3eca9e1450068dbb5ff4a74819e8bc3099532846ea1
created_at: 1759229439.0
error: None
incomplete_details: None
instructions: None
metadata: {}
model: gpt-4.1-2025-04-14
object: response
output: [ResponseOutputMessage(id=‘msg_05b1a0c3eca9e1450068dbb600147c819e8684cbe7fe3adc40’, content=[ResponseOutputText(annotations=[], text=‘Hey! How can I help you today? 😊’, type=‘output_text’, logprobs=[])], role=‘assistant’, status=‘completed’, type=‘message’)]
parallel_tool_calls: True
temperature: 1.0
tool_choice: auto
tools: []
top_p: 1.0
background: False
conversation: None
max_output_tokens: None
max_tool_calls: None
previous_response_id: None
prompt: None
prompt_cache_key: None
reasoning: Reasoning(effort=None, generate_summary=None, summary=None)
safety_identifier: None
service_tier: default
status: completed
text: ResponseTextConfig(format=ResponseFormatText(type=‘text’), verbosity=‘medium’)
top_logprobs: 0
truncation: disabled
usage: ResponseUsage(input_tokens=9, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=11, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=20)
user: None
billing: {‘payer’: ‘developer’}
store: True
General Purpose Caching
Although this post focuses on caching LLM responses, cachy can be used to cache any calls made with httpx. All you need to do is tell cachy what urls you want to cache.
enable_cachy(doms=["api.example.com", "api.demo.com"])
Conclusion
cachy is one of those little quality of life improvements that keeps us in a flow state for longer and help us move that little bit faster. We hope you’ll find it useful.
関連記事
退屈な PDF タスクを自動化する Python スクリプト 5 つ
KDnuggets は、PDF の処理や変換など日常的な作業を自動化するための有用な Python スクリプト 5 つを紹介した。
Google の技術を採用した Siri AI が登場、しかし世界の多くは利用不可
Apple は WWDC 2026 で、ゼロから再構築された新 Siri AI を発表し、Google の技術を組み込んで多段階対話を実現したが、多くの地域ではまだ利用できない。
マクドナルド、Google 支援の AI ドライブスルー注文システムをテスト中
マクドナルドは、Google が支援する「ArchIQ」と呼ばれるAIシステムを米国の5店舗で試験運用しており、このシステムがドライブスルーでの注文受付や店舗運営をサポートしている。
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み