ReMe/plugins/auto-fin/README_ZH.md
jinliyl cae613d1c4
refactor(auto-fin): one note per topic, merged by a separate digest step (#558)
* fix(auto-fin): parse topic IDs from fenced JSON replies

* fix(auto-fin): limit report agent tool calls in prompt

* refactor(auto-fin): research news by topic before market open

* fix(config): update default model version for claude_code backend

- Change model version from qwen3.8-max to qwen3.7-plus
- Use environment variable LLM_MODEL_NAME to allow override
- Ensure backend configuration reflects updated model setting

* refactor(auto-fin): write one note per topic before the daily digest

The merge step did two jobs at once: it researched every topic and
combined the results into a single report. Split it the way daily-paper
separates analysis from its brief, so each topic earns a durable note of
its own.

- auto_fin_research_step writes one note per topic that had relevant
  news, tagged `kind: auto-fin-topic` and `topic` in frontmatter
- auto_fin_digest_step merges those notes into the day's brief with no
  tools of its own and appends a `## 主题详解` section linking back to
  each note
- a same-day rerun finds a topic's note by its `topic` frontmatter and
  replaces it in place, deleting the old file when the title changed
- base.py now owns the shared Markdown layer: title sanitizing, report
  normalizing, wikilink validation, note lookup, atomic frontmatter
  writes, and change tracking, so both steps share one write path
- the DingTalk step maps `auto_fin_digest_path` to `markdown_path`
  explicitly instead of relying on whichever step ran last
- drop the unused AutoFinTopicOutput schema and read `job_tools` from
  the step config rather than hardcoding `search`

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(dingtalk): surface rejection details when delivery fails

A failed group send only reported HTTPStatusError, so an operator had to
reproduce the request by hand to learn why DingTalk refused it. Include
the status code and the whitelisted error keys from the response body in
both the log line and the raised RuntimeError.

Only `code`, `message`, and `requestid` are reported: the request body
carries the message content and credentials, so an error response that
echoes it back must not reach the log. Detail is truncated to 200 chars.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(test): make the suite green on CI

- Point the cookbook claude_code model assertion at qwen3.7-plus, the
  default commit 9404e600 set, so the pre-existing red stops blocking
- Satisfy pylint on the auto-fin tests: prefer implicit booleaness for
  the recorded Agent calls and drop an unused tmp_path fixture

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(auto-fin): name notes after the topic, not the Agent title

The research Agent returned a whole paragraph as its title; that became a
filename and blew past the filesystem's 255-byte name limit, failing with
ENAMETOOLONG inside resolve_note_path. Topics are configured values, so
they are short and predictable - use them for file names and keep the
Agent title in frontmatter.

- Name topic notes after the topic and the digest after the run date
- Fold a byte budget into normalize_title as a safety net for long topics
- Take an AutoFinReportOutput in _write_report instead of loose fields
- Ask both prompts for a short title now that it is display-only

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: isolate per-topic research failures and scope frontmatter reads

A single failing topic used to fail the whole cron job and discard the news
already gathered for the topics that had not run yet -- the 09-19 09:24 run
lost its robot notes that way. Research now logs the failure, continues with
the remaining topics, and only fails the run when no topic produced a note.

frontmatter_read was the only frontmatter step without the _allowed_paths
check that read, write, edit, and frontmatter_update already honour, so an
Agent scoped to one file could still read another file's metadata.

- Isolate per-topic research failures and report them as failed_topics
- Fail loudly when every topic fails so an empty brief is never sent
- Apply _check_path_permission in FrontmatterReadStep

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(auto-fin): keep hand-edited notes and wikilink delimiters from breaking the run

Addresses three review findings on the topic-per-note rework.

- `find_note` and `read_note` now skip a note whose YAML frontmatter does
  not parse. A hand-edited note in the day directory raised
  `yaml.parser.ParserError`, which per-topic isolation surfaced as
  "Auto Fin research failed for every topic" and took the run down with it.
- `normalize_title` also strips `[`, `]` and `#`, which `WikilinkHandler`
  treats as target delimiters. `AI[算力]` used to emit a trailer link the
  parser could not read at all, and `C#` resolved to `.../C` plus an anchor.
- `_write_report` returns the body it actually wrote, and both callers
  propagate it, so the digest answer and the note handed to the digest Agent
  no longer carry links that validation had already downgraded on disk.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-20 14:52:56 +08:00

6.7 KiB
Raw Permalink Blame History

Auto Fin 插件

English

Auto Fin 自动拉取一个滚动时间窗口内的财联社电报(默认 24 小时),按配置 topics 归类相关新闻,逐主题搜索 ReMe 中有回顾价值的历史材料并为每个主题写一份笔记, 最后把所有主题笔记合并成一份带校验 wikilink、且回链到各主题笔记的当日总览。当前新闻和筛选结果只存在于本次运行内存中,只有主题笔记和总览成为持久记忆。本目录是一个独立 Python distribution:单个 reme.plugins entry point 暴露 plugin.yaml,其中声明四个 Step backend,并在 application_defaults 下提供 Job 配置;通过 plugins=["auto-fin"] 显式启用这个已安装插件。

Auto Fin 没有可靠行情数据,不计算收益、目标价或买卖点,也不提供投资建议。

快速开始

1. 安装 ReMe 和 Auto Fin

python -m pip install "reme-ai[core]>=0.4.1.12"
reme plugins install plugins/auto-fin

2. 配置模型环境变量

按照 ReMe README 的可选模型配置说明配置 LLM 环境变量,也可以使用其他兼容的模型和服务商。

3. 带插件启动 ReMe

reme start plugins='["auto-fin"]'

未显式传入 config 时,ReMe 会加载 default.yaml,并将插件叠加到该服务上。

在另一个终端中,通过 ReMe CLI client 调用正在运行的 HTTP 服务:

reme auto_fin topics="黄金,AI,存储芯片"

也可以直接调用 HTTP endpoint:

curl -s http://127.0.0.1:2333/auto_fin \
  -H 'Content-Type: application/json' \
  -d '{"topics":"黄金,AI,存储芯片"}'

HTTP service 也会在 /mcp 中将同一个 Job 暴露为 auto_fin MCP tool。默认 topics 是 黄金,机器人,半导体, 传入空值也会使用默认值。

如果需要同时通过 JSON 和 MCP 访问同一个应用:

reme start plugins='["auto-fin"]' \
  service.backend=http

自定义应用配置需要提供 agent_wrapper.default、启用 tag index 的 file_store.default,以及 Auto Fin 和自动标签使用的 search、list_tags、frontmatter_read 和 frontmatter_update Jobs。

流程

财联社公开电报接口(滚动24小时)
        ↓
在 RuntimeContext 中规范化和去重
        ↓
Topic Agent 按提示词长度分批输出“主题 → news_id”
        ↓
每个有新闻的主题由独立 Research Agent 研究最新 20 篇,最多搜索 3 次,
并写出一份带校验历史 wikilink 的主题笔记
        ↓
Digest Agent 把各主题笔记合并成当日总览,代码在末尾追加回链到每一份笔记
        ↓
生成记忆标签,由后台文件 watcher 刷新索引

auto_fin_data_step 使用财联社网页同源接口的签名和分页方式,从分析时刻开始向前翻页,直到完整覆盖严格的最近 24 小时。请求带有限速和重试;损坏记录及窗口外记录会被丢弃。

auto_fin_topic_step 按完整提示词的 10 万字符上限分批接收当前新闻,返回每个主题的相关 news_id。代码会忽略未知 ID、去除重复 ID,并保持源新闻顺序;一条新闻可属于多个主题。如果没有相关新闻,Job 会成功跳过,不写任何文件。

auto_fin_research_step 为每个有新闻的主题研究最新 20 篇,并各写一份主题笔记;只向 Agent 开放 search,按主题限制最多 3 次搜索。当前新闻以 CLS ID、时间和标题作为普通证据。 Prompt 要求 Agent 只链接实际使用过的历史 Markdown;代码边界则独立保证只保留真实存在、相对 workspace 的 Markdown 目标。不存在、绝对路径、越界、带反斜杠和自引用的目标都会降级为可读 alias。

auto_fin_digest_step 把各主题笔记合并成当日总览,自身不开放任何工具——研究结论已经在笔记里。代码在正文末尾追加 ## 主题详解 章节,回链到每一份主题笔记,然后刷新当天索引并把总览路径交给下游步骤。两步在工作流已被判定跳过时都会提前返回。

同日重跑会按 frontmatter 中的 topic 找回该主题已有的笔记,把它的正文作为上下文重新研究并原地覆盖;总览同理。所有写入都是原子的,随后通过 auto_tag_step 更新这些文件的记忆标签 frontmatter;常规后台文件 watcher 会观察源文件变化并刷新派生索引。流程不会写入 JSONL、 中间 Markdown 或 Agent 结构化输出。

参数

参数 默认值 作用
date "" 空值使用上海时区当天;显式日期必须等于当天
now "" 测试或回放使用的 ISO 8601 分析时间
topics "黄金,机器人,半导体" 逗号分隔的主题;空值也使用这些默认值
window_hours 24 向前抓取财联社电报的滚动小时数,必须大于 0
request_interval 10 每次财联社请求尝试后的最小等待秒数,可设为 0
max_retries 3 每页财联社请求的最大尝试次数,至少为 1

插件的 cron Job 随应用启动,并按应用配置的时区在每天 09:00 运行,默认时区为 Asia/Shanghai。滚动窗口按时间戳计算,允许跨自然日;09:00 是启动时间,报告完成时间取决于新闻量与模型耗时。

产物

.reme/daily/YYYY-MM-DD/<主题>.md                    # 每个有相关新闻的主题一份
.reme/daily/YYYY-MM-DD/主题新闻观察(YYYY-MM-DD).md  # 合并总览,回链到每一份主题笔记

文件名取自配置的主题(topics)和运行日期,而不是 Agent 生成的标题——Agent 标题是自由文本,可能长到超出文件名长度上限。文件名经过 非法字符净化、字节数截断和重名消歧;Agent 标题保留在 frontmatter 的 title 中。每份文件都带 kind frontmatter(auto-fin-topic 或 auto-fin-digest),因此同日重跑会找回并覆盖自己产出的笔记,而不是重复生成。文件包含标题、说明、当前 CLS 证据、历史分析、上下文 wikilink 和固定非投资建议声明;总览末尾另有一个 ## 主题详解 列表,链接到各主题笔记。抓取失败与全部主题研究失败 会明确失败;单个主题研究失败只记 warning 并继续其余主题;没有相关当前新闻则成功跳过。

验证

python -m pytest plugins/auto-fin -v

单元测试 mock CLS 与 Agent 边界,不访问外部服务。