
1. 金融研报 Agent 为什么会在 LangGraph 上崩掉多步链路上下文溢出实战复盘金融研报自动生成这个场景本质上是一个「多源数据采集 多步指标计算 长文组装」的重链路任务。我最早用 LangGraph 配合 qwen3-max 搭过一版节点之间靠本地文件传递中间结果上下文里只留摘要。想法很美好但真跑起来问题一个接一个。先说数据量。一家 A 股上市公司通过 AKShare 拉一张资产负债表返回的 DataFrame 大概有 100 到 150 个字段按年返回十几行。单张表转成文本大约 15000 到 20000 tokens。你要算三年就是三张表乘以三年再对标三到五家同行就是 9 份报表乘以 5 家公司45 份数据文件光原始数据就 90 万 tokens 起步。qwen3-max 的 256K 上下文窗口连原始数据都装不下更别提还要留空间给 Agent 推理和工具调用记录。这就是「上下文溢出」的真实体感。它不是模型不够聪明而是上下文管理这件事靠自己在 LangGraph 里手搓节点间的文件传递逻辑边界情况太多。每个节点处理完得把结果写到本地文件下一个节点只读文件路径上下文里只留一个摘要。听起来简单但实际会遇到某个节点忘了写文件、文件路径拼错、并发写同一个文件、摘要丢掉了关键字段、重试时状态不一致。这些坑一个个踩下来调试成本极高。我试过在 LangGraph 里加各种守卫节点执行前检查文件存在、执行后校验产物、失败重试三次。但这些都是业务代码在补基础设施的洞。上下文压缩、工具结果转存、历史消息折叠这些本该是 Agent 运行时框架的职责不该由业务开发者自己实现。后来我把目光转向 Claude Agent SDK。它是 Claude Code CLI 的库形式封装完整继承了 Claude Code 的 Agent Loop、内置工具、Skills 机制以及最关键的五级渐进式上下文压缩策略。我当时就想如果这套压缩策略真的好用那我在 LangGraph 里手搓的那套文件传递机制是不是可以全扔掉于是有了 FinScribe 这个项目。它的目标很明确让多步研报生成链路稳定不崩从数据采集到研报组装全流程自主完成中间不需要人工干预。下面我把迁移过程、TaoToken 统一 Key 接入、可复制配置、验证动作和踩坑排查完整拆一遍。2. TaoToken 统一 Key 接入 Claude Agent SDK 的前置准备Base URL 与模型通道配置Claude Agent SDK 兼容 Anthropic 协议所以你可以接国产模型也可以接统一的 API 通道。这里我用 TaoToken 作为统一 Key 入口好处是一个 Key 管多个模型后端切换模型只改环境变量不用改业务代码。TaoToken 的官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。注意 API 地址不带 UTM 参数配置里写干净的 Base URL 就行。前置准备分三步。第一步拿到 API Key。登录后在控制台的 API Keys 页面创建一个 Key复制保存。这个 Key 后面会写进.env文件作为ANTHROPIC_AUTH_TOKEN的值。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite API Keys 页面是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。第二步确认模型 ID。TaoToken 支持多种模型你需要在模型对话页面确认你要用的模型 ID。模型对话入口是 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。比如你要用 Claude 系列就选对应的模型 ID要用国产模型也在这里切换验证。第三步配置环境变量。Claude Agent SDK 读取的是ANTHROPIC_BASE_URL、ANTHROPIC_AUTH_TOKEN、ANTHROPIC_MODEL这几个变量。Base URL 填https://taotoken.net/apiKey 填你刚创建的Model ID 填你在模型对话页面确认的那个。这里有个关键点Claude Agent SDK 的认证变量分两种。非 Anthropic 原生后端用ANTHROPIC_AUTH_TOKEN原生 Anthropic 用ANTHROPIC_API_KEY。用 TaoToken 统一通道时写ANTHROPIC_AUTH_TOKEN即可。如果你用的是 Claude Code 本身配置方式略有不同。Claude Code 的配置文件在~/.claude/settings.json里面可以写env字段。Claude Code 的接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的 Base URL 和 Key 配置说明。前置准备做完你的目录结构大概是这样finscribe/ ├── .env ├── config.py ├── agent.py ├── skills/ │ ├── competitor_research/ │ ├── financial_data_collection/ │ ├── financial_ratio_calculation/ │ ├── financial_visualization/ │ ├── valuation_modeling/ │ ├── report_writing/ │ └── report_assembly/ └── data/.env文件内容ANTHROPIC_BASE_URLhttps://taotoken.net/api ANTHROPIC_AUTH_TOKEN你的_TaoToken_Key ANTHROPIC_MODEL你的模型ID ANTHROPIC_SMALL_FAST_MODEL你的小模型ID注意ANTHROPIC_SMALL_FAST_MODEL是给轻量任务用的比如摘要、分类。Claude Agent SDK 内部有些辅助调用会走这个模型配一个便宜快速的模型能省成本。如果你需要长期编码或跑 Agent 任务可以考虑 Coding Plan入口是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。它适合高频调用场景比按量计费更划算。前置准备的核心就一句话一个 Base URL一个 Key一个 Model ID三件套配齐Claude Agent SDK 就能通过 TaoToken 统一通道跑起来。3. 可复制配置auth.json、settings.json 与 config.py 三件套这一节给你可以直接复制的配置片段。Claude Agent SDK 的配置分几个层面环境变量、auth.json、settings.json、以及项目内的 config.py。我把它们都列出来你按需取用。先说auth.json。如果你用 Codex 或类似工具认证信息会写在~/.codex/auth.json。格式如下{ OPENAI_API_KEY: 你的_TaoToken_Key, OPENAI_BASE_URL: https://taotoken.net/api }注意Codex 用的是 OpenAI 协议所以变量名是OPENAI_API_KEY和OPENAI_BASE_URL。Claude Agent SDK 用的是 Anthropic 协议变量名不同。两者不要混。再说 Claude Code 的settings.json。路径是~/.claude/settings.json内容{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: 你的_TaoToken_Key, ANTHROPIC_MODEL: 你的模型ID, ANTHROPIC_SMALL_FAST_MODEL: 你的小模型ID } }这个文件是 Claude Code CLI 读取的。如果你用 Claude Agent SDK 写 Python 代码SDK 会继承进程环境变量所以你也可以在 shell 里 export或者用.env文件加载。然后是项目内的config.py。这是我在 FinScribe 里实际用的配置模块负责检测后端、设置环境变量、返回后端信息。# config.py - 模型后端配置模块 import os from dotenv import load_dotenv def configure_model_backend(): 通过 os.environ 直接赋值配置模型后端。 # 先加载 .env 文件让下面的 os.getenv() 能读到配置 load_dotenv() # 检测当前配置的是哪个后端 if os.getenv(TAOTOKEN_API_KEY): backend taotoken elif os.getenv(KIMI_API_KEY): backend kimi elif os.getenv(ALI_API_KEY): backend aliyun else: # 没有配置任何后端使用继承的环境变量 return {backend: environment} # 关键直接赋值而非 setdefault确保覆盖已有变量 for key in [ ANTHROPIC_BASE_URL, ANTHROPIC_MODEL, ANTHROPIC_SMALL_FAST_MODEL, ANTHROPIC_DEFAULT_HAIKU_MODEL, ANTHROPIC_DEFAULT_SONNET_MODEL, ANTHROPIC_DEFAULT_OPUS_MODEL, ]: value os.getenv(key) if value: os.environ[key] value # 直接赋值覆盖一切 # 设置认证 token api_key os.getenv(TAOTOKEN_API_KEY) if api_key: os.environ[ANTHROPIC_AUTH_TOKEN] api_key return {backend: backend} # 模块导入时自动执行配置import config 就会触发副作用 _ACTIVE_BACKEND configure_model_backend()这里有个坑我要单独说。最早我用的是os.environ.setdefault()结果死活不生效。排查半天才发现如果你用了 cc-switch 这类工具或者 IDE 预设了ANTHROPIC_*环境变量setdefault就不会覆盖已有的值。这个函数只在变量不存在时才设置存在则静默跳过。正确做法是直接赋值os.environ[key] value。对应的.env文件TAOTOKEN_API_KEY你的_TaoToken_Key ANTHROPIC_BASE_URLhttps://taotoken.net/api ANTHROPIC_MODEL你的模型ID ANTHROPIC_SMALL_FAST_MODEL你的小模型ID然后是agent.py里的 ClaudeAgentOptions 配置# agent.py - Agent 初始化 from claude_agent_sdk import ClaudeAgentOptions, query import config # 导入即触发配置 options ClaudeAgentOptions( system_promptSYSTEM_PROMPT, skillsall, # 自动扫描 .claude/skills/ 下所有 Skill permission_modeacceptEdits, max_turns200, ) async def run_agent(user_input: str): async for message in query(promptuser_input, optionsoptions): yield messageskillsall会让 SDK 自动扫描工作目录下.claude/skills/文件夹里的所有 Skill。我在项目里用了一个符号链接.claude/skills指向../skills这样 Skill 文件和项目代码放在一起维护方便。如果你用 Cline MCP 或 CC Switch配置方式类似核心都是 Base URL Key Model ID 三件套。Cline 的 MCP 配置写在cline_mcp_settings.jsonCC Switch 的配置写在它自己的配置文件里。不管哪个工具只要支持 Anthropic 协议就把 Base URL 指向https://taotoken.net/apiKey 填 TaoToken 的 KeyModel ID 填你选的模型。三件套配齐后验证配置是否生效可以跑一个最小请求# test_config.py import os import config from claude_agent_sdk import query, ClaudeAgentOptions print(Base URL:, os.environ.get(ANTHROPIC_BASE_URL)) print(Model:, os.environ.get(ANTHROPIC_MODEL)) print(Token set:, bool(os.environ.get(ANTHROPIC_AUTH_TOKEN))) async def test(): options ClaudeAgentOptions(max_turns1) async for msg in query(prompt回复 OK, optionsoptions): print(msg) import asyncio asyncio.run(test())如果输出里有OK说明配置生效。如果报 401说明 Key 不对如果报连接错误说明 Base URL 不对。4. 验证请求与成功结果MCP 工具调用与 Skills 编排的完整链路配置配好之后下一步是验证 MCP 工具调用和 Skills 编排能不能跑通。这一节我给你完整的工具定义、Skill 结构、系统提示词以及跑通后的成功结果。先说 MCP 工具。Claude Agent SDK 通过tool装饰器和create_sdk_mcp_server把自定义工具注册为 MCP Server。工具设计有一个原则少而精。Agent 的工具集膨胀会挤占上下文窗口影响推理质量。只有独立、通用的工具才定义为 MCP 工具需要配合特定业务流程的作为 Skills 中的脚本存在。FinScribe 最终只保留了 6 个 MCP 工具前 5 个是 AKShare 的金融数据接口第 6 个是博查网络搜索。# tools/akshare_tools.py from claude_agent_sdk import tool import akshare as ak import os import time def retry_call(func, retries: int 3, sleep_seconds: float 0.5): Wrap a function call with automatic retry on failure. last_exception None for attempt in range(retries): try: return func() except Exception as e: last_exception e if attempt retries - 1: time.sleep(sleep_seconds * (attempt 1)) raise last_exception tool( get_balance_sheet, 获取沪深A股公司的资产负债表并保存到文件中。, stock_code 需带市场前缀如 SH600600。, {stock_code: str, year: str}, ) async def get_balance_sheet(args: dict) - dict: Fetch balance sheet for a given stock code and year. stock_code args.get(stock_code, SH600600) year args.get(year, 2025) try: prefixed_code normalize_a_share_code(stock_code) clean_code clean_a_share_code(stock_code) df retry_call( lambda: ak.stock_balance_sheet_by_yearly_em(symbolprefixed_code) ) df filter_annual_report(df, year, REPORT_DATE) if df is None or df.empty: return { content: [ {type: text, text: fNo data for {prefixed_code} in {year}} ] } data_dir _get_data_dir() filepath os.path.join(data_dir, f{clean_code}_{year}_资产负债表.csv) save_dataframe(df, filepath) return { content: [ {type: text, text: fBalance sheet saved to: {filepath}} ] } except Exception as e: return {content: [{type: text, text: fFailed: {e}}]}这段代码有几个设计要点。工具只返回文件路径不返回完整数据。如果工具直接返回完整的 DataFrame 内容150 个字段的表格会立刻塞满上下文。返回路径之后Agent 需要的时候可以用 Read 工具去读文件Tool Result Budget 机制会自动管理大块数据的磁盘转存。异常处理不抛异常。tool函数如果抛异常会中断整个 Agent Loop。所以所有异常都在函数内部 catch返回一段错误文本让 Agent 自己判断该怎么处理。Agent 看到「Failed to get balance sheet」之后可能会换个股票代码重试也可能跳过这一步继续后面的流程。retry 机制是标配。AKShare 走的是公开接口网络波动是家常便饭。retry_call默认重试 3 次延迟递增。再说 Skills。MCP 工具解决的是「怎么拿到数据」的问题但研报生成的完整流程从数据采集到最终成稿中间有很多标准化的步骤。这些步骤如果全写进 system_prompt提示词会膨胀到影响 Agent 的推理质量。Claude Code 官方的做法是 Skills 机制。Skills 采用渐进式加载Agent 平时不需要看到 Skill 的完整内容只有在需要执行某个任务时对应的 Skill 才会被动态载入上下文。FinScribe 沉淀了 7 个标准化 Skillcompetitor_research、financial_data_collection、financial_ratio_calculation、financial_visualization、valuation_modeling、report_writing、report_assembly。每个 Skill 的目录结构skills/ ├── financial_ratio_calculation/ │ ├── SKILL.md │ ├── requirements.txt │ └── scripts/ │ └── calculate_ratios.pySKILL.md的标签部分是 Agent 决定是否调用这个 Skill 的依据--- name: financial-ratio-calculation description: | 从中国 A 股上市公司三大报表计算关键财务比率。 当用户提到财务指标计算、毛利率、净利率、ROE、资产负债率、 流动比率、速动比率等任何与财务比率计算相关的需求时使用此 skill。 ---然后是系统提示词。LangGraph 的核心是显式状态图你定义节点、边、条件转移。但金融研报生成其实是一个高度固定的线性工作流不需要那么重的编排。Claude Agent SDK 的做法是用一段系统提示词把流程写清楚交给 Agent 自主调度。SYSTEM_PROMPT You are a financial research report project coordinator. The user will provide a stock code, company name, market type, and analysis years. Your workflow (execute ALL phases in order, in a SINGLE session): ## Phase 1: Data Collection - Call the competitor_research skill to research competitors and industry - Call the financial_data_collection skill to collect financial statements ## Phase 2: Metric Calculation - Call the financial_ratio_calculation skill to calculate financial ratios ## Phase 3: Analysis Visualization - Call the financial_visualization skill to generate trend charts - Call the valuation_modeling skill to generate valuation reports ## Phase 4: Report Writing - Call the report_writing skill (implicitly follow its writing guidelines) - Call the report_assembly skill to assemble the final research report ## Critical Execution Rules - Execute ALL four phases in ONE session. Do NOT stop after Phase 1. - Do NOT end your turn after spawning background tasks. Wait for each task to finish, verify its output files exist, THEN proceed to the next phase. - Only end your turn AFTER the final report is assembled in Phase 4. 这段提示词看着简单但有几个细节我踩了坑才搞明白。「Do NOT stop after Phase 1」这句是血泪教训。最早的版本没有这句Agent 在 Phase 1 启动了两个后台任务然后说了一句「请稍候任务完成后会自动进入下一阶段」就结束了当前 turn。结果后台任务跑完了Agent 也不见了Phase 2 到 Phase 4 压根没执行。加了一句「不要在启动后台任务后结束 turn等每个任务完成并验证产物存在后再进入下一阶段」之后Agent 就老老实实地在原地等着了。「verify its output files exist」也很关键。Agent 有时候会「以为」某个任务完成了但实际上脚本执行失败了。让它在每个阶段结束后用 Read/Glob 工具检查产物文件是否存在能大幅减少这类幻觉。跑起来之后我在前端输入「青岛啤酒 SH600600分析年份 2024、2025」点开始。WebSocket 实时推送的第一条消息是 Agent 的初始化信息[System] Agent initialized | Model: doubao-seed-2.0-pro | Tools: 34 loaded34 个工具加载完毕Agent 开始按系统提示词的四阶段流程执行。Phase 1Agent 先调了 competitor_research Skill通过博查搜索识别出了青岛啤酒的主要竞争对手燕京啤酒、珠江啤酒这些。然后调 financial_data_collection Skill通过 MCP 工具批量采集了青岛啤酒和竞争对手的三年财务报表。我看着 data 目录下的 CSV 文件一个个冒出来600600_2024_资产负债表.csv 600600_2024_利润表.csv 600600_2024_现金流量表.csv 600600_2024_财务指标.csv 000729_2024_资产负债表.csv 000729_2024_利润表.csv ...最后数了一下56 个 CSV 文件。Phase 2Agent 调了 financial_ratio_calculation 的脚本读取这些 CSV算出了毛利率、净利率、ROE、资产负债率等指标。Phase 3Agent 生成了趋势图和对比图还做了估值测算。Phase 4Agent 按照研报写作规范组装了一份完整的 Markdown 研报保存到data/final_output/目录。整个过程Agent 执行了 300 多条消息中间有大量的工具调用、文件读写、脚本执行。得益于 SDK 的五级上下文压缩从头到尾没有出现 Token 溢出错误。如果还是用之前的 LangGraph 方案光 56 个 CSV 的原始数据就已经超出上下文窗口了。迁移前后的对比数据指标LangGraph 方案Claude Agent SDK 方案任务成功率约 40%约 95%平均超时重试次数8 次/任务1 次/任务上下文溢出错误频繁0单任务耗时不稳定常中断约 20 分钟人工干预需要不需要这个对比是我在相同输入下跑了 20 次任务统计出来的。LangGraph 方案的成功率低主要是因为上下文溢出导致 Agent Loop 中断或者节点间文件传递出错。Claude Agent SDK 方案的五级压缩策略从 Tool Result Budget 到 Autocompact层层递进你不需要知道每一级的实现细节只需要信任它会在合适的时候触发合适的策略。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 报错对照这一节我把迁移过程中遇到的真实报错和排查方法列出来你遇到类似问题时可以对照。报错一401 UnauthorizedError: 401 Unauthorized {error: {message: Invalid API key, type: authentication_error}}这个报错说明 Key 不对。排查步骤第一检查.env里的TAOTOKEN_API_KEY是否复制完整有没有多余空格。第二检查config.py里是否把 Key 赋值给了ANTHROPIC_AUTH_TOKEN。第三检查是否有其他工具比如 cc-switch覆盖了环境变量。第四确认你用的是ANTHROPIC_AUTH_TOKEN而不是ANTHROPIC_API_KEY非 Anthropic 原生后端用前者。如果确认 Key 没问题还是 401可能是 Base URL 写错了。检查ANTHROPIC_BASE_URL是否是https://taotoken.net/api注意不要带 UTM 参数不要多写斜杠。报错二local proxy failedError: local proxy failed: connection refused这个报错通常出现在你用了本地代理工具的情况下。排查步骤第一检查是否有本地代理进程在运行如果有确认它的端口和配置。第二检查ANTHROPIC_BASE_URL是否被代理工具改写。第三如果你不需要代理直接连 TaoToken 的 API 端点即可把 Base URL 设为https://taotoken.net/api。这个报错的本质是网络链路问题。Claude Agent SDK 发起请求时会走系统代理设置。如果你的代理配置和 Base URL 冲突就会报这个错。最简单的排查方法是先用 curl 测试curl -X POST https://taotoken.net/api/v1/messages \ -H Content-Type: application/json \ -H x-api-key: 你的_TaoToken_Key \ -H anthropic-version: 2023-06-01 \ -d {model: 你的模型ID, max_tokens: 10, messages: [{role: user, content: hi}]}如果 curl 能通说明网络没问题问题在 SDK 配置。如果 curl 不通说明网络链路有问题。报错三reading choicesError: reading choices: unexpected end of JSON input这个报错通常出现在流式响应解析时。排查步骤第一检查模型 ID 是否正确有些模型不支持流式输出。第二检查max_tokens是否设置得太小导致响应被截断。第三检查 SDK 版本是否和模型兼容。这个报错的本质是响应格式不符合预期。Claude Agent SDK 期望的是 Anthropic 协议的流式响应格式如果你的模型后端返回的格式不同就会解析失败。解决方法是确认你用的模型支持 Anthropic 协议或者用 TaoToken 统一通道它会做协议转换。报错四OAuth 相关错误Error: OAuth token expired Error: invalid_grant这个报错通常出现在你用了 OAuth 认证而不是 API Key 的情况下。排查步骤第一确认你用的是 API Key 而不是 OAuth。第二如果必须用 OAuth检查 token 是否过期重新获取。第三检查auth.json或settings.json里的认证配置。Claude Agent SDK 支持两种认证方式API Key 和 OAuth。用 TaoToken 统一通道时用 API Key 即可不需要 OAuth。如果你看到 OAuth 报错说明配置里混入了 OAuth 相关设置把它删掉。报错五模型 ID 不存在Error: model not found: xxx这个报错说明你填的模型 ID 不对。排查步骤第一去 TaoToken 的模型对话页面确认可用的模型 ID。第二检查.env里的ANTHROPIC_MODEL是否和确认的一致。第三注意大小写有些模型 ID 是大小写敏感的。报错六Skills 未加载Warning: no skills found in .claude/skills/这个报错说明 Skill 目录结构不对。排查步骤第一确认.claude/skills/目录存在。第二确认每个 Skill 子目录下有SKILL.md。第三确认SKILL.md的 frontmatter 格式正确有name和description字段。第四确认ClaudeAgentOptions里设置了skillsall。如果你用符号链接确认链接指向正确ln -s ../skills .claude/skills ls -la .claude/skills/报错七工具调用参数错误Error: tool call failed: missing required parameter stock_code这个报错说明 Agent 生成的工具调用参数不完整。排查步骤第一检查tool装饰器里的参数 schema 是否声明正确。第二检查工具描述是否清晰Agent 需要根据描述生成参数。第三在系统提示词里补充参数说明。工具描述写得好不好直接决定 Agent 能不能正确调用。我的经验是参数说明直接写在描述里比如「stock_code 需带市场前缀如 SH600600」这样 Agent 生成参数时就有参考。报错八上下文溢出Error: context length exceeded这个报错说明上下文管理出了问题。排查步骤第一确认工具返回的是文件路径而不是完整数据。第二确认没有把大块数据塞进 system_prompt。第三确认 SDK 版本支持五级压缩策略。如果还是溢出可以手动调小max_turns或者把部分 Skill 拆分为独立 SubAgent 执行。7 个 Skill 挤在一个 Agent 里确实有些臃肿后续可以考虑拆分。排查完这些报错你的 Agent 应该能稳定跑起来了。如果还有问题可以去 TaoToken 的接入文档页面查更多配置说明地址是 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。6. 从 LangGraph 迁移到 Claude Agent SDK 的工程经验与后续优化方向迁移完成后我最大的感受不是「Claude Agent SDK 多么厉害」而是「不要自研基础设施」。之前在 LangGraph 里手搓文件传递机制的时候我花了很多时间在上下文管理这个事情上。但上下文管理是 SDK 的职责不是业务代码的职责。你自研得再好也不可能比官方团队做得更完善因为人家有完整的工程团队在持续迭代。Claude Agent SDK 的五级压缩策略从 Tool Result Budget 到 Autocompact层层递进。你不需要知道每一级的实现细节你只需要信任它会在合适的时候触发合适的策略。这种感觉就好像你以前每天手动管理服务器的内存分配现在换了个自动垃圾回收的语言。你知道底层在干什么但你不需要操心了。Skills 机制的设计也很聪明。把「怎么做一件事」的方法论从上下文中剥离出来按需加载。这比把所有指令都塞进 system_prompt 要高效得多。7 个 Skill 挤在一个 Agent 里确实有些臃肿后续可能需要考虑把部分 Skill 拆分为独立 SubAgent 执行。财务指标计算这块我把公式都固化在 Skill 的脚本里不依赖 LLM 的推理能力。Agent 只需要调脚本拿到结果在研报里解读就行了。这就是「将执行逻辑代码化、将编排逻辑 Skill 化」的好处消除了模型在简单计算上的推理不确定性。比如杜邦分析把 ROE 分解成三个驱动因子ROE 净利率 × 总资产周转率 × 权益乘数其中总资产周转率 营业收入 / 平均总资产衡量资产的使用效率。这三个因子分别代表盈利能力、运营效率和财务杠杆。杜邦分析的精妙之处在于它让你看清 ROE 的提升到底是来自真正的经营改善还是来自加杠杆。同样的 ROE 数字背后风险水平可能完全不同。这些计算全部在脚本里完成Agent 只负责解读。后续优化方向有几个。第一把 7 个 Skill 拆分成 SubAgent每个 SubAgent 负责一个阶段通过消息传递协调。这样单个 Agent 的上下文压力更小。第二给工具调用加缓存同一家公司的报表数据不需要重复采集。第三给研报组装加模板校验确保输出格式一致。第四加监控和日志记录每个阶段的耗时和成功率方便定位瓶颈。如果你也想搭类似的金融研报 Agent我的建议是先从最小可用版本开始。不要一上来就搞 7 个 Skill先跑通「采集数据 计算指标 生成报告」三步确认链路稳定后再加功能。配置方面用 TaoToken 统一 Key 接入一个 Base URL 管多个模型后端切换模型只改环境变量不用改业务代码。代码已经开源了GitHub 仓库地址是 https://github.com/xiaoyesoso/finscribe 有兴趣的可以拉下来跑跑看。模型后端支持 Anthropic、Kimi、阿里通义、SiliconFlow 四种按 README 里的说明配置即可。如果你用 TaoToken 统一通道把 Base URL 设为https://taotoken.net/apiKey 填你的 TaoToken KeyModel ID 填你选的模型三件套配齐就能跑。说真的当看到 Agent 自主完成从数据采集到研报组装的全流程中间没有任何人工干预那一刻我还是挺震撼的。不是因为技术多么复杂是因为这套流程以前需要一个分析师干好几天的工作现在一个 Agent 二十分钟就跑完了。