深涌智能(Alpha-Dojo)开源的面向个人投资的全市场 AI Agent 框架。
完整 Clone 源码,逐文件阅读,1523 行核心引擎逐行分析。
一句话:DojoAgents 不是"回答问题"的金融 AI,而是自主完成全链路投研的 Agent 引擎——市场扫描、财报拆解、产业链分析、风险测算,Agent 自己判断"下一步该查什么、如何验证"。
dojoagents/
├── agent/ # ★ 核心 Agent 引擎 (41 个文件)
│ ├── loop.py # ★ AgentLoop — 核心编排器 (1523行)
│ ├── runtime.py # ★ Runtime — 全局依赖注入组装 (361行)
│ ├── harness.py # ★ Harness 基类 — 自动验证框架
│ ├── harnesses/ # 3 个具体 Harness (portfolio/tool_orchestrated/artifact_synthesis)
│ ├── hooks/ # Agent 生命周期 Hook 系统
│ ├── guardrails.py # 工具调用安全护栏
│ ├── compressor.py # LLM 辅助上下文压缩
│ └── session_manager.py # 会话持久化 (27KB)
├── tasks/ # ★ 任务系统 — YAML 合约 + TASK.md 提示词
├── planning/ # ★ 自动规划 — LLM 生成计划 → DAG 执行
├── multi_agent/ # ★ 多 Agent — 池化管理 + 事件驱动分发
├── cron/ # ★ 定时调度 — APScheduler + Gateway 推送
├── memory/ # 记忆系统 — Hook 自动同步
├── skills/ # 技能系统 — 可复用工作流
├── plugins/ # 插件系统 — PluginRegistry (34KB)
├── tools/ # 工具系统 (24 个文件)
├── gateway/ # 多通道聊天网关
├── dashboard/ # FastAPI + React 前端
└── utils/event_bus.py # ★ 事件总线 (仅23行!) — 主动式 Agent 的神经中枢
| 组件 | 技术选型 | 作用 |
|---|---|---|
| Agent 运行时 | strands-agents + strands-agents-tools | Agent 生命周期管理、工具注册调度 |
| 工具协议 | mcp>=1.26.0 | 标准化工具接入(Model Context Protocol) |
| LLM 接口 | openai>=1.20.0 兼容协议 | 模型无关 — 支持 OpenAI / Claude / Gemini / DeepSeek / GLM / Ollama |
| 自主调度 | apscheduler>=3.10.0 | Cron 定时任务 — 驱动后台自主推送 |
| 后端 | FastAPI + Uvicorn (SSE 推送) | 实时 Agent 状态流式推送 |
| 数据处理 | pandas + pyarrow | 多市场数据 ETL |
| 前端 | React SPA (Vite) | 机构级仪表盘 |
| 自研 SDK | dojosdk>=0.1.8 | Alpha-Dojo 金融数据接口 |
DojoAgents 的"主动性"不是单一特性,而是由六层机制叠加而成的系统行为。
dojoagents/agent/loop.py 是整个系统最核心的文件,1523 行实现完整的 Agent 编排逻辑。
# loop.py:1134-1144 — 创建 Strands Agent 实例
agent = Agent(
model=model, # DojoStrandsModelBridge (桥接 LLM)
messages=agent_messages, # 历史会话
tools=strands_tools, # 桥接后的工具列表
system_prompt=system, # 动态构建的系统提示
hooks=hooks, # 5 个 Hook Provider
plugins=plugins, # 插件
callback_handler=..., # 流式输出回调
agent_id=strands_agent_id,
session_manager=strands_session_manager,
)
# loop.py:1170-1177 — 执行 Agent (多轮 Tool Calling)
result = await agent.invoke_async(
prompt=user_prompt,
invocation_state=invocation_state,
limits=limits, # Limits(turns=100) — 防止无限循环
)
# loop.py:830-995 — Before Tool Call Hook (简化版)
async def check_guardrails_before(event: BeforeToolCallEvent) -> None:
tool_name = event.tool_use.get("name")
args = event.tool_use.get("input") or {}
# 1. 图片模式 → 阻止代码执行工具
if tool_name in IMAGE_TURN_EXCLUDED_工具:
event.cancel_tool = blocked_res["content"]
# 2. 动态创建工具 (延迟绑定 — 支持运行时注册)
if not event.selected_tool:
event.selected_tool = DojoBridgedTool(...)
# 3. execute_code 护栏 → LLM 辅助分类判断是否允许
if tool_name in EXECUTE_CODE_TOOL_NAMES:
classification = await classify_execute_code(code, ...)
if blocked: event.cancel_tool = ...
# 4. write_session_file 护栏 → LLM 判断写入安全性
if tool_name == "write_session_file":
classification = await classify_write_session_file(...)
if blocked: event.cancel_tool = ...
# 5. 通用护栏 → 规则引擎检测
decision = self.guardrails.before_call(tool_name, args)
if decision.should_halt:
raise GuardrailHaltException(...) # 彻底终止
elif not decision.allows_execution:
event.cancel_tool = ... # 阻止此工具
# 6. Harness 工具修复 → 任务模式下限制工具使用
if active_harness:
repaired = active_harness.repair_tool_calls(calls, state)
blocked = active_harness.block_tool_call(call, state)
# loop.py:1002-1045 — After Tool Call Hook (简化版)
async def check_guardrails_after(event: AfterToolCallEvent) -> None:
# ★ 工具执行失败 → 发布事件,MultiAgentAutoDispatcher 自动修复
if is_failed:
failure_results = await event_bus.publish(
"ToolExecutionFailed",
{"tool_name": tool_name, "args": args, "error": raw_result}
)
if failure_results:
event.result["content"] = [{"text": failure_results[0]}]
# ↑ Reviewer Agent 的修复结果自动注入!
# ★ 大数据量 (>5000字符) → 发布事件,Analyst Agent 自动分析
if len(raw_result) > 5000:
data_results = await event_bus.publish(
"DataVolumeLarge",
{"data_summary": raw_result[:2000] + "..." + raw_result[-1000:]}
)
if data_results:
event.result["content"] = [{"text": data_results[0]}]
# ↑ Analyst Agent 的洞察自动替换原始数据!
这是 DojoAgents 最独特的设计——Agent 不是一次性生成输出就结束,而是通过 Harness 系统持续自我验证,不满足条件就自动继续。
TaskHarness (抽象基类) # agent/harness.py
├── matches(request, state) → bool # 是否匹配当前任务
├── block_tool_call(call, state) → str # 阻止不适合的工具调用
├── repair_tool_calls(calls, state) → [] # 修复/限制工具调用
├── validate_progress(state) → Decision # ★ 验证任务是否完成
└── build_recovery_prompt(dec, locale) # ★ 构建恢复引导
具体实现:
├── PortfolioTaskHarness (21KB) # 验证投资组合任务
├── ToolOrchestratedHarness # 引导工具编排步骤
└── ArtifactSynthesisHarness # 验证产物 Schema
# loop.py:1280-1330 — 自动验证与纠错循环
harness_recovery_turns = 0
while True: # ★ 自动循环直到任务完成或达到上限!
decision = active_harness.validate_progress(harness_state)
if decision.complete:
break # 任务完成,退出循环
# 构建恢复提示 (中英文双语)
recovery_prompt = active_harness.build_recovery_prompt(decision, locale)
# 达到上限 → 输出当前状态并停止
if harness_recovery_turns >= max_recovery_turns:
response_text = recovery_prompt
break
harness_recovery_turns += 1
# ★ 自动重新调用 Agent,注入引导提示!
result = await agent.invoke_async(
prompt=recovery_prompt,
invocation_state=invocation_state,
limits=limits,
)
# harnesses/tool_orchestrated.py:62-96
def validate_progress(self, state) -> HarnessDecision:
# 1. 检查输出 Schema 验证 — 如果合法则完成
filename, issues = self._output_write_issues(state)
if filename and not issues:
return HarnessDecision(complete=True)
# 2. 引导下一步操作
if not state.tool_results:
issues.append("Start with get_market_overview.")
elif not has_tool(state, "get_sector_movers"):
issues.append("Call get_sector_movers before web_search.")
elif not has_tool(state, "write_session_file"):
issues.append("Finish by writing output with write_session_file.")
# 3. 返回决策 — 允许继续但指定下一步
return HarnessDecision(
complete=False,
issues=issues,
next_steps=issues[:1],
allow_extra_steps=True, # ★ 关键: 允许继续
)
dojoagents/utils/event_bus.py 仅 23 行代码,却驱动了整个自主响应体系。
# utils/event_bus.py — 仅 23 行!
class EventBus:
def __init__(self):
self._subscribers: dict[str, list[Callable]] = {}
def subscribe(self, event_type: str, handler: Callable) -> None:
self._subscribers.setdefault(event_type, []).append(handler)
async def publish(self, event_type: str, payload: dict) -> list[Any]:
results = []
for handler in self._subscribers.get(event_type, []):
res = await handler(payload)
if res is not None:
results.append(res)
return results
event_bus = EventBus() # 全局单例
以下追踪一个典型主动式请求 "分析今天A股半导体板块" 经过的完整路径:
| 维度 | 传统被动式 Agent | DojoAgents 主动式 |
|---|---|---|
| 执行模式 | 单次 LLM推理 → 输出 | ✔ 多轮 推理→工具→观察→再推理 循环 |
| 完成标准 | 模型说"完成了" | ✔ Harness 系统客观验证(Schema校验、数据检查) |
| 纠错能力 | 用户手动纠正 | ✔ 自动检测 + 事件总线触发修复 Agent |
| 工具调用 | 单次或少量调用 | ✔ 多步骤编排,每步受 Pre/Post Hook 检查 |
| 时间维度 | 同步即时响应 | ✔ 同步 + Cron 定时后台自主运行 |
| 复杂度适应 | 固定流程 | ✔ 自动检测 → LLM 生成计划 → DAG 执行 |
| 多 Agent | 单 Agent | ✔ 正则检测 + 事件触发 = 按需激活 |
| 记忆 | 单次会话 | ✔ 跨会话 Memory + Skill 蒸馏 |
| 质量保证 | 依赖 Prompt | ✔ 护栏 + 验证 + 自动修复 + 恢复循环 |
| 上下文 | 固定窗口 | ✔ 动态 LLM 压缩 + 溢出自动重试 |