Files
2026Technology-Competition/docs/milestone3-inference-review.md
T
lhl 8239a37a99 feat(inference): LLM 客户端全异步化(T8 架构审查整改)
- Issue9: client.py 由同步 httpx.Client 全异步化
  - LLMClient Protocol / HttpLLMClient.chat → async;httpx.AsyncClient + asyncio.sleep 退避
  - __enter__/__exit__ → __aenter__/__aexit__(async with 生命周期闭环)
- engine.py chat/chat_structured/_call 全部 async + await
- FakeLLMClient.chat → async;测试用 anyio pytest 插件转换(engine 32 + client 10 用例)
- 同步 inference-engine spec 与 milestone3 review 的 httpx 描述
- 全量 182 passed / 100.00%(987 stmts/252 br)
2026-08-12 09:50:37 +08:00

5.8 KiB
Raw Blame History

里程碑 3.1 InferenceEngine 交付报告

1. 概述

完成 agent-runtime-design.md §2 定义的推理引擎(InferenceEngine),为多 Agent 协作提供统一 LLM 调用入口:模型选择/降级、重试、超时、Token 管理、Prompt 注册表与结构化输出。全程 TDD、零真实网络、覆盖率红线 100% 达成。

  • 评审对象docs 阶段(spec + plan)经 plan-design-review 批准后,实施提交范围 2fbff07..HEAD(共 6 个 commit
  • 验收结论With fixes → 2 项 Important 已修复 → 重新验证 Ready117 passed / 100% 覆盖)

2. 交付内容

模块 文件 说明
数据模型 src/genesis/inference/types.py TokenUsage / ChatMessage / ChatResult / StructuredResult / Prompt
异常体系 src/genesis/inference/exceptions.py LLMError 五类:Network / Timeout / NotConfigured / ResponseError
Token 估算 src/genesis/inference/token.py approximate4 字符≈1 token+ tiktoken 可选懒加载回落
Prompt 注册表 src/genesis/inference/prompt_registry.py register / get / list_versions / renderjinja2
LLM 客户端 src/genesis/inference/client.py LLMClient Protocol + HttpLLMClientOpenAI 兼容)
编排引擎 src/genesis/inference/engine.py InferenceEnginechat / chat_structured
测试 tests/test_inference_{types,errors,token,prompt_registry,client,engine}.py + tests/inference_helpers.py 全部离线用例

3. 关键设计决策

  1. httpx 选型httpx.AsyncClient(timeout=..., transport=...) 支持 transport 注入(MockTransport 离线测试);5xx/网络错误指数退避重试(1s/3s/7s),超时→LLMTimeoutError4xx 不重试→LLMNetworkError2xx 结构损坏→LLMResponseError3xx 不误判成功。(T8 整改后为异步 AsyncClient + asyncio.sleep 退避 + async with 生命周期)
  2. 结构化重试语义chat_structured 对 JSON 解析失败带错误信息重试(retry_count+1),重试成功仍为 status="ok"(fallback 语义保留给模型降级);最终失败返回 parse_error + raw_text,不抛异常(spec §3.4 验收 3)。
  3. 异常折叠边界chat 失败降级备用模型(status=fallback),双模型全败 status=failed;仅折叠 LLMError,非 LLM 异常照常冒出。
  4. 显式延后(非目标)
    • chat_structured 不做模型降级——plan 明确「生产建议显式传主模型,实现已足够 v1」(plan L1142),结构化重试固定主模型。
    • Prompt 目录加载(prompts/{name}/{version}.txt)为 v2v1 仅代码内注册。
    • LLMResponseError 在 v1.0 spec 已定义但无触发点,最终评审后补齐(见 §5)。

4. 测试与验证

  • 聚焦测试:各模块 RED→GREEN 全绿(types 8 / token 7 / registry 6 +1 / client 9 / engine 16
  • 全量回归:python -m pytest -q117 passed / 覆盖率 100.00%785 stmts / 186 brfail_under=99 达成
  • 零真实网络:单测全部注入 FakeLLMClient / httpx.MockTransportDEEPSEEK_API_KEY 不落代码
  • 基线不回退:既有 parsers / data_models / config 全绿保持

5. 最终评审(base 2fbff07 → 8bc5e5e)与修正

评审判定 With fixes(无 Critical2 项 Important;若干 Minor):

  1. [已修复] 响应结构异常逃逸:2xx 畸形响应(非 JSON / 缺 choices 字段)抛裸 KeyError/JSONDecodeError,逃出 engine 的 except LLMError,与「不把异常抛给调用方」承诺冲突。修复:client 内捕获 (json.JSONDecodeError, KeyError, IndexError, TypeError)raise LLMResponseError(spec §3.8 既有类型首次被触发,闭环 api-design §7 LLM_PARSE_ERROR 来源列)。TDD:新增 test_chat_3xx_no_retrytest_chat_malformed_response_raises_llm_response_error
  2. [已修复] 3xx 误判成功:httpx 默认不跟随重定向,301/302 落入成功路径。修复:显式 200 <= resp.status_code < 300 判定,非 2xx 一律 LLMNetworkError(不重试)。
  3. [确认为有意] chat_structured 无模型降级:plan 已声明 v1 单模型链,生产显式传主模型,非回归缺陷。
  4. [Minor 挂账 backlog]chat 失败仅保留最后一次模型错误信息;__init__.py 缺尾换行;prompt_registry 模块级 jinja2 导入冗余;chat_structured 未做 token 超限检测;HttpLLMClient 无 __enter__/__exit__(调用方负责关闭生命周期)。

修复提交 14062d1(fix: 客户端响应结构损坏归入 LLMResponseError3xx 不再误判成功)。

6. 提交清单

c2fad77 feat: 推理引擎数据模型与异常层(types/exceptions)及 httpx/jinja2 依赖
71d38b9 feat: Token 估算(approximate 内置 / tiktoken 可选回落)
59c1ea7 feat: PromptRegistry 注册/版本/渲染(jinja2
add7b15 feat: HttpLLMClienthttpx + 重试退避/超时/鉴权)
8bc5e5e feat: InferenceEngine chat/chat_structured 全流程 + 文档补丁(补丁1/2/3
14062d1 fix: 客户端响应结构损坏归入 LLMResponseError3xx 不再误判成功

7. 涉及文件

  • 新建:src/genesis/inference/{types,exceptions,token,prompt_registry,client,engine}.pytests/inference_helpers.pytests/test_inference_{types,errors,token,prompt_registry,client,engine}.py
  • 修改:src/genesis/inference/__init__.pypyproject.tomlhttpx/jinja2 依赖)、docs/agent-runtime-design.mddocs/api-design.mddocs/config-design.md
  • 文档:spec docs/superpowers/specs/2026-08-09-inference-engine-design.md、plan docs/superpowers/plans/2026-08-09-inference-engine.md

8. 后续工作

  • 接入 Parser Agent 自由记述型结构化(待后续里程碑)
  • LLMResponseError 已闭环,可为 engine 增加按错误分类的重试选项(api §7 retry/skip/abort UX