我们要做的是一个包含前后端完整可运行的小 Agent 产品:
🎨 AI 服装灵感设计师
功能 :用户输入一句话:Agent 会自动完成 5 个步骤:
1️⃣ 拆解子查询
2️⃣ 联网搜索
3️⃣ 总结趋势
4️⃣ 生成文生图 Prompt
5️⃣ 生成图片
通过这个小项目,你可以了解到:
- 怎么使用langchain搭建简单agent流程
- 如何进行搜索、文生图等工具调用
- 怎么将搭建的agent流程部署到一个简单的后端
- 如何添加一个简单的前端入口
完整代码在链接
博主会一步步拆解任务,跟着进行你也可以跑起来!
目录
- [Step0 先从请求一次LLM开始](#Step0 先从请求一次LLM开始)
- [Step1 升级为langchain调用](#Step1 升级为langchain调用)
- [Step2 任务分解执行](#Step2 任务分解执行)
-
- 1)拆分子query
- 2)搜索子query
- [3) 总结搜索信息](#3) 总结搜索信息)
- 4)根据搜索结果凝练文生图prompt
- 5)调用文生图
- [Step3 从最简单的后端开始](#Step3 从最简单的后端开始)
- [Step4 流式返回结果](#Step4 流式返回结果)
- [Step5 加一个简单的前端](#Step5 加一个简单的前端)
- 总结
Step0 先从请求一次LLM开始
在谈 Agent 之前,我们先做一件非常朴素但关键的事:
👉 直接请求大模型帮我们把一句话拆成多个"子查询"。
如果你已经对此很熟悉,可以直接跳到下一步~
所谓请求大模型,就是把我们日常从网页端输入问题问各种大模型得到回答的过程,变成用代码直接发送你的请求到大模型提供的接口,然后拿到输出结果的过程。
比如输入:
赛博朋克风金属反光外套
我们希望模型输出:
{
"sub_queries": [
"赛博朋克风服装风格",
"金属反光材质 外套 设计",
"未来感 女装 外套 趋势"
]
}
各家大模型的调用方式大差不差,以seed为例:
替换为你自己的KEY之后,运行这段代码,就可以看到模型针对我们的问题"将以下服装设计主题拆解为3-6条可搜索的子query:赛博朋克风格女外套"进行的回答
python
from openai import OpenAI
# 直接调用
client = OpenAI(
base_url=OPENAI_API_BASE,
api_key=OPENAI_API_KEY )
completion = client.chat.completions.create(
# Replace with Model ID .
model="doubao-seed-1-6-251015",
messages = [
{"role": "user", "content": "将以下服装设计主题拆解为3-6条可搜索的子query:赛博朋克风格女外套"},
],
)
print(completion.choices[0].message.content)
Step1 升级为langchain调用
OK 经过Step0 我们已经可以成功的请求到一次LLM了,而整个agent就建立在多次调用LLM的基础之上~
但是,在继续进行之前,我们要了解一下Langchain
LangChain 是一个用于开发由语言模型驱动的应用程序的框架。他主要拥有 2 个能力:
可以将 LLM 模型与外部数据源进行连接
允许与 LLM 模型进行交互
可能有人会问,像上面这样直接调用不也能拿到结果吗?为什么还要加别的方式?
像上面这种直接请求的方式能跑,但有 3 个硬伤:
messages = [
{"role": "user", "content": "将以下服装设计主题拆解为3-6条可搜索的子query:赛博朋克风格女外套"},
],
1️⃣ Prompt 写在字符串里,不好维护
2️⃣ 没法复用逻辑
3️⃣ 后面步骤一多,代码逻辑会变得很难看==
---> 升级为langchain的方式调用:
可以统一的修改sysprompt 每次调用的时候 不同的query可以像参数一样传进去,而整个代码都不用变
python
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate,SystemMessagePromptTemplate,HumanMessagePromptTemplate
from langchain_classic.chains import LLMChain
# langchain调用
llm = ChatOpenAI(model="doubao-seed-1-6-251015", temperature=0.4, api_key=OPENAI_API_KEY, openai_api_base=OPENAI_API_BASE)
# 系统指令
sub_sys = """
将以下服装设计主题拆解为3-6条可搜索的子query。只返回JSON格式数据,json_key为sub_queries,value为list格式
主题:{query}
"""
sub_query_prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template(sub_sys),
HumanMessagePromptTemplate.from_template('{query}')
])
sub_query_chain = LLMChain(
llm=llm,
prompt=sub_query_prompt
)
# 每次调用 query可以通过参数传进去
sub_queries = sub_query_chain.run({"query": "赛博朋克风格女外套"})
print(sub_queries)
升级为langchain后
✅ 可复用 Chain
✅ Prompt 与逻辑分离
✅ 后面可以无限接新步骤
✅ 每个步骤都可以单测 / 替换模型
Step2 任务分解执行
在Step1之后,我们只要按照类似的方式,就可以多次请求大模型,上一步的输出可以作为下一步的输入,比如我们的主体逻辑就可以浓缩为这样一个函数:
python
def run_fashion_agent(user_query):
# Step1 拆解子query
sub_query_raw = sub_query_chain.run({"query": user_query})
sub_queries = json.loads(sub_query_raw)['sub_queries']
print("get {} sub queries".format(len(sub_queries)))
# Step2 web search
search_results = []
for q in sub_queries:
print("searching {}".format(q))
search_results.append(web_search.run(q))
print("search result [0], type:{} content:{}".format(type(search_results[0]), search_results[0]))
# Step3 总结搜索结果
summary = summary_chain.run({
"result": search_results
})
if summary:
summary = summary.strip()
print('get summary :{}'.format(summary))
# Step4 根据总结的搜索信息构建图像prompt
final_prompt = prompt_builder_chain.run({
"summary": summary
})
print("#### 文生图 prompt ####")
print(final_prompt)
# Step5 文生图
image_url = generate_image.run(final_prompt)
1)拆分子query
python
sub_sys = """
将以下服装设计主题拆解为3-6条可搜索的子query。只返回JSON格式数据,json_key为sub_queries,value为list格式
主题:{query}
"""
sub_query_prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template(sub_sys),
HumanMessagePromptTemplate.from_template('{query}')
])
sub_query_chain = LLMChain(
llm=llm,
prompt=sub_query_prompt
)
2)搜索子query
大模型本身并不能直接联网搜索(截至目前),我们需要调用外部工具来进行web search。
SerpAPI/Tavily API等许多api都可以
这里使用Tavily为例子 摘自知乎
Tavily Search 是一个专为大型语言模型(LLM)和 AI Agent 优化的搜索引擎 API。
Tavily Search 不仅仅是一个简单的搜索接口,更是一个智能的信息处理层。当一个查询被发送到 Tavily 时,它会在后台执行复杂的搜索、抓取、过滤和信息提取流程,最终将最核心、最相关的内容整合起来 ,以一种对 LLM 友好的格式返回。
使用前需要获取Tavily API 官网
使用起来则很简单
python
from tavily import TavilyClient
tavily = TavilyClient(api_key=TAVILY_API_KEY)
# 搜索
tavily.search(query=query, max_results=5)
3) 总结搜索信息
和拆分子query类似 将搜索结果给到大模型进行总结(prompt可以自由发挥)
python
summary_prompt = ChatPromptTemplate.from_messages([
("system", "你是服装设计趋势分析师"),
("user", """请根据以下搜索结果,总结:
- 风格关键词
- 色彩
- 材质
- 廓形
- 情绪氛围
- 标志性主题
搜索结果:
{result} """)
])
summary_chain = LLMChain(llm=llm, prompt=summary_prompt)
4)根据搜索结果凝练文生图prompt
与1) 3)类似 请求一次LLM
python
# 文生图prompt 构建
prompt_builder_prompt = ChatPromptTemplate.from_messages([
("system", "你是专业文生图 Prompt工程师,而且非常了解时尚和服装设计领域"),
("user", """根据以下服装设计总结,生成一个高质量的文生图prompt。
要求:时尚杂志风格,摄影级画面,秀场风格,中文prompt
设计总结:{summary}
请直接输出文生图prompt,不需要其他内容""")
])
prompt_builder_chain = LLMChain(llm=llm,
prompt=prompt_builder_prompt)
5)调用文生图
支持文生图的模型也很多,这里简单用了seedream,和调用LLM的方式很类似:
python
image_client = OpenAI(
base_url=OPENAI_API_BASE,
api_key=OPENAI_API_KEY,
)
result = image_client.images.generate(
model="doubao-seedream-4-5-251128",
prompt=prompt,
size="2K",
response_format="url",
extra_body={
"watermark": True,
},
)
以上就是我们agent中执行的全部操作了,其实并没有很多代码对不对?可以放到chain.py 和tool.py中方面上面的整个流程run_fashion_agent中按顺序调用
Step3 从最简单的后端开始
既然我们agent的逻辑基本理清,每一步的agent都执行什么操作已经验证过了,现在可以考虑将我们的agent逻辑封装为小型后端服务了。
先从最简单的开始:
现在我们做第一版后端,只干一件事:
接收 query → 调用 sub_query_chain → 返回结果
python
from fastapi import FastAPI, Query
from fastapi.middleware.cors import CORSMiddleware
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
app = FastAPI(title="Fashion Design Agent")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# 调用LLM的逻辑
llm = ChatOpenAI(model="doubao-seed-1-6-251015", temperature=0.4, api_key=OPENAI_API_KEY, openai_api_base=OPENAI_API_BASE)
# 系统指令
sub_sys = """
将以下服装设计主题拆解为3-6条可搜索的子query。只返回JSON格式数据,json_key为sub_queries,value为list格式
主题:{query}
"""
sub_query_prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template(sub_sys),
HumanMessagePromptTemplate.from_template('{query}')
])
sub_query_chain = LLMChain(
llm=llm,
prompt=sub_query_prompt
)
@app.post("/design")
async def design(req: dict):
query = req.get("query", "")
result = sub_query_chain.run({"query": query})
return result
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=9111)
可以看到design函数中调用的sub_query_chain.run()就是我们在Step2中的内容
安装相应依赖后
python -m main
就可以启动后端项目了,如果正常启动,会显示一个地址,可以通过curl命令验证
Step4 流式返回结果
/backend_app
/agent
- __init__.py
- tools.py
- chains.py
- runner.py
- main.py
按照项目目录,我们可以把上面所有的agent逻辑都放在runner中的run_fashion_agent函数中,然后在main中进行调用,那么会等到所有步骤都完成后一次性拿到结果
python
def run_fashion_agent(user_query):
# Step1 拆解子query
sub_query_raw = sub_query_chain.run({"query": user_query})
sub_queries = json.loads(sub_query_raw)['sub_queries']
# 中间步骤
......
# Step5 文生图
image_url = generate_image.run(final_prompt)
但这样等待时间会很长......
更好的方式是流式返回,每一个步骤完成后立即返回
后端可以进一步修改为fast AP i+ SSE流式返回
main函数中:
python
@app.get("/design/stream")
async def design(query: str = Query(...)):
return StreamingResponse(
fashion_agent_stream(query),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
}
)
SSE返回:
python
def sse_event(event_name: str, data: dict):
"""格式化 SSE 事件"""
return f"data: {json.dumps({'step': event_name, 'payload': data}, ensure_ascii=False)}\n\n"
agent逐步返回数据:
python
async def fashion_agent_stream(user_query):
"""SSE流式返回
"""
# step1
yield sse_event("status", {"step":1, "message": "Generating sub queries..."})
sub_query_raw = sub_query_chain.run({"query": user_query})
sub_queries = json.loads(sub_query_raw)['sub_queries']
print("get {} sub queries".format(len(sub_queries)))
yield sse_event("step1", {
"step": 1,
"sub_queries": sub_queries
})
# Step2: seach
yield sse_event("status", {"step":2, "message": "Searching web..."})
search_results = []
for q in sub_queries:
print("searching {}".format(q))
search_results.append(web_search.run(q))
print("finish search, search 0:", search_results[0])
yield sse_event("step2", {
"step":2,
"search_results": search_results
})
.......
yield sse_event("done", {"message": "All steps completed"})
那么到现在为止,你可以得到一个可以完整执行agent全部逻辑,逐步返回每一步结果,最终生成图片链接的后端项目了!
Step5 加一个简单的前端
前端只做 3 件事:
1️⃣ 发送 query
2️⃣ 监听 SSE
3️⃣ 根据 step 更新 UI
核心代码(极简版)
javascript
const BACKEND_URL = "http://127.0.0.1:9111/design/stream";
const url = `${BACKEND_URL}?query=${encodeURIComponent(query)}`;
const es = new EventSource(url);
esRef.current = es;
es.onmessage = (e) => {
try {
const data = JSON.parse(e.data);
const eventType = data.step;
const payload = data.payload;
// 映射后端步骤名到前端步骤名
const stepMapping: Record<string, string> = {
"step1": "sub_queries",
"step2": "search_results",
"step3": "summary",
"step4": "final_prompt",
};
具体可以看链接 frontend就是前端项目,在后端启动之后,启动前端,点击按钮就会执行后端的任务
总结
如果你一路看到这里,你应该已经大致了解了:
✅ 如何用 LLM 拆解任务
✅ 如何用 LangChain 组织多步骤逻辑
✅ 如何用 FastAPI 做 Agent 服务
✅ 如何用 SSE 做实时流式返回
✅ 如何用前端可视化 Agent 思考过程
可以尝试运行[完整项目] (https://github.com/JoJoJun/ai_agent_for_clothes_design/tree/main)整体感受一下 (顺便点个star👍)
后面也许可以扩展成:
🧳 旅行规划 Agent
📈 投资研究 Agent
🧠 学习助理 Agent
🛍 电商选品 Agent
结构都是类似的~