校对:soup
Cloudflare工人应用软件的助手和代理部件. 从 Worker 服务 MCP 服务器, 只保留只读调用器的读取工具, 测量每个工具调用, 以及聊天助理的服务器侧(@columenlabs/lumen-ai/assistant) (中文(简体) ).
安装
bun add @columenlabs/lumen-ai @columenlabs/lumen-ops @modelcontextprotocol/sdk
切换 @columenlabs/lumen-* 软件包到相同的准确版本. @modelcontextprotocol/sdk 是一个对等点: 应用程序自己的副本构建服务器 .
服务 MCP 服务器
为您已检查的呼叫者创建一个服务器, 然后将请求交给它 。 每项请求都是单独提出的(无法说明的可流式HTTP、JSON答复),因为一个工人在请求之间没有任何关系.
import { jsonResult, keepReadTools, meterTools, serveMcp } from '@columenlabs/lumen-ai';
import { setToolSink } from '@columenlabs/lumen-ops/tools';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
setToolSink(env.TOOL_CALLS); // once per isolate; tool calls are metered only when it is set
function build(person: string) {
const mcp = new McpServer({ name: 'my-app', version: '1.0.0' });
mcp.registerTool(
'account_list',
{ description: 'Accounts and balances', annotations: { readOnlyHint: true } },
async () => jsonResult(await accounts(person)),
);
return mcp;
}
app.all('/mcp', async (c) => {
const server = meterTools(build(c.get('person')), { app: 'my-app' });
if (c.get('viewOnly')) keepReadTools(server);
return serveMcp(server, c.req.raw, { onRequest: ({ tool }) => c.set('tool', tool) });
});
出口
| 导出 | 它会做什么 |
|---|---|
serveMcp(server, request, opts?) | 答复一个MCP请求并关闭服务器; onRequest 先听到方法和工具 |
jsonResult(value) | 一个工具结果将一个 JSON 值作为文本 |
keepReadTools(server, isRead?) | 删除每个写入的工具;默认情况下,当工具被读取时只读 readOnlyHint 设定 |
meterTools(server, opts) | 环绕每个注册工具, 因此每个调用是一个工具调用点: 墙上时间、 结果、 JSON 列表的行、 输入和输出大小 |
registeredTools(server) | 服务器上的每个工具, 按名称 |
mcpCallOf(body) | JSON-RPC机构要求的方法和工具 |
meterTools 写入 @columenlabs/lumen-ops/tools; 查看其列的读取.
版本数
每次合并到 main 立即发布每个 Lumen 软件包的下一条补丁 .
一个聊天助手
@columenlabs/lumen-ai/assistant 是聊天侧面板背后的服务器: Claude 或 OpenAI 模型在应用程序自己的分级, 应用程序的工具来自任何 MCP 服务器, 网络, 起草人物保存, 交给更高分级, 建议, 以及每月每人开支上限 。 面板和设置部分 @columenlabs/lumen-ui';共享类型是 @columenlabs/lumen-ui-types/assistant。 。 。 .
import {
assistantRoutes,
createAssistant,
doSql,
mcpServerTools,
sqlAssistantStore,
} from '@columenlabs/lumen-ai/assistant';
const assistant = createAssistant({
name: 'Helper',
system: 'You help people with their notes. Use the notes tools for facts.',
// Smallest first. The app names them; a person picks each tier's model and effort.
tiers: [
{ id: 'fast', label: 'Fast', about: 'Quick lookups', model: 'claude-haiku-5-5' },
{ id: 'steady', label: 'Steady', about: 'Most work', model: 'claude-sonnet-5-5' },
{ id: 'deep', label: 'Deep', about: 'Hard judgement', model: 'claude-opus-5-5' },
],
groups: [
{ id: 'notes', label: 'Notes', about: 'Read and edit notes', tools: (caller) => mcpServerTools(() => notesServer(caller.id)) },
],
web: { fetch: { blockHosts: ['app.example.com'] }, scrapeql: { url: 'https://scrapeql.example.com/mcp' } },
store: sqlAssistantStore(doSql(ctx.storage.sql)), // or d1Sql(env.DB); { table } names another table
sealer, // the app's own sealing: keys and chats are stored sealed
// A provider's key when none is set in Settings.
appKey: async (provider) => (provider === 'openai' ? env.OPENAI_API_KEY : env.ANTHROPIC_API_KEY),
});
const routes = assistantRoutes(assistant, {
base: '/api/assistant',
caller: (req) => ({ id: personId(req), admin: isAdmin(req) }),
});
app.all('/api/assistant/*', (c) => routes(c.req.raw));
- 供养者. 级别运行任意模型
models(克劳德和OpenAI默认,CLAUDE_MODELS, (中文(简体) ),OPENAI_MODELS:克洛德通过Anthropic's Messages API,OpenAI通过反应API,拥有相同的工具,草稿,停放和盖. XAI的Grok模型(XAI_MODELS)选择在: 列出它们的应用程序在 Model 提供者中获得一行 xAI (一个 API 密钥,xai-…);它们运行在同一个响应客户端在XAI_BASE_URL(单位:千美元)https://api.x.ai/v1. xAI没有其他应用程序的公开登录,因此Grok只取了一把钥匙. 交接可能交叉供货商. 顾问和快活的死记号是克劳德的. - 钥匙. 每个供应商都有自己的钥匙(
?provider=anthropic|openai|xai在关键路线上。 一个管理员粘贴每个人使用的共享关键字(PUT /key;在保存之前通过一个免费呼叫进行测试,然后被封住,没有路线返回. 一个人可以增加自己的(第1款)PUT /my-key),在自己的密钥上加了1个月盖(PUT /my-key/cap) (中文(简体) ). - ChatGPT登录. 代替自己的OpenAI密钥,一个人可以签入他们的ChatGPT计划(ChatGPT)
POST /my-signin,则POST /my-signin/finish {url}: 登录在新标签中打开并结束于http://127.0.0.1:1455/auth/callback,不装入的一页,其地址被贴回。 道具被封为自己的钥匙,在用完之前被刷新;计划上的答案数了道具,没有美元. - 谁付. 个人的取出(默认情况下的共享密钥),而其拥有一个关卡提供者的密钥和本月在其封顶下留下的钱(一个管理员设定了共享密钥的每人封顶,
PUT /app而不是另一个. - 工具 来. 每组为Off,Read,Read + reader + Read + Ready + Ready 每人. 工具是读取其 MCP
readOnlyHint已经设定。 在Read + 草稿时,写作只起草一张卡片,并在再次检查访问后按下 Save 时作为个人运行. 一组readOnly: true仅提供阅读工具。 读取 + 写只提供给一组writes: true:它一时运行undo设定聊天中每个人的行有 Undo, 它调用undo(caller, { tool, args, result }); 模型被告知什么被撤销。 卡片逐一起草,作为"保存"的一叠. 使用mcpServerTools(一个SDK)McpServer, (中文(简体) ),mcpFetchTools(一个服务器担任取取处理器,任何SDK),remoteMcpTools或者说plainTools。 。 。 . - 腾讯网.
fetch是一个普通的从工作人( 10 s, 2 MB, 从来不是应用程序自己的主机或私人地址) 。 ScrapeQL 自己的工具一旦管理员粘贴了 Cloudflare 访问服务标志( Cloudflare Access service service service services),就加入到它的MCP API上(PUT /web/scrapeql) (中文(简体) ). 一个在ScrapeQL自己的Cloudflare账户上的工人 转而通过服务约束,scrapeql: { url: 'https://scrapeql/mcp', fetch: env.SCRAPEQL.fetch.bind(env.SCRAPEQL) }: 不存储任何令牌, 工具总是打开, 设置显示一个测试的链接 。 每个网络步骤都带web(单位:千美元)StepWeb: 一个搜索query财务报告和审定财务报表results,或者页面read),因此面板会在其行下折叠一个搜索结果,并在答案下读取每页的数字为"源芯片";模型用这些数字([1])来标记事实. - 手取出并劝. 这个
escalate工具将一个任务交给一个更高的级别(下一级、任意或仅在被问及时); Anthropic 的顾问工具让一个更高的级别发送一个计划,最多两个答案. - 聊天 运行于代理( e)
./agent, (中文(简体) ),LumenAgent应用程序路线POST /chat每次聊天都会给自己的经纪人读取卡片和复出assistantRoutes答题POST /chat含410个moved。通过旧转弯循环保存的聊天可以读取(GET /chats, (中文(简体) ),GET|DELETE /chats/:id直至assistant.purge()在他们最后的消息发出30天后. - 屏幕移动:在人物设置 QQ Screen 移动(默认)时,面板会发送其页面映射(
AssistantMessage.screen: 路由、导航页面、设置区域、屏幕上的卡片、要打开的行)和LumenAgent给模型一个screen工具 (./agentscreen.ts) (中文(简体) ). 每个呼叫的动作(去,设置,图表,打开,点)与地图对照检查,保存在聊天中并流出为一screen事件; 面板通过AssistantScreenProvider(计有:LumenShell) 带有Uddo芯片,其结果会回溯到下一条消息(Uoutcomes) (中文(简体) ). 没有什么能通过移动来挽救. - 背景工作情况 用于个人运行的应用程序(分数、摘要)是
assistant.complete(caller, { task, system, prompt, images?, maxTokens }): 关于任务的层级的答案( :tasks,因此此人在 Sets QQ模型中选择其模型,没有聊天和工具(images, base64 及其介质类型, 阅读时提示: 扫描页, 截图 , 向付款人计费, 并在开支和津贴中计费, 如聊天转折.maxTokens(默认值1,000)升至COMPLETE_MAX_TOKENS(32,000,包括思考) 答案是流出而来,所以一个长长的连接cut: true表示在结束前就停止了(应用程序可以再次询问,缩短或继续). 它会扔AssistantErrornot_connected, (中文(简体) ),app_cap或者说own_cap当没人付得起钱,所以打电话者等待,然后尝试。 它还原了答案usd, (中文(简体) ),model财务报告和审定财务报表usage(键入,缓存读出,每个模型).run: { id, title }发出共享的呼叫id一个人的用法历史的一行(每轮重建);没有它,一天的任务呼叫共享一行. - 用地历史. 每次聊天( E)
agentTurn与chat),每跑一行背景工作是个人月份的一行(usage:YYYY-MM,最新的300:其标题,第一和最后使用,符号,成本及其每层和每型号的零件.GET /usage?month=YYYY-MM在月花和月花之外还原;LumenAssistant'页脚显示这个月的总和并打开它(UsageHistoryView) (中文(简体) ).
命令 :, @ 财务报告和审定财务报表 #
面板的作曲者列出了在被打出时可以跟随三个键的. 套件自己的条目是每个应用程序的;一个应用程序只添加自己的.
-
/命令. 内建于:/compact(总结聊天),/clear(一个新的聊天),/model <tier>(从现在开始对另一层进行回答)和/escalate <task>(把一个任务交给下一层) 应用程序在服务器上添加了自己的功能commands: 聊天显示打字人和模型得到的prompt,与{args}替换为名称后(或函数返回的)的词. 应用指令取自内置名.createAssistant({ // ... commands: [ { name: 'story', about: 'Find stories for a question', args: '<question>', prompt: 'Search my stories for: {args}. List the best three with why each fits.' }, { name: 'prep', about: 'Prep for an interview', args: '<company>', prompt: async (company, caller) => prepPrompt(caller.id, company) }, ], });一个在浏览器中起作用的命令(打开一页,设置过滤器)会放在面板上:
LumenAssistant(或useLumenAssistantDock获取commands,每个{ name, about, args?, options?, run }· ;run返回要发送的文本, 或当它自己采取行动时不发送,以及options列出输入时其单词的选择. -
#a 等级.#列出应用程序的分级; 选择是一个芯片, 只随该消息去取(tier) (中文(简体) ). 一个更高层次把它当作自己聊天部分的任务;同一个或一个更低层次的回答. -
@东西.@列出个人可以指向的,每个源的一组;每个取出是芯片,并随消息作为参考(kind, (中文(简体) ),label无论发现什么 模型在消息之前读到的 常备消息来源进入小组sources); 页面在显示时添加自己的页面 :import { useAssistantSource } from '@columenlabs/lumen-ui/assistant'; useAssistantSource({ id: 'page', label: 'On this page', items: (q) => rows .filter((r) => r.title.toLowerCase().includes(q.toLowerCase())) .map((r) => ({ id: r.id, label: r.title, target: `card:${r.id}`, ref: { kind: 'story', label: r.title, id: r.id } })), });target在屏幕上点亮它的行被徘徊。 页面的源头取代了相同的站台id。 。 。 .文本 页面上人物的亮点首先在
@(所选的文本),它从最近的地方坐在那里data-assistant-where属性。 要交接页面上的亮点,请询问按钮composeAssistant({ refs: [selectionRef({ text, where })], text: '' })(或任何参考和文本):面板会用作曲中的芯片打开,专注供人物完成.
轴心
模型得到的每个消息都从几行轴承开始:现在的人行区的时间(面板发送浏览器的时间),UTC,聊天的最后消息和app的名字还有多久. 他们走在消息中,而不是系统即时,所以即时缓存持有. 应用程序添加自己的行 bearings;每通电话得到300毫秒,当它晚到或者抛出其行被忽略.
createAssistant({
// ...
app: 'Notes',
timeZone: 'America/Los_Angeles', // when the browser sends none
bearings: ({ now, at, span }) => `Next backup: ${at(nextBackup(now))} (in ${span(nextBackup(now) - now)})`,
});
拥有自己的助手循环的应用程序会创建一个相同的块 bearings({ timeZone, lastAt, app, extras }) 并把它摆在信息之前.
查找:一个问题,一个查询
搜索应用程序记录的标准方式(Columen's canonical setup, 2026-10-28):每个显示助手收藏的应用程序都会给它一个找取工具,从没有列表加上一所记录读取. 一个呼叫将问题变成一个运行在行所居住的查询(D1或持久对象的SQLite). 模型只取回相匹配行和所要的字段,并自行作最后调用.
- 过滤器 (tag,数字和日期范围)是准确的SQL.
- 单词 与FTS5指数相匹配(
porter unicode61,所以"蛋"发现"蛋":any, (中文(简体) ),all, (中文(简体) ),none(放出:"不吃牛奶"),和in说出哪块地值得看. - 含义 来源于矢量化,选择每个应用程序:
like("某事光为一热天") 问出最近的行的索引,相同的过滤器和词来修剪. - 工作领域: 数字,日期或标记字段可能是应用程序在行上自有的SQL(
sql: 'json_array_length(t.steps)')而不是一栏. - 计数: 结果带有
count(所有匹配),returned财务报告和审定财务报表same_name(不同行同名),所以模型说出正确的数字. 按意义进行搜索nearest: true没有count:它的行是最接近的几行,而不是每一行相合的行,因此模型从不引用它们作为数个相配.
import { d1Sql, defineFind, findSql, findTool } from '@columenlabs/lumen-ai/assistant';
export const notesFind = defineFind({
name: 'notes', label: 'note', table: 'notes', key: 'id',
where: 'deleted = 0', // the app's own SQL, always applied
order: 'updated_at DESC', // with no words to rank by
fields: {
title: { kind: 'text' },
body: { kind: 'text', show: false }, // searchable, returned only when asked
tags: { kind: 'text', json: true }, // a JSON column, returned parsed
folder: { kind: 'tag' }, // exact filter; its values are listed to the model
updated_at: { kind: 'date' }, // updated_at_from, updated_at_to
},
});
// In a group's tool source, per answer (a Durable Object passes doSql(ctx.storage.sql)):
plainTools([await findTool(notesFind, d1Sql(env.DB))]);
findTool制造<name>_find带有平面输入图案(每个提供者都使用),并读取每个标记字段的值一次以列出它们. 助理告诉模特儿把一个问题放在一个寻人电话里.runFind(def, sql, query)是没有工具的相同的查询:只绑定参数,从不模型写入SQL. 3秒后就放弃了.findSql(def)给出索引的 SQL.create是一个外部内容 FTS5 表格外加触发器, 使其与每个插入, 更新和删除, 不管是谁写的 .fill索引已经在那里的行,和drop把它都拿出来。 打开create财务报告和审定财务报表fill在迁移(D1)或预算背景步骤(持久目标)中,从未提出要求。 在索引存在之前,单词会回落到一个纯文本匹配,结果也这么说,所以代码可以在它的迁移之前运出.
意思是,对于人们要求松散的应用软件(一本食谱书,一份职业日志;而不是像卡片福利那样的准确记录):
import { meaningStep, vectorMeaning, workersEmbed } from '@columenlabs/lumen-ai/assistant';
// On the definition: what a row means, and the column every write moves.
meaning: { fields: ['title', 'body', 'tags'], changed: 'updated_at' },
const embed = workersEmbed(env.AI); // bge-base-en-v1.5: a 768-dimension cosine index
await findTool(notesFind, d1Sql(env.DB), { meaning: vectorMeaning({ embed, index: env.NOTES_INDEX }) });
// Off the request path (waitUntil after a write and when the tool opens, an alarm, a queue):
ctx.waitUntil(meaningStep({ def: notesFind, sql: d1Sql(env.DB), embed, index: env.NOTES_INDEX, mark }));
meaningStep仅嵌入其changed栏移动到其书签上(mark: 得到并放置, 由应用程序保存), 最多50个步骤在一个嵌入调用和一个上升where)出于指数. 恢复停放地点,一次安全运行两次;done在行左行时是虚假的.- 一个人的行得到一个
namespace每一个vectorMeaning财务报告和审定财务报表meaningStep因此,一个指数服务于一个应用. - 当指数不能回答 3 s,
like其余的搜索结果都是这样.
云花盘思维的代理
@columenlabs/lumen-ai/agent 作为 Cloudflare 思维代理运行一个聊天(@cloudflare/think,实验性),在应用的分级,键和工具组. 思考保持了循环,消息,流传,恢复和收缩;套件在付款人的钥匙上添加了模型,个人访问时的工具,花帽和用法. 这将取代 ./assistant自己的循环.
| 导出 | 它会做什么 |
|---|---|
LumenAgent | 一个抽象的思维代理人. 应用程序回答 plan(turn) (模型、钥匙、努力、指示、工具来源、剩余资金) settle(end) (结果、使用部分、上下文大小、时间) |
aiModel(model, key, opts?) | AI SDK模型用于API密钥上的分级,或用于ChatGPT计划上签名的分级(key.plan,通过 planFetch(中文(简体) ) |
planFetch(fetch?, name?) | 重写计划登录规则的响应: 一个命名空间中的工具, 没有输出封顶, 强制调用是唯一的工具 |
planFallback(primary, fallback, onSwitch) | 一个计划模型 接通电话, 和其余的转弯 fallback 当计划达到使用限值时(在任何输出前为429或用法代码) |
turnSettings(model, opts) | 供货商使用时输出上限和努力 |
openAiTools(sources, opts) | 该应用程序的工具组作为 AI SDK 工具:通过访问读取、起草和写作,结果切入为 16k 字符 |
UsageMeter, (中文(简体) ), tokenUsage(u) | 将每个步骤的代号(读写、推理)定价为一个用法部分 |
ChatGPT计划签入程序免费(priced 与 free) (中文(简体) ). 计划用完后 转弯 fallback (单位:千美元)agentTurn 设定应用程序的第一个 Claude 在同一付款人的 Anthropic 密钥上 ) 的答案,而不是在限制错误中结束,它使用自己的部分. 这里没有钥匙或标志:应用程序每转一转就通过.
附带一个应用程序 Assistant 从它得到整个计划: assistant.agentTurn(caller, { message, tier?, context?, refs?, time_zone? }) 返回时 { plan, settle } 人物的等级和型号 在谁付费,工具组 在他们的访问,工具包的规则,轴承和页面 在消息之前, escalate 和克劳德的顾问. settle 将支出加到付款人的月份 并报告转弯 onStep。它抛出 AssistantError 当没有人能支付.
草稿和写作是聊天中自己存储的卡片(lumen_cards 在代理的 SQLite 中。 写一个人的访问只写出草稿,就可以开出一张卡片,而不会改变任何东西. saveCard(id) 通过应用程序运行 applyDraft,以及 dismissCard(id) 放下枪 一次写作保存 undoCard(id) 通过应用程序 undoWrite。 。 。 . cards() 列出牌。 接下来的答案是,每张卡是如何结束的,什么被取消。 与一个 Assistant, (中文(简体) ), applyDraft 实值 assistant.runDraft(caller, card) 财务报告和审定财务报表 undoWrite 实值 assistant.undoChange(caller, card)。思考压缩聊天的过去 COMPACT_AT (150k令牌) 在最后一个答案的模型和密钥上,这种花被添加到下一个答案中.
连接器. 一个人在聊天时添加一个 MCP 服务器 addConnector(name, url)。地址必须是https。 服务器通过 Think 的 MCP 客户端使用自己的 OAuth 签名, 答案包含 authUrl 当它问. connectors() 列出他们和 removeConnector(id) 掉一个 认为永远不要 合并自己的工具(includeMcpTools (这是虚假的)。 转一圈打开那些 plan.connectors 列表, 应用程序在访问时按组给出每个列表 mcp_<id>。“读取”保留仅读取服务器标记的工具。 阅读 + 草稿 制作休息卡 saveCard 在服务器上运行。 读取 + 写入 一次运行它们, 没有撤消 。 一个连接器的文本是不信任的数据.
懒活的工具. 过去 LAZY_AT (30) 工具,转而发送模型的核心工具和 tool_search,在指令中每组有一个目录行。 默认核心工具是应用程序的发现,关闭的手和顾问. plan.lazy 套装 { at, core }。一个搜索匹配工具名称和描述中的单词,然后从下一步开始打开最多8个工具.
图像. AskInput.images (基数64 PNG、JPEG、WebP或GIF;最多 ASSISTANT_IMAGES_MAX (四)和 ASSISTANT_IMAGE_CHARS 一起)作为文件部分(imageParts),所以模型看到它们,聊天会再次显示它们. LumenAssistant images 让人物粘贴 (Cmd+V) 或将图像放入框上; 作曲家在长边上分别缩放到1568 px. 一个应用 POST /chat 通过 images 编号 AskInput。 。 。 .
那个面板. LumenAssistant 读取 想着聊天,没有变化. ask(input, sink) 运行一个答案并发送面板本身的事件( q)AssistantEvent:聊天、文字、每个工具行、卡片、交接部分、建议、使用)至 sink应用程序 POST /chat 返回时 askResponse((sink) => chat.ask(input, sink), { signal, stop: () => chat.stop() }),相同的服务器发送事件 assistantRoutes 发送. view() 作为面板项目提供存储聊天,以及 usage() 到现在为止 聊天还是有用的 一张卡的ID是它的工具呼叫, 所以面板的保存, 解散和撤消去 saveCard, (中文(简体) ), dismissCard 财务报告和审定财务报表 undoCard. 该应用保留了聊天列表,每个聊天都是自己的代理,例如个人商店的一面. chunkEvents 财务报告和审定财务报表 itemsOf 是绘图, 对于一个应用 流出一些其他方式.
阶行. 只有一个答案最多 plan.maxSteps 模式调用MAX_STEPS最后一个没有工具 LAST_STEP_NOTE,所以一个答案总是用词来结束:它找到了什么,当任务不合适时剩下的是什么.
引导和排队. 当一个答案运行时,面板的框会保持打开: Enter 将消息引导到答案中( E)steer(message) 代理; 应用程序路径 POST /chats/:id/steer { message } 对它和答案 { steered }),Alt+Enter为它排队。 一个方向盘在答案的下一步, 在它的工具之后,与 STEER_NOTE 用于模型; 面板显示它在那里标记了方向(活动) steered),以及 view() 保持原样SteerNote,在信息下,答案是). 方向盘在读出前结束 unsteered 并排在队列中第一. plan.steer: false 关闭它作为模型;没有路径的应用程序(404)只显示队列. 被排队的消息在答案结束前分别作为自己的消息去发;每个消息现在都有指向,编辑和去除.
重试. 以出错结束的转弯显示在Copy旁边的重试图标(QQ),总是可见,而没有答案运行,有人可以支付(status.wait 未设置; 隐藏在其它处, 永不禁用). 它发出转弯的话, @ 组件, # 等级和图像再次作为新消息( e)chat.retry(at, user, extra) 内 useAssistantChat),在气泡上方标记;旧错误变为灰色(retried 并停止提供重试。 服务器上没有任何变化:重审后的消息是普通的消息.
class Chat extends LumenAgent<Env> {
#settle?: (end: AgentTurnEnd) => Promise<void>;
async plan(turn: TurnContext) {
// this.input: what the person sent (message, page, refs, tier, time zone).
// chat: this chat's id, so its use is one line of the person's usage history.
const { plan, settle } = await assistant.agentTurn(caller, { ...(this.input ?? { message: '' }), chat: this.name });
this.#settle = settle;
return plan;
}
settle(end: AgentTurnEnd) { return this.#settle?.(end) ?? Promise.resolve(); }
protected applyDraft(card: AgentCard) { return assistant.runDraft(caller, card); }
protected undoWrite(card: AgentCard) { return assistant.undoChange(caller, card); }
}
工作空间: 食谱上的标签, 按要求再次运行
@columenlabs/lumen-ai/workspaces 是工具箱的服务器 WorkspacesSection 财务报告和审定财务报表 WorkspacePage 电话 工作空间是个人的标签页; 每个标签是一个表格, 超过一个 边框: 第一组 食谱 拉出,食谱本身 和人的手编辑。 食谱是对产品读取工具之一的调用({kind: 'call', fn: 'jobs.list', args}),所以再次运行它不需要模型. 没有在计时器上运行:在标签上运行它的食谱,在页眉中,每个标签一个接一个地运行,每个标签在15秒之内(15秒)RUN_MS);失败的运行保留了最后一行并说原因.
import { createWorkspaces, doSql, workspaceMigrations, workspaceRoutes } from '@columenlabs/lumen-ai/workspaces';
// In the store that holds the person's data (a Durable Object's SQLite, or D1 with d1Sql):
for (const q of workspaceMigrations({ spaces: 'app_ws_spaces', frames: 'app_ws_frames' })) sql.exec(q);
const store = createWorkspaces({
db: doSql(sql),
tables: { spaces: 'app_ws_spaces', frames: 'app_ws_frames' },
run: (recipe, { owner, signal }) => callReadTool(owner, recipe, signal), // the product's own tools, as the person
});
// In the Worker, at the assistant's api:
const routes = workspaceRoutes({ base: '/api/assistant', owner: callerOf, store: () => store });
预算:一帧为一行(最多为一行1.5MB,一行一万一行,五十列),所以一跑会写一行;每读一行有一行LIMIT(30个工作空间,12个分页,100个分页). 列从它们的值(数字,日期,文本)中打出;一个单词列表,或从一个小集中取出短文本,成为了标记列(chips). 装备箱 WorkspacesProvider 带 recipes 从标签栏中的食谱中提供 + 新的标签.
Web 数据并按下数据集( 没有服务器代码运行)
还有三种方式
- 获取食谱 (单位:千美元)
{kind: 'fetch', requests: [{url, method?, key?}], each?: {values}, rows?, pick?}:声明网读出产品运行在其服务器上.{field}从每个值的 URL 填充中的孔each(文字填写){value}(三);fetchCalls扩展( 最多)FETCH_CALLS, 50 a run))和 (英语:fetchedRows将答案转换成行each值的字段,然后是pick'被点出的道路 (current.temperature_2m) (中文(简体) ). 一个数据密钥被命名( A)key),从未发送:产品添加其服务器侧. - 浏览器脚本 (单位:千美元)
{kind: 'script', runtime: 'browser', code, requests?}:在人物页面上运行的JavaScript. 装备箱runBrowserFrame负载<base>/sandbox(这个包裹)SANDBOX_HTML,服务于SANDBOX_CSP数字 :connect-src 'none'在一个框中sandbox="allow-scripts",在网络工作员中运行代码(停止于BROWSER_RUN_MS, 30 s),并保存其返回的内容PUT /frames/:id/rows(一作一出,取出相框盖. 密码是async (fetched, hub) => rows数字 :fetched拥有答案requests, (中文(简体) ),hub.fetch(url, {key})通过产品的代用读取(fetch内workspaceRoutes, (中文(简体) ),POST <base>/fetch) (中文(简体) ). 服务器从未运行过 :refresh拒绝(409美元)refreshAll将此类标签列表为browser。 。 。 . - 已按下数据集 (单位:千美元)
store.pushFrame(owner, {name, rows, mode, by, key?}): 行外代理 wronged (一个 CSV 解析 :parseCsv最多CSV_BYTES一个电话,其余的作为append部件; 与key,后面的一行用键替换。 来源harness没有食谱;by指定代理和标签的状态栏 读作"4分钟前被克洛德代码压住".frameTab(owner, workspace, {name, frame})在标签中显示一个( 不运行) ;removeFrame删除带有标签的标签.
workspaceRoutes 让我们 call, (中文(简体) ), fetch 浏览器 script 菜谱通过产品 allow; 服务器 sql 财务报告和审定财务报表 script 拒绝.