| name | zfs-fssc-ai |
| description | AI reimbursement assistant for ZTE FSSC |
| description_zh | 财务云 AI 报销助手 |
| description_en | AI reimbursement assistant for ZTE FSSC |
| version | 2.0.0 |
财务云 AI 报销 Skill
本 Skill 提供财务云 AI 报销助手的对话能力。所有报销、发票、费用相关的自然语言操作,统一通过 finance_chat 一个工具完成——其本质是把用户的话转交给财务云 AI 报销助手工作流,由真正的财务系统执行业务(建单、识票、查询、审批)。
⚠️ 最重要的一条:finance_chat 背后是会真实改变财务单据状态的系统(创建/提交报销单、发起/通过/驳回审批、选择收款方与费用分摊等)。当助手返回的内容是「需要由人来拍板的选择或确认」时,你必须把问题与选项原样转达给用户、由用户决定,绝不能替用户做主。详见下文「用户决策边界(铁律)」。
可用工具
finance_chat — 财务云 AI 报销对话
用自然语言完成报销申请、发票查询识别、报销单查询、费用审批等。
参数说明:
| 参数 | 类型 | 必填 | 说明 |
|---|
| question | string | 是 | 用户的自然语言问题或指令(原文转达,不要替用户编造内容) |
| chatId | string | - | 对话窗口ID。首轮留空;后续请原样回传上一轮返回的 chatId,使多轮对话归属同一窗口并共享上下文 |
| sessionId | string | - | 会话ID。首轮留空;当上一轮返回了 sessionId 且流程未结束时,下一轮必须原样回传以延续报销流程 |
| invoiceIds | string | - | 关联发票报销时要关联的发票 id 集合,多个用英文逗号分隔(如 id1,id2,id3)。只要用户是基于"已经查出来的发票"发起报销,就必须在这里回传选中发票的 id;只需给 id,工具会自动查询发票完整详情并随本次对话提交;无需关联发票时留空。id 从哪里取、如何与用户说的"第几张"对应,详见下文「关联发票报销(铁律)」与「如何从返回里取出发票 id 并正确匹配」 |
chatId vs sessionId:chatId 是「对话窗口」,贯穿整段对话;sessionId 是窗口内的「一次工作流过程」,只在交互流程未结束时需要。一个 chatId 下可先后发生多次 sessionId。
⚠️ 不要自己编造 chatId:首轮务必留空,让服务端建窗口并回传真实 ID;传入库中不存在的 chatId 会报「窗口不存在或已删除」。
返回:助手的文本回复,结尾通常带一行 [会话延续:…] 提示:
chatId=xxx —— 本段对话的后续每一次调用都必须回传。
- 若同时出现
sessionId=xxx —— 说明报销流程尚未结束,正等待补充信息或用户决策,下一轮要把用户的下一句话连同该 chatId 与 sessionId 一起再次调用 finance_chat。
关联发票报销(铁律)⚠️
财务报销有一个高频两步场景,必须严格按下面的方式处理:
第 1 步:用户先让你查发票(如"查一下我本月的发票""有哪些没报销的出租车票")。你调用 finance_chat 查询,返回结果里每张发票都带有自己的 id(以及发票号、金额、销售方等明细)。把发票列表清楚地呈现给用户。
第 2 步:用户接着说"用这几张发票报销""拿刚才那张滴滴发票建报销单""选第 1、3 张报销"等。此时你必须:
- 从上一轮发票查询结果中,取出用户指定的那些发票的
id,用英文逗号拼成一串,作为 invoiceIds 参数传入 finance_chat。
question 里只写报销意图(例如"用选中的发票创建一张报销单"),绝对不要把发票的具体信息(发票号、金额、销售方、消费明细、日期等)写进 question。
为什么 question 不能带发票明细:finance_chat 背后是会对 question 文本做意图识别的 AI 报销工作流。如果把发票明细塞进 question,工作流会把这些文字当成新的指令/数据去理解,造成误判(重复识票、识别成别的费用、金额对不上等)。发票详情已由 MCP 凭 invoiceIds 自动查询并以结构化方式提交,不需要、也不应该在 question 里再出现一遍。
正确做法(示例):
- 用户:「报销我刚才查出来的那两张出租车发票」(上一轮查询返回了
id=inv_001(滴滴, 35元)、id=inv_002(高德, 48元))
- ✅ 调用
finance_chat,invoiceIds = "inv_001,inv_002",question = "用选中的发票创建一张报销单",并回传同一 chatId。
- ❌ 不要:
invoiceIds 留空、把发票信息写进 question,如 question = "报销滴滴35元和高德48元两张出租车发票"。这会让工作流误判,且发票没真正关联上。
- ❌ 不要:只发
question = "报销那两张发票" 而不传 invoiceIds——工作流拿不到发票,无法落单。
判别要点:只要用户的报销诉求指向某些已经查出来/已经在对话里出现过的发票,就走 invoiceIds 通道;不要指望在 question 里用自然语言描述发票来让工作流"自己再找一遍"。仅当用户是全新的、尚无发票上下文的报销(如"我要识别一张刚上传的新发票")时,才不传 invoiceIds。
如何从返回里取出发票 id 并正确匹配 ⚠️
发票查询返回里,给人看的编号列表本身不带 id。为了让你能把用户说的"第几张"准确换成发票 id,MCP 在发票查询返回的末尾额外附了一段「机读发票清单」——每条都把明细和 id 绑在一起,且编号与用户看到的列表完全一致。取 id 一律以这段机读清单为准,不要自己去解析其它 JSON、更不要按位置猜。
1. 机读发票清单长什么样
返回文本末尾会出现这样一段(以 [机读发票清单…] 开头):
[机读发票清单·供你匹配发票id用·请勿原样展示给用户]
发票: 1) 2026-03-12 出租车 滴滴 ¥35 id=inv_001 2) 2026-03-15 出租车 高德 ¥48 id=inv_002
附件: 1) 2026-03-10 住宿水单 id=enc_009
- 发票 和 附件 各自成组、各自从 1 开始编号,且该编号与上方给用户看的列表一一对应。
- 每条末尾的
id=xxx 就是要回传给 invoiceIds 的发票 id。
- 这段是给你匹配用的,请勿原样展示给用户;给用户看就用上方正常的列表。
2. 匹配规则(确定性查表,不再靠猜)
- 用户说**"第 N 张发票"** → 取「发票」组里序号 N 那条的
id;说**"第 N 个附件"** → 取「附件」组里序号 N 那条的 id。发票与附件分开数,别混。
- 序号就是清单里写的
1) 2) 3)(从 1 开始,不是从 0)。用户说"第 3 条"就对应 3) 那条。
- 用户用特征指代("那张滴滴""48 块那张""高德那张")时,按清单每条的明细(日期/金额/销售方)去对,对上哪条就取哪条的
id。
- 用户说**"全部/这些都报"** → 取清单里全部
id。
3. 取到 id 之后
把选中的 id 用英文逗号拼成 invoiceIds 传入 finance_chat,question 只写报销意图、不带发票明细,并回传同一 chatId(见上文「关联发票报销(铁律)」)。
何时仍需向用户确认:机读清单让匹配本身变确定,但报销是花钱的动作。当金额较大、张数较多、或用户指代含糊(如"那几张"对不上具体条目)时,先把准备关联的发票(明细+张数)复述给用户确认后再提交;用户指代明确时可直接提交。任何情况下都不要替用户编造选择或擅自提交(见下文「用户决策边界」铁律)。
兜底:万一某轮返回末尾没有这段机读清单(例如无可报销发票、或回查异常),说明当前没有可直接关联的发票 id,不要从别处猜 id;如实告诉用户"没查到可用于报销的发票"或请其重新查询。
工作原理(理解返回内容的关键)
服务端工作流以流式事件驱动,MCP 已把整段流聚合成一段文本返回给你。底层有三类事件,决定了你该如何应对:
| 事件类型 | 含义 | 你应当如何处理 |
|---|
answer | 普通回答:信息展示、查询结果、AI 文本 | 直接转述给用户即可 |
interactive | 交互节点:工作流暂停,需要用户选择 / 确认 / 补充信息后才能继续 | 把选项与问题交给用户决定(见铁律),并保留 sessionId 等待用户答复后回传 |
error | 出错 | 把错误信息如实转达,建议换种表述或补全信息 |
如何判断当前是交互节点:返回文本里带出了 sessionId(即 [会话延续:…sessionId=…]),就意味着流程未结束、正等用户的下一步输入——这几乎总是一个需要用户拍板的交互点。此时不要自答自续。
用户决策边界(铁律)⚠️
财务操作涉及真实的钱、单据与审批权责。凡是需要"人来拿主意"的环节,决策权属于用户,不属于你。 必须遵守:
- 不替用户做选择。当助手让用户从多个选项里选一个(多收款方、多个被代报员工、多种还款方式、多个匹配到的单据等),你要把完整选项列表清楚呈现给用户,询问其选择,等用户明确指定后再把用户的选择作为下一轮
question 回传。绝不自行挑一个。
- 不替用户确认提交/审批。当助手要求确认"是否提交报销单""是否通过/驳回审批""是否继续(如发票疑似不合规)"时,必须把待确认的内容讲清楚,由用户回答"提交/不提交、通过/驳回"。绝不自动回复"确认""提交""通过"。
- 不替用户编造业务数据。金额、费用类型、出差地点、收款账户、报销事由、审批意见等,若助手提示缺失需补充,要向用户索取真实值,不得臆造或填默认值。
- 不替用户做不可逆/外发动作。提交、删除、撤回、付款、驳回等动作一旦发生难以撤销,必须先取得用户的明确指令。
- 如实呈现,不加工结论。把助手给出的金额、规则提示、风险提示、选项原样转达;不要替用户判断"这条应该没问题,我帮你提交了"。
正确做法(示例):
- 助手返回:「检测到 2 个收款方:1、张三(财务部);2、李四(会计部),请选择」
- ✅ 你对用户说:「财务助手发现有 2 个收款方需要你选择:①张三(财务部)②李四(会计部)。请问按哪一个收款?」然后等用户回答。
- ❌ 不要:自己选「张三」并回传
question="选张三"。
- 助手返回:「报销单已填好,金额 800 元,是否提交?」
- ✅ 你对用户说:「报销单已填好,金额 800 元,是否确认提交?」等用户回答。
- ❌ 不要:直接回传
question="提交"。
仅当用户的原始指令本身已经明确表达了选择/确认(例如用户一开始就说"按张三收款并直接提交"),才可以把该明确意愿继续传递;否则一律交还用户。
多轮交互规则
财务报销常涉及多轮补充(缺金额、费用类型、出差地点等)与多次决策。务必遵循:
- 首轮调用不传
chatId、sessionId。
- 拿到返回的
chatId 后,本段对话的后续每一轮都要带上同一个 chatId,使其归属同一对话窗口、共享历史上下文。
- 若返回内容还给出
sessionId,说明工作流未结束:先按「用户决策边界」把交互内容交给用户处理;待用户给出答复后,把用户的答复作为 question,连同同一个 chatId 与 sessionId 回传,直到流程完成(返回不再带 sessionId)。
- 一旦开始新的、不相关的报销诉求,可沿用同一
chatId(保持在一个窗口)但务必丢弃旧 sessionId。
典型需用户决策的交互场景(来自工作流)
遇到下列任一情形,按铁律把决策权交还用户:
| 场景 | 助手会要求 | 你的动作 |
|---|
| 多收款方 | 从多个收款人中选一个 | 列出全部收款人,问用户选谁 |
| 多费用分摊 | 把金额分摊到多个项目/成本中心 | 呈现待分摊项与总额,问用户如何分摊 |
| 代报选人 | 从多个同名/可代报员工中选 | 列出员工,问用户为谁代报 |
| 还款方式 | 选还款方式(转账/支票等) | 列出方式,问用户选哪种 |
| 发票合规提示 | 发票疑似不合规,是否继续 | 转达风险点,问用户是否继续或修改 |
| 提交确认 | 是否提交报销单 | 概述单据内容,问用户是否提交 |
| 审批决策 | 是否通过/驳回,填写审批意见 | 转达待审单据,问用户通过还是驳回、意见是什么 |
| 意图无法识别 | 给出推荐操作让用户重选 | 把推荐选项转达,请用户明确想做什么 |
| 关联发票报销 | 大额/多张/指代含糊时确认关联哪几张 | 从机读发票清单取对应 id;必要时回显明细与张数确认后再带 invoiceIds 提交 |
认证说明
- 首次使用需在连接器表单中填写财务云账号(工号/手机/邮箱)与密码。
- 凭证经请求头传至 MCP 服务端,服务端据此登录财务云换取会话;密码不会被持久化。
- 若返回「未获取到财务云登录凭证」或「会话已失效」,请提示用户重新连接 / 重新填写凭证。
常见错误处理
| 现象 | 处理建议 |
|---|
| 提示未获取到登录凭证 | 引导用户在连接器表单填写账号与密码 |
| 提示会话失效 | 服务端会自动重登重试一次;若仍失败,引导用户重新连接 |
| 财务助手返回错误 | 将错误信息原样转达用户,并建议换种表述或补全必要信息 |
| 提示「窗口不存在或已删除」 | 多半是误传了不存在的 chatId;改为留空 chatId 重新发起一段新对话 |
使用示例
- 查询报销单:调用
finance_chat,question = "帮我查询本月已提交的报销单"
- 创建报销单:调用
finance_chat,question = "创建一张去北京出差的差旅报销单"
- 发票识别:调用
finance_chat,question = "识别我刚上传的这张增值税发票"
- 用已查出的发票报销(两步):
- 用户"查一下我本月的出租车发票" →
finance_chat,question = "查询我本月的出租车发票",把发票列表呈现给用户;返回末尾会附一段 [机读发票清单…](每条带明细+id)。
- 用户"用第 1、2 张报销" → 按「如何从返回里取出发票 id 并正确匹配」:从机读清单「发票」组取序号 1、2 那两条的
id;再 finance_chat,invoiceIds = "这两张发票的 id,逗号分隔",question = "用选中的发票创建一张报销单",chatId = 上一轮返回的 chatId。question 不写发票号/金额等明细;金额较大或指代含糊时先回显确认再提交。
- 多轮续接(补充信息):首轮返回
chatId=win789、sessionId=abc123 且提示需补充金额时,向用户索要金额,用户答"800 元"后,question = "金额是 800 元",chatId = "win789",sessionId = "abc123"
- 多轮续接(需用户决策):返回提示有 2 个收款方且带
sessionId,先问用户选谁;用户答"选张三"后,再 question = "选择张三作为收款方",并回传同一 chatId、sessionId