第19章 构建文件分析 AI Agent
接下来,我们将把书中涉及的几个概念结合起来,搭建一个文件分析 AI Agent的架构。
这个项目在 Gradio 实战中特别实用,因为它融合了多个核心概念:
文件上传
文本提取
状态管理(State)
AI 模型
聊天界面
多输入
错误处理
什么让它成为 Agent?
在 AI 领域,"agent" 这个词的含义五花八门。
本项目采用一个务实的定义:AI Agent 是一个能接收信息、判断需要何种处理、调用工具或函数、并生成有效回复的系统。
我们的文件分析应用可以:
接收文档
提取内容
存储处理后的文本
接收用户提问
分析文档
生成回答
工作流程
应用按以下流程运行:
上传文档
接着:
提取文本
然后:
存储文档上下文
再然后:
用户提问
最后:
AI 分析相关内容
从文档提取开始
为了简洁,我们先从文本文件入手。
def extract_text(file):
if file is None:
return ""
with open(file.name, "r", encoding="utf-8") as f:
return f.read()
存储提取的文本
使用 State:
document_text = gr.State("")
接着:
extract_button.click(
fn=extract_text,
inputs=file,
outputs=[document_text, preview]
)
添加提问框
question = gr.Textbox(
label="Ask a question",
placeholder="What does this document say about..."
)
编写分析函数
def answer_question(document, question):
if not document:
return "Please upload a document first."
if not question.strip():
return "Please enter a question."
return (
"An AI model would analyze the document "
"and answer the question here."
)
接入模型
真实的函数大概会变成这样:
def answer_question(document, question):
prompt = f"""
Answer the user's question using only the document below.
DOCUMENT:
{document}
QUESTION:
{question}
"""
return model.generate(prompt)
为什么要限定文档范围
如果目标是文档问答,通常希望模型只依赖给定的文档作答。
否则,模型可能基于自身的一般知识来回答,导致结果产生误导。
更强的指令可以这样写:
Use only the provided document.
If the answer cannot be found, say that the document does not contain enough information.
处理大文档
每次提问都把整个大文档发给模型,效率可能很低。
设想一个 300 页的 PDF,用户每次问:
What was the conclusion?
你肯定不想每次都把 300 页全部发过去。
这时检索技术就派上用场了。
把文档切分成块
可以把文档切分成更小的片段。
概念上就是:
chunks = split_document(document)
例如:
Chunk 1
Chunk 2
Chunk 3
...
Chunk 100
找到相关的块
检索系统可以在这些块中搜索与用户问题相关的内容,然后只把最相关的片段发给模型。
这种模式通常被称为检索增强生成(retrieval-augmented generation,RAG)。
一个简化的检索流程
Document
→ Split into chunks
→ Store chunks
→ User asks question
→ Retrieve relevant chunks
→ Send chunks + question to model
→ Generate answer
为 Chunks 添加状态
可以保存处理后的 chunks:
chunks_state = gr.State([])
文档处理完成后:
def process_document(file):
text = extract_text(file)
chunks = split_text(text)
return chunks, text
然后:
process_button.click(
fn=process_document,
inputs=file,
outputs=[chunks_state, preview]
)
基于检索的问答
概念上:
def answer_question(chunks, question):
relevant_chunks = retrieve(chunks, question)
context = "\n\n".join(relevant_chunks)
prompt = f"""
Use the following context to answer the question.
CONTEXT:
{context}
QUESTION:
{question}
"""
return model.generate(prompt)
添加对话历史
当用户可以追问时,文件分析 Agent 会实用得多。
例如:
User:
What is this report about?
Assistant:
It discusses...
User:
Who conducted the study?
Assistant:
The study was conducted by...
User:
When was it published?
Assistant:
According to the document...
聊天机器人需要同时利用文档上下文和对话上下文。
完整架构
一个简化版的应用大致如下:
import gradio as gr
def process_document(file):
if file is None:
return "", "No document uploaded."
text = extract_text(file)
return text, text[:5000]
def answer_question(document, question, history):
if not document:
return "Please upload a document first."
if not question.strip():
return "Please enter a question."
prompt = f"""
Answer the question using the document.
DOCUMENT:
{document}
QUESTION:
{question}
"""
return call_model(prompt)
with gr.Blocks() as demo:
gr.Markdown("# File Analysis AI Agent")
document = gr.State("")
with gr.Row():
with gr.Column():
file = gr.File(
label="Upload Document"
)
process_button = gr.Button(
"Process Document"
)
preview = gr.Textbox(
label="Document Preview",
lines=15
)
with gr.Column():
chatbot = gr.Chatbot()
question = gr.Textbox(
label="Ask a Question"
)
ask_button = gr.Button(
"Ask"
)
process_button.click(
fn=process_document,
inputs=file,
outputs=[document, preview]
)
demo.launch()
这还不是一个完整的 AI Agent,这是有意为之。
应用架构才是关键所在。
为什么架构重要
你可以把所有逻辑塞进一个函数里:
def do_everything(...):
...
但这样很快就会变得难以理解。
更好的做法是拆分:
extract_text()
split_text()
retrieve()
build_prompt()
call_model()
format_response()
每个函数只负责一件事。
工具调用
AI Agent 能做的远不止生成文本,它还能通过工具与外部系统交互,执行模型自身无法完成的操作。
工具本质上就是一个函数,AI 模型在需要执行特定任务时调用它。比如,一个 Agent 可以拥有搜索网页、读取文件、执行计算、查询数据库或调用 API 等工具。
基本流程如下:
用户向 Agent 发出请求。
Agent 判断能否凭已有知识回答,还是需要调用工具。
如果需要工具,Agent 会带上合适的参数生成一次工具调用。
工具执行相应操作并返回结果。
Agent 根据返回的结果继续处理用户的请求。
Agent 基于获取到的信息给出最终回答。
举个例子,用户问 AI Agent“今天纽约的天气怎么样?”,Agent 会意识到自己需要最新信息。它不会瞎猜,而是调用天气工具获取当前天气状况,再据此回答用户。
在 Gradio 应用中,工具通常以 Python 函数或外部服务的形式实现,Gradio 界面则为 Agent 提供调用这些能力的方式。
关键区别在于:模型负责判断什么时候该用工具,而工具负责实际执行操作。这样一来,AI Agent 就不再局限于生成文本,而是能真正与数据、软件、API 和其他系统交互。
该用确定性工具时就用确定性工具
既然 Python 一行代码就能算出来:
sum(values) / len(values)
就没有必要让语言模型去猜结果。
让模型做它擅长的事,需要精确计算的任务交给确定性工具。
文件分析的安全性
这个应用可能会处理任意的文档,所以要考虑:
文件大小
支持的格式
恶意文件
敏感信息
临时存储
API 传输
数据留存
如果文档会被发送到外部 AI API,用户应当知道自己的内容正在被传输给该服务。
动手试一试
构建一个文本文件分析助手,要求:
接受
.txt文件提取文本内容
显示预览
把文本存入 state
支持提问
返回答案
接着升级为支持 PDF。
再添加检索功能,避免将大文档整体发送给模型。
核心要点
文件分析 Agent 融合了多个 Gradio 概念。
State 可存储已提取的文档信息。
AI 模型可借助文档上下文回答问题。
大文档受益于分块与检索。
聊天历史提供对话上下文。
精确计算等任务应使用确定性工具。
拆分独立函数让 Agent 架构更易维护。
文件处理应用需格外关注安全与隐私。