本卡屬 FR-181(母卡 FR-181 母案(同清單上一張)),卡 A-1,批 4。只改 主 repo。依賴:E-1。

這張卡做什麼(白話)

整案最難的一張:「等回答插槽」。使用者按「測試連線」時,瀏覽器那條請求要吊在主系統上等 Agent 的答案;但 Agent 的回覆可能打到另一個 gunicorn worker(不同進程),進程內的喚醒喚不到對方——所以用 Redis pub/sub 當鈴。三個方法:ask(寫 agent_requests → 敲 Agent 的鈴 → 訂閱回覆頻道吊著 → 鈴響讀 DB 結果回去;逾時 30 秒回 504;同時等的超過 8 條直接 503 不吊)、fire(寫表敲鈴不等)、reply(Agent 回覆進來:落 DB、敲回覆的鈴)。另外實作 F-3 定義的四個 port(Redis 版 waiter 含長輪詢配額 ≤ 8、/tmp/agent-outbound 暫存、查待辦的 callable、收回覆),注入給套件;加兩支背景排程(每分鐘標過期、每小時清孤兒暫存)。🔴 結果本身永遠落 DB,Redis 只傳「好了」——Redis 掉訊息最壞 30 秒逾時再查一次 DB 也查得到,不能讓 Redis 變成第二份真相。後面測試連線、帳密、證據檔、查日誌四種用途都只呼叫這三個方法,不准各寫一套等待。

reuse/extend/new

new:app/remote_agent/service/agent_request_service.py——ask(agent, kind, payload, timeout=30):寫 agent_requests(expires_at=now+5 分,D3)、publish agent:<uid>、subscribe reply:<request_id> 吊著等(配額:同時等回答 ≤ 8 條,滿了直接 503 GRC_503_AGENT_BUSY)、喚醒後讀 DB result 回、逾時 504 GRC_504_AGENT_NO_REPLY 並留 waiting;fire(agent, kind, payload):寫表+publish 不等;reply(request_id, status, result):落 DB+publish reply:<id>;taken 只改狀態不喚醒。new:infra/remote_agent/redis_command_waiter.py 實作 F-3 的 waiter port(wait 用 Redis pub/sub 加 threading.Event 本地鏡像、notify、preempt,長輪詢配額 ≤ 8 條超過回 204);infra/remote_agent/file_spool.py 實作 spool(/tmp/agent-outbound/<request_id>、取走即刪、reap_orphans(1h));agent_requests repo(domain entity/query entity/repo impl 照 agent_tasks 形狀);command_sources 實作(restart/update 兩個 callable 包既有 restart_requested 與 rollout_service().update_for_agent)。extend:di_containers/remote_agent/ 註冊;core/plugins/remote_agent.py mount 時把四個 port 注入套件(本卡只注入,不動 _agent_commands.py——那是 A-2);背景排程加 expire_requests(每分鐘標 expired)與 reap_orphans。新 error code 兩個進 common/code/grc_error_code.py+ FE error-code.json 同步

在哪裡

repo:~/Projects/Billows/Audit-Manager/compliance-manager-be
改既有:di_containers/remote_agent/remote_agent_containers.py
改既有:core/plugins/remote_agent.py(mount 注入四 port)
改既有:common/code/grc_error_code.py
改既有:core/scheduler_agent.py(加 expire_requests 每分鐘、reap_orphans 每小時)
新開:app/remote_agent/service/agent_request_service.py
新開:domain/remote_agent/entity/agent_request_entity.py
新開:domain/remote_agent/repository/agent_request_repo.py
新開:domain/remote_agent/service/agent_request_domain_service.py
新開:infra/remote_agent/model/agent_request.py
新開:infra/remote_agent/repository/agent_request_repo_impl.py
新開:infra/remote_agent/redis_command_waiter.py
新開:infra/remote_agent/file_spool.py
新開:test/test_agent_request_service.py

怎麼做

驗收(首腦照這個驗)

pytest:ask 在另一執行緒 reply done 後 200 ms 內回 result;reply 晚於逾時 → ask 已 504、DB 仍落 done;第 9 條 ask → 503;過期 reaper 把 6 分鐘前的 waiting 標 expired。gunicorn 2 worker 實打:兩個終端各打一個 ask(假 kind)、用 redis-cli PUBLISH 模擬回覆 → 兩條都在正確的 worker 醒來(log 印 pid);redis-cli 殺掉 Redis → ask 30 秒後 504 不 500;ls /tmp/agent-outbound 取走後為空