39 lines
2.7 KiB
Markdown
39 lines
2.7 KiB
Markdown
# LLM JSON 模式使用约定
|
||
|
||
本文档说明 Life Echo API 中 **结构化输出** 与 **自然语言输出** 的分工,以及与 [DeepSeek JSON Output](https://api-docs.deepseek.com/guides/json_mode) 的对齐方式。
|
||
|
||
## 实现位置
|
||
|
||
- **绑定方式**:[`app/core/langchain_llm.py`](../app/core/langchain_llm.py) 中的 `bind_json_object_mode`(等价于 `response_format={"type": "json_object"}`)。**禁止**使用已废弃的 `model_kwargs={"response_format": ...}` 绑定方式(见 [`app/adapters/llm/deepseek.py`](../app/adapters/llm/deepseek.py) 注释)。
|
||
- **统一调用**:`invoke_json_object` / `ainvoke_json_object`(同文件)在 JSON 模式下提供空内容检测、可选一次重试与结构化日志(`agent`、`prompt_sha12`)。
|
||
|
||
## 何时必须使用 `json_object`
|
||
|
||
以下路径需要稳定 `json.loads` / 下游解析,**必须**通过 `bind_json_object_mode` 或 `invoke_json_object` / `ainvoke_json_object` 调用:
|
||
|
||
| 能力 | 代码入口 |
|
||
|------|----------|
|
||
| 用户资料字段抽取 | `ProfileAgent.extract_profile_from_message` |
|
||
| 回忆录阶段与 slots 抽取 | `ExtractionAgent.extract` |
|
||
| 叙事段落 JSON(`paragraphs`) | `NarrativeAgent.generate_narrative` |
|
||
| Story 路由 / 批量规划 | `StoryRouteAgent.decide` / `plan_batch` |
|
||
| 回忆录配图 prompt | `MemoirImagePromptService.build_prompt` / `build_cover_prompt` |
|
||
|
||
## 何时保持普通文本(禁止强行 JSON)
|
||
|
||
- 访谈对话、资料追问开场与跟进:自然口语与 `[SPLIT]` 分条,使用 `ChatOpenAI` 默认输出(`InterviewAgent`、`ProfileAgent` 中非 extraction 方法)。
|
||
- 章节分类、创意标题等若仍为纯文本解析:由业务选择是否后续改为小 JSON(见产品决策)。
|
||
|
||
## DeepSeek 官方建议对齐
|
||
|
||
1. `response_format`:已由 `bind_json_object_mode` 设置。
|
||
2. Prompt 中应包含 **json** 字样及与解析代码一致的 **字段示例**(各 `prompts_*.py` 中维护)。
|
||
**集中兜底**:[`ensure_json_object_prompt_has_json_keyword`](../app/core/langchain_llm.py) 在 `invoke_json_object` / `ainvoke_json_object` 与 `llm_json_call` / `allm_json_call` 发请求前会检测;若整段 prompt 中仍无子串 `json`(大小写不敏感),会自动追加一行中文 JSON 说明,避免 OpenAI/DeepSeek 返回 400。
|
||
3. `max_tokens`:在 `invoke_json_object(..., max_tokens=...)` 与绑定处统一传入;长叙事等场景按需调大。
|
||
4. 偶发空内容:由 `invoke_json_object` 记录并重试一次;仍失败则由各 Agent 既有 `try/except` 回退。
|
||
|
||
## 架构注意
|
||
|
||
- `app/core` 仅承载跨切 LLM 约定,**不**依赖 `features/*` 或具体 Agent。
|
||
- Service 层通过 `get_llm_provider().langchain_llm` 获取 Runnable,**不**直接 import `adapters`。
|