开发本地数据库检索插件与轨迹追踪

约 40 分钟

构建 SQLite/SQL 查询检索工具,并在 Harness Trajectory 视图中分析多轮调用轨迹。

在上一节中,你已经掌握了通过 defineTool 与 ctx.tools.register 向 DeepSeek Harness 注册外部 API 聚合工具的方法,并借助 parameters 参数规范、output 呈现管道与 execute 异步逻辑,让智能体准确解析用户自然语言意图、完成外部数据获取。

顺着这条工具注册链路,当所需数据并不在外部 API 中、而是沉淀在本地数据库里时,智能体同样需要一类注册工具来触达它。本节我们将构建一个本地 SQLite 只读检索插件 sqlite-query-tool:智能体能够自主编写 SQL 查询本地数据库、获取结构化结果并综合回答用户问题。同时,你将学会如何借助 Harness 内置的 Trajectory 视图,逐帧复盘智能体的多轮工具调用轨迹。

核心概念

Trajectory 执行轨迹追踪与 Session 日志流

把智能体的推理执行过程想象成飞机上的“黑匣子飞行记录仪”。黑匣子不会只记录飞机最终降落的地点,而是以不可篡改的流水账(Append-only)形式忠实记录每一秒的高度、航向与操作指令。如果在飞行途中发生颠簸或故障,工程师可以通过黑匣子完整重放飞行轨迹。

在技术定义上,Trajectory 执行轨迹追踪是 DeepSeek Harness 提供的核心可观测性机制。Harness 内部维护着一条仅追加(append-only)的 Session 日志流,记录了包括 turn/*、step/*、tool/call 和 tool/result 在内的全部持久化会话事件。Trajectory 视图将该事件流直观呈现为时间线,使开发者能够精确审查每一次工具调用的入参、执行耗时、返回内容以及 Token 消耗分布,实现多轮交互与状态回放的透明化调试。

编写 SQLite 只读检索插件并观察 Trajectory

  1. 1

    在本地初始化 SQLite 测试数据库,创建包含示例数据的 analytics.db 文件。

    bash
    node -e "const Database = require('better-sqlite3'); const db = new Database('analytics.db'); db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, role TEXT, active INTEGER); INSERT INTO users VALUES (1, \'Alice\', \'admin\', 1), (2, \'Bob\', \'developer\', 1), (3, \'Charlie\', \'tester\', 0);'); db.close();"
  2. 2

    编写 SQLite 数据库检索插件 src/sqlite-plugin.ts,声明服务依赖并在 apply 中注册只读 SQL 查询工具。

    typescript
    import type { Context } from '@deepseek-ai/cordis'
    import { defineTool } from '@deepseek-ai/dsh-tools'
    import Database from 'better-sqlite3'
    
    export const name = 'sqlite-query-tool'
    export const inject = ['tools']
    
    export function apply(ctx: Context) {
      ctx.tools.register(defineTool({
        name: 'query_sqlite',
        description: '在本地 SQLite 数据库中执行只读 SQL 查询并返回 JSON 格式结果。',
        parameters: {
          query: {
            type: 'string',
            required: true,
            description: '需要执行的 SQL SELECT 语句'
          }
        },
        output: {
          schema: {
            type: 'string'
          },
          render: (_args, value) => [{
            type: 'text',
            text: value
          }]
        },
        async execute(args) {
          const trimmed = args.query.trim()
          if (!/^select\b/i.test(trimmed)) {
            throw new Error('只允许执行 SELECT 只读查询语句。')
          }
          const db = new Database('analytics.db', { readonly: true, fileMustExist: true })
          try {
            const rows = db.prepare(trimmed).all()
            return JSON.stringify(rows)
          } finally {
            db.close()
          }
        }
      }))
    }
  3. 3

    使用 dsh web 启动开发服务器并挂载该插件。

    bash
    pnpm dsh web --patch ./cordis.yml
  4. 4

    在浏览器打开 Web 界面(默认 http://127.0.0.1:3080),向智能体发送任务指令:“请查询数据库中所有处于 active 状态的管理员用户名”。

  5. 5

    在 Web 界面右侧或调试面板中切换到 Trajectory 视图,检查 Session 事件流中记录的 tool/call 参数(SELECT name FROM users WHERE active = 1 AND role = 'admin')与 tool/result 响应数据。

你应该看到

智能体成功生成正确的 SQL 语句调用 query_sqlite 工具,获取到用户 Alice 的记录并生成解答。在 Trajectory 视图中能够清晰看到橙色高亮的工具调用记录、返回值及耗时明细。

遇到问题:智能体调用 query_sqlite 时直接抛出错误:“只允许执行 SELECT 只读查询语句”或数据库文件无法打开。

可能原因

  • 智能体尝试生成包含 PRAGMA、SHOW 或包含写操作的非 SELECT 语句,触发了前端校验拦截。
  • analytics.db 文件路径不正确或进程没有该文件的读取权限。

排查步骤

  1. 在 parameters.query.description 中明确提示模型:“必须是标准 SELECT 查询语句,不支持其他指令”。
  2. 确保数据库文件位于 Harness 运行根目录下,或在插件配置中传入可配置的绝对路径。
自检:在 DeepSeek Harness 中,为什么 Trajectory 调试不仅依赖聊天消息本身,而是基于底层仅追加(append-only)的 Session 日志流?

聊天窗口通常会折叠或省略中间环节的原始请求与完整工具 Payload;而基于 append-only 的 Session 日志流记录了包括 turn/*、step/*、tool/call 和 tool/result 在内的全部不可篡改的原子事件,为断点恢复、分叉重放和精准排查提供了完整的数据轨迹支撑。