从一次对话
到可追溯的工作流
用 Agent 完成研究与策略任务,用 SDK 接入同一个运行时。工具调用、会话记录、图表和报告,都有各自清晰的契约。
#先分清三层
Nerya Agent 是工作区原生的任务执行入口。Agent SDK 是调用它和相关能力的客户端,不是另一个独立的 Agent Loop。官网演示是隔离的示例界面,不是你的本地运行时。
| 层级 | 职责 | 入口 |
|---|---|---|
| Agent | 对话、研究、策略任务、工具执行与会话记录 | AgentKernel → WorkspaceNativeAgentLoop |
| Python SDK | 在本地 Python 进程中访问工作区 | nerya_sdk.connect(workspace=...) |
| TypeScript SDK | 通过 HTTP 调用已经运行的 Nerya 服务 | connect({ baseUrl, timeoutMs, fetchImpl }) |
| 官网演示 | 只使用隔离示例数据;不连接模型或账户 | /demo/index.html |
#安装与第一次运行
已安装桌面端时,从初始化向导配置模型、按需绑定交易账户,并设置外部访问密码。本机研发环境可从 Nerya 产品源码目录安装;下方命令在含 pyproject.toml 的目录执行,而不是 landing-page。
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e .
nerya setup --web
nerya doctor
nerya runnerya run 在当前终端启动服务与 Dashboard;需要检查状态时,在另一个终端执行 nerya status。service 子命令用于 install、uninstall、status,并没有 service start。
源码声明 Python ≥ 3.10。Python SDK 与产品一起安装。交易连接器、MCP、浏览器等可选依赖按实际功能安装;安装基础包不意味着所有外部连接器都已配置完成。
- 先在 Agent 中完成一个只读任务,确认模型和工具调用记录正常。
- 再创建策略候选,检查脚本、约束与回测结果。
- 模拟验证与真实执行分开处理,不因为生成了策略就自动开始实盘。
#Agent 的完整调用链
Dashboard / Python SDK / HTTP client
↓
AgentKernel.run_turn
↓
WorkspaceNativeAgentLoop
↔ LLM gateway (messages + tools)
↔ Tool registry → permissions → tool execution
↔ Skill instructions / approved scripts
↓
blocks · tool_trace · events · artifacts
↓
Session transcript / DashboardKernel 负责绑定会话、取消令牌、钩子和日志,构建工具目录与提示上下文,再交给 WorkspaceNativeAgentLoop 执行。模型通过原生消息与工具协议推进任务,直到正常结束、预算限制、等待审批或其他停止条件。
Gateway 负责模型路由与适配。SDK、Dashboard 和外部调用不应各自重新实现一套工具执行逻辑;下游要消费运行时返回的结构化结果。
#选择你的 SDK
| 能力 | Python | TypeScript |
|---|---|---|
| 调用一次 Agent | agent.run_turn(...) | agent.runTurn(...) |
| 查看工具目录 | agent.list_tools() | agent.tools() |
| 取消执行 | 公开 facade 暂无同名方法 | agent.interrupt(sessionId) |
| 触发器 | triggers.emit / dry_run | triggers.emit / dryRun |
| 策略历史与复盘 | strategy.history / review | strategy.history / review |
| 发布图表 | charts.publish_and_announce | 当前客户端未暴露 charts facade |
| 传输 | 进程内;工作区路径 | HTTP;已启动的本地服务 |
Python:先检查工具目录
from pathlib import Path
from nerya_sdk import connect
client = connect(workspace=str(Path.home() / ".nerya"))
catalog = client.agent.list_tools()
for tool in catalog.get("tools", []):
print(tool["name"], tool["risk"], tool["read_only"])Python:显式执行一个回合
from pathlib import Path
from nerya_sdk import connect
client = connect(workspace=str(Path.home() / ".nerya"))
# Reuse the same session ID for follow-up turns.
session_id = "sdk-research-example"
result = client.agent.run_turn(
text="Inspect the available research workflow. Do not place orders.",
session_id=session_id,
)
print(result.get("reply_text", ""))
for envelope in result.get("blocks", []):
block = envelope.get("block", envelope)
print(block.get("kind"))TypeScript:连接已有服务
仓库中的 SDK 源码位于 sdk/typescript,构建脚本是 npm run build。先在该目录安装依赖并构建,再从消费项目安装该本地目录。包名是 @nerya/sdk;这里不假定它已发布到 npm。
# Run in the product repository (not landing-page).
npm install --prefix sdk/typescript
npm run build --prefix sdk/typescript
# In your consuming project, use the real local SDK path:
npm install /absolute/path/to/product/sdk/typescriptimport { connect } from "@nerya/sdk";
const client = connect({
baseUrl: "http://127.0.0.1:18317",
timeoutMs: 120_000,
caller: "script:research-example",
});
const catalog = await client.agent.tools();
console.log(catalog.tools.map(tool => tool.name));
// Executing this call invokes your configured runtime and model.
const result = await client.agent.runTurn({
text: "Inspect the research workflow without placing orders.",
session_id: "sdk-research-example",
});
console.log(result.reply_text);
console.log(result.tool_trace);TypeScript 默认超时为 10 秒,较长 Agent 任务应显式配置 timeoutMs。caller 只设置 X-Nerya-Caller,不是鉴权凭据。需要鉴权时,通过可信服务端环境中的 fetchImpl 接入现有鉴权;不要把服务令牌放进公开前端。
#会话与执行记录
session_id 标识连续对话;turn_id 标识一次执行。对同一任务继续提问时复用 session_id,不要每调用一个工具就创建新会话。策略上下文通过 strategy_id 显式传入,附加 Skills 通过 attached_skills 指定。
| 字段 | 如何使用 |
|---|---|
| reply_text | 快速显示最终文本,但不要只保留这个字段 |
| blocks | 按 envelope.block.kind 渲染文本、工具、图表等内容,保留原顺序 |
| tool_trace | 用 call_id 关联工具调用与结果;保留错误、耗时与 payload |
| events / steps | 执行事件与步骤视图;不要凭文本推断成功 |
| stopped_reason | 区分完成、暂停和其他停止原因,不能把 HTTP 成功当作任务成功 |
刷新后使用会话 transcript 接口恢复消息和结构化内容。长期记忆与对话历史不是同一个概念;不要把浏览器 localStorage 当成运行时权威状态。
#进度、续读与取消
保存事件响应中的 epoch 与 cursor,并在下一页传回 epoch、after_seq。has_more 为真时继续读取;reset_required 为真时先恢复持久 transcript,再建立新的事件游标,不能把旧进程的 seq 当成新进程的连续序号。
GET /agent/stream/events 是事件分页/轮询接口,不是 SDK 的 SSE 生成器。用 session_id 过滤会话、after_seq 续读,并按 seq 去重;消费返回的 cursor,同时尊重响应中的重置或截断信息。运行时事件缓冲有限,不替代持久 transcript。
GET /agent/stream/events?session_id=sdk-research-example&after_seq=0&limit=100
GET /agent/session/transcript?session_id=sdk-research-example
POST /agent/interrupt
Content-Type: application/json
{"session_id":"sdk-research-example","reason":"operator_interrupt"}取消是协作式的:请求取消不代表已经撤销外部副作用。客户端 HTTP 超时也不等于服务端回合停止。超时后先查询状态与 transcript,再决定是否续接,避免盲目重复执行。
#工具与 Skills
工具是可执行能力及其输入 schema;Skill 是指导 Agent 何时、如何使用这些能力的工作说明。通过工具目录读取 name、input_schema、risk、permission_scope、read_only 等字段,不要根据名字猜参数。
my_research_skill/
├── SKILL.md # Required: when to use, workflow, boundaries
├── references/ # Optional: methods, report templates, source rules
├── scripts/ # Optional: deterministic executable helpers
└── templates/ # Optional: reusable artifacts- 把触发场景、输入、步骤、产物和失败处理写入 SKILL.md。
- 把长方法论和报告模板放进 references,按需加载。
- 需要执行时使用 scripts,副作用走 SDK、Vault 或变更提案,不绕过风控。
#Agent 团队与独立审阅
主 Agent 可以把研究任务拆给研究员、策略作者和审阅员。并行任务仍需遵守依赖关系;汇总报告不代表每个必需任务都成功完成。
- 明确研究范围、时间尺度和所需产物。
- 给成员明确职责,并保留工具调用、来源与成员会话。
- 独立审阅要保留反证和分歧,缺证据就标记缺失。
- 主 Agent 汇总结论、局限和下一步,不把报告自动转成真实订单。
#卡片、图表与研究产物
前端应展示每次 Agent 回复自己的产物,而不是把所有研究品种堆在当前输入框旁。策略提案、回测、行情和报告都有不同的数据契约;只在文本中写“已生成图表”不会产生可渲染图表。
| 产物 | 应显示的信息 |
|---|---|
| 策略卡片 | strategy_id、提案状态、工作流、约束、审阅与验证入口 |
| 回测卡片 | 数据窗口、费用、滑点、净值曲线、回撤、交易记录与样本外边界 |
| 行情卡片 | 市场/品种、时间戳、来源、K 线与指标;缺数据不造价格 |
| 研究报告 | 结论、来源、证据、情景、分歧、风险与失效条件 |
Python:发布图表并交给当前回合
from nerya_sdk import connect
client = connect()
# Illustrative observations, not live market data.
chart = {
"kind": "chart",
"chart_kind": "line",
"title": "Example observations (illustrative)",
"series": [{
"type": "line", "name": "observed_value",
"data": [
{"time": 1704067200, "value": 100},
{"time": 1704153600, "value": 102},
{"time": 1704240000, "value": 101},
],
}],
"time": {"timezone": "UTC", "format": "unix_seconds"},
"source": {
"skill": "agent", "action": "example",
"as_of": "2024-01-03T00:00:00Z",
},
"warnings": ["Illustrative data; not a backtest or live quote."],
}
result = client.charts.publish_and_announce(chart)
print(result["chart_id"])publish_and_announce 会写入当前工作区的图表产物,并输出 @@nerya:chart@@ 标记。Agent 的脚本执行链路读取标记后,将对应 chart block 放入回合;单独在终端运行脚本只会发布文件并打印标记,不会凭空附加到已有对话。
图表支持 inline 与 bulk。公开 Python 发布方法会转为 bulk,用 chart_id 和 bulk_data_uri 读取数据,避免把大数组塞进模型上下文。保留 source.as_of、数据时间格式、指标名称和 warnings;不要丢掉 envelope 的其他字段。
#一份可以审阅的投研报告
- 研究问题:品种、市场、观察窗口和假设。
- 证据:每项关键结论的来源、时间戳、原始链接或产物标识。
- 方法:数据清洗、指标口径、样本划分和局限。
- 情景:基准、上行、下行,以及触发这些情景的条件。
- 独立审阅:支持、反对、缺失证据和仍未解决的问题。
- 行动边界:失效条件、下一次复查点和执行前仍需满足的要求。
详细研究规范放在 Skill 的 references 中按需加载。报告应可通过工作区产物重新打开,图表与品种卡片绑定到产生它们的回复。示例、回测、模拟执行和真实成交需要明确区分。
#策略、回测与执行边界
策略任务应携带 strategy_id,保留自然语言目标、脚本、解释性工作流和运行证据。定时检查是工作流里的触发职责,不能用一个模糊的“已配置”代替可检查的运行记录。
Strategy candidate → Validation / backtest → Review
↓
Paper execution
↓
Explicit live enablement + account capability
↓
Risk Gate → Approval policy
↓
Order result / reconciliationsubmit_intent / submitIntent 表示向运行时提交交易意图,不是直接调用交易所。是否允许执行由账户能力、实盘开关、风控、审批策略与执行处理器共同决定。审批策略可含自动审批规则,不应宣传每笔订单必然手动点击。
#触发器与幂等
触发器把外部事件或定时事件路由到明确目标。先 dry_run 检查路由,再决定是否发送;为可能重试的事件使用稳定的 idempotency_key,不要每次重试都生成一个新键。
from nerya_sdk import connect
client = connect()
preview = client.triggers.dry_run(
source="script",
kind="research.requested",
payload={"question": "Review the available market evidence"},
target="main",
idempotency_key="research-example-001",
)
print(preview)Python 的 emit_to_file 写入工作区 inbox,SDK 不会因此自动启动守护进程。需要有相应运行时消费文件,才会产生后续处理。
#连接器与外部 Agent
接入交易平台前先检查当前连接器与账户能力。CEX 优先复用 CCXT/provider-spec 路径;不支持的平台走可审阅的适配方案和验证流程。生成了接入计划,不代表已经实现、安装或验证了连接器。
通过 MCP 或其他外部 Agent 接入时,复用运行时工具目录、会话与产物展示契约。外部模型发起工具调用,不应导致每次调用创建一条孤立会话,也不应把工具轨迹和卡片降级为一大段纯文本。
#复盘与变更提案
启用复盘计划后,Agent 可以检查运行历史并提出调整。证据不足时可以 hold 或 skip,不应承诺每次复盘都会优化策略,更不能保证改善收益。策略调优计划与工作区 Dream 反思计划不是同一开关。
- 收集运行记录和证据,说明问题与不确定性。
- 生成可审阅的提案与差异,保留验证结果。
- 根据提案类型和策略审批,再应用、观察和必要时回滚。
存在默认关闭、范围受限的自动应用路径,允许特定 prompt_patch 与 core_config_patch,并有保护路径和验证要求。不能泛称“所有变更自动上线”或“没有任何自动应用”。策略调优提案不属于该自动应用白名单。
#安全、凭据与权限
| 控制 | 边界 |
|---|---|
| Vault | 账户凭据通过 vault:// 引用使用;解密后的值可能存在于进程内存中 |
| 本地密钥 | 可使用显式口令或本机生成密钥;不要假定必须输入主密码 |
| 工具权限 | 由权限配置和执行器判断,不由 SDK 的 caller 字段授权 |
| 交易控制 | 账户能力、实盘开关、Risk Gate 与审批策略分别检查 |
| 变更保护 | 受保护路径与提案审批约束变更通道,不等同整个主机的操作系统沙箱 |
标准依赖安装包含 cryptography,对应加密路径使用 scrypt 与 AES-GCM;源码仍存在缺少加密依赖时的兼容分支,因此不要假设所有部署都具备相同加密强度。尤其是实盘运行,应检查本机诊断与加密依赖。
敏感文本扫描是启发式处理,不能保证任意密钥永不进入模型。避免把凭据放入提示、报告、代码示例或日志。不要将 localhost 的可达性视为外部暴露场景下的鉴权保证。
#常用 HTTP 接口
| 方法与路径 | 用途 |
|---|---|
| GET /agent/tools | 查询工具目录与 schema |
| POST /agent/run_turn | 发起一个 Agent 回合 |
| GET /agent/sessions | 列出会话 |
| GET /agent/session/transcript | 恢复对话和结构化内容 |
| GET /agent/stream/events | 分页获取进度事件 |
| POST /agent/interrupt | 协作式取消 |
| POST /agent/steer | 向进行中的回合追加引导 |
| POST /agent/trace | 重建关联执行轨迹 |
| POST /agent/turn_state | 读取回合恢复状态 |
此表列出运行时路径。Dashboard 的 /api/proxy 前缀是前端代理,不要把它误当成 TypeScript SDK 默认 baseUrl 的一部分。内部 loopback 专用端点不应当作公共集成接口。
#从旧接入方式迁移
| 旧做法 | 迁移到 |
|---|---|
| 只显示最终回答文本 | 保留 blocks、tool_trace、产物与停止状态 |
| 每个工具调用新建会话 | 复用 session_id,用 turn_id 与 call_id 关联执行 |
| 默认 Python 和 TS 所有接口一致 | 按实际 facade 调用,尤其注意 charts、取消与恢复能力 |
| Skill 的 YAML action 清单 | SKILL.md + scripts / references |
| 把图表大数组放入提示 | 发布 bulk 图表产物并保留 chart_id |
| 超时立即重新执行 | 查询原回合状态与记录,避免重复副作用 |
#诊断与源码导航
nerya --help
nerya doctor
nerya service status| 源码位置 | 用途 |
|---|---|
| nerya/agent/kernel.py | 统一 Agent 回合入口 |
| nerya/agent/loop.py | 原生消息与工具循环 |
| sdk/python/nerya_sdk/client.py | 公开 Python facade |
| sdk/typescript/src/client.ts | TypeScript HTTP 客户端 |
| nerya/api/routes_agent.py | Agent HTTP 接口 |
| nerya/sdk/agent_api.py | 进程内 Agent API |
| dashboard/lib/chartBlock.ts | 图表结构契约 |
| dashboard/components/chat/ResearchReplyCards.tsx | 每次回复的研究卡片 |
排查时记录 request/turn/session 标识、停止原因和相关错误,不复制秘密值。当前页面是源码核对后的接入说明,不是对模型正确性、账户连接或真实下单链路的验收证明。