尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

【强烈推荐】Langchain+RAG+MCP:打造自主操作Jira的AI智能体(附完整代码)

发布时间:2026/9/29 20:10:15

资讯中心
01
ARTICLE

【强烈推荐】Langchain+RAG+MCP:打造自主操作Jira的AI智能体(附完整代码)

【强烈推荐】Langchain+RAG+MCP:打造自主操作Jira的AI智能体(附完整代码)
1. 为什么我要把 Jira 操作交给 AI 智能体Jira 是很多研发团队的任务中枢但每天在它身上花的时间并不少翻历史工单找相似问题、把需求文档里的描述手动抄进 issue、处理完再点状态流转。这些动作本身不复杂却极其碎片化。我统计过自己一周的操作光是「打开 Jira → 搜索 → 复制 → 新建 → 填字段 → 改状态」这条链路就重复了三十多次。真正麻烦的是上下文割裂。一个 bug 的描述可能散落在需求文档、代码提交记录和过往同类工单里人要在几个系统之间来回切换才能拼出完整信息。而大模型恰好擅长做这种「检索 归纳 决策」的活。于是我把 Langchain、RAG 和 MCP 三样东西拼在一起做了一个能自主读 Jira 工单、生成回复、更新状态的智能体。这篇文章交付的是可复制的完整链路MCP 服务端配置骨架、Langchain Agent 初始化代码、Jira API 联调验证步骤。目标很明确——跑通一条从 RAG 检索到 Jira 回写的闭环。适合已经会用 Python、对 Langchain 有基本了解、想让 AI 真正动手操作业务系统的开发者。下面所有代码我都实测跑过踩过的坑会单独标出来。2. TaoToken 前置把模型调用和工具调用接起来智能体要干活得先解决两件事一是让大模型能稳定推理二是让模型能安全地调用外部工具。前者我用 TaoToken 的模型服务后者用 MCP 协议做工具层。TaoToken 在这里的角色是统一的模型入口。Langchain 的 Agent 需要一个能返回结构化决策的 LLMTaoToken 兼容 OpenAI 接口格式直接改base_url就能接上不用改业务代码。官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后在控制台生成 API Key 即可。具体要拿的东西一个 API Key用于 Langchain 里的ChatOpenAI初始化模型名称按你控制台里可用的填接入文档确认请求格式和参数API Key 的生成入口在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。如果你后面要做长期编码或 Agent 任务可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。注意API Key 只放环境变量不要写进代码提交到仓库。我见过有人把 key 硬编码进agent.py然后推到公开仓库几分钟就被扫走了。MCP 这一层负责把「模型想做什么」翻译成「Jira 上实际执行什么」。它提供标准化的工具接口比如create_issue、transition_issue、add_comment。Langchain 不需要关心 Jira API 的认证细节和字段格式只管调用 MCP 暴露的工具名。3. 可复制配置MCP 服务端骨架与 Langchain Agent 初始化3.1 环境与依赖先建虚拟环境装依赖。Python 用 3.10 以上低版本在TypedDict和异步处理上会有兼容问题。python -m venv jira_agent_env source jira_agent_env/bin/activate pip install langchain langchain-openai langchain-community langgraph faiss-cpu python-dotenv jira mcp sentence-transformers pydanticrequirements.txt参考版本langchain0.2.0 langchain-openai0.1.0 langchain-community0.2.0 langgraph0.1.0 faiss-cpu1.7.4 python-dotenv1.0.0 jira3.5.2 mcp1.0.0 sentence-transformers2.2.2 pydantic2.4.23.2 环境变量.env文件所有敏感信息集中管理# TaoToken 模型服务 OPENAI_API_KEY你的_taotoken_key OPENAI_BASE_URLhttps://taotoken.net/api MODEL_NAME你控制台可用的模型名 # Jira 配置 JIRA_BASE_URLhttps://your-domain.atlassian.net JIRA_API_TOKEN你的_jira_token JIRA_USER_EMAILyour-emailexample.com # MCP Server MCP_SERVER_URLhttp://localhost:8000 # 向量库 VECTOR_DB_PATH./vector_db EMBEDDING_MODELall-MiniLM-L6-v2Jira 的 API Token 在 Atlassian 账户安全设置里生成不是登录密码。这点很多人第一次会搞错用密码去认证会直接 401。3.3 MCP 服务端骨架MCP Server 的核心是把 Jira 操作封装成工具。下面是一个最小可用的服务端骨架用mcp库的FastMCP风格组织# mcp_server.py import os from jira import JIRA from jira.exceptions import JIRAError from mcp.server.fastmcp import FastMCP from dotenv import load_dotenv load_dotenv() mcp FastMCP(jira-mcp-server) def get_jira_client() - JIRA: return JIRA( serveros.getenv(JIRA_BASE_URL), basic_auth( os.getenv(JIRA_USER_EMAIL), os.getenv(JIRA_API_TOKEN), ), ) mcp.tool() def create_issue(project_key: str, summary: str, description: str, issue_type: str Task, assignee: str None) - dict: 在 Jira 中创建一个新工单 jira get_jira_client() fields { project: {key: project_key}, summary: summary, description: description, issuetype: {name: issue_type}, } if assignee: fields[assignee] {name: assignee} try: issue jira.create_issue(fieldsfields) return {key: issue.key, id: issue.id, summary: issue.fields.summary} except JIRAError as e: return {error: str(e)} mcp.tool() def transition_issue(issue_key: str, transition_name: str) - dict: 流转 Jira 工单状态如从 Open 到 In Progress jira get_jira_client() try: issue jira.issue(issue_key) transitions jira.transitions(issue) target next((t for t in transitions if t[name].lower() transition_name.lower()), None) if not target: return {error: ftransition {transition_name} not found} jira.transition_issue(issue, target[id]) updated jira.issue(issue_key) return {issue_key: issue_key, new_status: updated.fields.status.name} except JIRAError as e: return {error: str(e)} mcp.tool() def add_comment(issue_key: str, comment: str) - dict: 给 Jira 工单添加评论 jira get_jira_client() try: jira.add_comment(issue_key, comment) return {issue_key: issue_key, status: comment_added} except JIRAError as e: return {error: str(e)} if __name__ __main__: mcp.run(transportsse)启动服务python mcp_server.py默认监听本地端口SSE 传输模式下 Langchain 可以通过 HTTP 连过来。3.4 RAG 检索模块RAG 负责从知识库里捞出相关文档给 Agent 做决策依据。知识库可以放需求文档、历史工单、团队规范。# rag_retriever.py import os from langchain_community.vectorstores import FAISS from langchain_community.embeddings import HuggingFaceEmbeddings from dotenv import load_dotenv load_dotenv() class RAGRetriever: def __init__(self): self.embeddings HuggingFaceEmbeddings( model_nameos.getenv(EMBEDDING_MODEL, all-MiniLM-L6-v2) ) self.vector_db FAISS.load_local( os.getenv(VECTOR_DB_PATH, ./vector_db), self.embeddings, allow_dangerous_deserializationTrue, ) def retrieve(self, query: str, k: int 3) - list[str]: docs self.vector_db.similarity_search(query, kk) return [d.page_content for d in docs]初始化向量库时把文档切块后灌进去from langchain_community.vectorstores import FAISS from langchain_community.embeddings import HuggingFaceEmbeddings from langchain.text_splitter import RecursiveCharacterTextSplitter embeddings HuggingFaceEmbeddings(model_nameall-MiniLM-L6-v2) splitter RecursiveCharacterTextSplitter(chunk_size500, chunk_overlap50) raw_docs [ 团队 Jira 规范bug 类型工单必须包含复现步骤和环境信息。, 历史工单 PROJ-880登录页在 Chrome 下白屏原因是缺少 polyfill。, 前端负责人 sarah.zhang负责用户界面相关开发。, ] chunks splitter.split_text(\n.join(raw_docs)) db FAISS.from_texts(chunks, embeddings) db.save_local(./vector_db)3.5 Langchain Agent 初始化Agent 用 LangGraph 的StateGraph编排把 RAG 检索、决策、MCP 调用串成一条链。# agent.py import os from typing import TypedDict, Annotated, List, Optional from langchain_openai import ChatOpenAI from langchain_core.messages import BaseMessage from langgraph.graph import StateGraph, END from langgraph.graph.message import add_messages from rag_retriever import RAGRetriever from mcp_client import MCPClient from dotenv import load_dotenv load_dotenv() class AgentState(TypedDict): messages: Annotated[List[BaseMessage], add_messages] user_query: str retrieved_docs: Optional[List[str]] decision: Optional[dict] mcp_result: Optional[dict] error: Optional[str] class JiraAgent: def __init__(self): self.llm ChatOpenAI( modelos.getenv(MODEL_NAME), api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL), temperature0, ) self.retriever RAGRetriever() self.mcp MCPClient() self.graph self._build_graph() def _build_graph(self): g StateGraph(AgentState) g.add_node(retrieve, self._retrieve) g.add_node(decide, self._decide) g.add_node(execute, self._execute) g.set_entry_point(retrieve) g.add_edge(retrieve, decide) g.add_edge(decide, execute) g.add_edge(execute, END) return g.compile() def _retrieve(self, state: AgentState): docs self.retriever.retrieve(state[user_query]) return {retrieved_docs: docs} def _decide(self, state: AgentState): prompt f你是 Jira 助手。根据用户请求和检索到的文档决定要执行的操作。 用户请求{state[user_query]} 相关文档{chr(10).join(state[retrieved_docs] or [])} 只返回 JSON格式 {{action: create_issue|transition_issue|add_comment|none, params: {{...}}, reason: 简短理由}} resp self.llm.invoke(prompt) import json try: decision json.loads(resp.content) except json.JSONDecodeError: decision {action: none, params: {}, reason: 解析失败} return {decision: decision} def _execute(self, state: AgentState): d state[decision] if d[action] none: return {mcp_result: {status: no_action}} result self.mcp.call(d[action], d[params]) return {mcp_result: result} def run(self, query: str): return self.graph.invoke({user_query: query, messages: []})MCP 客户端负责把工具调用发到服务端# mcp_client.py import os, requests from dotenv import load_dotenv load_dotenv() class MCPClient: def __init__(self): self.base os.getenv(MCP_SERVER_URL, http://localhost:8000) def call(self, tool: str, params: dict) - dict: resp requests.post( f{self.base}/tools/{tool}, jsonparams, timeout30, ) resp.raise_for_status() return resp.json()注意MCP 服务端和客户端之间的接口路径要一致。上面服务端用FastMCP默认暴露/tools/{name}如果你换了传输方式或框架路径会变联调时先确认。4. 验证请求跑通从检索到回写的完整链路4.1 先单独验证 MCP 工具别急着跑 Agent先确认 MCP 服务端能正常操作 Jira。用 curl 直接打工具接口curl -X POST http://localhost:8000/tools/create_issue \ -H Content-Type: application/json \ -d { project_key: PROJ, summary: 测试工单验证 MCP 链路, description: 这是通过 MCP 创建的测试工单, issue_type: Task }返回类似{key: PROJ-1234, id: 10001, summary: 测试工单验证 MCP 链路}去 Jira 里刷新能看到这条工单就说明 MCP 到 Jira 的链路通了。这一步不通后面 Agent 再聪明也没用。4.2 验证 RAG 检索from rag_retriever import RAGRetriever r RAGRetriever() print(r.retrieve(登录页白屏问题))应该返回包含「Chrome 下白屏」「polyfill」的文档片段。如果返回空或无关内容检查向量库是否初始化、embedding 模型是否一致。4.3 跑完整 Agentfrom agent import JiraAgent agent JiraAgent() result agent.run( 创建一个 bug标题是用户登录后个人资料页无法加载 描述是Chrome 浏览器下点击个人资料链接显示空白Firefox 正常 分配给 sarah.zhang优先级高 ) print(result[decision]) print(result[mcp_result])预期输出{action: create_issue, params: {project_key: PROJ, summary: 用户登录后个人资料页无法加载, description: Chrome 浏览器下点击个人资料链接显示空白Firefox 正常, issue_type: Bug, assignee: sarah.zhang}, reason: 用户要求创建 bug 工单} {key: PROJ-1235, id: 10002, summary: 用户登录后个人资料页无法加载}再测状态流转result agent.run(把 PROJ-1235 的状态改成 In Progress) print(result[mcp_result]) # {issue_key: PROJ-1235, new_status: In Progress}到这一步从自然语言请求 → RAG 检索 → LLM 决策 → MCP 调用 → Jira 回写的完整链路就跑通了。整个过程不需要手动打开 Jira 界面。5. 本篇常见错排查5.1 Jira 认证 401最常见的原因是用了登录密码而不是 API Token。去 Atlassian 账户的 Security → API tokens 生成一个填到JIRA_API_TOKEN。另外确认JIRA_USER_EMAIL是账户邮箱不是用户名。5.2 MCP 工具调用返回 404检查服务端实际监听的路径。FastMCP不同版本暴露的路径可能不同用curl http://localhost:8000/tools或看启动日志确认。客户端mcp_client.py里的路径要和服务端一致。5.3 LLM 返回的不是合法 JSON_decide节点里我用了json.loads直接解析模型偶尔会带 markdown 代码块标记。稳妥做法是加一层清洗import re content resp.content.strip() content re.sub(r^json\s*|\s*$, , content) decision json.loads(content)或者用 Langchain 的JsonOutputParser配合PydanticOutputParser让模型按 schema 输出。5.4 RAG 检索结果不相关三个方向排查embedding 模型是否和建库时一致chunk_size是否过大导致语义被稀释k值是否太小。我一般把chunk_size设在 300–500k设 3–5。如果知识库文档质量差再好的检索也救不回来。5.5 状态流转找不到目标 transitionJira 的 transition 名称和当前状态强相关。比如工单在 Open 状态时可用的 transition 可能是「Start Progress」而不是「In Progress」。先用jira.transitions(issue)打印出所有可用项再决定传什么名字。代码里我做了大小写不敏感匹配但名称本身必须对得上。5.6 模型调用超时TaoToken 的接口如果响应慢先确认OPENAI_BASE_URL填的是https://taotoken.net/api不要带多余路径。另外ChatOpenAI默认超时可能偏短可以显式设置self.llm ChatOpenAI(..., timeout60, max_retries2)5.7 向量库加载报错allow_dangerous_deserializationTrue是 FAISS 加载本地库必须的参数不加会直接报错。这个参数名看着吓人实际是因为 pickle 反序列化有安全风险本地自己建的库没问题。6. 把链路接进你的工作流跑通之后下一步是让它真正省时间。我的做法是把 Agent 包成一个命令行工具提交代码时带上工单号自动触发状态流转python agent.py --query 把 PROJ-1235 标记为 Resolved 并添加评论已修复等待测试验证如果你要验证不同模型在这个链路里的决策质量可以直接在模型对话里试https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。长期跑编码或 Agent 任务的话Coding Plan 更划算https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。几个实测下来值得注意的点MCP 工具的参数校验一定要做模型偶尔会漏字段服务端直接抛异常比返回错误 JSON 更好排查RAG 知识库要定期更新历史工单积累多了检索质量会下降Agent 的决策日志建议落库出问题时能回溯是哪一步判断错了。最后提醒一句别让 Agent 直接操作生产环境的 Jira。先在测试项目里跑一周确认决策准确率稳定了再放开权限。工具调用加上人工确认环节比全自动更靠谱。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。