File size: 4,474 Bytes
98acb70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
-- record.lua - Regenerate traces/ and examples/ from the eval set.
--
--   lua54 record.lua
--
-- Writes, for each eval task:
--   traces/<id>.jsonl    the machine-readable trace, replayable
--   examples/<id>.txt    the human-readable transcript
--
-- The recorded prompts must match what main.lua builds by default, or
-- replaying needs a pile of flags to line the prompt back up. So this uses
-- compact rendering (main.lua's default) and the task's own tool set, and
-- writes the exact replay command into each transcript.

package.path = "./?.lua;" .. package.path

local Agent    = require('agent')
local tools    = require('tools')
local trace    = require('trace')
local protocol = require('protocol')
local TASKS    = require("eval.tasks.tasks").tasks

-- One place, so the recording and the printed replay command cannot drift.
local MAX_STEPS = 10

-- Match main.lua's default: the compact catalogue.
local real_render = protocol.render_system
protocol.render_system = function(t, task) return real_render(t, task, true) end

local function scripted(turns)
    local i = 0
    return { complete = function() i = i + 1 return turns[i], {} end }
end

-- Record with the real tokenizer when it is reachable, so the transcripts
-- and the demo GIFs show the same token counts a reader will see when they
-- run the replay command. Falls back to the estimator so this still works
-- in a checkout without repo 1 beside it.
local bpe = require('bpe')
local tokenizer, vocab = bpe.load("../lua_llama/tokenizer.bin")
if not tokenizer then
    tokenizer = bpe.approximate()
    io.write("note: recording with estimated token counts (", tostring(vocab), ")\n")
end

for _, task in ipairs(TASKS) do
    local reg = tools.registry()
    for _, n in ipairs(task.tools) do reg:add(tools[n]) end

    local agent = Agent.new({
        backend = scripted(task.script),
        tools = reg,
        tokenizer = tokenizer,
        max_steps = MAX_STEPS,
        max_tokens = task.budget or 4096,
        approve = function() return task.approve == true end,
        trace_path = "traces/" .. task.id .. ".jsonl",
    })

    local answer, reason, steps = agent:run(task.prompt)

    -- The printed command must reproduce the recording exactly, which means
    -- naming every setting where main.lua's default differs from the one
    -- used here. --max-steps is the one that bit: main.lua defaults to 6,
    -- loop-bait needs 7, and the replay silently stopped one step short of
    -- the answer -- reported as a max_steps failure of a run that had in
    -- fact succeeded.
    local cmd = string.format(
        'lua54 main.lua "%s" --replay traces/%s.jsonl --tools %s --max-steps %d%s%s',
        task.prompt, task.id, table.concat(task.tools, ","), MAX_STEPS,
        task.budget and (" --max-tokens " .. task.budget) or "",
        -- Without --yes the gate prompts on stdin and defaults to deny, so
        -- a replay of an approved run would silently become a denied one.
        task.approve and " --yes" or "")

    local out = { "task:   " .. task.prompt,
                  "tools:  " .. table.concat(task.tools, ", "),
                  "model:  scripted (see eval/tasks/tasks.lua)",
                  "replay: " .. cmd, "" }
    for _, s in ipairs(steps) do
        out[#out + 1] = string.format("[step %d] %s", s.step, s.status)
        if s.tokens and s.tokens.window and s.tokens.window > 0 then
            out[#out + 1] = string.format("  window: %d / %d tokens%s",
                s.tokens.window, s.tokens.budget, s.evicted and "  (evicted)" or "")
        end
        if s.thought then out[#out + 1] = "  thought: " .. s.thought end
        if s.call then
            out[#out + 1] = string.format('  call:    {"tool":"%s","args":%s}',
                s.call.tool, trace.encode(s.call.args or {}))
        end
        if s.observation then
            local o = s.observation
            if #o > 300 then o = o:sub(1, 300) .. "\n... [trimmed for display]" end
            out[#out + 1] = "  result:  " .. o:gsub("\n", "\n           ")
        end
        out[#out + 1] = ""
    end
    out[#out + 1] = string.rep("-", 60)
    out[#out + 1] = answer and ("ANSWER: " .. answer)
                            or ("No answer. Stopped because: " .. reason)

    local fh = io.open("examples/" .. task.id .. ".txt", "w")
    fh:write(table.concat(out, "\n"), "\n")
    fh:close()

    print(string.format("%-24s %-8s %s", task.id, reason, tostring(answer)))
end