跳到正文

目录

Dexter 全面解读:从零到一的 AI 研究代理平台(含架构设计、工具生态与扩展开发)

Dexter 全面解读:从零到一的 AI 研究代理平台(含架构设计、工具生态与扩展开发)

预计阅读时间:60 分钟 | 难度:⭐⭐⭐⭐

目标读者:AI 开发者、金融科技研究者、想在本地搭建 Agent 系统的用户


概述:Dexter 解决什么问题

Dexter 是一个运行在终端的自主金融研究代理。它的定位和 Claude Code 相似:Claude Code 替程序员改代码、跑命令,Dexter 替研究者拆解问题、查数据、汇总结论,只是专业方向落在金融研究上。

为什么需要它,而不是直接问一个对话模型?模型给出的回答依赖训练数据,时效性有限,也难以验证来源。Dexter 不同——它把一次提问当成一次“研究”:先拆成执行步骤,再用实时行情、财报文件、网页搜索这些真实工具逐项执行,反复核对,直到给出有依据的结论。它留下的不是一段“看起来像答案”的文字,而是可以逐条回溯的执行记录。

一点溯源:Dexter 由开发者 virattt 发起。本文以社区 fork 仓库 A1pha3/dexter 为准——它在主线基础上同步能力,并附带了完整的中文文档集,后文内容均对应当前仓库代码与这份文档。


能力全景

Dexter 的能力可以拆成 8 个子系统,模型层先按下不表,单独在架构一节讲:

#子系统核心能力代表工具 / 组件
1多模型路由8 家提供商统一接入OpenAI、Anthropic、Google、xAI、Moonshot、DeepSeek、OpenRouter、Ollama
2金融工具链财报、价格、新闻、筛选get_financialsget_market_dataread_filingsstock_screener
3网页工具搜索、抓取、渲染web_searchweb_fetchbrowser
4文件系统本地文件读写编辑read_filewrite_fileedit_file
5技能系统可复用工作流skill(读取 SKILL.md)
6持久记忆跨会话知识积累memory_searchmemory_getmemory_update
7WhatsApp 网关消息通道接入自聊模式、群聊模式
8自动化引擎心跳监控 + 定时任务heartbeatcron

记住这张表的目的是建立“什么问题该找哪个工具”的判断,而不是背工具名。后面各节会解释每个子系统为什么这样设计。


快速入门

动手之前,先确认一套可运行的环境。Dexter 用 Bun(v1.0 及以上)作为运行时,配套的 playwright 会随安装一并拉取。

前置条件

  • 安装了 Bun 运行时
  • 至少一个模型提供商 API Key(默认 OpenAI)
  • (强烈建议)Financial Datasets 的 API Key,用于金融数据
  • (强烈建议)一个搜索服务 Key:Exa(首选)、Perplexity 或 Tavily

安装与配置

# 1. 安装依赖(包含 playwright chromium)
bun install

# 2. 生成 .env 并填入必需项
cp env.example .env
# 至少填写:
# OPENAI_API_KEY=your-key
# FINANCIAL_DATASETS_API_KEY=your-key(强烈建议)
# EXASEARCH_API_KEY=your-key(强烈建议)
# 3. 启动 CLI 交互模式
bun run start

开发时可用 bun run dev 进入带热更新的 watch 模式。

验证能跑起来

启动后,用一条能命中金融工具的最小问题验证,例如:

Tesla 最近一季的营收是多少?

观察作答过程中是否调用了 get_financialsget_market_data。如果只返回一个没有数据来源的普通回答,说明对应工具没有真正启用,先回到 §故障排查。


系统架构:六层协同设计

分层总览

┌─────────────────────────────────────────────────────────────┐
│                        入口层                                │
│           src/index.tsx、src/gateway/index.ts               │
├─────────────────────────────────────────────────────────────┤
│                        交互层                                │
│            CLI 终端 UI、输入历史、状态显示                   │
├─────────────────────────────────────────────────────────────┤
│                        代理层                                │
│         规划、迭代执行、上下文管理、工具协同                 │
├─────────────────────────────────────────────────────────────┤
│                        模型层                                │
│          多提供商适配、调用路由、重试策略                    │
├─────────────────────────────────────────────────────────────┤
│                        工具层                                │
│      金融工具、网页工具、文件工具、自动化工具、记忆工具       │
├─────────────────────────────────────────────────────────────┤
│                      扩展能力层                              │
│           技能系统、记忆系统、网关系统、调度系统             │
└─────────────────────────────────────────────────────────────┘

下面自上而下拆开讲每一层,重点放在“为什么这样设计”。

模型层:前缀路由

