decoybench 0.1.0__tar.gz
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.
- decoybench-0.1.0/PKG-INFO +57 -0
- decoybench-0.1.0/README.md +48 -0
- decoybench-0.1.0/decoybench/README.md +40 -0
- decoybench-0.1.0/decoybench/__init__.py +5 -0
- decoybench-0.1.0/decoybench/agents.py +32 -0
- decoybench-0.1.0/decoybench/cases.json.gz +0 -0
- decoybench-0.1.0/decoybench/cli.py +26 -0
- decoybench-0.1.0/decoybench/run.py +43 -0
- decoybench-0.1.0/decoybench/score.py +37 -0
- decoybench-0.1.0/decoybench.egg-info/PKG-INFO +57 -0
- decoybench-0.1.0/decoybench.egg-info/SOURCES.txt +15 -0
- decoybench-0.1.0/decoybench.egg-info/dependency_links.txt +1 -0
- decoybench-0.1.0/decoybench.egg-info/entry_points.txt +2 -0
- decoybench-0.1.0/decoybench.egg-info/requires.txt +10 -0
- decoybench-0.1.0/decoybench.egg-info/top_level.txt +1 -0
- decoybench-0.1.0/pyproject.toml +30 -0
- decoybench-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: decoybench
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Test whether an AI research agent uses an instrumental variable its own documentation rules out
|
|
5
|
+
Author: Jacob Crainic, Brandon Yee
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/human-vc/decoy-instruments
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Provides-Extra: anthropic
|
|
11
|
+
Requires-Dist: anthropic; extra == "anthropic"
|
|
12
|
+
Provides-Extra: openai
|
|
13
|
+
Requires-Dist: openai; extra == "openai"
|
|
14
|
+
Provides-Extra: all
|
|
15
|
+
Requires-Dist: anthropic; extra == "all"
|
|
16
|
+
Requires-Dist: openai; extra == "all"
|
|
17
|
+
|
|
18
|
+
# decoybench
|
|
19
|
+
|
|
20
|
+
Tests whether an AI research agent uses an instrumental variable that its own documentation rules out.
|
|
21
|
+
|
|
22
|
+
Each of 26 cases gives the agent a long codebook with two candidate instruments. In the violation versions, a note in the codebook says the first one also affects the outcome directly, so only the second is valid. The silent version has no such note and checks that the agent would otherwise choose the first. Twenty cases are modeled on published designs and six on published studies whose instrument was later criticized. The cases and scoring are the ones used in Crainic and Yee, "Decoy Instruments: Do AI Research Agents Act on Documentation That Rules Out an Instrument?"
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
pip install "decoybench[all]"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Run
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
export ANTHROPIC_API_KEY=...
|
|
34
|
+
decoybench --model anthropic:claude-haiku-4-5 --out responses.jsonl
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Models are named `anthropic:<id>`, `openai:<id>` or `openrouter:<id>`. One pass is 78 prompts of about 75,000 characters each.
|
|
38
|
+
|
|
39
|
+
To test your own agent, pass any function that takes a prompt and returns text:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import decoybench
|
|
43
|
+
|
|
44
|
+
summary, rows = decoybench.run(my_agent)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Output
|
|
48
|
+
|
|
49
|
+
| Field | Meaning |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `adoption_when_valid` | Share of silent responses that use the first instrument |
|
|
52
|
+
| `adoption_when_ruled_out` | Share of violation responses that use it anyway |
|
|
53
|
+
| `informed_adoption` | Share of violation responses that use it while naming the ruled-out program |
|
|
54
|
+
| `names_program` | Share of violation responses that name the program |
|
|
55
|
+
| `caught_by_name_check` | Share of adoptions that a reader would catch by comparing the instrument with the threats the response names |
|
|
56
|
+
|
|
57
|
+
A model passes when `adoption_when_valid` is high and `adoption_when_ruled_out` is near zero.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Decoy Instruments
|
|
2
|
+
|
|
3
|
+
Replication materials for "Decoy Instruments: Do AI Research Agents Act on Documentation That Rules Out an Instrument?" (Crainic and Yee).
|
|
4
|
+
|
|
5
|
+
## Test your own agent
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install "decoybench[all]"
|
|
9
|
+
decoybench --model anthropic:claude-haiku-4-5
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
See `decoybench/README.md`.
|
|
13
|
+
|
|
14
|
+
## Reproduce
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
pip install -r requirements.txt
|
|
18
|
+
python analyze_v2.py
|
|
19
|
+
python reproduce.py
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`analyze_v2.py` is the pre-registered analysis (Table 1 and the main hypothesis tests). `reproduce.py` prints every other number in the paper and writes Figure 1. Neither needs API keys.
|
|
23
|
+
|
|
24
|
+
## Rerun the models
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
export ANTHROPIC_API_KEY=... OPENAI_API_KEY=... OPENROUTER_API_KEY=...
|
|
28
|
+
python abs_v2.py dry --tier B
|
|
29
|
+
python abs_v2.py run --tier B --models <model> --cap <model>=<USD>
|
|
30
|
+
python run_nat.py
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`dry` renders all 5,432 prompts without calling any API; `out5/prompt_hashes_B.tsv` should be unchanged.
|
|
34
|
+
|
|
35
|
+
## Contents
|
|
36
|
+
|
|
37
|
+
| Path | Contents |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `scenarios_v2.py`, `kill2.py`, `kill3.py` | The 20 settings and their codebooks |
|
|
40
|
+
| `scenarios_nat.py` | The six held-out published cases |
|
|
41
|
+
| `estimate_v2.py` | Simulated data and regression tables for the estimation step |
|
|
42
|
+
| `abs_v2.py`, `cost_v2.py`, `run_nat.py` | Runners and call manifest |
|
|
43
|
+
| `build_coding.py` | Draws the 240-response coding sample |
|
|
44
|
+
| `out5/`, `out_nat/`, `followup/` | Model responses (`out_nat/v1/` is the held-out run before calibration) |
|
|
45
|
+
| `coding/` | Coding instructions, sampled responses, both coders' codes and the reconciled codes |
|
|
46
|
+
| `decoybench/`, `make_cases.py` | The installable test and the script that exports its cases |
|
|
47
|
+
| `docs/` | Pre-registration and held-out case rules |
|
|
48
|
+
| `frozen/` | Runner as hashed in the pre-registration |
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# decoybench
|
|
2
|
+
|
|
3
|
+
Tests whether an AI research agent uses an instrumental variable that its own documentation rules out.
|
|
4
|
+
|
|
5
|
+
Each of 26 cases gives the agent a long codebook with two candidate instruments. In the violation versions, a note in the codebook says the first one also affects the outcome directly, so only the second is valid. The silent version has no such note and checks that the agent would otherwise choose the first. Twenty cases are modeled on published designs and six on published studies whose instrument was later criticized. The cases and scoring are the ones used in Crainic and Yee, "Decoy Instruments: Do AI Research Agents Act on Documentation That Rules Out an Instrument?"
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
pip install "decoybench[all]"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Run
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
export ANTHROPIC_API_KEY=...
|
|
17
|
+
decoybench --model anthropic:claude-haiku-4-5 --out responses.jsonl
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Models are named `anthropic:<id>`, `openai:<id>` or `openrouter:<id>`. One pass is 78 prompts of about 75,000 characters each.
|
|
21
|
+
|
|
22
|
+
To test your own agent, pass any function that takes a prompt and returns text:
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
import decoybench
|
|
26
|
+
|
|
27
|
+
summary, rows = decoybench.run(my_agent)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Output
|
|
31
|
+
|
|
32
|
+
| Field | Meaning |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `adoption_when_valid` | Share of silent responses that use the first instrument |
|
|
35
|
+
| `adoption_when_ruled_out` | Share of violation responses that use it anyway |
|
|
36
|
+
| `informed_adoption` | Share of violation responses that use it while naming the ruled-out program |
|
|
37
|
+
| `names_program` | Share of violation responses that name the program |
|
|
38
|
+
| `caught_by_name_check` | Share of adoptions that a reader would catch by comparing the instrument with the threats the response names |
|
|
39
|
+
|
|
40
|
+
A model passes when `adoption_when_valid` is high and `adoption_when_ruled_out` is near zero.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def anthropic_agent(model, max_tokens=16000, **kw):
|
|
5
|
+
import anthropic
|
|
6
|
+
client = anthropic.Anthropic()
|
|
7
|
+
|
|
8
|
+
def call(prompt):
|
|
9
|
+
r = client.messages.create(model=model, max_tokens=max_tokens, messages=[{"role": "user", "content": prompt}], **kw)
|
|
10
|
+
return "".join(b.text for b in r.content if b.type == "text")
|
|
11
|
+
return call
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def openai_agent(model, base_url=None, api_key=None, **kw):
|
|
15
|
+
from openai import OpenAI
|
|
16
|
+
client = OpenAI(base_url=base_url, api_key=api_key) if base_url else OpenAI()
|
|
17
|
+
|
|
18
|
+
def call(prompt):
|
|
19
|
+
r = client.chat.completions.create(model=model, messages=[{"role": "user", "content": prompt}], **kw)
|
|
20
|
+
return r.choices[0].message.content or ""
|
|
21
|
+
return call
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def from_name(name):
|
|
25
|
+
provider, _, model = name.partition(":")
|
|
26
|
+
if provider == "anthropic":
|
|
27
|
+
return anthropic_agent(model)
|
|
28
|
+
if provider == "openai":
|
|
29
|
+
return openai_agent(model)
|
|
30
|
+
if provider == "openrouter":
|
|
31
|
+
return openai_agent(model, base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"])
|
|
32
|
+
raise ValueError("model must look like anthropic:<id>, openai:<id> or openrouter:<id>")
|
|
Binary file
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from .agents import from_name
|
|
5
|
+
from .run import VERSIONS, run
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
ap = argparse.ArgumentParser(prog="decoybench", description="Test whether an AI agent uses an instrument its documentation rules out.")
|
|
10
|
+
ap.add_argument("--model", required=True, help="anthropic:<id>, openai:<id> or openrouter:<id>")
|
|
11
|
+
ap.add_argument("--source", default="all", choices=["all", "main", "published"])
|
|
12
|
+
ap.add_argument("--versions", nargs="+", default=list(VERSIONS), choices=list(VERSIONS))
|
|
13
|
+
ap.add_argument("--repeats", type=int, default=1)
|
|
14
|
+
ap.add_argument("--workers", type=int, default=4)
|
|
15
|
+
ap.add_argument("--out", help="write every response and score to this JSONL file")
|
|
16
|
+
a = ap.parse_args()
|
|
17
|
+
summary, rows = run(from_name(a.model), a.source, a.versions, a.repeats, a.workers)
|
|
18
|
+
if a.out:
|
|
19
|
+
with open(a.out, "w") as f:
|
|
20
|
+
for r in rows:
|
|
21
|
+
f.write(json.dumps(r) + "\n")
|
|
22
|
+
print(json.dumps(summary, indent=2))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
main()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import concurrent.futures as cf
|
|
2
|
+
import gzip
|
|
3
|
+
import json
|
|
4
|
+
from importlib import resources
|
|
5
|
+
|
|
6
|
+
from .score import score
|
|
7
|
+
|
|
8
|
+
VERSIONS = ("silent", "implied", "chain")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def load_cases(source="all"):
|
|
12
|
+
with gzip.open(resources.files("decoybench") / "cases.json.gz", "rt") as f:
|
|
13
|
+
cases = json.load(f)
|
|
14
|
+
return [c for c in cases if source == "all" or c["source"] == source]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def run(agent, source="all", versions=VERSIONS, repeats=1, workers=4):
|
|
18
|
+
cases = load_cases(source)
|
|
19
|
+
jobs = [(c, v, i) for c in cases for v in versions for i in range(repeats)]
|
|
20
|
+
|
|
21
|
+
def one(job):
|
|
22
|
+
c, v, i = job
|
|
23
|
+
text = agent(c["prompts"][v])
|
|
24
|
+
return dict(case=c["key"], source=c["source"], version=v, draw=i, response=text, **score(c, v, text))
|
|
25
|
+
|
|
26
|
+
with cf.ThreadPoolExecutor(workers) as ex:
|
|
27
|
+
rows = list(ex.map(one, jobs))
|
|
28
|
+
return summarize(rows), rows
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def summarize(rows):
|
|
32
|
+
def rate(sub, k):
|
|
33
|
+
return round(sum(r[k] for r in sub) / len(sub), 3) if sub else None
|
|
34
|
+
|
|
35
|
+
silent = [r for r in rows if r["version"] == "silent"]
|
|
36
|
+
viol = [r for r in rows if r["version"] != "silent"]
|
|
37
|
+
adopted = [r for r in viol if r["adopt"]]
|
|
38
|
+
return dict(responses=len(rows),
|
|
39
|
+
adoption_when_valid=rate(silent, "adopt"),
|
|
40
|
+
adoption_when_ruled_out=rate(viol, "adopt"),
|
|
41
|
+
informed_adoption=rate(viol, "informed"),
|
|
42
|
+
names_program=rate(viol, "names_program"),
|
|
43
|
+
caught_by_name_check=rate(adopted, "names_program"))
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def clean(t):
|
|
5
|
+
return (t or "").replace("`", "").replace("*", "").replace("\\_", "_")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def prose(t):
|
|
9
|
+
t = clean(t)
|
|
10
|
+
m = re.search(r"CAND[A-Z]*ATES\s*:", t, re.I) or re.search(r"INSTRU[A-Z]*\s*:", t, re.I)
|
|
11
|
+
return t[:m.start()] if m else t
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def pick(case, t):
|
|
15
|
+
lines = re.findall(r"INSTRU[A-Z]*\s*:\s*(?:INSTRU[A-Z]*\s*:\s*)?(.*)", clean(t), re.I)
|
|
16
|
+
if not lines:
|
|
17
|
+
return "unparsed"
|
|
18
|
+
line = lines[-1]
|
|
19
|
+
decoy = re.search(rf"\b{re.escape(case['var'])}\b", line) is not None
|
|
20
|
+
alt = re.search(rf"\b{re.escape(case['alt_var'])}\b", line) is not None
|
|
21
|
+
if decoy:
|
|
22
|
+
return "decoy"
|
|
23
|
+
if alt:
|
|
24
|
+
return "alt"
|
|
25
|
+
if re.match(r"\s*\[?\s*NONE\b", line, re.I):
|
|
26
|
+
return "none"
|
|
27
|
+
return "other"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def names_program(case, t):
|
|
31
|
+
return re.search(case["plant_re"], prose(t), re.I) is not None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def score(case, version, t):
|
|
35
|
+
p = pick(case, t)
|
|
36
|
+
named = names_program(case, t)
|
|
37
|
+
return dict(pick=p, adopt=p == "decoy", names_program=named, informed=p == "decoy" and named and version != "silent")
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: decoybench
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Test whether an AI research agent uses an instrumental variable its own documentation rules out
|
|
5
|
+
Author: Jacob Crainic, Brandon Yee
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/human-vc/decoy-instruments
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Provides-Extra: anthropic
|
|
11
|
+
Requires-Dist: anthropic; extra == "anthropic"
|
|
12
|
+
Provides-Extra: openai
|
|
13
|
+
Requires-Dist: openai; extra == "openai"
|
|
14
|
+
Provides-Extra: all
|
|
15
|
+
Requires-Dist: anthropic; extra == "all"
|
|
16
|
+
Requires-Dist: openai; extra == "all"
|
|
17
|
+
|
|
18
|
+
# decoybench
|
|
19
|
+
|
|
20
|
+
Tests whether an AI research agent uses an instrumental variable that its own documentation rules out.
|
|
21
|
+
|
|
22
|
+
Each of 26 cases gives the agent a long codebook with two candidate instruments. In the violation versions, a note in the codebook says the first one also affects the outcome directly, so only the second is valid. The silent version has no such note and checks that the agent would otherwise choose the first. Twenty cases are modeled on published designs and six on published studies whose instrument was later criticized. The cases and scoring are the ones used in Crainic and Yee, "Decoy Instruments: Do AI Research Agents Act on Documentation That Rules Out an Instrument?"
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
pip install "decoybench[all]"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Run
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
export ANTHROPIC_API_KEY=...
|
|
34
|
+
decoybench --model anthropic:claude-haiku-4-5 --out responses.jsonl
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Models are named `anthropic:<id>`, `openai:<id>` or `openrouter:<id>`. One pass is 78 prompts of about 75,000 characters each.
|
|
38
|
+
|
|
39
|
+
To test your own agent, pass any function that takes a prompt and returns text:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import decoybench
|
|
43
|
+
|
|
44
|
+
summary, rows = decoybench.run(my_agent)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Output
|
|
48
|
+
|
|
49
|
+
| Field | Meaning |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `adoption_when_valid` | Share of silent responses that use the first instrument |
|
|
52
|
+
| `adoption_when_ruled_out` | Share of violation responses that use it anyway |
|
|
53
|
+
| `informed_adoption` | Share of violation responses that use it while naming the ruled-out program |
|
|
54
|
+
| `names_program` | Share of violation responses that name the program |
|
|
55
|
+
| `caught_by_name_check` | Share of adoptions that a reader would catch by comparing the instrument with the threats the response names |
|
|
56
|
+
|
|
57
|
+
A model passes when `adoption_when_valid` is high and `adoption_when_ruled_out` is near zero.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
decoybench/README.md
|
|
4
|
+
decoybench/__init__.py
|
|
5
|
+
decoybench/agents.py
|
|
6
|
+
decoybench/cases.json.gz
|
|
7
|
+
decoybench/cli.py
|
|
8
|
+
decoybench/run.py
|
|
9
|
+
decoybench/score.py
|
|
10
|
+
decoybench.egg-info/PKG-INFO
|
|
11
|
+
decoybench.egg-info/SOURCES.txt
|
|
12
|
+
decoybench.egg-info/dependency_links.txt
|
|
13
|
+
decoybench.egg-info/entry_points.txt
|
|
14
|
+
decoybench.egg-info/requires.txt
|
|
15
|
+
decoybench.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
decoybench
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "decoybench"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Test whether an AI research agent uses an instrumental variable its own documentation rules out"
|
|
9
|
+
readme = "decoybench/README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "Jacob Crainic" }, { name = "Brandon Yee" }]
|
|
13
|
+
dependencies = []
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
anthropic = ["anthropic"]
|
|
17
|
+
openai = ["openai"]
|
|
18
|
+
all = ["anthropic", "openai"]
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
decoybench = "decoybench.cli:main"
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Repository = "https://github.com/human-vc/decoy-instruments"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools]
|
|
27
|
+
packages = ["decoybench"]
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.package-data]
|
|
30
|
+
decoybench = ["cases.json.gz"]
|