第 4 章 · 02 结构化输出约定 本节摘要:本节聚焦 里一个容易被忽略却很关键的设计——给 risk/execution/quant 三个 Agent 的 systemprompt 追加一段「当收到消息时,它包含…请提供…」的结构化输出约定。这段追加文字不在 里,而是直接写在 的字符串拼接里。它做了两件事:一是声明输入契约(你会收到哪几段),二是用编号清单约束输出字段(必须给哪些项)。本节逐行拆这三段追加文字,讲清「输入契约 + 编号清单」为何能显著提升 LLM 输出的可用性,以及它与下游解析、与 prompts.py 里的「prompt 模板」是什么关系。读完本节,你掌握多 Agent 系统里「输出可被下游消费」的 prompt 套路。
本节摘要:本节聚焦
workers.py里一个容易被忽略却很关键的设计——给 risk/execution/quant 三个 Agent 的 system_prompt 追加一段「当收到消息时,它包含…请提供…」的结构化输出约定。这段追加文字不在prompts.py里,而是直接写在workers.py的字符串拼接里。它做了两件事:一是声明输入契约(你会收到哪几段),二是用编号清单约束输出字段(必须给哪些项)。本节逐行拆这三段追加文字,讲清「输入契约 + 编号清单」为何能显著提升 LLM 输出的可用性,以及它与下游解析、与 prompts.py 里的「prompt 模板」是什么关系。读完本节,你掌握多 Agent 系统里「输出可被下游消费」的 prompt 套路。
内容来源:原项目源码
autohedge/workers.py(第 37-38、49-50、61-62 行的追加段)、autohedge/prompts.py(模板段),精读并套用体系化模板。
阅读完本节,你应当能够:
回顾 workers.py 第 35-45 行的 risk_agent(关注 system_prompt 拼接):
risk_agent = Agent( agent_name="Risk-Manager", system_prompt=RISK_PROMPT.strip() + "\n\nWhen you receive a message, it will contain:\nStock, Thesis, Quant Analysis.\n\nProvide risk assessment including:\n1. Recommended position size\n2. Maximum drawdown risk\n3. Market risk exposure\n4. Overall risk score" + _SYSTEM_SUFFIX, ... )
system_prompt 是三段拼接:RISK_PROMPT.strip() + 追加约定 + _SYSTEM_SUFFIX(时间注入,下节讲)。本节聚焦中间那段「追加约定」。三个 Agent 的追加段结构完全一致,只是字段不同。
把追加段单独拎出来看:
When you receive a message, it will contain: Stock, Thesis, Quant Analysis. Provide risk assessment including: 1. Recommended position size 2. Maximum drawdown risk 3. Market risk exposure 4. Overall risk score
这段话做了两件事:
When you receive a message, it will contain: Stock, Thesis, Quant Analysis.
它预先告诉 LLM:你收到的消息会包含「股票、假设、量化分析」三段。这是输入契约——明确数据来源与组成。
为什么这很重要?因为 handoff 时,Director 交给 risk_agent 的消息是一整段文本,没有字段标签。如果没有这句约定,LLM 可能不知道这段文本里哪些是假设、哪些是量化分析,只能瞎猜。有了这句,LLM 会主动在输入里寻找这三段,解读更准确。
Provide risk assessment including: 1. Recommended position size 2. Maximum drawdown risk 3. Market risk exposure 4. Overall risk score
用编号清单要求 LLM 必须给出这 4 项。编号清单的约束力来自:
💡 核心心法:编号清单是 prompt 工程里性价比最高的招式——一行「1. ... 2. ... 3. ...」就能把 LLM 的自由文本约束成半结构化输出。它不如 JSON schema 严格,但比纯散文可控得多,且 LLM 遵守率高。
workers.py 第 49-50 行:
When you receive a message, it will contain: Stock, Thesis, Risk Assessment. Generate trade order including: 1. Order type (market/limit) 2. Quantity 3. Entry price 4. Stop loss 5. Take profit 6. Time in force
结构与 risk 完全对称。注意两个细节:
Order type (market/limit) 给了枚举提示——告诉 LLM 订单类型无非 market 或 limit,别瞎编别的。这种「字段 + 枚举提示」是结构化输出的进阶写法。workers.py 第 61-62 行:
When you receive a message, it will contain: Stock and Thesis from your Director. Generate quantitative analysis with: ticker, technical_score (0-1), volume_score (0-1), trend_strength (0-1), volatility, probability_score (0-1), key_levels (support, resistance, pivot).
这段风格略不同——输出没用编号清单,而是逗号分隔的字段列表,且每个字段标了类型/量程:
technical_score (0-1):明确 0~1 量程。volatility:无量程(自由浮点)。key_levels (support, resistance, pivot):复合字段,含三个子项。这种写法更接近「数据 schema」——它几乎在要求 LLM 输出一段类 JSON。配合 prompts.py 里的 QUANT_ANALYSIS_PROMPT 模板(里面有真正的 JSON 结构,第 432 行起),能看出作者想让量化产出可被程序解析的指标结构。
⚠️ 现实澄清:尽管追加段要求量化产出 0~1 的
technical_score等,但量化 Agent 无真实工具(第 3 章第 2 节)——这些「指标」是 LLM 凭直觉编的,数值看似精确实则虚构。结构化输出能让 LLM 输出「长得像数据」的东西,但不能让它「真的是数据」。真假取决于有没有真工具,不取决于 prompt 多漂亮。
prompts.py 里还有一组「Prompt 模板」(第 139-202 行),如 RISK_ASSESSMENT_PROMPT:
RISK_ASSESSMENT_PROMPT = """ Stock: {stock} Thesis: {thesis} Quant Analysis: {quant_analysis} Provide risk assessment including: 1. Recommended position size 2. Maximum drawdown risk 3. Market risk exposure 4. Overall risk score """
它是 .format(stock=..., thesis=..., quant_analysis=...) 的字符串模板。注意:这套模板在当前 workers.py 里根本没被使用——五个 Agent 用的是 prompts.py 顶部的五个 PROMPT 常量 + workers.py 里追加的约定,不是这套模板。
那这套模板是干什么的?推测是早期/计划中的「手动拼接 prompt」方案——作者原本想自己用 .format() 拼出完整 prompt 再调 LLM,后来改用 swarms 的 handoffs 机制(让 Director 自动分发),模板就废弃了。但代码没删,留作痕迹。
对比两种方案:
| 方案 | 实现 | 输入填充 | 现状 |
|---|---|---|---|
| 模板方案(prompts.py 模板段) | 自己 .format() 拼字符串 |
显式传参 | 未使用(废弃) |
| handoffs 方案(workers.py 追加段) | swarms 自动分发 | Director 把消息塞进上下文 | 在用 |
💡 核心心法:这段「废弃模板」是很好的学习材料——它展示了不靠框架时怎么做多步 prompt 拼接。理解它,你就能在不用 swarms 的场景(比如纯 OpenAI SDK)自己实现类似的链路。这也是读开源项目的一大收获:看「没被用的代码」能反推出设计的演变。
结构化输出的根本动机:让下游能消费上游的产出。在 AutoHedge 的链路里:
Director → (假设) → quant → (指标) → risk → (风控) → execution → (订单)
每个箭头都是「上游 LLM 产出文本 → 下游 LLM 读」。如果上游输出是自由散文,下游 LLM 解读时容易遗漏或误读。结构化约定(输入契约 + 编号清单)相当于上下游之间的协议——上游按协议产出,下游按协议解读,链路稳定性大增。
不过,AutoHedge 的结构化只到「半结构化文本」(编号清单),没到「严格 JSON」。后者更可控(LLM 输出可直接 json.loads),但对 LLM 的指令遵循要求更高。生产系统往往用 OpenAI 的 JSON mode 或 function calling 强制结构化,而非仅靠 prompt。
(0-1))描述输出,近乎要求类 JSON;但仍受限于无真实工具。RISK_ASSESSMENT_PROMPT 等 .format() 模板未被使用,是早期手动拼接方案的残留,反映设计从「手拼」到「handoffs」的演变。下一节,我们看 workers.py 顶部那段
_SYSTEM_SUFFIX——它是 AutoHedge 让 LLM「知道现在是几点」的巧妙设计。