前言
在AI快速发展的今天,如何将文本中的技术信息自动整理成可视化关系图,是很多开发者和数据分析师面临的需求。本文将分享一个完整项目案例------AI关系图生成助手 ,它基于 LangChain 、LangGraph 和 Chainlit 构建,能够自动抽取实体关系三元组并生成 Graphviz 和 Mermaid 可视化图表。
本文将从项目架构、核心代码、提示词设计到部署指南,全面讲解如何从零搭建这样一个智能助手。



项目概述
AI关系图生成助手主要功能:
- 自动从文本中提取技术相关实体关系(三元组)
- 支持 Graphviz 和 Mermaid 两种可视化方式
- 可通过 Chainlit 提供 Web 界面进行交互
- 支持中文显示,适合技术文档和系统架构可视化
项目结构如下:
project/
├── agent_graph.py # AI代理核心逻辑和图结构定义
├── app.py # Chainlit应用入口,处理用户交互
├── chainlit.md # 用户使用指南和提示词示例
├── graphviz_render.py # 图形渲染工具函数(Graphviz和Mermaid)
├── mermaid_renderer.py # Mermaid图表HTML渲染器
└── requirements.txt # 项目依赖
环境依赖
Python依赖
txt
chainlit
langgraph
langchain
langchain-ollama
pydantic
typing_extensions
graphviz
系统依赖
- Ubuntu/Debian :
sudo apt-get install graphviz - macOS :
brew install graphviz - Windows :https://graphviz.org/download/
写入PATH
核心实现
1️⃣ AI代理逻辑(agent_graph.py)
- 定义
Relationship和State数据结构 - 使用 LangChain + Ollama LLM 处理文本
- 通过 LangGraph 构建状态流
核心流程:
- 接收用户消息
- 调用 LLM 抽取技术实体关系
- 生成 Graphviz 或 Mermaid 可视化
- 返回 Web 页面或图片给用户
主要代码片段:
python
# agent_graph.py
from typing import Annotated, List
from typing_extensions import TypedDict
from pathlib import Path
from pydantic import BaseModel, Field
from langchain_core.messages import (
HumanMessage,
AIMessage,
AnyMessage,
)
from langchain_core.tools import tool
from langchain_ollama import ChatOllama
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from graphviz_render import relationships_to_png, relationships_to_mermaid
# -----------------------
# 1️⃣ 数据结构
# -----------------------
class Relationship(BaseModel):
subject: str = Field(...)
predicate: str = Field(...)
object: str = Field(...)
class State(TypedDict):
messages: Annotated[list[AnyMessage], add_messages]
relationships: Annotated[List[Relationship], lambda a, b: a + b]
# -----------------------
# 2️⃣ 工具
# -----------------------
@tool
def multiply(a: int, b: int) -> int:
"""将两个整数相乘"""
return a * b
tools = [multiply]
tool_node = ToolNode(tools)
# -----------------------
# 3️⃣ LLM
# -----------------------
llm = ChatOllama(model="qwen2.5:7b", temperature=0)
model_with_tools = llm.bind_tools(tools)
class Extraction(BaseModel):
relationships: List[Relationship]
extract_llm = llm.with_structured_output(Extraction)
# -----------------------
# 4️⃣ 节点定义
# -----------------------
def agent(state: State):
response = model_with_tools.invoke(state["messages"])
return {"messages": [response]}
def extract_relationships(state: State):
# ✅ 抽 Human + AI(避免漏掉解释性内容)
text = "\n".join(
m.content
for m in state["messages"]
if isinstance(m, (HumanMessage, AIMessage))
and isinstance(m.content, str)
)
if not text.strip():
return {"relationships": []}
prompt = f"""
请从以下文本中抽取技术相关的实体关系三元组(subject, predicate, object)。
要求:
1. 只抽取技术相关内容,例如编程语言、框架、工具、算法、系统、方法等。
2. 忽略与技术无关的实体或句子(如寒暄、提问、假设)。
3. 输出格式为 JSON 数组,每个元素包含 "subject", "predicate", "object"。
4. 对同一实体的多重关系分别列出。
文本:
{text}
输出示例:
[
{{"subject": "Python", "predicate": "支持", "object": "面向对象编程"}},
{{"subject": "LangChain", "predicate": "使用", "object": "LLM 模型"}},
{{"subject": "Docker", "predicate": "部署", "object": "Web 应用"}}
]
"""
result = extract_llm.invoke(prompt)
return {"relationships": result.relationships}
STATIC_DIR = Path(__file__).parent / "static"
def visualize(state: State):
relationships = state.get("relationships", [])
if not relationships:
return {"messages": [AIMessage(content="暂无可视化关系")]}
png_path = STATIC_DIR / "relationships.png"
relationships_to_png(relationships, png_path)
# 返回一个特殊标记,让app.py知道需要显示图片
content = f"GRAPHVIZ_IMAGE:{png_path}:当前提取的关系图(共 {len(relationships)} 条关系)"
# ✅ 关键:只返回 AIMessage,避免重复
return {"messages": [AIMessage(content=content)]}
def visualize_mermaid(state: State):
relationships = state.get("relationships", [])
if not relationships:
return {"messages": [AIMessage(content="暂无可视化关系")]}
mermaid_code = relationships_to_mermaid(relationships)
content = (
"**当前提取的关系图(Mermaid格式):**\n\n"
f"```mermaid\n{mermaid_code}\n```\n\n"
f"(共 {len(relationships)} 条关系)"
)
return {"messages": [AIMessage(content=content)]}
# -----------------------
# 5️⃣ 构建 LangGraph
# -----------------------
builder = StateGraph(State)
builder.add_node("agent", agent)
builder.add_node("tools", tool_node)
builder.add_node("extract", extract_relationships)
builder.add_node("visualize", visualize)
builder.add_node("visualize_mermaid", visualize_mermaid)
builder.add_edge(START, "agent")
builder.add_conditional_edges(
"agent",
tools_condition,
{"tools": "tools", END: "extract"},
)
builder.add_edge("tools", "agent")
builder.add_edge("extract", "visualize")
builder.add_edge("visualize", "visualize_mermaid")
builder.add_edge("visualize_mermaid", END)
graph = builder.compile()
2️⃣ Chainlit应用入口(app.py)
python
# app.py
import chainlit as cl
from pathlib import Path
import mimetypes
from langchain_core.messages import (
HumanMessage,
AIMessage,
ToolMessage,
)
from agent_graph import graph
STATIC_DIR = Path(__file__).parent / "static"
STATIC_DIR.mkdir(exist_ok=True)
@cl.on_chat_start
async def start_chat():
await cl.Message(
content="✅ Agent 已启动(Graphviz 关系图版)"
).send()
# Agent 结构图
try:
png = graph.get_graph().draw_png()
path = STATIC_DIR / "agent_structure.png"
path.write_bytes(png)
# 将图片作为元素添加
elements = [
cl.Image(name="agent_structure", display="inline", path=str(path))
]
await cl.Message(
content="**Agent 结构图:**",
elements=elements
).send()
except Exception as e:
await cl.Message(content=f"结构图生成失败:{e}").send()
@cl.on_message
async def handle_message(message: cl.Message):
inputs = {"messages": [HumanMessage(content=message.content)]}
sent = set() # ✅ 防止重复发送
async for step in graph.astream(inputs, stream_mode="values"):
msgs = step.get("messages", [])
if not msgs:
continue
last = msgs[-1]
if isinstance(last, AIMessage) and last.content:
if last.content not in sent:
sent.add(last.content)
content = str(last.content)
# 检查是否是需要显示图片的特殊标记
if content.startswith("GRAPHVIZ_IMAGE:"):
parts = content.split(":", 2)
if len(parts) >= 3:
img_path = parts[1]
caption = parts[2]
elements = [
cl.Image(name="relationships", display="inline", path=img_path)
]
await cl.Message(
content=f"**{caption}**",
elements=elements,
author="Assistant"
).send()
else:
# 普通内容直接显示
await cl.Message(
content=content,
author="Assistant"
).send()
elif isinstance(last, ToolMessage) and last.content:
await cl.Message(
content=last.content,
author="Tool"
).send()
通过 Chainlit,用户可直接在 Web 界面与 AI 代理交互,实时查看关系图。
3️⃣ Graphviz与Mermaid渲染工具
python
def relationships_to_mermaid(relationships: List):
mermaid_lines = ["graph TD"]
for r in relationships:
mermaid_lines.append(f"{r.subject}[{r.subject}] -->|{r.predicate}| {r.object}[{r.object}]")
return "\n".join(mermaid_lines)
def relationships_to_png(relationships: List, output_path: Path):
from graphviz import Digraph
dot = Digraph(format="png", graph_attr={"fontname": "SimHei"})
for r in relationships:
dot.node(r.subject, r.subject)
dot.node(r.object, r.object)
dot.edge(r.subject, r.object, label=r.predicate)
dot.render(output_path.with_suffix(""), cleanup=True)
完整代码开源
本项目的完整源代码已托管在 Gitee 上,方便大家下载、学习和二次开发:
🔗 https://gitee.com/michah/langgraph-relation-visualize
提示词设计
为了让 LLM 提取出干净、准确的技术关系,需要设计高质量提示词:
- "抽取下面文本中的技术实体及调用关系,输出JSON三元组,只保留技术相关"
- "分析系统架构中的服务、模块和接口关系,生成Graphviz可视化图"
- "提取微服务调用链,标注服务A → 服务B → 数据库的关系,忽略业务流程"
使用示例
技术关系
输入:
Python连接数据库,数据库存储用户信息,API处理请求
生成 Graphviz 或 Mermaid 图:
- 节点:Python、数据库、API
- 边:连接、存储、处理
业务流程关系
输入:
用户注册账户,系统验证邮箱,发送欢迎邮件
系统可生成技术实现对应关系图。
部署指南
- 安装 Python 依赖:
bash
pip install -r requirements.txt
- 安装 Graphviz:
bash
sudo apt-get install graphviz
- 启动应用:
bash
chainlit run app.py
访问 http://localhost:8000 即可使用。
总结
通过本项目,我们实现了一个完整的 AI关系图生成助手:
- LangChain 负责与 LLM 交互
- LangGraph 管理状态流和工具调用
- Chainlit 提供 Web 界面
- Graphviz / Mermaid 提供多样化可视化
该项目可应用于技术文档分析、系统架构可视化、知识图谱构建等场景,是 技术探索者 值得收藏和二次开发的工具。
💡 建议
- 文本中明确写出系统、模块、接口、工具、算法等技术实体,提高提取准确率
- 可扩展为自动化流程分析、微服务调用链追踪、前端可视化展示