Function Calling实战:基于Qwen与LangChain构建金融智能问答机器人

📅 2026/7/4 10:37:23
Function Calling实战:基于Qwen与LangChain构建金融智能问答机器人
1. 项目概述从对话到行动的桥梁在AI应用开发领域让大语言模型LLM从“能说会道”进化到“能说会做”一直是个核心挑战。过去我们通过复杂的Prompt工程试图让模型输出结构化的JSON指令再由后端解析执行这个过程不仅繁琐而且稳定性差模型稍有“自由发挥”整个流程就可能崩溃。OpenAI的Function Calling功能正是为了解决这个痛点而生。它本质上是一套标准化的协议让开发者可以像定义API接口一样向模型“注册”一系列它能调用的工具函数模型在理解用户意图后会主动判断是否需要调用工具、调用哪一个并精准地输出符合函数参数要求的JSON对象。这就像给模型配备了一个标准化的“工具包”和一份清晰的“工具使用说明书”使其从单纯的文本生成器升级为一个可以协调外部能力和资源的智能中枢。对于一名AI应用开发工程师而言掌握Function Calling意味着你能构建出真正具备“执行力”的智能体。无论是查询实时天气、从数据库检索信息、发送邮件还是操作复杂的业务系统都可以通过函数调用来实现。这不仅仅是技术上的优化更是产品形态的升级。用户感受到的不再是一个“知道很多但做不了事”的聊天机器人而是一个能真正解决问题、完成任务的智能助手。接下来我将以一个综合性项目——“金融大模型问答机器人”为例拆解如何运用Function Calling结合RAG、智能体等前沿技术打造一个能理解、能推理、能执行的金融领域智能应用。2. 项目整体设计与核心思路拆解2.1 核心需求与业务场景解析这个“金融大模型问答机器人”项目源于一家中型券商对内部投研和客户服务效率提升的迫切需求。传统的客服系统或文档检索工具在面对复杂的、非结构化的金融问题时例如“对比一下A公司和B公司过去三年的毛利率和研发投入趋势并分析其背后的可能原因”往往力不从心。项目的核心需求可以拆解为三层精准知识问答能基于公司内部的研报、公告、规章制度等非公开文档回答事实性问题。例如“公司Q3财报中提到的‘其他收益’主要构成是什么”复杂分析与推理能结合实时市场数据股价、新闻和内部知识进行简单的推理和总结。例如“今天新能源板块普跌结合我们最新的行业研报主要原因可能有哪些”任务自动化执行能根据对话意图自动执行一些标准化操作。例如用户说“帮我查一下茅台的最新股价”机器人应自动调用行情查询函数并返回结果用户说“把刚才关于降准的分析摘要发到我的邮箱”机器人应能调用邮件发送函数。Function Calling在这里扮演了“决策与执行枢纽”的角色。它将用户的自然语言指令转化为对具体后端服务或工具的调用指令是实现第2、3层需求的关键。2.2 技术架构选型与考量面对上述需求我们设计了一个分层、解耦的技术架构核心思想是让LLM专注于其最擅长的“理解与规划”而将“知识检索”和“任务执行”交给更专业的模块。核心架构组件LLM核心大脑我们选择了Qwen通义千问系列模型作为基座。相较于直接使用OpenAI API选择国内优秀的开源模型如Qwen在数据隐私、定制化微调和成本控制上具有显著优势。Qwen-72B-Chat在复杂指令理解和推理能力上已接近GPT-4水平足以胜任本项目。应用框架神经系统LangChain。它是一个用于构建LLM应用的框架其Agent和Tool的概念与Function Calling天然契合。LangChain帮助我们优雅地管理工具集、维护对话状态并处理复杂的多步骤推理链条。知识库长期记忆RAG检索增强生成技术栈。我们使用LangChain的文本加载器、分割器处理PDF、Word等格式的金融文档通过text-embedding模型生成向量存入Chroma或Milvus这类向量数据库。当用户提问时先从知识库中检索最相关的文档片段连同问题一起交给LLM生成答案确保回答的准确性和有据可查。复杂知识推理进阶大脑对于涉及多实体、多关系深度推理的问题如产业链分析、风险传导我们引入了GraphRAG的初步理念。即利用LLM从文档中抽取实体公司、人物、产品和关系投资、竞争、上下游构建一个知识图谱。问答时可以先在图谱上进行查询和推理再将结果喂给LLM生成更深入的洞察。服务接口躯干FastAPI。用于构建高效、异步的Web服务接口对外提供聊天、文档上传、任务执行等API。它的自动生成API文档Swagger特性也便于内部调试和集成。任务执行工具包手脚这就是Function Calling的用武之地。我们将一系列外部能力封装成“工具”get_stock_quote(symbol: str): 调用第三方金融数据API如聚宽、AKShare查询实时股价。search_financial_news(keywords: str, days: int): 调用新闻API获取近期相关新闻。query_internal_database(query_sql: str): 在权限控制下查询内部业务数据库需谨慎定义避免SQL注入。send_email(to: str, subject: str, body: str): 调用公司邮件服务接口发送摘要。calculate_financial_ratio(company: str, ratio_name: str): 根据已有数据计算财务比率。模型优化大脑训练对于高度专业化的金融术语和内部行文风格我们采用高效微调技术对Qwen基座模型进行领域适应。具体采用LoRA低秩适应技术这是一种参数高效的微调方法只需训练极少量参数就能让模型掌握领域知识成本低、效果好。在需要强化模型对特定任务如报告摘要、风险判断的遵循能力时会采用SFT有监督微调。而对于更复杂的、需要与外部环境交互优化的场景如对话策略则探索使用PPO/RLHF强化学习进行对齐。最后为了部署到资源有限的环境会对微调后的模型进行量化在几乎不损失精度的情况下大幅降低存储和计算开销。这个架构的优势在于清晰的分工和强大的扩展性。LLMQwen作为总指挥RAG提供知识支持Function Calling负责调动“手脚”而FastAPI将它们整合成一个可对外服务的整体。3. Function Calling的实现细节与核心要点3.1 Function Calling的工作原理解析很多人误以为Function Calling是模型“学会”了执行你的代码。其实不然。它的核心是一个标准化的“请求-响应”协议。工作流程可以分为三步定义与声明开发者在请求LLM时除了常规的对话消息messages额外提供一个tools参数。这个参数是一个列表里面包含了所有可供调用的函数工具的“说明书”。每份“说明书”需要明确描述函数名、函数描述这个至关重要模型靠它理解工具用途、参数列表每个参数的名称、类型、描述。模型的理解与决策LLM如Qwen接收到用户消息和工具列表后会基于对用户意图的理解进行判断如果用户问题无需调用外部工具如普通聊天、知识问答则正常生成文本回复。如果判断需要调用工具它不会执行代码而是会生成一个结构化的JSON对象。这个对象一定包含tool_call_id本次调用的唯一ID和function对象其中function里有name要调用的函数名和arguments一个符合参数定义的JSON字符串。开发者的执行与回调你的应用程序收到这个JSON响应后解析出函数名和参数在你的本地或服务器环境中执行对应的真实函数代码。执行完毕后你将函数返回的结果也是一个JSON可序列化的对象再次封装作为一条新的tool消息附加到对话历史中并再次发送给LLM。模型的总结与回复LLM收到了工具执行的结果结合之前的对话上下文生成最终面向用户的自然语言回复。关键理解模型从不直接执行你的get_stock_quote函数。它只做两件事(1) 判断是否需要调用(2) 如果需要则告诉你调用哪个函数、并填好参数。真正的“执行”动作永远发生在你信任的后端环境中。这保证了安全性。3.2 工具Function的定义最佳实践定义工具的质量直接决定了模型调用的准确率。以下是一些核心要点1. 函数描述 (description) 是灵魂描述必须清晰、具体、无歧义说明这个工具“做什么”和“何时用”。好的描述应该让一个“外星人”模型也能看懂。差“获取股票数据”太模糊什么数据好“根据给定的股票代码例如‘000001.SZ’代表平安银行查询该股票的实时最新价格、今日开盘价、最高价、最低价以及涨跌幅。适用于用户询问某只股票当前行情时。”2. 参数设计要严谨类型严格使用JSON Schema支持的类型如string,integer,number,boolean,array,object。描述每个参数都必须有description解释其含义和格式。枚举 (enum)对于有限选项的参数使用enum列表。这能极大提高模型填参的准确性。例如ratio_name参数可以枚举为[“pe_ratio“, “pb_ratio“, “roe“, “gross_margin“]。必需参数 (required)明确列出哪些参数是调用时必须的。一个完整的工具定义示例使用OpenAI格式Qwen兼容tools [ { type: function, function: { name: get_stock_quote, description: 根据股票代码查询实时行情数据。股票代码需符合标准格式如A股为‘000001.SZ‘港股为‘00700.HK‘美股为‘AAPL‘。, parameters: { type: object, properties: { symbol: { type: string, description: 标准格式的股票代码。 }, fields: { type: array, items: {type: string}, description: 需要返回的字段列表可选值latest_price, open, high, low, change, change_percent, volume。默认为全部返回。, default: [latest_price, change_percent] } }, required: [symbol] } } } ]3. 工具集的规划策略不要一次性把所有可能的函数都丢给模型。工具集应该根据会话的上下文或用户的身份进行动态加载。例如在普通的客服场景可能只需要知识库检索工具当识别到用户是投资顾问时再动态加载行情查询、新闻搜索等专业工具。这可以减少模型的决策负担提高准确率。4. 基于LangChain的智能体Agent实操实现LangChain将Function Calling的概念抽象为Tool和Agent让实现变得更加模块化和高效。下面我们以金融机器人的核心问答流程为例展示如何串联起RAG和Function Calling。4.1 环境准备与核心组件初始化首先安装必要依赖并初始化核心组件。# 示例依赖 pip install langchain langchain-community qwen-api fastapi chromadb pymilvus sentence-transformers# core_init.py import os from langchain.agents import AgentExecutor, create_openai_tools_agent from langchain_community.chat_models import ChatQwen # 假设有Qwen的LangChain集成 from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain.memory import ConversationBufferMemory from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter # 1. 初始化LLM (使用Qwen API 需设置API_KEY) llm ChatQwen( modelqwen-max, # 或 qwen-plus, qwen-turbo api_keyos.getenv(QWEN_API_KEY), temperature0.1, # 金融场景要求高确定性温度调低 ) # 2. 准备知识库 (RAG) # 加载文档 loader DirectoryLoader(./internal_docs/, glob**/*.pdf, loader_clsPyPDFLoader) documents loader.load() # 分割文本 text_splitter RecursiveCharacterTextSplitter(chunk_size1000, chunk_overlap200) texts text_splitter.split_documents(documents) # 生成向量并存储 embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-small-zh-v1.5) # 中文嵌入模型 vectorstore Chroma.from_documents(texts, embeddings, persist_directory./chroma_db) retriever vectorstore.as_retriever(search_kwargs{k: 4}) # 检索最相关的4个片段 # 3. 定义工具 (Tools) from langchain.tools import Tool from typing import Optional, List import akshare as ak # 示例使用AKShare获取金融数据 import smtplib from email.mime.text import MIMEText # 工具1: 检索内部知识库 def rag_retriever(query: str) - str: 使用内部知识库检索与问题相关的文档片段。 docs retriever.get_relevant_documents(query) content \n\n.join([doc.page_content for doc in docs]) return f根据内部知识库检索到以下相关信息\n{content} # 工具2: 查询股票行情 def get_stock_quote(symbol: str, fields: Optional[List[str]] None): 查询股票实时行情。symbol格式如‘sh000001‘或‘sz300750‘。 try: # 使用AKShare这里需要根据其最新接口调整 stock_zh_a_spot_df ak.stock_zh_a_spot_em() stock_info stock_zh_a_spot_df[stock_zh_a_spot_df[代码] symbol] if stock_info.empty: return f未找到股票代码为 {symbol} 的行情信息。 # 简化处理返回部分字段 result { name: stock_info.iloc[0][名称], latest_price: stock_info.iloc[0][最新价], change_percent: stock_info.iloc[0][涨跌幅], volume: stock_info.iloc[0][成交量], } if fields: filtered_result {k: v for k, v in result.items() if k in fields} return str(filtered_result) return str(result) except Exception as e: return f查询行情时出错{e} # 工具3: 发送邮件 (示例) def send_email(to: str, subject: str, body: str) - str: 发送邮件到指定地址。需要预先配置SMTP服务器。 # 此处为示例实际需配置真实的SMTP服务器、端口、账号密码 # msg MIMEText(body, plain, utf-8) # msg[Subject] subject # msg[From] os.getenv(EMAIL_USER) # msg[To] to # ... 发送逻辑 # 模拟成功 return f邮件已成功发送至 {to}主题{subject} # 将函数包装成LangChain Tool tools [ Tool( nameInternal_Knowledge_Search, funcrag_retriever, description当用户的问题涉及公司内部知识如产品细节、规章制度、历史报告、财务数据解读时使用此工具进行检索。输入应为具体的问题或关键词。 ), Tool( nameStock_Quote_Query, funcget_stock_quote, description查询A股股票的实时行情数据。输入必须包含‘symbol‘参数即股票代码格式如‘sh000001‘上证或‘sz300750‘深证。 ), Tool( nameEmail_Sender, funcsend_email, description当用户明确要求将对话内容、分析摘要或文件通过邮件发送时使用此工具。需要收件人地址(to)、邮件主题(subject)和正文(body)。 ) ]4.2 构建智能体Agent与提示工程接下来我们使用LangChain的create_openai_tools_agent来创建一个能使用上述工具的智能体。提示词Prompt是指导智能体行为的关键。# agent_setup.py from langchain.agents import AgentExecutor # 定义系统提示词塑造智能体的角色和行为准则 system_prompt 你是一个专业的金融领域AI助手名为“金智助理”。你的核心能力包括 1. 回答关于公司内部产品、政策、历史报告等知识性问题使用内部知识库工具。 2. 查询公开的金融市场实时数据如股票行情使用行情查询工具。 3. 根据用户指令协助完成发送邮件等任务使用邮件工具。 请遵循以下原则 - **优先使用工具**当用户问题涉及实时数据、内部知识或明确任务时优先考虑使用合适的工具。在回复中提及工具获取的信息。 - **诚实与精确**如果不知道或工具未返回有效信息直接告知用户“根据现有信息无法回答”不要编造。 - **金融严谨性**所有涉及数据的回答务必准确注明数据来源如“根据实时行情...”或“根据内部文档...”。 - **分步思考**对于复杂问题可以分步骤调用多个工具并整合信息后给出最终答案。 当前对话历史 {chat_history} # 构建提示词模板 prompt ChatPromptTemplate.from_messages([ (system, system_prompt), MessagesPlaceholder(variable_namechat_history), (human, {input}), MessagesPlaceholder(variable_nameagent_scratchpad) # 用于存放Agent的思考过程 ]) # 创建Agent agent create_openai_tools_agent(llm, tools, prompt) # 创建执行器并注入记忆 memory ConversationBufferMemory(memory_keychat_history, return_messagesTrue) agent_executor AgentExecutor(agentagent, toolstools, memorymemory, verboseTrue, handle_parsing_errorsTrue) # 使用示例 if __name__ __main__: # 示例1知识问答 response1 agent_executor.invoke({input: 我们公司对科创板IPO项目的内部审核流程是怎样的}) print(response1[output]) # 模型会先调用‘Internal_Knowledge_Search‘工具获取相关文档片段再生成答案。 # 示例2混合查询 response2 agent_executor.invoke({input: 宁德时代300750今天股价怎么样另外帮我查一下公司内部关于动力电池技术路线的最新观点。}) print(response2[output]) # 模型可能会先调用‘Stock_Quote_Query‘查询股价再调用‘Internal_Knowledge_Search‘检索内部观点最后整合回答。 # 示例3任务执行 response3 agent_executor.invoke({input: 把刚才关于宁德时代股价和内部观点的分析总结一下发到我的邮箱 zhangsancompany.com。}) print(response3[output]) # 模型需要理解“刚才的分析”指代历史对话从记忆memory中提取相关内容生成摘要然后调用‘Email_Sender‘工具。4.3 集成FastAPI提供Web服务最后我们用FastAPI将智能体包装成可调用的API服务。# main.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import Optional from agent_setup import agent_executor # 导入上面创建的agent_executor app FastAPI(title金融大模型问答机器人API) class ChatRequest(BaseModel): message: str session_id: Optional[str] None # 用于支持多会话此处简化处理 class ChatResponse(BaseModel): response: str session_id: str app.post(/chat, response_modelChatResponse) async def chat_endpoint(request: ChatRequest): 核心聊天接口。接收用户消息返回智能体回复。 try: # 这里可以根据session_id从数据库加载不同的memory实现多会话隔离 result agent_executor.invoke({input: request.message}) return ChatResponse(responseresult[output], session_idrequest.session_id or default) except Exception as e: # 处理例如工具调用失败、模型解析错误等情况 raise HTTPException(status_code500, detailf处理请求时出错{str(e)}) app.post(/upload_docs) async def upload_documents(files: List[UploadFile]): 上传文档以更新知识库简化示例实际需要异步处理。 # 实现文档接收、处理、更新向量数据库的逻辑 pass if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)5. 高级技巧、常见问题与避坑指南在实际开发和上线过程中你会遇到各种预料之外的问题。以下是我从项目中总结的核心经验。5.1 提升工具调用准确性的技巧描述驱动的提示工程工具的description和参数的description是模型决策的唯一依据。务必用最清晰、无歧义的自然语言撰写。可以包含正面例子“当用户问...时使用”和反面例子“当用户只是...时不要使用”。结构化输出要求在系统提示词中明确要求模型“分步思考”。对于复杂问题可以鼓励模型先规划步骤“首先我需要查询...然后我需要检索...”这能显著提高多工具调用的逻辑性。LangChain Agent的verboseTrue模式可以打印出这些思考过程便于调试。后处理与验证模型返回的arguments是JSON字符串解析后务必进行有效性验证。例如检查股票代码格式、邮箱地址格式、参数是否在合理范围内如查询天数不能为负数。在调用真实API前增加一层校验逻辑。温度Temperature设置在工具调用场景应将LLM的温度参数设置为较低值如0.1或0.2以降低其回答的随机性确保工具调用决策的稳定性和一致性。5.2 典型错误与排查方案问题现象可能原因排查与解决方案模型不调用工具直接生成回答1. 工具描述不清晰模型不理解何时该用。2. 用户问题模糊模型无法判断是否需要工具。3. 系统提示词未强调“优先使用工具”。1. 重写工具描述使其更具体、场景化。2. 在对话中引导用户提出更明确的需求如“您是想查询实时股价吗”。3. 强化系统提示词中的指令。模型调用了错误的工具1. 工具间功能描述有重叠或歧义。2. 工具名称或参数名容易混淆。1. 重新规划工具边界确保每个工具职责单一。描述中明确区分。2. 使用更具体、差异化的命名如SearchInternalNews和SearchPublicFinancialNews。模型生成的参数值错误或格式不对1. 参数描述不够具体未说明格式如日期格式YYYY-MM-DD。2. 模型“幻觉”编造了不存在的参数值。1. 在参数描述中严格规定格式并使用enum限制可选值。2. 在后端代码中添加严格的格式校验和类型转换对非法参数返回友好错误并让模型重新尝试。多轮对话中工具调用混乱对话历史Memory管理不当导致模型上下文混乱。1. 确保chat_history被正确传入提示词。2. 对于超长对话考虑使用ConversationSummaryMemory或ConversationBufferWindowMemory来压缩或限制历史长度防止token超限。工具执行超时或失败导致流程中断外部API不稳定或网络问题。1. 为所有工具调用添加超时timeout和重试机制retry。2. 在工具函数内部做好异常捕获返回结构化的错误信息如{“error”: “API timeout“}让LLM能理解并生成相应的用户回复如“当前行情服务暂时不可用”。5.3 安全与权限考量工具权限隔离不是所有用户都能调用所有工具。需要在应用层如FastAPI路由或Agent调用前根据用户身份、会话上下文进行权限校验。例如只有认证过的投资顾问角色才能调用query_internal_database工具。输入净化与防注入对于执行数据库查询、系统命令等敏感操作的工具绝对不要将模型生成的参数直接拼接执行。必须使用参数化查询、白名单校验等手段防止潜在的提示词注入攻击诱导模型生成恶意参数。成本与频次控制工具调用可能涉及外部API费用如金融数据API。需要实施限流策略防止恶意或异常请求导致成本激增。5.4 性能优化与扩展异步工具调用如果多个工具之间没有依赖关系可以使用异步async方式并发调用显著降低整体响应延迟。LangChain支持异步Agent。流式输出Streaming对于生成内容较长的回答可以结合FastAPI的StreamingResponse实现逐词或逐句的流式输出提升用户体验。引入GraphRAG对于“找出所有受某供应链断裂影响的公司”这类涉及深度关系推理的问题纯向量检索的RAG可能力不从心。可以结合知识图谱GraphRAG。流程可以是先用RAG检索相关文档用LLM抽取实体关系在图数据库如Neo4j中查询/推理再将结果交给LLM生成最终答案。这相当于为智能体增加了“逻辑推理”专用工具。模型微调LoRA/SFT如果发现模型在特定领域的工具调用决策上持续表现不佳可以考虑使用该领域的对话数据包含正确的工具调用序列对模型进行SFT微调使其更擅长你的业务场景。LoRA微调则是轻量级适配能有效提升模型对专业术语和内部工具的理解。通过以上设计、实现和优化我们构建的金融大模型问答机器人就不再是一个简单的聊天接口而是一个集成了知识、数据和行动能力的真正意义上的“智能体”。Function Calling是赋予其行动力的关节而RAG、GraphRAG、微调等技术则是强化其大脑和记忆的肌肉。这个架构具有强大的通用性稍作改造就能应用到法律、医疗、客服等众多需要“知识行动”的垂直领域。