Agent-10-01.LLM调用与结构化输出

系列:Agent-10-00 索引 · 下一篇:02 Tool Calling


1. 行业常见问题

现象 业务影响
回答结构每次不同 下游无法解析、无法渲染 UI
口吻随意、越权建议 合规风险(医疗/金融/法务)
输出夹杂 Markdown 废话 自动化流水线需要复杂正则清洗
「看起来对」但字段缺失 批量任务 silent failure

典型场景:调研报告要固定四段、工单要 JSON、实验参数要可校验的 schema。


2. 该技术如何解决

通过 消息角色(system / user / assistant)约束任务边界,再用 结构化输出 把生成空间限制在可解析的类型上。

  • System Prompt:角色、禁止项、输出格式说明
  • Structured Output:JSON Schema / Pydantic 模型,让模型返回可校验对象
  • 低温度(temperature):减少格式漂移

段末注释:temperature 控制采样随机性;结构化任务通常取 0–0.3。


3. 核心原理

3.1 Chat Completions 消息栈

1
2
3
system: 你是…必须按以下 JSON 输出…
user: 具体任务输入
assistant: (模型回复,或 tool 结果回填后的续写)

模型并不「记住程序状态」;每次请求携带的 messages 即上下文

3.2 结构化输出的两种主流路径

路径 机制
JSON mode / response_format API 层约束输出为 JSON
Pydantic + parse 先自然语言生成,再解析校验(或 native structured output)

校验失败应 重试或报错,不能把脏 JSON 当成功。


4. 典型实现与代码示例

本仓库 Planner 使用 LangChain structured output,而非手写 JSON 解析。

4.1 Pydantic 契约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# agent/uni/contracts/research_plan.py
class PlanStep(BaseModel):
id: str # Analyst 写 execution_manifest 时的 step_id;generate_name 前缀
title: str # HITL 展示用人类可读标题
mcp: str = "" # 文档/审计:该步应使用的 MCP Server 名(如 argo-sirna)
template: str = "" # submit_template_tool 的 template_name(Argo Workflow 模板)
params: dict[str, Any] = Field(default_factory=dict) # 序列化为 parameters_json 提交
depends_on: list[str] = Field(default_factory=list) # 步骤 DAG 依赖(规则/LLM 规划用)

class ResearchPlan(BaseModel):
schema_version: str = "research_plan.v1" # artifact 版本标识,下游校验契约
goal: str # 报告「Research Plan」节与 execution_manifest.goal
gene_symbol: str = "" # 与 PipelineState.gene_symbol 对齐
transcripts: list[str] = Field(default_factory=list) # 目标转录本 accession 列表
metrics: list[str] = Field(default_factory=list) # 期望评估指标(knockdown 等)
evidence_refs: dict[str, str] = Field(default_factory=dict) # 上游 artifact 绝对路径,供审计/HITL
steps: list[PlanStep] = Field(default_factory=list) # Analyst 顺序 submit 的步骤列表
selection_rationale: str = "" # 转录本/策略选择理由,写入 planner_summary

4.2 LangChain:with_structured_output

1
2
3
4
5
6
7
8
9
10
11
12
# agent/planner/llm.py(节选)
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage, SystemMessage

model = init_chat_model("gpt-4.1-mini", model_provider="openai", temperature=0.2)
structured = model.with_structured_output(ResearchPlan) # LangChain 绑定 Pydantic

plan: ResearchPlan = structured.invoke([
SystemMessage(content="你是 siRNA 研究方案制定专员…"),
HumanMessage(content=f"基因 symbol={gene_symbol},artifact 上下文…"),
])
plan_dict = plan.model_dump()

要点:

  • with_structured_output(ResearchPlan) 替代 response_format + 手工 json.loads
  • 读取上游 artifact(transcripts.jsonliterature_manifest.json)作为 user 上下文
  • LLM 失败时 规则回退 build_research_plan(),保证流水线可跑

4.3 工程验收

1
2
3
4
5
6
7
8
# 规则版(无 API Key)
uv run python -m agent.planner.cli plan BRCA1 --run-id demo001 --workspace workspace

# LangChain 结构化版
uv run python -m agent.planner.cli plan BRCA1 --run-id demo001 --llm

# 嵌入 full 流水线(LangGraph planner 节点,见 Agent-10-09)
uv run python -m agent.orchestrator.cli run --symbol BRCA1 --full --llm-planner

5. 替代方案与优缺点

方案 优点 缺点
纯 Prompt 要求 Markdown 标题 人类可读 解析脆弱、多语言/多模型不稳定
JSON mode + Pydantic 可校验、可测试 需处理偶发 schema 违规
Function/Tool Calling 返回结构化参数 与 Agent 循环统一 过度用于「无工具」场景时复杂
后处理 LLM「再整理一遍」 救场脏输出 成本翻倍、仍可能幻觉
模板填空(slot filling) 极稳 灵活性差,适合字段固定表单

6. 自检题

  1. 为什么结构化任务通常降低 temperature?
  2. system 与 user 分工应如何划分「角色」与「任务数据」?
  3. JSON 解析失败时,生产系统应重试、降级还是直接失败?各适用什么场景?

7. 延伸阅读

-------------本文结束感谢您的阅读-------------