
简介太平洋证券《AI投研应用系列之四OpenClaw投研实践——从部署到应用》是一份面向金融投研人员与AI应用开发者的实战报告解决开源框架OpenClaw从环境搭建到投研场景落地的关键问题。报告对比纯本地、WSL2云端模型、纯云端三种部署方案的适用场景与优劣并以WSL2云端模型为例演示完整配置流程数据源部分详解Tushare、AkShare等金融数据Skill的接入方法。应用实践涵盖持仓监控报告推送、量化策略回测与行业动量拥挤度轮动测试、前沿因子挖掘三大场景也提示了大模型生成脚本可能存在的偏差与数据安全风险。整份资料为1个PDF文件大小约2MB便于移动端或桌面端阅读目前已有100人学习下载。阅读后可获得OpenClaw部署选型思路、飞书集成与常用指令、金融数据源配置及因子挖掘Agent构建方法适合希望将AI智能体引入日常投研工作流的技术型研究员。1. 从「研报标题」到「能跑的链路」OpenClaw投研实践在解决什么券商研究所的日常工作里公告解读、纪要整理、数据交叉验证这三件事吃掉了一半以上的人工工时。OpenClaw投研实践想解决的正是这三件事的自动化把开源Agent框架OpenClaw部署到本地把投研规则拆成可复用的技能再让它按固定流程产出纪要。这个标题挂在「AI投研应用系列」下面指向的不是又一个聊天机器人而是一条从部署到应用的完整链路。这套实践适合三类人被公告淹没的基本面研究员、想把AI接入内部系统的金融科技工程师以及所有受够了「大模型只会聊天」的团队负责人。读完你会知道OpenClaw落地的最短路径是什么参数在哪里调以及哪些坑值得在动手前先避开。2. 部署 OpenClaw 前的选型本地模型还是 API为什么我从 Ollama 起步2.1 投研场景下先定推理后端数据边界决定模型放哪部署 OpenClaw 的第一步不是装框架而是定推理后端。投研材料里有大量未公开信息、内部纪要、尚未发布的评级草稿这些内容不适合逐条送到外部 API 去跑。所以我建议的默认组合是OpenClaw 本体跑在 Docker 里推理后端用 Ollama 拉起一个本地大语言模型API 只留给那些确实不敏感、且本地模型搞不定的场景。推理后端适合场景主要代价投研推荐度Ollama 本地模型内部纪要、未公开公告、离线环境7B 模型能力有限需要 16GB 以上内存高OpenAI 兼容 API公开研报摘要、英文财报翻译数据出域有合规争议按量计费中内部托管的模型服务已有 GPU 集群或公司统一模型平台需要运维接口标准不一高有条件时这里要说明白OpenClaw 本身不绑定某个模型它只负责把任务路由给后端。常见做法是在配置文件里维护一个模型列表把「读公告」路由到本地 14B 模型把「润色标题」路由到更小更快的模型。部署阶段不用一次配齐先把本地链路打通后面随时可以加。2.2 环境检查与 Docker Compose 最小部署我一般会在干净目录下建openclaw-deploy/先跑一遍环境检查避免后面排错排到想砸电脑。# 1. 检查基础环境Python、Docker、以及 Ollama 是否已就绪 python3 --version # 需要 3.10 或更高部分 Agent 组件依赖新语法 docker version # 需要 20.10Compose V2 已集成在 docker 命令里 ollama list # 如果还没装 ollama先到官网装或者用容器跑 # 2. 拉取并启动 Ollama 服务容器如果本机没装 docker run -d --name ollama \ -v ollama_data:/root/.ollama \ -p 11434:11434 \ ollama/ollama:latest # 3. 拉一个适合中文公告解析的模型推荐 qwen2.5:14b-instruct-q8_0 ollama pull qwen2.5:14b-instruct-q8_0参数说明11434是 Ollama 的默认 API 端口OpenClaw 接的就是这个地址q8_0是量化精度比默认的 Q4 保留了更多数值能力投研场景里经常要从公告里抠出数字Q8 的幻觉率明显低一些。如果你的机器只有 16GB 内存可以退到 7B 模型但后面避坑章节会提到长公告会被截断。接着写docker-compose.yml把 OpenClaw 和 Ollama 放到同一个网络里这样服务名可以直接当主机名用。services: openclaw: image: 你的OpenClaw镜像名:latest # 从项目仓库拉取不同发行渠道镜像名不同 container_name: openclaw ports: - 8080:8080 # 管理面板 / API 入口 volumes: - ./data:/app/data # 保存会话记忆、任务状态 - ./skills:/app/skills # 挂载技能目录改代码不用重启容器 environment: - OPENCLAW_MODEL_ROUTERlocal # 先只走本地模型避免误调外部 API - OPENCLAW_OLLAMA_BASE_URLhttp://ollama:11434 depends_on: - ollama restart: unless-stopped ollama: image: ollama/ollama:latest container_name: ollama volumes: - ollama_data:/root/.ollama ports: - 11434:11434 restart: unless-stopped volumes: ollama_data:这个 Compose 文件的关键点有两个一是depends_on保证 OpenClaw 启动时 Ollama 已经在跑但要注意它只保证容器启动了不保证模型加载完成二是我把./skills单独挂出来因为投研实践的日常就是改技能脚本挂载目录能省掉频繁 docker restart 的麻烦。镜像名我特意写成占位符因为 OpenClaw 在不同时期、不同渠道发布的镜像仓库不统一以你实际拉取的为准。2.3 启动后先做连通性验证不要急着配技能部署最忌讳一上来就配一堆技能结果模型都没通。我先用一条 curl 验证 Ollama 能响应再用一个最简对话验证 OpenClaw 能把请求转发出去。# 进入部署目录后台拉起整套服务 docker compose up -d # 等 10 秒后检查所有容器状态 docker compose ps # 验证 Ollama 本体是否可对话 curl http://localhost:11434/api/chat \ -d {model:qwen2.5:14b-instruct-q8_0,messages:[{role:user,content:只回复两个字正常}],stream:false} # 验证 OpenClaw 管理端口是否开了 curl http://localhost:8080/health如果 Ollama 容器起来了但 curl 超时多半是模型还在加载。14B 的 Q8 权重首次加载要几十秒不是故障。/health端点如果返回 200说明 OpenClaw 主服务正常部署闭环就完成了——到这一步你已经有了一个「本地模型 Agent 框架」的最小可运行底座接下来才进入真正花时间的部分把投研能力装进去。3. 把投研能力拆成 Skill 体系从目录结构到提示词模板3.1 Skill 的标准结构声明文件加执行脚本一个技能一个目录OpenClaw 这类 Agent 框架的可扩展点几乎都落在 Skill 上。我把它理解成「给 Agent 装插件」每个技能一个目录目录里放一个声明文件描述什么条件下触发、一个脚本写具体执行逻辑、一段提示词告诉模型怎么干。这个结构的好处是投研团队的工程师可以写脚本研究员可以专心改提示词互不干扰。skills/ └── announcement_extract/ # 公告要点提取技能 ├── manifest.yaml # 触发条件和参数声明 ├── run.py # 执行入口读 PDF、调模型、出 JSON └── prompt.md # 系统提示词模板研究员主要改这里这个目录挂在 OpenClaw 的skills/挂载点下。放进去之后在管理端重载技能列表新技能就能被对话触发了。我踩过的第一个坑就是改完run.py忘记重载导致怎么调都不生效——后面避坑章节会细说。实际落地时一个投研团队一般会维护 8 到 15 个这样的技能目录从「新股招股书速读」到「年报异常项检查」都独立成技能而不是堆在一个大脚本里。3.2 一个「公告要点提取」Skill 的完整配置与参数manifest.yaml负责告诉 OpenClaw这个技能在什么时候被触发以及调用时用哪些参数。name: announcement_extract version: 1.0 description: 从上市公司公告 PDF 中提取关键要素输出结构化结果。 trigger: type: file_watch # 监听指定目录下新出现的 PDF 文件 watched_dir: /app/data/inbox/announcements patterns: [*.pdf] model: provider: ollama name: qwen2.5:14b-instruct-q8_0 temperature: 0.1 # 投研场景尽量低减少创造性发挥 top_p: 0.9 max_tokens: 2048 output: format: json schema_file: schema/announcement_schema.json参数说明里最值得较真的是temperature。写公告要点提取不是写作文0.1已经算高了有些团队直接设为 0。一旦设成 0.7 以上模型会开始「润色」原文把公告里没写的意思补进去这在投研场景属于重大翻车。max_tokens设 2048 是因为公告摘要的输出一般不会超过这个长度设太大反而让模型啰嗦。run.py是实际干活的部分。它的任务链是定位新 PDF → 提取文本 → 按块送入模型 → 把结果写回指定目录。核心代码如下。#!/usr/bin/env python3 import json import re from pathlib import Path import urllib.request OLLAMA_URL http://localhost:11434/api/chat MODEL_NAME qwen2.5:14b-instruct-q8_0 def extract_text_from_pdf(path: Path) - str: 从 PDF 提取文本。 普通文本型 PDF 直接用 pdftotext 类工具 扫描件先走 OCR否则后面模型看到的是一堆乱码。 # 省略具体 PDF 解析库调用项目里按实际依赖引入 return raw_text def chat_once(prompt: str, system: str, temperature: float) - str: payload { model: MODEL_NAME, stream: False, temperature: temperature, messages: [ {role: system, content: system}, {role: user, content: prompt}, ], } req urllib.request.Request( OLLAMA_URL, datajson.dumps(payload).encode(utf-8), headers{Content-Type: application/json}, ) with urllib.request.urlopen(req, timeout120) as resp: return json.loads(resp.read().decode(utf-8))[message][content] def main(): inbox Path(/app/data/inbox/announcements) out_dir Path(/app/data/out/announcements) out_dir.mkdir(parentsTrue, exist_okTrue) for pdf_file in sorted(inbox.glob(*.pdf)): text extract_text_from_pdf(pdf_file) system_prompt Path(/app/skills/announcement_extract/prompt.md).read_text() result chat_once(text[:3000], system_prompt, temperature0.1) out_path out_dir / f{pdf_file.stem}.json out_path.write_text(result, encodingutf-8) pdf_file.rename(pdf_file.with_suffix(.pdf.done)) # 处理完改名避免重复消费 if __name__ __main__: main()这段脚本的边界我特意写得保守只取text[:3000]个字符。原因是本地 14B 模型的上下文窗口有限把整份 30 页年报塞进去前面的内容会被截断模型会把后文的内容张冠李戴到前面的字段上。更稳的做法是分章节抽取再汇总但那是进阶话题。.done后缀是个小技巧防止同一份文件在下次运行时被重复解析。这里敲个黑板timeout120不是玄学。本地模型在解析长文本时经常要跑 30 秒以上如果设成默认的 10 秒OpenClaw 会报超时这个技能的调用会一败涂地。在投研场景宁可单次调用慢一点也要把超时给足。3.3 提示词模板里藏着投研领域知识研究员最该关注的文件其实是prompt.md因为投研知识都沉淀在提示词里。一个合格的公告提取提示词至少包含四段角色定义、任务步骤、输出字段约束、反例警告。你是一名券商研究所的公告分析助理。 对给定公告原文按以下步骤处理 1. 先判断公告类型业绩预告、对外投资、股权变动、诉讼仲裁、其他。 2. 提取字段公司名称、公告日期、涉及金额、交易对方、对当期利润的影响。 3. 标注风险点是否涉及关联交易、是否需股东大会审议、是否存在不确定性表述。 4. 所有数值必须来自原文禁止估算找不到的字段填 null不要编。 输出严格按如下 JSON 结构 {announce_type: , company: , amount: , counterparty: , risk_points: [], source_sentences: []}为什么要把「找不到就填 null」写进提示词因为模型在自由发挥时倾向于「补全」缺失信息这是它在对话任务里养成的坏习惯但在投研任务里会直接导致数据污染。source_sentences字段是交叉验证用的后面工作流章节会讲到。技能配好之后OpenClaw 就不再是个聊天框了——你往inbox/announcements丢一份 PDF它自动吐出一个 JSON。这就是从部署到应用的关键跃迁。4. 投研工作流落地把公告、纪要和交叉验证串成一条线4.1 三个高频场景的工作流设计技能只是零件工作流才是产线。我建议投研团队第一个月只盯三个场景公告解读、纪要整理、指标交叉验证。每个场景定义清楚输入、处理和输出不要一上来就想做自动写深度研报——那是半年后的事。场景输入处理方式输出产物公告解读交易所公告 PDF公告要点提取 → 风险点标注结构化 JSON 摘要 Markdown纪要整理会议录音转写文本角色分离 → 观点提取 → 待办识别纪要 MD 待办清单交叉验证模型初版输出再次送模型逐句核对原文引用带置信度标记的复核报告这三个场景的共同特征是结果必须可复核。投研输出的每条结论都要能回溯到原文否则模型再聪明也不敢用。这就是为什么我在提示词里强制要求source_sentences字段——它让每一轮输出都能被快速验证。这里顺便说一个我对「多AI协作」的理解协作不是多个模型同时回答一个问题而是把任务拆给不同角色的 Skill。OpenClaw 里一个「角色」就是一套独立的 Skill 加提示词读公告的、写纪要的、查数据的各管一段通过文件目录传递结果。多AI协作的价值是让每个环节都足够简单简单到不容易出错。4.2 串起「抓取到纪要」的完整脚本下面这段代码演示的是把多个技能串成一条流水线监听公告目录 → 运行提取 Skill → 生成摘要 Markdown → 触发复核 Skill。这是投研实践中最常见的链路骨架。#!/usr/bin/env python3 from pathlib import Path import json, time import subprocess DATA_DIR Path(/app/data) INBOX DATA_DIR / inbox/announcements OUT_DIR DATA_DIR / out/announcements REVIEW_DIR DATA_DIR / out/reviewed def watch_and_process(): processed set() while True: for pdf in INBOX.glob(*.pdf): if pdf.name in processed: continue # 步骤 1调用公告提取技能调 run.py 或直接 import subprocess.run([python3, /app/skills/announcement_extract/run.py], checkTrue) # 步骤 2读取提取结果组织成纪要片段 json_file OUT_DIR / f{pdf.stem}.json if json_file.exists(): data json.loads(json_file.read_text(encodingutf-8)) md f## {data[company]}公告摘要\n\n类型{data[announce_type]}\n金额{data[amount]}\n风险点{; .join(data[risk_points])}\n引用{data[source_sentences]} (OUT_DIR / f{pdf.stem}.md).write_text(md, encodingutf-8) # 步骤 3触发复核检查上面的纪要是否有原文支持 subprocess.run([python3, /app/skills/review/run.py, pdf.stem], checkTrue) processed.add(pdf.name) time.sleep(30) # 轮询间隔 30 秒避免高频扫目录 if __name__ __main__: watch_and_process()这段代码展示了两个必要的工程习惯一是用processed集合记住已处理文件名重启进程后重新遍历也不会重复执行二是步骤 2 里直接拼的是「结构化摘要」不让模型自由发挥写长文。投研纪要的定位是准确而非华丽一句话把金额、主体、风险点说清楚比三段落修辞有用得多。time.sleep(30)这个参数值得说两句。投研公告一般不会密集到秒级到达30 秒轮询足够而且给模型留出了处理上一份文件的时间。如果你把轮询改到 3 秒很可能会因为上一轮还没跑完、下一轮又开始而重复处理同一批文件。4.3 交叉验证让模型给自己挑错只跑一轮就出结果的应用在投研场景基本都会翻车。我采用的折中方案是「二次复核」第一轮正常抽取第二轮把第一轮输出的每条结论连同原文片段一起送模型问它「这句话有没有原文依据数值和原文是否一致」。这一招能把幻觉率压下来一大截代价只是耗时翻倍。复核 Skill 的提示词里我会写一层硬约束凡是没有原文直接支持的结论必须标记为low_confidence不允许模型「推断」出结论。这一步做好之后投研团队拿到 Agent 输出时能一眼看到哪些可以直接用、哪些需要人工确认而不是拿着整份报告重新读一遍——那样自动化就失去意义了。5. OpenClaw 投研部署常见问题排查现象、原因、解决5.1 Windows Companion 连不上 OpenClaw 主服务现象在 Windows 机器上启动 Companion 客户端界面一直显示连接失败但 Docker 里的 OpenClaw 明明在跑浏览器访问localhost:8080也是通的。原因有三个层次最常见的是防火墙拦了 8080 端口其次是 Companion 和服务端用的鉴权 token 不一致还有一种容易被忽略的情况是 Windows 的「智能应用控制」拦截了未签名的 Companion 安装包导致程序根本没完整启动。热词里能搜到大量这类求助基本都逃不开这三个原因。解决先到 Windows 防火墙里放行 8080 入站规则再把 OpenClaw 管理端生成的访问 token 复制到 Companion 配置里。第三类问题直接改用 Docker Desktop 跑一份 Companion 服务端绕开智能应用控制的拦截。如果你发现 Companion 进程没起来打开事件查看器看一眼应用日志通常会有「已阻止可能不安全的应用」之类记录那就确认是第三个原因。5.2 本地模型答非所问长公告被截断和量化精度现象用 7B 模型跑业绩预告提取输出的公司名称是对的但金额经常变成公告里的另一个数字甚至出现原文不存在的数字。原因第一个是上下文窗口——整份公告塞进去后被截断模型只看到后半段自然只能从后半段找数字第二个是量化精度Q4 量化下的小模型对数字敏感度下降可能把 3.2 亿看成 1.2 亿。解决先把输入从「全文塞入」改成「分章节抽取」这是最有效的第二把量化等级提到 Q8或者直接换 14B 模型第三把temperature调到 0.1 以下。我的经验是这三步按顺序做做完第一步能解决一半问题三步全做完报告里的数字级错误基本绝迹。5.3 扫描版公告 PDF 解析乱码现象模型输出的 JSONcompany字段全是乱码或者提取出来的「涉诉金额」和原文对不上。原因扫描版 PDF 没有文本层直接用pdftotext提取出来的是空壳甚至乱码。金融公告里大量历史文件是扫描件这条踩坑率极高。解决在 Skill 的extract_text_from_pdf里加 OCR 步骤。常见做法是调用 OCR 服务识别之后再送模型。注意 OCR 后文本会带识别错误所以在提示词里要写「遇到明显不合理的数值必须标注存疑」。这里稍微啰嗦一句OCR 不是万能的表格类公告的识别经常串行我一般要求在输出 JSON 里再加一个ocr_warning字段提醒下游人工确认。5.4 多任务并发时上下文串扰现象同时跑「公告提取」和「纪要整理」两个任务纪要里突然出现了公告里的公司名两边任务的结果混在一起。原因两个任务共享了同一份会话历史目录后启动的任务读到了前面的上下文。这不是模型问题是状态管理问题。解决每个任务建独立的工作目录任务结束后归档。具体做法是在调用脚本时传入任务 ID把临时目录、输出目录、会话记忆目录全部按任务 ID 隔离。这个改动很小但能根治串扰。我见过有团队在 Compose 文件里给每个任务起一个容器那是把问题复杂化了——目录隔离就够了。6. 进阶给投研 Agent 建一套评测集把玄学变成可回归的指标6.1 从真实纪要里攒 20 到 50 条评测样本调模型、调提示词最怕的就是凭感觉。我今天改了一下 Prompt感觉输出好了一点但好多少、是否在其他样本上更好根本说不清。我的做法是从过去三个月的公告里挑 30 条典型样本每一条手工标注标准答案公告类型、公司名称、关键金额、风险点。这个工作看起来枯燥但它是整个投研 Agent 落地中最值得投入的一步。评测集建好之后每改一次prompt.md每换一个模型都可以跑一遍全量评测。跑完看三个硬指标字段抽取准确率、原文引用一致率、JSON 格式合法率。字段准确率回答「有没有抽对」引用一致率回答「有没有编造」格式合法率回答「下游流程能不能跑通」。其中最容易翻车的是引用一致率——模型经常能给出漂亮的结果但引用原文时张冠李戴这是投研场景最危险的错误。6.2 用脚本批量回归盯住三个硬指标评测脚本不复杂核心就是把每条样本输入 Agent拿到输出后和标准答案比对。我的工作目录里放一个eval.py每次改完都跑一遍用数字说话。# 跑评测集自动汇总三个指标 python3 eval/eval.py \ --samples eval/samples.json \ --model qwen2.5:14b-instruct-q8_0 \ --out eval/report.md脚本内部做的事很简单逐条调用 OpenClaw 的技能接口把输出的 JSON 字段和标注答案做精确比对数值字段要求完全一致。一开始准确率可能只有 60% 上下不要慌这是正常起点。把失败的样本列出来你会发现基本都是两类提示词没约束住的格式问题和抽取字段定义不清晰的问题。修提示词再跑反复迭代到 90% 以上这之后投入产出的性价比就会明显下降适可而止。最后说一个我的习惯所有实验都固定一个「基准配置」当对照组改参数永远只动一个变量。OpenClaw 这类 Agent 框架的可调点太多模型、量化、温度、提示词、上下文长度、技能目录结构每个都能改变输出。我以前吃过亏一次改了三个变量结果输出变坏了也不知道是谁干的只能全部回滚重来——那是时间的大出血也是我后来坚持评测集回归的直接原因。把流程钉死再动参数这句话值得印在团队墙上。希望帮到你。本文还有配套的精品资源点击获取