|
| 1 | +# Context-as-Structure (v2.2.1) |
| 2 | + |
| 3 | +> **v2.2.1 新特性** — 把 Context 提升为 agent 声明的结构属性。 |
| 4 | +
|
| 5 | +## 设计动机 |
| 6 | + |
| 7 | +面试官观点:**Agent = (Model, Tools, Context) 三元组完全定义**。 |
| 8 | + |
| 9 | +在 v2.1 及之前,Nexa 的 `agent` 声明只定义了 `model` 和 `uses`(tools),但 **context 缺失** —— 运行时 `self.messages = []` 硬编码为空,导致 pipeline `A >> B` 只能传字符串,agent 间的上下文无法结构化传递。 |
| 10 | + |
| 11 | +v2.2.1 在 agent 声明块里增加 `context { ... }` 子块,让 context 行为像 model/tools 一样**在结构上完全定义**。 |
| 12 | + |
| 13 | +## 语法 |
| 14 | + |
| 15 | +```nexa |
| 16 | +agent Researcher { |
| 17 | + model: "deepseek/deepseek-chat", |
| 18 | + uses: [web_search], |
| 19 | + |
| 20 | + // v2.2.1: Context 声明(结构属性,与 model/tools 同级) |
| 21 | + context { |
| 22 | + source: upstream, // "upstream" | "shared:name" | "fresh" |
| 23 | + sink: downstream, // "downstream" | "shared:name" | "discard" |
| 24 | + input_schema: ResearchInput, // Protocol 类型(可选) |
| 25 | + output_schema: ResearchOutput, // Protocol 类型(可选) |
| 26 | + max_history_turns: 20, |
| 27 | + inherit: [messages, artifacts] // 从上游继承的字段 |
| 28 | + } |
| 29 | + |
| 30 | + prompt: "..." |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +### 字段说明 |
| 35 | + |
| 36 | +| 字段 | 类型 | 默认值 | 说明 | |
| 37 | +|------|------|--------|------| |
| 38 | +| `source` | `str` | `"upstream"` | context 来源:`upstream`(从上游继承)、`shared:name`(共享内存)、`fresh`(全新) | |
| 39 | +| `sink` | `str` | `"downstream"` | context 去向:`downstream`(传给下游)、`shared:name`、`discard`(丢弃) | |
| 40 | +| `input_schema` | `Protocol` | `None` | 声明此 agent 需要的输入 context 类型(编译期检查) | |
| 41 | +| `output_schema` | `Protocol` | `None` | 声明此 agent 产出的 context 类型 | |
| 42 | +| `max_history_turns` | `int` | `None` | 最大保留对话轮数 | |
| 43 | +| `inherit` | `List[str]` | `[]` | 从上游继承的字段:`messages`、`artifacts`、`tool_results` | |
| 44 | + |
| 45 | +## 编译期类型检查(C-004) |
| 46 | + |
| 47 | +Harness Validator 在编译期检查 pipeline `A >> B` 中,A 的 `output_schema` 是否匹配 B 的 `input_schema`。不匹配则编译失败: |
| 48 | + |
| 49 | +``` |
| 50 | +C-004: Context incompatible in pipeline 'A >> B': |
| 51 | + output_schema='ResearchOutput' is not a subtype of input_schema='WeatherInput' |
| 52 | + Suggestion: Align A.context.output_schema with B.context.input_schema |
| 53 | +``` |
| 54 | + |
| 55 | +### 示例:不兼容的 pipeline |
| 56 | + |
| 57 | +```nexa |
| 58 | +protocol ResearchOutput { summary: "string" } |
| 59 | +protocol WrongInput { foo: "string" } |
| 60 | +
|
| 61 | +agent A { |
| 62 | + model: "x", |
| 63 | + context { output_schema: ResearchOutput } |
| 64 | + prompt: "a" |
| 65 | +} |
| 66 | +
|
| 67 | +agent B { |
| 68 | + model: "x", |
| 69 | + context { input_schema: WrongInput } // ❌ 与 A 的 output_schema 不匹配 |
| 70 | + prompt: "b" |
| 71 | +} |
| 72 | +
|
| 73 | +flow main { |
| 74 | + result = "test" >> A >> B; // 编译失败:C-004 |
| 75 | +} |
| 76 | +``` |
| 77 | + |
| 78 | +### 示例:兼容的 pipeline |
| 79 | + |
| 80 | +```nexa |
| 81 | +protocol ResearchOutput { summary: "string", key_points: "string" } |
| 82 | +protocol SummaryResult { title: "string", abstract: "string" } |
| 83 | +
|
| 84 | +agent Researcher { |
| 85 | + model: "deepseek/deepseek-chat", |
| 86 | + context { |
| 87 | + source: upstream, |
| 88 | + sink: downstream, |
| 89 | + output_schema: ResearchOutput, |
| 90 | + max_history_turns: 10, |
| 91 | + inherit: [messages] |
| 92 | + } |
| 93 | + prompt: "Research the topic." |
| 94 | +} |
| 95 | +
|
| 96 | +agent Summarizer { |
| 97 | + model: "deepseek/deepseek-chat", |
| 98 | + context { |
| 99 | + source: upstream, |
| 100 | + sink: downstream, |
| 101 | + input_schema: ResearchOutput, // ✅ 匹配 Researcher 的 output_schema |
| 102 | + output_schema: SummaryResult, |
| 103 | + max_history_turns: 5, |
| 104 | + inherit: [messages, artifacts] |
| 105 | + } |
| 106 | + prompt: "Summarize the research." |
| 107 | +} |
| 108 | +
|
| 109 | +flow main { |
| 110 | + result = "quantum computing 2024" >> Researcher >> Summarizer; |
| 111 | +} |
| 112 | +``` |
| 113 | + |
| 114 | +## 运行时行为 |
| 115 | + |
| 116 | +### AgentContext 数据结构 |
| 117 | + |
| 118 | +每个 agent 在 `run()` 结束时会 snapshot 一个 `AgentContext` 对象,包含: |
| 119 | + |
| 120 | +- `messages` — 完整对话历史 |
| 121 | +- `tool_results` — 工具调用记录 |
| 122 | +- `artifacts` — 结构化产物 |
| 123 | +- `output_text` — 最终输出文本 |
| 124 | +- `output_schema` — 声明的输出 schema 名 |
| 125 | + |
| 126 | +### Pipeline handoff |
| 127 | + |
| 128 | +`A >> B` 在 v2.2.1 中默认走 `nexa_context_pipeline`: |
| 129 | + |
| 130 | +1. A 执行,产出 `AgentContext` |
| 131 | +2. B 的 `run()` 检测到上游 `AgentContext`,根据 B 声明的 `inherit` 字段选择性继承 |
| 132 | +3. B 执行后产出自己的 `AgentContext`,传给下游 |
| 133 | + |
| 134 | +### 向后兼容 |
| 135 | + |
| 136 | +**不加 `context { ... }` 块的 agent 行为完全不变**(保持 v2.1 的字符串传递语义)。`nexa_context_pipeline` 会自动检测:无 `context_spec` 的 agent 走旧路径。 |
| 137 | + |
| 138 | +## 与 Harness 六元组的关系 |
| 139 | + |
| 140 | +v2.2.1 不是推翻现有设计,而是补全它: |
| 141 | + |
| 142 | +| Harness 维度 | v2.2.1 关系 | |
| 143 | +|-------------|------------| |
| 144 | +| **C(Context)** | `context { ... }` 块是 C 维度的声明式扩展 | |
| 145 | +| **S(State)** | 每次 handoff 自动 snapshot,支持回溯 | |
| 146 | +| **V(Verification)** | verify 结果作为 context artifact 流转 | |
| 147 | +| **L(Lifecycle)** | handoff 触发 `before_step` / `after_step` 钩子 | |
| 148 | + |
| 149 | +## 完整示例 |
| 150 | + |
| 151 | +见 [`examples/v2.2/01_context_handoff.nx`](https://github.com/Nexa-Language/Nexa/blob/main/examples/v2.2/01_context_handoff.nx)。 |
| 152 | + |
| 153 | +## 测试 |
| 154 | + |
| 155 | +见 [`tests/test_context_handoff.py`](https://github.com/Nexa-Language/Nexa/blob/main/tests/test_context_handoff.py)(18 个测试用例,覆盖 grammar、AST、runtime、validator、codegen、向后兼容)。 |
0 commit comments