Datasette Agent 0.2a0:AI代理工具执行中向用户提问的新机制

Simon Willison 发布了 datasette-agent 的最新 alpha 版本 0.2a0,引入了两个值得关注的特性:工具执行过程中向用户提问的能力,以及内置的 SQL 查询保存工具。这些改进让 AI 代理与人类的协作模式更加灵活和安全。
核心特性:工具执行中的人机交互机制

本次更新最重要的特性是 ask_user() 机制。在传统的 AI 代理工作流中,工具一旦被调用就会一路执行到底,中间没有与用户交互的机会。而 datasette-agent 0.2a0 打破了这一限制——工具现在可以在执行过程中暂停,向用户提出问题,等待回答后再继续。
在现代 AI 代理架构中,工具调用(Tool Calling / Function Calling)是 LLM 与外部世界交互的核心机制。这一机制的演进经历了几个重要阶段:2023年6月,OpenAI 率先在 GPT 模型中引入 Function Calling 能力,随后 Anthropic 的 Claude、Google 的 Gemini 等模型纷纷跟进。其核心原理是开发者以 JSON Schema 格式描述可用工具的名称、参数和功能说明,模型在推理过程中根据用户意图决定是否调用工具,并生成结构化的调用参数。本质上,这将 LLM 从一个纯文本生成器扩展为能够与外部系统交互的决策引擎。值得注意的是,模型本身并不执行工具——它只是生成调用指令,实际执行由宿主应用完成,这种分离设计是安全性的基础。这种模式在 LangChain、CrewAI、AutoGen 等框架中被广泛采用,但传统实现中工具调用是原子性的——要么执行完毕返回结果,要么报错终止,中间没有暂停和交互的余地。datasette-agent 的 ask_user() 正是对这一局限的突破。
具体实现上,声明了 context 参数的工具会接收一个 ToolContext 对象,通过 await context.ask_user(...) 可以发起三种类型的提问:
- 是/否问题:简单的二选一确认
- 多选题:通过
options=[...]参数提供选项列表 - 自由文本:通过
free_text=True让用户输入任意内容
ToolContext 对象采用 Python 的 async/await 异步编程模式,这与现代 Python Web 框架(如 FastAPI、Starlette)的设计理念一致。async/await 语法自 Python 3.5(2015年)引入,允许在不阻塞主线程的情况下等待 I/O 操作完成。在 datasette-agent 的场景中,await context.ask_user() 的语义是:暂停当前工具的执行协程,将控制权交还给事件循环,直到用户提供回答。这种设计使得工具代码的编写体验非常自然——开发者可以像编写同步代码一样编写包含人机交互的工具逻辑,而底层的挂起、持久化和恢复机制对开发者透明。
这个设计在工程细节上颇为考究。当问题未被回答时,代理的当前轮次会被挂起(suspend),问题以表单形式渲染在聊天 UI 中,同时持久化到内部数据库。这意味着即使服务器重启,挂起的对话也不会丢失。用户回答后,工具会从头重新执行,之前已存储的答案会被自动回放。
这种挂起-重放(suspend-replay)模式借鉴了事件溯源(Event Sourcing)的思想。事件溯源最早由 Martin Fowler 在 2005 年系统阐述,核心思想是将系统状态的变化记录为一系列不可变的事件,恢复状态时通过按顺序重放事件来重建。在分布式工作流引擎领域,Temporal(由 Uber 的 Cadence 项目演化而来,2020 年独立)是这一模式的典型代表——它允许长时间运行的工作流在等待外部信号时挂起,恢复后从头重放已完成的步骤。工作流代码看起来像普通的顺序代码,但引擎会在每个关键步骤记录事件。datasette-agent 的实现是这一模式的简化版本——它选择了重新执行而非事件重放,因此对幂等性的要求更为严格。不过这种模式也带来了幂等性(idempotency)的挑战:重放过程中,已经执行过的操作不应该产生重复的副作用。
幂等性是指同一操作执行一次和执行多次产生的效果完全相同。这一概念源自数学中的幂等函数(f(f(x)) = f(x)),在分布式系统设计中至关重要。HTTP 协议中,GET、PUT、DELETE 方法被设计为幂等的,而 POST 不是。在支付系统中,幂等性通过幂等键(Idempotency Key)实现——Stripe 等支付平台要求客户端在请求中附带唯一标识符,服务端据此判断是否为重复请求。datasette-agent 面临的幂等性挑战与此类似:当工具因用户回答而重新执行时,之前已完成的数据库写入、API 调用等副作用不应被重复执行。
有意思的是,官方文档特别提醒开发者:应在执行副作用(side effects)之前调用 ask_user()。这是因为工具重新执行的机制——如果先执行了数据库写入等操作再提问,重新执行时这些操作会被重复执行。这一约束正是挂起-重放模式下幂等性问题的具体体现。本质上,官方建议通过约定而非机制来规避幂等性问题,这在简单场景下有效,但在复杂工具链中可能需要更严格的保障,例如引入操作日志或幂等键机制。
内置 save_query 工具:人类审批的安全保障
第二个新特性是内置的 save_query 工具。AI 代理在与数据库交互过程中编写的 SQL 查询,现在可以被保存为 Datasette 的存储查询(stored query),方便后续复用。
这里需要了解 Datasette 的存储查询(Canned Queries)机制。Datasette 由 Simon Willison 创建于 2017 年,诞生于一个具体的新闻报道需求——当时他在为一个调查报道项目寻找快速发布数据库的方式。自发布以来,Datasette 已发展成一个拥有超过 100 个插件的生态系统,被新闻编辑室、政府开放数据项目、学术研究机构广泛采用。它选择 SQLite 作为唯一支持的数据库引擎,这一看似局限的决策实际上带来了显著优势:SQLite 数据库是单一文件,易于分发和版本控制,且读取性能极佳。SQLite 是全球部署量最大的数据库引擎(估计超过一万亿个活跃实例),由 D. Richard Hipp 于 2000 年创建,其嵌入式特性意味着无需独立的数据库服务器进程。在 datasette-agent 中,对话状态、挂起的问题、已保存的查询都存储在 SQLite 数据库中,这使得整个系统的部署和迁移极为简单——只需复制文件即可。此外,SQLite 的 WAL(Write-Ahead Logging)模式支持并发读取,适合 AI 代理这种读多写少的工作负载。Datasette 遵循"数据即 API"的理念,将 SQLite 文件自动转化为可浏览、可查询、可通过 REST API 访问的 Web 应用,支持分面搜索、JSON API、GraphQL 查询等功能。存储查询功能允许管理员预定义 SQL 查询并以命名 URL 暴露给用户,这是一种安全的数据访问模式——终端用户无需直接编写 SQL。datasette-agent 的 save_query 工具正是将 AI 生成的查询纳入这一治理框架。
存储查询模式在数据库安全领域有着深厚的理论基础,与参数化查询(Parameterized Queries)和存储过程(Stored Procedures)的安全理念一脉相承。其核心思想是将数据访问逻辑从终端用户手中收回,由管理员预定义并审核。这有效防止了 SQL 注入攻击和未授权的数据访问。在 AI 代理场景中,这一机制的价值更加突出:LLM 生成的 SQL 可能包含意料之外的查询逻辑——模型可能因为幻觉(hallucination)生成访问敏感表的查询,或因为对 schema 的误解生成低效的全表扫描。通过 save_query 工具将 AI 生成的查询转化为经过人类审核的存储查询,实质上是在 AI 生成内容和生产环境之间建立了一道人工审核的防火墙。
这里体现了一个重要的设计哲学:保存操作始终需要人类审批。代理会展示完整的 SQL 语句、建议的查询名称、目标数据库和可见性设置,只有用户点击确认后才会真正存储。这正是 ask_user() 机制的一个典型应用场景——在执行不可逆操作前征求人类同意。
这种"人在回路中"(Human-in-the-Loop, HITL)的设计在当前 AI 代理生态中尤为重要。HITL 是 AI 安全领域的核心设计原则之一,在 AI 代理日益获得自主行动能力的背景下,确保人类在关键决策点保持控制权。这与 NIST AI 风险管理框架中强调的"可治理性"(Governability)原则一致。在实际 AI 代理产品中,HITL 已有多种实现路径:GitHub Copilot Workspace 在生成代码修改方案后会等待开发者审批再执行,Devin 等 AI 编程代理在执行系统命令前会请求确认,Salesforce 的 Einstein GPT 和 ServiceNow 的虚拟代理都内建了审批工作流。学术界对 HITL 粒度的研究表明,最有效的策略是"基于风险的分级审批"——低风险操作(如只读查询)自动执行,中等风险操作(如数据修改)需要确认,高风险操作(如删除数据)需要多重验证。实践中,过于频繁的确认会降低效率(所谓的"确认疲劳"),而过于宽松则可能导致 AI 执行未经授权的操作。datasette-agent 将 HITL 内建到工具层面,让开发者自行决定哪些操作需要人类审批,是一种灵活但需要开发者安全意识的设计方案。随着 AI 代理获得越来越多的工具调用能力,如何在自动化效率和安全可控之间取得平衡,是每个开发者都需要面对的问题。
技术背景:LLM Alpha 与 Claude 的辅助开发
据 Simon Willison 透露,ask_user() 特性的实现依赖于他前一天借助 Claude 构建的新版 LLM alpha。这形成了一个有趣的递归场景:用 AI 辅助编程工具来构建 AI 代理框架的新功能。
Simon Willison 是 AI 辅助编程的积极实践者和布道者,他长期记录使用 Claude、ChatGPT 等工具辅助开发的经验。他维护的 LLM 项目是一个 Python 命令行工具和库,提供统一接口访问多种大语言模型,支持通过插件系统接入 OpenAI、Anthropic、Google、本地模型等多种后端。LLM 项目的设计哲学与 Datasette 一脉相承——提供简洁的命令行接口和 Python API,让开发者能够快速实验和集成 LLM 能力,而无需处理各家 API 的差异。用 AI 辅助工具来构建 AI 基础设施,这种递归模式在当前开发者社区越来越常见——从 Cursor 编辑器到 GitHub Copilot,AI 正在加速自身生态的构建。这也引发了关于"AI 编写 AI"可审计性和可靠性的讨论:当 AI 辅助生成的代码本身成为 AI 系统的一部分时,传统的代码审查流程是否足够?如何确保 AI 生成的工具调用逻辑不会引入微妙的安全漏洞?这些问题目前尚无定论,但 Simon Willison 的实践至少表明,在有经验的开发者手中,AI 辅助编程可以显著加速复杂系统的原型开发。
Datasette 作为一个轻量级的数据探索和发布工具,一直在积极拥抱 AI 能力。datasette-agent 将 LLM 的自然语言理解能力与 Datasette 的数据查询能力结合,让用户可以用对话的方式探索数据库。而 0.2a0 版本的人机交互改进,则让这个过程更加可控和实用。
对 AI 代理框架开发者的启示
ask_user() 的设计模式值得其他 AI 代理框架借鉴。它解决了一个普遍存在的问题:AI 代理在执行复杂任务时,往往需要在某些关键节点获取人类的判断或确认。将这种能力内建到工具调用协议中,比在外部包装一层审批流程要优雅得多。
从更广阔的视角来看,这一设计模式与 Model Context Protocol(MCP)的发展方向不谋而合。MCP 由 Anthropic 于 2024 年底提出,旨在标准化 LLM 与外部工具和数据源的交互协议。虽然当前 MCP 规范尚未包含工具执行中的人机交互原语,但 datasette-agent 的 ask_user() 实践可能为未来的协议扩展提供参考。如果类似的交互能力被纳入标准化协议,将使得不同 AI 代理框架之间的工具共享和互操作成为可能。
同时,挂起状态的持久化设计也很实用。在生产环境中,用户可能不会立即回答代理的问题,服务可能会重启或迁移,对话状态的持久化保证了这些场景下的可靠性。这种设计在企业级应用中尤为重要——想象一个数据分析场景,AI 代理在分析过程中发现数据异常需要人类确认,而负责确认的分析师可能正在开会,几小时后才能回复。持久化机制确保了这种异步协作模式的可行性。
目前 datasette-agent 仍处于 alpha 阶段(0.2a0),API 可能会继续演进,但其设计方向——让 AI 代理在关键时刻"停下来问一问"——无疑是正确的。
核心要点
核心要点
相关推荐

托管Agent时代来临:Anthropic与Google的两条路线之争
深度解析Anthropic与Google托管Agent的架构差异、定价策略与选型建议。托管Agent将Agent运行时从基础设施工作中解放出来,成为AI基础设施的新产品品类。

零基础搭建Claude Code开发环境:安装配置避坑指南
详细记录零基础用户从安装VS Code到配置Claude Code的完整流程,涵盖插件安装报错、API配置、模型切换等常见问题的解决方案,帮助新手快速上手AI编程工具。

AI召唤力:零代码用AI开发游戏的启示与实践
一位没有编程经验的UP主,仅凭自然语言提示词用AI开发出完整游戏。本文解析AI召唤力的核心维度,探讨零代码开发如何打破游戏开发工种壁垒,以及AI协作能力对产品经理、开发者和普通人的深刻启示。