コード構造
リクエストトレース
2 つのフロー図 — 1 つは CLI 経由、1 つはブラウザ経由。
8 つのファイルがどう協調するかを一番はっきり見るには、1 件の質問が 両方のエントリポイントをどう通るかを追いかけるのが手っ取り早いです。
CLI 質問トレース
CLI question
→ cli.ts (parse argv, instantiate LocalLensApp)
→ locallens.ts (createBrainFromFolder)
→ files.ts (discoverTextDocuments)
→ rag.ts (chunkDocuments → ragChunk)
→ qvac.ts (ingestChunks → ragIngest)
→ store.ts (saveBrain, saveChunks)
→ locallens.ts (askBrain)
→ qvac.ts (search → ragSearch)
→ rag.ts (buildGroundedHistory)
→ qvac.ts (answer → completion stream)
→ locallens.ts (assemble ChatAnswer)
→ cli.ts (print answer + sources, deleteBrain, close)押さえておきたい点が 2 つあります。
- トレースは
locallens.tsから各ゲートウェイへ外側に分かれ、戻って きます。内部から内部への呼び出しはすべてワークフロークラスを経由します。 domain.tsはトレースに現れません。これは意図的な設計です。型は実行時に 参加せず、それを制約するだけです。
ブラウザ質問トレース
Browser question
→ ui/app.js (event handler, fetch POST /api/brains/:id/chat)
→ server.ts (route match, JSON parse)
→ locallens.ts (askBrain)
→ qvac.ts (search)
→ rag.ts (buildGroundedHistory)
→ qvac.ts (answer)
→ locallens.ts (assemble ChatAnswer)
→ server.ts (JSON serialize, write response)
→ ui/app.js (render markdown, append to chat thread)ブラウザのトレースは CLI のトレースの両端に HTTP ホップを足しただけです。
リクエストが locallens.ts に到達したら、CLI と同じ経路になります。
この形に意味がある理由
ワークフロークラス 1 つと薄いエントリポイントから、3 つの性質が自然に 出てきます。
- 機能を足す場所が 1 か所。
LocalLensApp.askBrainが質問に答える 唯一のメソッドです。それに手を入れた瞬間、CLI とブラウザの両方が 恩恵を受けます。 - エラーの表面が安定している。 エラーは
LocalLensAppを上に流れ、cli.tsかserver.tsにAppErrorまたは一般Errorとして届き、 末端で 1 つのヘルパーがフォーマットします。 - テストが楽。
LocalLensAppはサーバーやシェルを起動せずに単体 テストできます。ストアは temp ディレクトリ用に差し替え可能、QVAC ゲートウェイはスタブできます。
形が崩れる場面
ある機能でブラウザパスと CLI パスが異なるワークフローを必要としたら、
対称性が破れます。そのときの正しい一手は、LocalLensApp に新しい
メソッド(またはそれを使う新しいヘルパー)を追加することであり、
server.ts にロジックを押し込むことではありません。エントリポイントは
薄く保ちましょう。