1. 金融场景下的智能体工程化落地思路1.1 为什么金融行业对智能体有真实需求金融行业每天要处理的东西说白了就是三件事数据、规则、沟通。数据来自行情、财报、交易流水、风控日志规则来自监管口径、内部合规、产品条款沟通则是客户经理、研究员、运营、客服之间的信息流转。这三件事有一个共同点——重复度高、容错率低、对时效敏感。我最早接触金融类项目是在做数据中台的时候当时最头疼的不是数据量而是业务方总在问“这个指标为什么变了”“这条规则到底怎么算的”。后来大模型能力起来之后我第一反应就是能不能把这种“解释型、查询型、汇总型”的工作交给智能体去做。financial-services这个项目标题看起来很大但落到工程上其实就是把金融业务里的高频动作拆成可编排的智能体能力再用 API 和插件把它们串起来。这里要先把一个概念说清楚智能体不是聊天机器人。聊天机器人是“你问我答”智能体是“你给目标它自己拆步骤、调工具、拿结果、做校验”。金融场景里真正有价值的是后者因为金融业务很少是一问一答就能闭环的往往需要查数据、比对规则、生成报告、再走审批流。1.2 整体架构为什么这样选我在设计这类项目时习惯先把系统分成四层这个分层不是拍脑袋而是踩过坑之后总结出来的层级职责常见选型选型理由接入层接收请求、鉴权、限流API 网关 密钥管理金融场景必须可审计所有调用要留痕编排层任务拆解、工具调度Agent 框架 Skills 注册表把“能力”和“流程”解耦方便替换能力层具体工具、插件、模型调用插件体系 多模型适配不同任务对模型要求不同不能绑死一家数据层行情、文档、向量库、日志关系库 向量库 对象存储结构化与非结构化数据要分开存为什么强调“编排层”和“能力层”分离因为金融业务变化太快。今天监管要求变了一个口径明天产品上线一个新规则如果你把逻辑写死在 Agent 里改一次就要重新测一遍全链路。把能力做成插件、把流程做成配置改起来才不至于伤筋动骨。还有一个现实问题模型不能只用一家。我在实际项目里试过同一个金融问答任务不同模型在数字准确性、长文本理解、工具调用稳定性上差异非常大。所以架构上必须留出多模型适配的口子通过统一的 API 层去路由而不是在每个 Agent 里硬编码模型名。1.3 关键词背后的技术栈映射把热搜词和这个项目对上号其实能看出一条很清晰的技术路线Claude、claude code、claude cli说明大家在用 Claude 系列做代码生成和 Agent 编排尤其是 CLI 形态适合本地开发调试。API、openrouter api key、deepseek api如何调用、智谱api多模型接入是刚需OpenRouter 这类聚合层能省掉大量适配工作。插件、vscode插件、pycharm ai插件、webstorm插件开发工具链的智能化本质是把 Agent 能力嵌进 IDE。agents、agent skills、skills开发、opencode skillsSkills 正在成为 Agent 能力复用的标准单元。api error: 400 this models maximum context length is 1048576 tokens长上下文处理是金融文档场景的硬骨头。代码诊断插件、前端开发skills说明 Skills 不只用于后端前端和诊断类场景也在铺开。这些词放在一起指向一个结论金融智能体项目的核心不是模型本身而是围绕模型构建的工程体系。模型是发动机插件是零件Skills 是装配说明书API 是油路缺一个都跑不起来。2. 核心模块拆解与实操要点2.1 Skills 体系怎么设计才不乱Skills 这个概念现在被说得有点玄我把它翻译成人话Skills 就是给 Agent 看的“操作手册 工具包”。一个 Skill 通常包含三部分——触发条件、执行步骤、输出格式。金融场景里我建议按业务域来切 Skills而不是按技术类型切。比如不要建“数据库查询 Skill”“HTTP 请求 Skill”这种太底层的东西而是建“财报指标提取 Skill”“合规条款比对 Skill”“客户风险等级计算 Skill”。原因很简单Agent 在编排时是按业务目标找能力的不是按技术手段找能力的。你给它一个“数据库查询 Skill”它不知道什么时候该用你给它一个“财报指标提取 Skill”它一看名字就知道该在什么场景调用。一个 Skill 的目录结构我通常这样组织skills/ financial-report-extract/ skill.md # 技能说明给模型看的 schema.json # 输入输出结构定义 handler.py # 实际执行逻辑 examples/ # 少样本示例 input-01.json output-01.jsonskill.md是核心它要写清楚这个技能解决什么问题、什么时候触发、输入需要哪些字段、输出是什么格式、有哪些边界情况。我见过太多项目把 Skill 写成一段模糊的自然语言描述结果 Agent 调用时全靠猜稳定性极差。注意Skill 的输入输出一定要用 JSON Schema 约束死。金融场景里数字精度、日期格式、币种单位错一个后面全盘皆输。别指望模型自己“理解”要用结构去限制它。2.2 多模型 API 接入的工程细节金融项目里模型调用有几个绕不开的问题成本、延迟、准确性、可用性。我的做法是建一个统一的模型路由层对外暴露一个标准接口内部根据任务类型路由到不同模型。路由策略我一般这样配任务类型推荐模型特征路由理由数字计算与校验推理强、输出稳定金融数字不能幻觉宁可慢一点长文档摘要上下文窗口大财报、合同动辄几十万字工具调用编排函数调用能力强Agent 核心链路稳定性优先客服话术生成响应快、成本低高频低价值任务控制成本接入时有个细节特别容易踩坑不同模型的 API 错误码和重试语义不一样。有的模型限流返回 429有的返回 400 带特定 message有的支持流式有的流式格式还不统一。所以路由层必须做错误归一化把各家错误映射成统一的内部分类再决定是重试、降级还是直接失败。# 错误归一化示意 ERROR_MAP { rate_limit: [429, rate limit, too many requests], context_overflow: [maximum context length, token limit exceeded], auth_failed: [api key is required, invalid api key, 401], model_unavailable: [model not found, 503, overloaded], } def normalize_error(raw_msg: str) - str: low raw_msg.lower() for category, keywords in ERROR_MAP.items(): if any(k in low for k in keywords): return category return unknown这个映射表看着简单但能省掉大量排查时间。尤其是context_overflow这类错误如果不单独识别重试多少次都没用必须走截断或分块策略。2.3 长上下文处理的实战方案热搜里那条maximum context length is 1048576 tokens的错误做金融文档处理的人几乎都遇到过。1048576 听起来很大但一份年报加上附注、审计报告、关联交易说明很容易就超了。而且就算没超把整份文档塞进去成本和延迟也受不了。我的处理策略是三层第一层是结构化预处理。财报、合同这类文档先用解析工具把表格、标题、段落结构抽出来不要直接扔原始文本。结构化之后很多信息可以用规则直接提取根本不需要模型。第二层是分块加索引。把文档切成语义完整的块每块打上元数据章节、页码、类型存进向量库。Agent 需要时先检索再生成而不是全文塞入。第三层是摘要加回溯。对超长文档先生成层级摘要Agent 先看摘要定位再回查原文块。这样既控制上下文长度又保证信息可追溯。实操心得分块时不要按固定字数切要按语义边界切。我试过按 512 token 硬切结果把一张财务报表从中间切开模型读出来的数字全是错的。后来改成按标题层级和表格边界切准确率明显提升。2.4 插件与 IDE 集成的价值vscode插件、pycharm ai插件、代码诊断插件这些词反映了一个趋势开发者希望 Agent 能力直接出现在写代码的地方而不是切到另一个网页。金融项目里这一点尤其重要因为很多逻辑需要边写边验证。我自己的做法是把核心 Skills 通过 CLI 暴露出来再在 IDE 里做轻量封装。这样同一套能力既能在终端里跑批处理也能在编辑器里做单点调用。CLI 的好处是可脚本化、可进 CIIDE 插件的好处是交互快、反馈即时。集成时要注意权限隔离。金融代码库往往有敏感信息IDE 插件不能无差别读取整个工作区。我的做法是显式声明插件可访问的目录和文件类型默认拒绝按需授权。3. 从零搭建金融智能体的完整流程3.1 环境准备与依赖安装先把基础环境搭起来。我习惯用 Ubuntu 做开发环境Windows 下如果用 WSL 也可以但要注意虚拟化平台相关组件要提前启用否则容器和虚拟机相关功能会报错。# 基础依赖 sudo apt update sudo apt install -y python3.11 python3.11-venv git curl # 创建虚拟环境 python3.11 -m venv venv source venv/bin/activate # 安装核心库 pip install fastapi uvicorn pydantic httpx pip install openai anthropic # 多模型 SDK pip install chromadb # 向量库本地开发够用如果你用 Docker 做依赖隔离注意 Docker Desktop 在部分环境下会报failed to connect to the docker api at npipe这类错误通常是 Docker 服务没启动或者管道配置不对。先确认服务状态再检查环境变量里的 socket 路径。# 检查 Docker 服务 docker info # 如果报管道错误确认服务已启动 sudo systemctl status docker3.2 模型接入配置多模型接入我建议用环境变量管理密钥绝对不要写进代码。下面是一个统一配置的示例import os from dataclasses import dataclass dataclass class ModelConfig: name: str base_url: str api_key: str max_context: int supports_tools: bool def load_models(): return { reasoning: ModelConfig( nameos.getenv(REASONING_MODEL, claude-sonnet), base_urlos.getenv(REASONING_BASE_URL), api_keyos.getenv(REASONING_API_KEY), max_context200000, supports_toolsTrue, ), long_context: ModelConfig( nameos.getenv(LONG_CONTEXT_MODEL, deepseek-chat), base_urlos.getenv(LONG_CONTEXT_BASE_URL), api_keyos.getenv(LONG_CONTEXT_API_KEY), max_context128000, supports_toolsFalse, ), }这里有个经验max_context 不要照抄官方文档的最大值。官方说支持 128K不代表你就能稳定用满 128K。实际可用上下文往往要打七到八折因为输出也要占额度而且接近上限时模型质量会下降。我一般按官方值的 70% 来规划。3.3 Skill 注册与调用链路Skill 注册我做成一个注册表启动时扫描目录把每个 Skill 的元信息加载进来。import json from pathlib import Path class SkillRegistry: def __init__(self, root: str): self.root Path(root) self.skills {} def load_all(self): for skill_dir in self.root.iterdir(): if not skill_dir.is_dir(): continue meta_file skill_dir / schema.json if not meta_file.exists(): continue meta json.loads(meta_file.read_text()) self.skills[meta[name]] { meta: meta, path: skill_dir, } def get_tool_specs(self): 输出给模型看的工具描述 specs [] for name, item in self.skills.items(): specs.append({ name: name, description: item[meta][description], parameters: item[meta][input_schema], }) return specs调用链路是这样的用户请求进来 → 编排 Agent 拿到工具列表 → 模型决定调用哪个 Skill → 路由层执行 Skill → 结果回传模型 → 模型生成最终回复。这个链路里最容易出问题的是工具描述写得不清楚导致模型该调不调、不该调乱调。注意工具描述要写“什么时候用”而不只是“这是什么”。比如不要写“查询数据库”要写“当需要获取某公司近三年营收数据时使用”。前者模型不知道触发时机后者一目了然。3.4 参数计算与上下文预算上下文预算是金融智能体必须算清楚的一笔账。假设一次请求包含系统提示 2000 token、工具描述 3000 token、历史对话 5000 token、检索到的文档块 20000 token、用户问题 500 token合计约 30500 token。如果模型输出预留 4000 token那么总需求约 34500 token。如果模型上限是 128000 token看起来绰绰有余。但实际场景里历史对话和文档块会随轮次增长。我的做法是设一个阈值比如用到上限的 60% 时就开始压缩历史对话用到 75% 时强制只保留最近三轮加摘要。def estimate_tokens(text: str) - int: # 粗略估算中文约 1.5 字/token英文约 4 字符/token chinese sum(1 for c in text if \u4e00 c \u9fff) other len(text) - chinese return int(chinese / 1.5 other / 4) def check_budget(messages, docs, model_max, reserve4000): total sum(estimate_tokens(m[content]) for m in messages) total sum(estimate_tokens(d) for d in docs) usable model_max * 0.7 - reserve return total usable, total, usable这个估算不精确但足够用来做预警。真要精确计数得用对应模型的 tokenizer但那个开销也不小日常预警用粗估就够了。4. 常见问题与排查技巧实录4.1 模型调用类问题速查现象可能原因排查方向解决方式400 上下文超限输入超过模型上限打印 token 估算值分块、摘要、压缩历史401 鉴权失败密钥缺失或过期检查环境变量重新配置密钥确认请求头格式429 限流调用频率过高看调用日志时间分布加退避重试做请求队列工具调用不触发工具描述不清检查 description 字段补充触发场景说明输出数字错误模型幻觉对比原始数据关键数字用规则校验不信任模型直出这张表是我从实际项目里攒出来的每一条都对应过真实故障。尤其是最后一条金融场景里模型把 1.2 亿写成 12 亿这种事出一次就够喝一壶的。所以我的原则是模型可以参与计算但最终数字必须过规则校验。4.2 Skill 加载失败的排查Skill 加载失败通常有几个原因目录结构不对、schema.json 格式错误、handler 导入报错。我一般按这个顺序查确认 skill 目录下有skill.md和schema.json。用json.loads单独验证 schema 文件能否解析。单独导入 handler 模块看有没有依赖缺失。检查 skill 名称是否重复重复会导致注册表覆盖。# 快速验证所有 schema 文件 find skills -name schema.json -exec python -c import json,sys; json.load(open(sys.argv[1])) {} \;这个命令能一次性把所有格式错误的 schema 揪出来比一个个点开看快得多。4.3 长文档处理的坑长文档处理我踩过最大的坑是表格跨页。一份 PDF 财报表格从第 5 页跨到第 6 页解析工具把它当成两个独立表格结果表头和表体对不上模型读出来的数据完全错位。后来我的做法是解析后先做表格合并检测如果相邻两页的表格列数一致、且第一页表格没有表尾就尝试合并。另一个坑是数字单位。有的文档写“万元”有的写“元”有的用“百万”。如果不统一单位Agent 汇总时就会出错。我的做法是在预处理阶段就做单位归一化全部转成基础单位并在元数据里记录原始单位。实操心得金融文档预处理阶段多花一小时后面能省十小时排查。别急着把原始文本喂给模型先把结构理清楚。4.4 多模型切换的稳定性问题多模型切换最大的问题是行为不一致。同一个 Skill模型 A 调用时参数格式对模型 B 调用时可能少传一个字段。我的应对方式是在 Skill 的 handler 里做参数校验和默认值填充不依赖模型每次都传全。def validate_and_fill(params: dict, schema: dict) - dict: result {} for field, spec in schema[properties].items(): if field in params: result[field] params[field] elif default in spec: result[field] spec[default] elif spec.get(required): raise ValueError(f缺少必填字段: {field}) return result这样即使模型漏传只要字段有默认值就能兜住。必填字段缺失就直接报错让编排层决定是重试还是换模型。4.5 成本控制的几个手段金融项目调用量大成本很容易失控。我常用的手段有四个缓存相同查询结果缓存尤其是行情、汇率这类高频低变数据。分级路由简单任务走小模型复杂任务才走大模型。批量合并多个小请求合并成一次调用减少往返开销。输出限制给模型设 max_tokens避免它长篇大论。缓存这块要注意失效策略。金融数据时效性强行情类缓存可能只保留几秒财报类可以保留几天。缓存键要把所有影响结果的参数都包含进去否则会串数据。5. 工程化落地的经验与边界5.1 什么该交给智能体什么不该这是我在金融项目里被问最多的问题。我的判断标准很简单可验证、可回滚、低实时性要求的任务可以交给智能体不可验证、不可回滚、高实时性要求的任务必须走确定性系统。举个例子生成一份客户持仓分析报告可以交给智能体因为报告出来有人复核错了能改。但执行一笔交易绝对不能交给智能体因为错了没法回滚而且实时性要求极高。再比如从合同里提取关键条款可以交给智能体因为提取结果可以人工抽检。但根据条款自动冻结账户就必须走规则引擎因为这是不可逆操作。这个边界划清楚项目才不会跑偏。我见过一些团队一上来就想让智能体做全流程自动化结果在关键节点上出了几次事故整个项目就被叫停了。5.2 可观测性怎么建金融智能体必须可观测否则出了问题根本不知道是哪一步错的。我一般埋三类日志调用日志每次模型调用记录模型名、输入 token 数、输出 token 数、耗时、错误码。Skill 日志每次 Skill 执行记录入参、出参、耗时、是否命中缓存。决策日志Agent 每次选择调用哪个 Skill、为什么这么选记录模型的推理过程。第三类最容易被忽略但排查时最有用。当 Agent 该调 A 却调了 B你看决策日志就能知道是工具描述的问题还是模型理解的问题。import logging import time def traced_skill_call(skill_name, params, handler): start time.time() try: result handler(params) logging.info({ event: skill_call, skill: skill_name, params: params, result_summary: str(result)[:200], duration_ms: int((time.time() - start) * 1000), status: ok, }) return result except Exception as e: logging.error({ event: skill_call, skill: skill_name, params: params, error: str(e), duration_ms: int((time.time() - start) * 1000), status: error, }) raise日志里参数和结果要脱敏金融数据不能明文落盘。我一般对账号、金额、身份证号做掩码处理只保留用于排查的结构信息。5.3 团队协作与 Skills 复用Skills 最大的价值是复用。一个团队里A 写的“财报指标提取 Skill”B 的项目也能用。但复用的前提是接口稳定、文档清楚。我的做法是建一个内部 Skills 仓库每个 Skill 有版本号、维护人、变更记录。用的时候按版本引用不要直接拷贝代码。这样 Skill 升级时下游能感知到不会突然被破坏性变更搞崩。版本管理我建议用语义化版本主版本号变更表示不兼容次版本号表示新增功能修订号表示修复。Skill 的 schema 一旦发布就不要改字段含义要改就发新版本。5.4 安全与合规的底线金融项目对安全的要求不用多说。我在工程上坚持几条底线密钥不落代码、不落日志、不进版本库。模型输入输出全程加密传输。敏感数据在进入模型前做脱敏能本地算的不要传给模型。所有智能体操作留审计日志可追溯。关键操作设人工确认环节不搞全自动。这几条不是建议是底线。尤其是最后一条金融场景里任何涉及资金、权限、客户信息的操作都必须有人工确认或者确定性规则兜底。智能体可以提建议、可以做预处理但最终决策权不能完全交出去。5.5 后续可以扩展的方向这套架构搭起来之后扩展性其实很好。往深了做可以接更多数据源比如把行情、舆情、公告都纳入检索范围往广了做可以把 Skills 开放给更多业务线让不同团队按需组合。我最近在试的一个方向是多智能体协作。单个 Agent 处理复杂金融任务时容易顾此失彼拆成几个专职 Agent 分工协作比如一个负责数据检索、一个负责规则比对、一个负责报告生成最后由一个协调 Agent 汇总。这样每个 Agent 的职责更清晰调试也更容易。不过多智能体也有代价就是链路变长、延迟增加、故障点变多。所以要不要上多智能体得看任务复杂度。简单任务单 Agent 就够了硬上多智能体反而是过度设计。我在实际项目里的体会是金融智能体这件事技术只占三成剩下七成是对业务的理解和对边界的把握。模型能力再强如果你不清楚哪些环节能容错、哪些不能项目就很难真正落地。先把业务拆明白再谈技术选型顺序反了就要返工。