模型层维护一份统一的提供商注册表(src/providers.ts)。关键设计是按模型名前缀判断提供商,而不是先让用户选提供商、再选模型:

提供商模型名前缀对应 API Key
OpenAI无前缀(如 gpt-5.4OPENAI_API_KEY
Anthropicclaude-ANTHROPIC_API_KEY
Googlegemini-GOOGLE_API_KEY
xAIgrok-XAI_API_KEY
Moonshotkimi-MOONSHOT_API_KEY
DeepSeekdeepseek-DEEPSEEK_API_KEY
OpenRouteropenrouter:OPENROUTER_API_KEY
Ollamaollama:无需云端 Key

两个值得注意的细节:

  • 部分提供商定义了 fastModel,为摘要、快速分类这类低成本轻量场景预留了优化空间。
  • 对 Anthropic,系统提示会被显式标记为 cache_control: ephemeral。原因是系统提示很长——包含工具描述、技能元数据、记忆信息和频道约束——缓存前缀能明显降低重复输入的云上成本。

工具层:工具装配原则

工具层有一个容易被忽略的设计约束:只把“当前真的能用”的工具暴露给模型

原因在代理机制。Agent 的规划来自系统提示,如果系统提示列出它根本没法调用的工具,模型就会据此做出错误的计划——体验比“压根不展示这个工具”更差。这个原则直接决定了游戏规则:

  • 对用户:先确认问题与工具职责是否匹配,再决定措辞。
  • 对开发者:新增工具时,除了实现功能,还要写清楚 rich description。工具描述本身就是代理行为的一部分,不是附属注释。

代理层:Agent 循环与上下文

代理层是研究过程发生的核心,完整链路见下一节《Agent 循环机制》。

扩展能力层:四件事各司其职

技能、记忆、心跳、定时任务这 4 个概念容易被混为一谈,职责其实不同:

能力作用
技能提供专业工作流说明(如 DCF 估值)
记忆保存用户长期信息和历史上下文
心跳按周期检查一份监控清单
定时任务创建通用计划任务,触发时执行 Agent 询问

技能系统工作流

技能的存在是为了让复杂方法论可沉淀、可复用,而不是每次从零解释:

1. 发现   → 扫描包含 SKILL.md 的目录
2. 暴露   → 仅注入技能名称和描述到系统提示
3. 按需   → Agent 匹配后调用 skill 工具
4. 执行   → 返回技能正文,解析相对路径

Agent 循环机制

用户输入 → CLI 接收 → Agent.create() 初始化
构建系统提示(工具 + SOUL.md + 记忆 + 技能元数据)
模型生成响应 → 判断是否有工具调用
执行工具 → 结果写入 scratchpad
上下文超限?→ 触发清理(先清最早工具结果)
更高压力?→ 触发 memory flush
收敛后生成最终答案 → 返回给用户

这串流程描述的是迭代执行。肉眼可见的代价是:一次研究可能来回执行多轮工具调用,比“问一句答一句”慢。换取的是回答能引用真实数据、能自我核对。


金融研究工作流

Dexter 的差异点在于金融。好的研究提问,通常同时具备四个要素:对象、时间范围、判断维度、期望输出。

先看一个反例——它太单薄,工具很难据此拆出有价值的研究步骤:

Compare Apple and Microsoft on revenue growth, operating margin,
and free cash flow trend over the last 5 years.
What is Tesla's latest revenue?

再看一个更完整的研究提示词,它隐含了“拆步骤 + 给结论”的要求:

What does Tesla's latest revenue, gross margin, and operating income
suggest about demand quality and pricing power?
对象 + 时间范围 + 比较维度 + 目标判断 + 期望输出格式

区别在于:前者只要“查个数”,后者要“基于多个指标做判断”。研究价值来自后者。

金融工具如何分工

  • get_financials:财务、指标、估值、分析师预期类问题——营收利润现金流趋势、财务比率、估值指标。
  • get_market_data:价格、新闻、市场动态、内幕交易等更偏“市场表层状态”的问题。
  • read_filings:读 SEC 原始文件,适合对 10-K、10-Q、8-K 做文本层研究。有些结论不能只靠结构化指标得出,必须回到公司自己披露的文本。
  • stock_screener:按条件筛选股票,而不是研究某个单一对象。

网页工具如何分工

  • web_search:通用网页搜索。只要 Exa、Perplexity、Tavily 中任一可用,就以同一个名字暴露,把供应商差异挡在规划层之外。
  • web_fetch:抓取页面正文。当搜索结果标题已足够回答时,不必再抓全文。
  • browser:处理依赖 JavaScript 渲染或交互的页面,比如需要点击、滚动、访问单页应用的场景。不要默认全用它——成本更高、更慢、失败面更多,系统提示也要求仅在必要时使用。

一个更普遍的判断:get_financials 管“公司基本面”,get_market_data 管“市场和价格”,read_filings 管“原始披露文本”。问之前先想清楚你的问题落在哪一层。


WhatsApp 网关

Dexter 可以通过 WhatsApp 接收消息并回传结果,把你从终端里解放出来。官方文档支持两种形态:自聊模式群聊模式

快速接入:

# 1. 扫码绑定你的 WhatsApp 账号
bun run gateway:login

# 2. 启动网关
bun run gateway

自聊模式:打开 WhatsApp,在和自己聊天的会话里发问题,Dexter 处理完后就回发到同一个会话。

详细的配置项和排错步骤,见 WhatsApp 网关的独立 README。


心跳与定时任务

这两套机制让 Dexter 从“被动等待提问”变成“主动汇报”。

heartbeat:周期性检查

用一份监控清单驱动,按周期检查一次状态。适合“每天盯一眼这几个标的”这类固定监控。

cron:定时任务

三个 kind 也覆盖了三种最常见的定时需求:

// 一次性时间点
{ "kind": "at", "at": "2026-04-01T14:00:00Z" }

// 固定间隔
{ "kind": "every", "everyMs": 3600000 }

// Cron 表达式 + 时区
{ "kind": "cron", "expr": "0 9 * * 1-5", "tz": "America/New_York" }

cron 触发时执行一次 Agent 询问,适合在固定时刻(如开盘前)生成一次研究结果。


调试:scratchpad

Dexter 把每次执行都记录成 scratchpad,方便复查数据来源——这也是它“可追溯”逻辑的来源。每条查询在 .dexter/scratchpad/ 下生成一个 JSONL 文件:

.dexter/scratchpad/
├── 2026-01-30-111400_9a8f10723f79.jsonl
├── 2026-01-30-143022_a1b2c3d4e5f6.jsonl
└── ...

每条 JSONL 记录三类事件:

  • init:原始提问
  • tool_result:工具调用、入参与原始返回
  • thinking:Agent 的推理步骤

例如:

{"type":"tool_result","timestamp":"2026-01-30T11:14:05.123Z","toolName":"get_financials","args":{"query":"Compare Apple and Microsoft revenue growth over the last 5 years"},"result":{"data":{...},"sourceUrls":["https://financialdatasets.ai/..."]}}

排查问题时,先看某次回答引用的 sourceUrls,再回看当时的 inittool_result,就能判断是数据源问题、工具问题还是提问本身的问题。


故障排查

从高频到低频,按这个顺序排查:

1. 检查 .env 是否缺少关键 Key
2. 重新运行 bun install(确认依赖完整)
3. 重新启动 CLI 或网关
4. 用一条最小可验证问题重试
5. 必要时重建网关登录状态

常见判断:

  • 回答里只有模型自述、看不到数据来源 → 对应工具没有真正启用,检查该提供商的 API Key。
  • 网页结果为空 → 检查搜索服务是否配了某一家的 Key;web_search 只要三家之一可用就会暴露。
  • WhatsApp 收不到回复 → 先确认扫码登录状态,再检查自聊/群聊模式配置。

扩展开发

给 Dexter 加能力,核心是熟练使用两条接入路径:新增工具新增技能。这不是把两个无关的方法并列——工具补“单一能力”,技能沉淀“整套工作流”。

开发前先回到《工具装配原则》:新增工具必须写清 rich description,因为它直接参与 Agent 的规划。技能则以提供专业工作流说明为主(如 DCF 估值),让复杂方法论能复用。

完整的接入方法、评估与发布边界,见官方文档的《开发与评估》《新增工具与技能》。


常见问题

Dexter 和直接问“GPT”有什么区别?

直接问对话模型,回答依赖训练知识且难溯源;Dexter 用真实工具逐项执行,结果可回溯、可核对。代价是更慢、更绕。

我该用哪个模型?

默认 OpenAI;任务重推理可切 Anthropic/DeepSeek,本地敏感场景可切 Ollama。选型参考“按模型名前缀路由”一节。

为什么不能强制 Dexter 用某个工具?

可以靠提问措辞引导,但最终由代理决定。更可靠的做法是把任务目标写清楚,而不是替它做所有规划。

技能、记忆、心跳、定时任务是一回事吗?

不是。看“四件事各司其职”表格,它们分别管工作流、信息记忆、周期检查、计划触发。


延伸阅读

参与讨论

使用 GitHub 登录。欢迎补充事实、异议与实践。