@rong/agentscript 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +11 -11
- package/dist/parser/parser.js +25 -0
- package/dist/runtime/context.js +13 -3
- package/dist/runtime/evaluator.js +1 -0
- package/dist/runtime/interpreter.js +5 -2
- package/dist/runtime/scope.js +2 -2
- package/dist/semantic/analyzer.js +4 -0
- package/docs/cn/final-expression-return.md +18 -0
- package/docs/cn/language.md +16 -0
- package/docs/cn/role-label.md +492 -0
- package/docs/en/final-expression-return.md +18 -0
- package/docs/en/language.md +16 -0
- package/examples/changelog.as +2 -2
- package/examples/extract.as +2 -2
- package/examples/review.as +3 -3
- package/examples/summarize.as +2 -2
- package/examples/translate.as +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to AgentScript will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.1.3 - 2026-05-08
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added final expression return for functions.
|
|
10
|
+
- Added literal context labels with `use context as label`.
|
|
11
|
+
- Added agent role and description to prompt identity construction.
|
|
12
|
+
- Added trace and built context output for context labels.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Updated README, examples, and language references for labeled context usage.
|
|
17
|
+
|
|
5
18
|
## 1.0.0 - 2026-05-07
|
|
6
19
|
|
|
7
20
|
### Added
|
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# AgentScript
|
|
2
2
|
|
|
3
3
|
> **Agent context as code.**
|
|
4
|
-
> `use` declares what the model can see.
|
|
4
|
+
> `use` declares what the model can see, with optional labels for prompt sections.
|
|
5
5
|
> `generate` defines the only LLM call site and optional output shape.
|
|
6
6
|
> Zero runtime dependencies. TypeScript-powered.
|
|
7
7
|
|
|
8
8
|
```agentscript
|
|
9
|
-
use scratch.summary < 2k
|
|
9
|
+
use scratch.summary < 2k as observations
|
|
10
10
|
generate({ input: "Answer from observations" }) -> {
|
|
11
11
|
ok boolean
|
|
12
12
|
text string
|
|
@@ -61,8 +61,8 @@ main agent FileSummarizer {
|
|
|
61
61
|
|
|
62
62
|
main func(input { path string }) {
|
|
63
63
|
content = File.read({ path: input.path })
|
|
64
|
-
use input.path
|
|
65
|
-
use content < 8k
|
|
64
|
+
use input.path as source path
|
|
65
|
+
use content < 8k as file content
|
|
66
66
|
|
|
67
67
|
generate({
|
|
68
68
|
input: "Summarize the file for a busy teammate"
|
|
@@ -113,7 +113,7 @@ It is a small language for one thing:
|
|
|
113
113
|
|
|
114
114
|
> making LLM prompt context explicit, scoped, typed, traceable, and compilable.
|
|
115
115
|
|
|
116
|
-
It gives you two things that general-purpose languages don't: a first-class `use` keyword that declares *which* data enters the LLM prompt
|
|
116
|
+
It gives you two things that general-purpose languages don't: a first-class `use` keyword that declares *which* data enters the LLM prompt and what role it plays via `as label`, and a first-class `generate` expression that defines an LLM call with an optional output contract. Everything else — variables, functions, agents, imports, loops — exists to support this core workflow. Scopes enforce context boundaries naturally: what's `use`d in one function stays there; child scopes inherit but never leak upward. Functions can also return their final top-level expression directly, which keeps typical LLM workflows concise.
|
|
117
117
|
|
|
118
118
|
## How it works
|
|
119
119
|
|
|
@@ -178,10 +178,10 @@ main agent ResearchAgent {
|
|
|
178
178
|
main func(input {
|
|
179
179
|
question string
|
|
180
180
|
}) {
|
|
181
|
-
use input.question
|
|
181
|
+
use input.question as user question
|
|
182
182
|
|
|
183
183
|
scratch = []
|
|
184
|
-
use scratch.summary < 2k
|
|
184
|
+
use scratch.summary < 2k as observations
|
|
185
185
|
|
|
186
186
|
done = false
|
|
187
187
|
loop until done < 6 {
|
|
@@ -195,8 +195,8 @@ main agent ResearchAgent {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
func answer(question, scratch) {
|
|
198
|
-
use question
|
|
199
|
-
use scratch.summary < 2k
|
|
198
|
+
use question as user question
|
|
199
|
+
use scratch.summary < 2k as observations
|
|
200
200
|
generate({ input: "Answer using only the observations" }) -> {
|
|
201
201
|
ok boolean
|
|
202
202
|
text string
|
|
@@ -208,7 +208,7 @@ main agent ResearchAgent {
|
|
|
208
208
|
|
|
209
209
|
## Key ideas
|
|
210
210
|
|
|
211
|
-
1. **`use` is explicit context** — nothing enters the LLM prompt unless `use`d
|
|
211
|
+
1. **`use` is explicit context** — nothing enters the LLM prompt unless `use`d; `as label` names the context section
|
|
212
212
|
2. **`generate` is the only LLM call site** — with a required input instruction and optional output shape
|
|
213
213
|
3. **Final expression return keeps flows concise** — a function returns its final top-level expression
|
|
214
214
|
4. **Scope is context boundary** — functions, agents, and blocks isolate prompt visibility
|
|
@@ -219,7 +219,7 @@ main agent ResearchAgent {
|
|
|
219
219
|
|
|
220
220
|
| | Python / TypeScript | AgentScript |
|
|
221
221
|
|---|---|---|
|
|
222
|
-
| Context management | Implicit (string concatenation, array append) | Explicit (`use` declaration) |
|
|
222
|
+
| Context management | Implicit (string concatenation, array append) | Explicit (`use` declaration, optional `as label`) |
|
|
223
223
|
| LLM call site | Anywhere in the code | One `generate` expression |
|
|
224
224
|
| Context isolation | Manual discipline | Scope-inherited, auto-isolated |
|
|
225
225
|
| Trace / audit | External tooling needed | Built-in, per-call |
|
package/dist/parser/parser.js
CHANGED
|
@@ -176,13 +176,26 @@ class Parser {
|
|
|
176
176
|
const start = this.consume("use").range.start;
|
|
177
177
|
const value = this.parseLogicalOr();
|
|
178
178
|
const budget = this.match("<") ? this.parseBudget() : undefined;
|
|
179
|
+
const label = this.match("as") ? this.parseUseLabel() : undefined;
|
|
179
180
|
return {
|
|
180
181
|
kind: "UseStmt",
|
|
181
182
|
value,
|
|
182
183
|
budget,
|
|
184
|
+
label,
|
|
183
185
|
range: { start, end: this.previous().range.end }
|
|
184
186
|
};
|
|
185
187
|
}
|
|
188
|
+
parseUseLabel() {
|
|
189
|
+
const tokens = [];
|
|
190
|
+
const line = this.previous().range.start.line;
|
|
191
|
+
while (!this.isAtEnd() && !this.check("}") && this.peek().range.start.line === line) {
|
|
192
|
+
tokens.push(this.advance());
|
|
193
|
+
}
|
|
194
|
+
if (tokens.length === 0) {
|
|
195
|
+
throw this.error("Expected context label after 'as'");
|
|
196
|
+
}
|
|
197
|
+
return stringifyLabelTokens(tokens);
|
|
198
|
+
}
|
|
186
199
|
parseConfigValue(key) {
|
|
187
200
|
if (key === "model") {
|
|
188
201
|
return this.parsePostfix();
|
|
@@ -656,3 +669,15 @@ class Parser {
|
|
|
656
669
|
function isImportResourceKind(value) {
|
|
657
670
|
return IMPORT_RESOURCE_KINDS.has(value);
|
|
658
671
|
}
|
|
672
|
+
function stringifyLabelTokens(tokens) {
|
|
673
|
+
let label = tokens[0]?.value ?? "";
|
|
674
|
+
for (let index = 1; index < tokens.length; index += 1) {
|
|
675
|
+
const previous = tokens[index - 1];
|
|
676
|
+
const current = tokens[index];
|
|
677
|
+
if (current.range.start.column > previous.range.end.column) {
|
|
678
|
+
label += " ";
|
|
679
|
+
}
|
|
680
|
+
label += current.value;
|
|
681
|
+
}
|
|
682
|
+
return label;
|
|
683
|
+
}
|
package/dist/runtime/context.js
CHANGED
|
@@ -51,6 +51,7 @@ function buildContextItem(item, index) {
|
|
|
51
51
|
return {
|
|
52
52
|
index,
|
|
53
53
|
source: item.source,
|
|
54
|
+
label: item.label,
|
|
54
55
|
value: clipped.value,
|
|
55
56
|
text: clipped.text,
|
|
56
57
|
budget: item.budget,
|
|
@@ -60,8 +61,15 @@ function buildContextItem(item, index) {
|
|
|
60
61
|
};
|
|
61
62
|
}
|
|
62
63
|
function buildSystemPrompt(agentName, identity) {
|
|
63
|
-
const
|
|
64
|
+
const role = typeof identity.role === "string" ? identity.role : agentName;
|
|
65
|
+
const lines = [`You are ${role}.`];
|
|
66
|
+
if (typeof identity.description === "string") {
|
|
67
|
+
lines.push(identity.description);
|
|
68
|
+
}
|
|
64
69
|
for (const [key, value] of Object.entries(identity)) {
|
|
70
|
+
if (key === "role" || key === "description") {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
65
73
|
lines.push(`${key}: ${renderJson(value)}`);
|
|
66
74
|
}
|
|
67
75
|
return lines.join("\n");
|
|
@@ -71,8 +79,9 @@ function buildFinalUserMessage(context, instructionText, returnSchema) {
|
|
|
71
79
|
if (context.length > 0) {
|
|
72
80
|
sections.push("Context:");
|
|
73
81
|
for (const item of context) {
|
|
74
|
-
const label = item.
|
|
75
|
-
|
|
82
|
+
const label = item.label ?? String(item.index);
|
|
83
|
+
const source = item.source ? `source: ${item.source}\n` : "";
|
|
84
|
+
sections.push(`[${label}]\n${source}${item.text}`);
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
sections.push("Instruction:", instructionText);
|
|
@@ -147,6 +156,7 @@ export function builtContextToJson(context) {
|
|
|
147
156
|
context: context.context.map((item) => ({
|
|
148
157
|
index: item.index,
|
|
149
158
|
source: item.source ?? null,
|
|
159
|
+
label: item.label ?? null,
|
|
150
160
|
value: item.value,
|
|
151
161
|
text: item.text,
|
|
152
162
|
budget: budgetToJson(item.budget),
|
|
@@ -209,8 +209,11 @@ class Interpreter {
|
|
|
209
209
|
return undefined;
|
|
210
210
|
case "UseStmt": {
|
|
211
211
|
const source = formatExpressionSource(stmt.value);
|
|
212
|
-
scope.addUse(stmt.value, source, stmt.budget);
|
|
213
|
-
this.trace.push({
|
|
212
|
+
scope.addUse(stmt.value, source, stmt.budget, stmt.label);
|
|
213
|
+
this.trace.push({
|
|
214
|
+
kind: "use",
|
|
215
|
+
data: { source, label: stmt.label ?? null, budget: budgetToJson(stmt.budget) },
|
|
216
|
+
});
|
|
214
217
|
return undefined;
|
|
215
218
|
}
|
|
216
219
|
case "AssignStmt": {
|
package/dist/runtime/scope.js
CHANGED
|
@@ -29,8 +29,8 @@ export class RuntimeScope {
|
|
|
29
29
|
}
|
|
30
30
|
this.define(name, value);
|
|
31
31
|
}
|
|
32
|
-
addUse(expr, source, budget) {
|
|
33
|
-
this.uses.push({ expr, source, budget, scope: this });
|
|
32
|
+
addUse(expr, source, budget, label) {
|
|
33
|
+
this.uses.push({ expr, source, budget, label, scope: this });
|
|
34
34
|
}
|
|
35
35
|
setConfig(name, value) {
|
|
36
36
|
this.config.set(name, value);
|
|
@@ -171,6 +171,9 @@ class Analyzer {
|
|
|
171
171
|
if (stmt.budget && stmt.budget.amount <= 0) {
|
|
172
172
|
this.error("INVALID_BUDGET", "Budget amount must be greater than 0", stmt.range);
|
|
173
173
|
}
|
|
174
|
+
if (stmt.label && RESERVED_CONTEXT_LABELS.has(stmt.label)) {
|
|
175
|
+
this.error("RESERVED_CONTEXT_LABEL", `Context label '${stmt.label}' is reserved`, stmt.range);
|
|
176
|
+
}
|
|
174
177
|
break;
|
|
175
178
|
case "AssignStmt":
|
|
176
179
|
this.checkAssignment(stmt, scope);
|
|
@@ -501,6 +504,7 @@ class Analyzer {
|
|
|
501
504
|
const IMPORTED_BINDING_KINDS = new Set(["tool", "llm", "file", "agent", "memory"]);
|
|
502
505
|
const NON_CONTEXT_BINDING_KINDS = new Set(["tool", "llm", "agent", "function", "memory"]);
|
|
503
506
|
const VALID_MEMORY_METHODS = new Set(["add", "query"]);
|
|
507
|
+
const RESERVED_CONTEXT_LABELS = new Set(["system", "assistant", "tool", "developer"]);
|
|
504
508
|
function functionBinding(agentName, fn) {
|
|
505
509
|
return {
|
|
506
510
|
kind: "function",
|
|
@@ -59,6 +59,24 @@ func run(input) {
|
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
这条规则适用于所有表达式形式,包括调用表达式。被调用者解析为本地函数还是 agent 调用,都不影响 final expression return。例如,直接用 agent 名称调用另一个 agent 时,会调用该 agent 的 `main func`,其结果会被隐式返回:
|
|
63
|
+
|
|
64
|
+
```agentscript
|
|
65
|
+
agent Planner {
|
|
66
|
+
main func(input) {
|
|
67
|
+
generate({ input: "Create a plan" }) -> {
|
|
68
|
+
steps list[string]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
agent Controller {
|
|
74
|
+
func run(input) {
|
|
75
|
+
Planner(input)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
62
80
|
```agentscript
|
|
63
81
|
func get_result(result) {
|
|
64
82
|
result.value
|
package/docs/cn/language.md
CHANGED
|
@@ -158,6 +158,8 @@ Shape 不是完整的静态类型系统。
|
|
|
158
158
|
use input.question
|
|
159
159
|
use Requirements < 4k
|
|
160
160
|
use past_lessons < 2k
|
|
161
|
+
use input.question as user
|
|
162
|
+
use docs.summary < 4k as evidence
|
|
161
163
|
```
|
|
162
164
|
|
|
163
165
|
### 规则
|
|
@@ -167,10 +169,24 @@ use past_lessons < 2k
|
|
|
167
169
|
- Memory 查询结果不会自动进入 prompt。
|
|
168
170
|
- Trace 事件不会自动进入 prompt。
|
|
169
171
|
- `use value < n` 应用上下文预算。
|
|
172
|
+
- `use value as label` 为选中的 context source 附加字面标签。
|
|
173
|
+
- `use value < n as label` 先应用预算,再附加标签。
|
|
170
174
|
- `llm`、`tool`、`agent`、`memory` 绑定不能被 `use`。
|
|
171
175
|
- 函数绑定不能被 `use`。
|
|
172
176
|
- `use` 声明被子作用域继承。
|
|
173
177
|
|
|
178
|
+
### Context label
|
|
179
|
+
|
|
180
|
+
`as` 后面的 label 是字面标签文本,不是表达式,不会求值,也不会读取作用域中的变量。
|
|
181
|
+
|
|
182
|
+
```agentscript
|
|
183
|
+
use docs as evidence
|
|
184
|
+
use docs.summary < 4k as retrieved evidence
|
|
185
|
+
use input.question as user
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
即使当前作用域中存在名为 `evidence` 的变量,`as evidence` 也只是把 context section 标记为 `evidence`。Label 用于组织 prompt section 和 trace 输出;它不会改变 `system`、`user`、`assistant` 等 provider message role。
|
|
189
|
+
|
|
174
190
|
### 延迟求值
|
|
175
191
|
|
|
176
192
|
`use expr < budget` 声明的是 context source,而不是当前值的快照。当 `generate` 构建 prompt 时,表达式会被重新求值。这意味着在 `use` 之后、`generate` 之前对变量的修改在生成时刻是可见的。
|
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
# AgentScript Role / Label 设计
|
|
2
|
+
|
|
3
|
+
本文档定义 AgentScript 中 `agent role`、`context label` 和底层 provider message role 的分层设计,用于指导当前 prompt 构造、`use ... as ...` 语法和 trace 展示的实现。
|
|
4
|
+
|
|
5
|
+
## 1. 核心结论
|
|
6
|
+
|
|
7
|
+
AgentScript 应明确区分三类概念:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
agent role 表示“谁在生成 / 谁在发言”。
|
|
11
|
+
context label 表示“这段上下文在当前 prompt 中的用途”。
|
|
12
|
+
provider role 表示底层 LLM API message 的 system/user/assistant/tool。
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
这三者属于不同层级,不能混用。
|
|
16
|
+
|
|
17
|
+
- `agent role` 是 AgentScript 层的生成身份。
|
|
18
|
+
- `context label` 是 prompt 内部的上下文组织标签。
|
|
19
|
+
- `provider role` 是 OpenAI / Anthropic / Ollama 等 adapter 的传输协议细节。
|
|
20
|
+
|
|
21
|
+
用户不应通过 `use ... as system` 之类的写法直接操纵底层 provider role。
|
|
22
|
+
|
|
23
|
+
## 2. Agent Role
|
|
24
|
+
|
|
25
|
+
`role` 是 agent 的身份声明:
|
|
26
|
+
|
|
27
|
+
```agentscript
|
|
28
|
+
agent ResearchAgent {
|
|
29
|
+
model Qwen
|
|
30
|
+
role "Senior Researcher"
|
|
31
|
+
description "Answer questions with search and structured reasoning."
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
它的含义是:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
当前 agent 以什么身份进行 generate。
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
每次 `generate` 都应把当前 agent 的 `role` 和 `description` 放入 identity 层 prompt。例如:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
You are a Senior Researcher.
|
|
45
|
+
Answer questions with search and structured reasoning.
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
因此当前实现目标是:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
agent.role -> generation identity
|
|
52
|
+
agent.description -> generation identity
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
在 multi-agent 场景中,agent 输出被其他 agent 使用时,`role` 也可以作为 speaker provenance 展示:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
[plan]
|
|
59
|
+
Produced by: Planner
|
|
60
|
+
Role: Planner
|
|
61
|
+
Output:
|
|
62
|
+
...
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
这里的 `Role: Planner` 仍然是 AgentScript 层的 agent role,不是 provider message role。
|
|
66
|
+
|
|
67
|
+
## 3. Context Label
|
|
68
|
+
|
|
69
|
+
Context label 是 `use` 注入上下文时的语义标签。
|
|
70
|
+
|
|
71
|
+
当前语法支持:
|
|
72
|
+
|
|
73
|
+
```agentscript
|
|
74
|
+
use expr
|
|
75
|
+
use expr < budget
|
|
76
|
+
use expr as label
|
|
77
|
+
use expr < budget as label
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
固定顺序是:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
选择什么 -> 限制多少 -> 作为何种上下文
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
例如:
|
|
87
|
+
|
|
88
|
+
```agentscript
|
|
89
|
+
use input.question as user
|
|
90
|
+
use scratch.summary < 2k as memory
|
|
91
|
+
use docs.summary < 4k as evidence
|
|
92
|
+
use search_result as observation
|
|
93
|
+
use policy as constraint
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`context label` 的含义是:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
这段内容在当前 generate prompt 中作为何种材料被呈现。
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
它只影响:
|
|
103
|
+
|
|
104
|
+
- prompt section label
|
|
105
|
+
- trace display
|
|
106
|
+
- context organization
|
|
107
|
+
- debug / audit readability
|
|
108
|
+
|
|
109
|
+
它不改变:
|
|
110
|
+
|
|
111
|
+
- agent identity
|
|
112
|
+
- provider message role
|
|
113
|
+
- tool 权限
|
|
114
|
+
- system / user / assistant 权限
|
|
115
|
+
|
|
116
|
+
## 4. Provider Role
|
|
117
|
+
|
|
118
|
+
Provider role 是底层 LLM API 的 message role,例如:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
system
|
|
122
|
+
user
|
|
123
|
+
assistant
|
|
124
|
+
tool
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
它属于 provider adapter 的实现细节。
|
|
128
|
+
|
|
129
|
+
默认映射:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
system = 当前 agent identity + runtime rules
|
|
133
|
+
user = generate instruction + selected context + output shape
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
也就是说,AgentScript 的 context label 应作为 prompt 内部结构渲染,而不是映射为多条 provider message role。
|
|
137
|
+
|
|
138
|
+
Runtime 不把 memory、evidence、observation 等材料伪装成 assistant message:
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
assistant: [memory] ...
|
|
142
|
+
assistant: [evidence] ...
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
原因是 `assistant` 在 chat protocol 中表示模型真实历史输出,而 memory / evidence / observation 是外部材料。
|
|
146
|
+
|
|
147
|
+
## 5. Prompt 构造模型
|
|
148
|
+
|
|
149
|
+
一次 `generate` 的 prompt 分为四层。
|
|
150
|
+
|
|
151
|
+
### 5.1 Agent Identity
|
|
152
|
+
|
|
153
|
+
来自当前 agent 的配置:
|
|
154
|
+
|
|
155
|
+
```agentscript
|
|
156
|
+
role "Senior Researcher"
|
|
157
|
+
description "Answer questions with search and structured reasoning."
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
渲染为 provider `system` message 的一部分:
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
You are a Senior Researcher.
|
|
164
|
+
Answer questions with search and structured reasoning.
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 5.2 Generate Instruction
|
|
168
|
+
|
|
169
|
+
来自 `generate(...)` 的 `input` 字段:
|
|
170
|
+
|
|
171
|
+
```agentscript
|
|
172
|
+
generate({
|
|
173
|
+
input: "Answer using only the selected context"
|
|
174
|
+
}) -> {
|
|
175
|
+
ok boolean
|
|
176
|
+
answer string
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
渲染为:
|
|
181
|
+
|
|
182
|
+
```text
|
|
183
|
+
Instruction:
|
|
184
|
+
Answer using only the selected context.
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 5.3 Selected Context
|
|
188
|
+
|
|
189
|
+
来自当前 scope 和父 scope 中可见的 `use` 声明:
|
|
190
|
+
|
|
191
|
+
```agentscript
|
|
192
|
+
use input.question as user
|
|
193
|
+
use scratch.summary < 2k as memory
|
|
194
|
+
use docs.summary < 4k as evidence
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
渲染为:
|
|
198
|
+
|
|
199
|
+
```text
|
|
200
|
+
Context:
|
|
201
|
+
|
|
202
|
+
[user]
|
|
203
|
+
...
|
|
204
|
+
|
|
205
|
+
[memory]
|
|
206
|
+
...
|
|
207
|
+
|
|
208
|
+
[evidence]
|
|
209
|
+
...
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
如果没有 label,可以使用 source expression 作为默认 label 或显示为普通 context item。
|
|
213
|
+
|
|
214
|
+
### 5.4 Output Shape
|
|
215
|
+
|
|
216
|
+
来自 `generate(...) -> shape`:
|
|
217
|
+
|
|
218
|
+
```agentscript
|
|
219
|
+
generate({ input: "Answer" }) -> {
|
|
220
|
+
ok boolean
|
|
221
|
+
answer string
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
渲染为:
|
|
226
|
+
|
|
227
|
+
```text
|
|
228
|
+
Required output shape:
|
|
229
|
+
{
|
|
230
|
+
ok: boolean
|
|
231
|
+
answer: string
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
当 `generate` 没有 `-> shape` 时,不应注入输出 schema,也不应要求 provider 结构化输出。
|
|
236
|
+
|
|
237
|
+
## 6. `use ... as label` 规则
|
|
238
|
+
|
|
239
|
+
### 6.1 Label 类型
|
|
240
|
+
|
|
241
|
+
`as` 后面的 label 是语法层面的字面标签,不是表达式,不参与变量解析,也不会求值。
|
|
242
|
+
|
|
243
|
+
```agentscript
|
|
244
|
+
use docs as evidence
|
|
245
|
+
use scratch as memory
|
|
246
|
+
use docs.summary < 4k as retrieved-evidence
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
其中:
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
evidence
|
|
253
|
+
memory
|
|
254
|
+
retrieved-evidence
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
都是 context label 的字面值。即使当前作用域中存在名为 `evidence` 或 `memory` 的变量,`as evidence` 和 `as memory` 也不会读取这些变量。
|
|
258
|
+
|
|
259
|
+
Label 不需要双引号。它不是字符串表达式,而是 `use` 语句的一部分:
|
|
260
|
+
|
|
261
|
+
```agentscript
|
|
262
|
+
use docs as retrieved evidence
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
上例中的 label 是字面标签 `retrieved evidence`。Parser 应把 `as` 之后到语句结束之间的内容作为 label 文本;如果存在预算,则 label 位于预算之后:
|
|
266
|
+
|
|
267
|
+
```agentscript
|
|
268
|
+
use docs.summary < 4k as retrieved evidence
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Label 文本应在 AST 中保存为普通字符串,例如:
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
{
|
|
275
|
+
"label": "retrieved evidence"
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Label 不支持嵌套表达式、函数调用、字段访问或插值:
|
|
280
|
+
|
|
281
|
+
```agentscript
|
|
282
|
+
use docs as label_name -- label 是 "label_name",不是变量 label_name 的值
|
|
283
|
+
use docs as input.label -- label 是 "input.label",不是字段访问结果
|
|
284
|
+
use docs as label() -- label 是 "label()",不会调用函数
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### 6.2 保留 label
|
|
288
|
+
|
|
289
|
+
为避免和 provider role 混淆,以下 label 保留,不作为普通 context label 使用:
|
|
290
|
+
|
|
291
|
+
```text
|
|
292
|
+
system
|
|
293
|
+
assistant
|
|
294
|
+
tool
|
|
295
|
+
developer
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
`user` 允许作为 context label,但它只是 context label,不是 provider role:
|
|
299
|
+
|
|
300
|
+
```agentscript
|
|
301
|
+
use input.question as user
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
这里的 `user` 表示“这段上下文是用户输入”,不是把该 context item 变成 provider `user` message。
|
|
305
|
+
|
|
306
|
+
### 6.3 继承规则
|
|
307
|
+
|
|
308
|
+
`use` 声明原本可被 child scope 继承。加入 label 后,label 应随 use declaration 一起继承。
|
|
309
|
+
|
|
310
|
+
```agentscript
|
|
311
|
+
use input.question as user
|
|
312
|
+
|
|
313
|
+
func answer() {
|
|
314
|
+
generate({ input: "Answer" }) -> {
|
|
315
|
+
answer string
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`answer()` 内部的 `generate` 仍可看到:
|
|
321
|
+
|
|
322
|
+
```text
|
|
323
|
+
[user]
|
|
324
|
+
input.question
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### 6.4 重复 label
|
|
328
|
+
|
|
329
|
+
允许多个 context source 使用同一个 label:
|
|
330
|
+
|
|
331
|
+
```agentscript
|
|
332
|
+
use docs1 as evidence
|
|
333
|
+
use docs2 as evidence
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
重复 label 按声明顺序分别渲染:
|
|
337
|
+
|
|
338
|
+
```text
|
|
339
|
+
[evidence]
|
|
340
|
+
docs1...
|
|
341
|
+
|
|
342
|
+
[evidence]
|
|
343
|
+
docs2...
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## 7. Multi-Agent Provenance
|
|
347
|
+
|
|
348
|
+
Agent 调用结果应在 trace 或内部 metadata 中保留 provenance:
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"agent": "Planner",
|
|
353
|
+
"role": "Planner",
|
|
354
|
+
"function": "main",
|
|
355
|
+
"trace_id": "..."
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
这类 metadata 不一定暴露给用户代码,但应可用于 trace 和 context renderer。
|
|
360
|
+
|
|
361
|
+
用法:
|
|
362
|
+
|
|
363
|
+
```agentscript
|
|
364
|
+
plan = Planner(input)
|
|
365
|
+
critique = Critic(plan)
|
|
366
|
+
|
|
367
|
+
use plan.summary < 2k as plan
|
|
368
|
+
use critique.summary < 2k as critique
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
渲染时可以结合 provenance:
|
|
372
|
+
|
|
373
|
+
```text
|
|
374
|
+
[plan]
|
|
375
|
+
Produced by: Planner
|
|
376
|
+
Role: Planner
|
|
377
|
+
Output:
|
|
378
|
+
...
|
|
379
|
+
|
|
380
|
+
[critique]
|
|
381
|
+
Produced by: Critic
|
|
382
|
+
Role: Skeptical Reviewer
|
|
383
|
+
Output:
|
|
384
|
+
...
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
这样能表达 multi-agent 中“不同角色发言”的感觉,同时不污染 provider message role。
|
|
388
|
+
|
|
389
|
+
## 8. Trace 展示
|
|
390
|
+
|
|
391
|
+
Trace 同时显示 agent identity、instruction、context label、source expression 和 budget。
|
|
392
|
+
|
|
393
|
+
```text
|
|
394
|
+
Generate #1
|
|
395
|
+
Agent:
|
|
396
|
+
name: ResearchAgent
|
|
397
|
+
role: Senior Researcher
|
|
398
|
+
|
|
399
|
+
Instruction:
|
|
400
|
+
Answer using only the selected context.
|
|
401
|
+
|
|
402
|
+
Selected context:
|
|
403
|
+
[user]
|
|
404
|
+
source: input.question
|
|
405
|
+
budget: none
|
|
406
|
+
|
|
407
|
+
[memory]
|
|
408
|
+
source: scratch.summary
|
|
409
|
+
budget: 2k
|
|
410
|
+
|
|
411
|
+
[evidence]
|
|
412
|
+
source: docs.summary
|
|
413
|
+
budget: 4k
|
|
414
|
+
|
|
415
|
+
Output shape:
|
|
416
|
+
ok boolean
|
|
417
|
+
answer string
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Multi-agent trace 可以显示:
|
|
421
|
+
|
|
422
|
+
```text
|
|
423
|
+
Agent call:
|
|
424
|
+
Planner.main
|
|
425
|
+
role: Planner
|
|
426
|
+
|
|
427
|
+
Agent call:
|
|
428
|
+
Critic.main
|
|
429
|
+
role: Skeptical Reviewer
|
|
430
|
+
|
|
431
|
+
Generate #3
|
|
432
|
+
Agent:
|
|
433
|
+
Coordinator
|
|
434
|
+
role: Coordinator
|
|
435
|
+
|
|
436
|
+
Selected context:
|
|
437
|
+
[plan]
|
|
438
|
+
produced_by: Planner
|
|
439
|
+
producer_role: Planner
|
|
440
|
+
|
|
441
|
+
[critique]
|
|
442
|
+
produced_by: Critic
|
|
443
|
+
producer_role: Skeptical Reviewer
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
## 9. 当前实现方案
|
|
447
|
+
|
|
448
|
+
当前实现包括:
|
|
449
|
+
|
|
450
|
+
1. `role` 和 `description` 进入每次 `generate` 的 system / identity prompt。
|
|
451
|
+
2. `UseStmt` 增加可选 `label` 字段,保存 label 字面文本。
|
|
452
|
+
3. Parser 支持 `use expr as label` 和 `use expr < budget as label`。
|
|
453
|
+
4. Parser 将 `as` 后面的 label 保存为原始标签文本,不按表达式解析。
|
|
454
|
+
5. Semantic analyzer 校验保留 label,避免和 provider role 混淆。
|
|
455
|
+
6. Context builder 在 trace 和 prompt renderer 中使用 label。
|
|
456
|
+
7. Agent call trace 保留 agent name、role、function 等 provenance。
|
|
457
|
+
|
|
458
|
+
当前设计保持执行和上下文注入分离:
|
|
459
|
+
|
|
460
|
+
```agentscript
|
|
461
|
+
critique = Critic(draft)
|
|
462
|
+
use critique as critique
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## 10. 术语表
|
|
466
|
+
|
|
467
|
+
```text
|
|
468
|
+
provider role:
|
|
469
|
+
底层 chat message role,例如 system/user/assistant/tool。
|
|
470
|
+
|
|
471
|
+
agent role:
|
|
472
|
+
agent 生成输出时使用的身份,也可作为 agent 输出的 speaker provenance。
|
|
473
|
+
|
|
474
|
+
context label:
|
|
475
|
+
通过 use ... as ... 附加到 selected context 的用途标签。
|
|
476
|
+
|
|
477
|
+
speaker provenance:
|
|
478
|
+
agent 调用产物携带的来源元信息,包括 agent name、role、function 和 trace id。
|
|
479
|
+
|
|
480
|
+
agent transcript:
|
|
481
|
+
在 prompt 或 trace 中按 agent 来源组织的多 agent 输出展示形式。
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
## 11. 一句话总结
|
|
485
|
+
|
|
486
|
+
```text
|
|
487
|
+
provider role 是消息协议;
|
|
488
|
+
agent role 是生成身份和发言者身份;
|
|
489
|
+
context label 是上下文用途标签。
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
AgentScript 应在语言层构造 labeled context 和 agent transcript,再由 runtime 映射到底层 provider messages。
|
|
@@ -59,6 +59,24 @@ func run(input) {
|
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
This rule applies to all expression forms, including calls. It does not matter whether the callee resolves to a local function or an agent call. For example, calling another agent by name invokes that agent's `main func`, and the result is returned implicitly:
|
|
63
|
+
|
|
64
|
+
```agentscript
|
|
65
|
+
agent Planner {
|
|
66
|
+
main func(input) {
|
|
67
|
+
generate({ input: "Create a plan" }) -> {
|
|
68
|
+
steps list[string]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
agent Controller {
|
|
74
|
+
func run(input) {
|
|
75
|
+
Planner(input)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
62
80
|
```agentscript
|
|
63
81
|
func get_result(result) {
|
|
64
82
|
result.value
|
package/docs/en/language.md
CHANGED
|
@@ -158,6 +158,8 @@ Shapes are not a full static type system.
|
|
|
158
158
|
use input.question
|
|
159
159
|
use Requirements < 4k
|
|
160
160
|
use past_lessons < 2k
|
|
161
|
+
use input.question as user
|
|
162
|
+
use docs.summary < 4k as evidence
|
|
161
163
|
```
|
|
162
164
|
|
|
163
165
|
### Rules
|
|
@@ -167,10 +169,24 @@ use past_lessons < 2k
|
|
|
167
169
|
- Memory query results do not automatically enter prompts.
|
|
168
170
|
- Trace events do not automatically enter prompts.
|
|
169
171
|
- `use value < n` applies a context budget.
|
|
172
|
+
- `use value as label` attaches a literal context label to the selected source.
|
|
173
|
+
- `use value < n as label` applies the budget first, then attaches the label.
|
|
170
174
|
- `llm`, `tool`, `agent`, `memory` bindings cannot be used.
|
|
171
175
|
- Function bindings cannot be used.
|
|
172
176
|
- `use` declarations are inherited by child scopes.
|
|
173
177
|
|
|
178
|
+
### Context labels
|
|
179
|
+
|
|
180
|
+
The label after `as` is literal label text. It is not an expression, is not evaluated, and does not read variables from scope.
|
|
181
|
+
|
|
182
|
+
```agentscript
|
|
183
|
+
use docs as evidence
|
|
184
|
+
use docs.summary < 4k as retrieved evidence
|
|
185
|
+
use input.question as user
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`as evidence` labels the context section as `evidence` even if a variable named `evidence` exists. Labels organize prompt sections and trace output; they do not change provider message roles such as `system`, `user`, or `assistant`.
|
|
189
|
+
|
|
174
190
|
### Deferred evaluation
|
|
175
191
|
|
|
176
192
|
`use expr < budget` declares a context source, not a snapshot. The expression is re-evaluated when `generate` builds the prompt. This means updates to a variable made after `use` but before `generate` are visible at generation time.
|
package/examples/changelog.as
CHANGED
|
@@ -13,8 +13,8 @@ main agent ChangelogWriter {
|
|
|
13
13
|
path: input.diff_path
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
-
use input.diff_path
|
|
17
|
-
use diff < 10k
|
|
16
|
+
use input.diff_path as diff path
|
|
17
|
+
use diff < 10k as git diff
|
|
18
18
|
|
|
19
19
|
generate({ input: "Write a changelog from this git diff", limit: 1200 }) -> {
|
|
20
20
|
title string
|
package/examples/extract.as
CHANGED
|
@@ -14,8 +14,8 @@ main agent ApiExtractor {
|
|
|
14
14
|
timeout: 10000
|
|
15
15
|
})
|
|
16
16
|
|
|
17
|
-
use input.url
|
|
18
|
-
use response < 8k
|
|
17
|
+
use input.url as endpoint
|
|
18
|
+
use response < 8k as api response
|
|
19
19
|
|
|
20
20
|
generate({ input: "Extract normalized data from the API response", limit: 1200 }) -> {
|
|
21
21
|
records list[json]
|
package/examples/review.as
CHANGED
|
@@ -22,9 +22,9 @@ main agent CodeReviewAssistant {
|
|
|
22
22
|
max: 100
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
use input.path
|
|
26
|
-
use todos < 4k
|
|
27
|
-
use fixmes < 4k
|
|
25
|
+
use input.path as source path
|
|
26
|
+
use todos < 4k as todo findings
|
|
27
|
+
use fixmes < 4k as fixme findings
|
|
28
28
|
|
|
29
29
|
generate({ input: "Turn TODO and FIXME scan results into prioritized repair suggestions", limit: 1200 }) -> {
|
|
30
30
|
summary string
|
package/examples/summarize.as
CHANGED
|
@@ -13,8 +13,8 @@ main agent FileSummarizer {
|
|
|
13
13
|
path: input.path
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
-
use input.path
|
|
17
|
-
use content < 8k
|
|
16
|
+
use input.path as source path
|
|
17
|
+
use content < 8k as file content
|
|
18
18
|
|
|
19
19
|
generate({ input: "Summarize the file for a busy teammate", limit: 1000 }) -> {
|
|
20
20
|
title string
|
package/examples/translate.as
CHANGED
|
@@ -17,9 +17,9 @@ main agent MarkdownTranslator {
|
|
|
17
17
|
max: 50
|
|
18
18
|
})
|
|
19
19
|
|
|
20
|
-
use input.path
|
|
21
|
-
use input.target_language
|
|
22
|
-
use files < 4k
|
|
20
|
+
use input.path as source path
|
|
21
|
+
use input.target_language as target language
|
|
22
|
+
use files < 4k as markdown files
|
|
23
23
|
|
|
24
24
|
generate({ input: "Create a practical markdown translation plan", limit: 1000 }) -> {
|
|
25
25
|
target_language string
|