本附录定义的 JSON 结构被称为 “Harmony Schema”,它是一种基于 OpenAI ChatCompletions API 的超集,旨在解决标准 API 在处理复杂多模态、多智能体协作、以及长链路状态管理时的不足。
设计原则 (Rule of Thumb):
这是系统中流转的最小数据单元。
{
"id": "msg_123456789", // 消息唯一 ID(用于引用和回滚)
"role": "user | assistant | system | tool",
"name": "ResearchAgent_01", // [可选] 发言者名称,多 Agent 场景必填
"created_at": 1715000000, // Unix 时间戳
"content": [ ... ], // 见下文 Content Parts
"metadata": { ... } // 见 A.6 元数据部分
}
content 字段不再是字符串,而是一个对象数组。
{
"type": "text",
"text": "根据用户上传的图片,我测到了明显的裂痕。",
"index": 0
}
注意:对于生产环境,强烈建议使用 URL(配合短期签名的 S3 链接),Base64 仅用于调试或极低延迟的本地交互。
{
"type": "image_url",
"image_url": {
"url": "https://s3.amazonaws.com/bucket/invoice_scan_01.jpg",
"detail": "high", // 选项: low (85 tokens), high (按 tiles 计算), auto
"rotation": 0 // [扩展] 预处理旋转角度,防止模型看歪
}
}
视频通常不直接发给模型,而是转为一组带时间戳的图片帧。
{
"type": "image_url",
"image_url": { "url": "..." },
"video_metadata": { // [扩展字段]
"video_id": "vid_abc",
"timestamp_sec": 12.5, // 该帧在视频中的秒数
"frame_index": 300
}
}
{
"type": "input_audio",
"input_audio": {
"data": "base64_string...", // PCM 原始数据或压缩格式
"format": "wav",
"sample_rate": 24000
}
}
这是 Agent 与外部世界交互的物理层。
{
"role": "assistant",
"content": null, // 或者是 "我将为您查询天气..."
"tool_calls": [
{
"id": "call_890xyz",
"type": "function",
"function": {
"name": "search_knowledge_base",
"arguments": "{\"keywords\": [\"Transformer architecture\", \"Attention mechanism\"], \"year_filter\": 2023}"
}
}
]
}
关键规则:
tool_calls 数组中有几个元素,后续就必须紧跟几个 role: tool 的消息。{
"role": "tool",
"tool_call_id": "call_890xyz",
"name": "search_knowledge_base",
"content": "{\"status\": \"success\", \"results\": [{\"title\": \"Attention is All You Need\", \"snippet\": \"...\"}]}"
}
{
"role": "tool",
"tool_call_id": "call_890xyz",
"content": "{\"status\": \"error\", \"error_type\": \"InvalidDateRange\", \"message\": \"The year 2023 is in the future relative to the database snapshot (2022). Please try an earlier year.\"}"
}
有些工具(如浏览器截图、matplotlib 绘图)返回的是图片。
{
"role": "tool",
"tool_call_id": "call_render_chart",
"content": [
{ "type": "text", "text": "{\"status\": \"rendered_successfully\"}" },
{ "type": "image_url", "image_url": { "url": "https://.../chart_temp.png" } }
]
}
当流程从 “Planner Agent” 转移到 “Coder Agent” 时,使用此 Schema。
{
"protocol": "harmony_handoff_v1",
"meta": {
"source_agent": "planner_v2",
"target_agent": "coder_v1",
"timestamp": "2024-03-20T10:00:00Z"
},
"context": {
// 1. 任务目标(不可变)
"primary_objective": "为用户构建一个 Python 贪吃蛇游戏",
// 2. 当前进度
"status": "in_progress",
"completed_steps": ["需求分析", "技术选型(Pygame)"],
"next_immediate_step": "编写核心游戏循环代码",
// 3. 关键约束 (Memory)
"constraints": [
"必须有详细注释",
"背景颜色设为黑色"
],
// 4. 共享资产 (Artifacts) - 避免重复上传/下载
"artifacts": {
"design_doc": "s3://.../spec.md",
"assets_folder": "./assets/"
}
},
// 5. [可选] 压缩后的对话历史摘要
"history_summary": "用户最初要求3D游戏,经协商确认为2D版本..."
}
为实现“可信回答”,每条生成内容都应关联证据。这通常存储在消息的 metadata 或专门的 context_block 中。
{
"citation_map": {
"cit_01": {
"source_type": "pdf_document",
"source_id": "doc_fin_report_2023",
"source_name": "2023_Financial_Report.pdf",
"location": {
"page_number": 42,
"bbox": [100, 200, 500, 600], // [x1, y1, x2, y2] 用于前端在 PDF 上画框
"text_snippet": "Revenue increased by 20% YoY." // 用于校验
},
"score": 0.89 // 检索相关性分数
},
"cit_02": {
"source_type": "web_search",
"url": "https://wikipedia.org/...",
"access_date": "2024-03-20"
}
}
}
模型输出的文本应包含如下标记:
“2023年的营收增长强劲 ,这主要是由于云服务市场的扩张 。”
为了生产环境的调试(Trace)和计费,每条消息应携带以下隐形信息。
"metadata": {
// 成本追踪
"token_usage": {
"prompt_tokens": 500,
"completion_tokens": 150,
"total_cost_usd": 0.002
},
// 延迟追踪
"latency_ms": {
"ttft": 400, // Time to First Token
"total_time": 1200
},
// 模型指纹
"model_version": "gpt-4-vision-preview-2024-04-09",
"system_fingerprint": "fp_12345",
// 业务上下文
"session_id": "sess_user_999",
"trace_id": "trace_uuid_v4", // 关联到 Datadog/LangSmith
// 安全标记
"safety_check": {
"flagged": false,
"categories": []
}
}
tool_call_id 是连接 “思考” 与 “结果” 的唯一纽带,丢失 ID 会导致上下文错乱。constraints 和 artifacts,否则接手的 Agent 会像失忆一样重复询问用户。场景:安防摄像头每秒截取一帧,Agent 需要分析第 10 秒到第 15 秒的画面,判断是否有人员入侵。
要求:使用 content parts 构造 User Message,包含 5 张关键帧,并附带时间戳元数据。
场景:Agent 想要执行 delete_database,这是一个高危操作。设计一个 Tool Call 响应,既不执行删除,又引导模型去询问用户。
tool_call 的 arguments 里直接放 JSON 对象。arguments 必须是 String。即 JSON.stringify(args_object)。这是最常见的新手错误,导致 API 400 报错。.append()。必须实现一个 trim_messages(history, max_tokens) 函数。对于多模态消息,要特别注意一张 detail: high 的图片可能占用 1000+ tokens,比几百行文字还多。优先丢弃旧的图片,保留旧的文本摘要。.mp4 文件。你必须在客户端或服务端通过 ffmpeg 抽帧(Frames Extraction)并提取音轨(Audio Extraction),分别作为 Image List 和 Audio Input 传入。不要试图把二进制流直接塞进 Text 字段。