智能对话式表单系统
1. 背景与需求分析
在智能化服务日益普及的今天,传统的表单填写方式已难以满足用户对交互体验的高要求。对话式表单系统通过自然语言交互,能够显著提升用户体验,尤其适用于:
- 复杂表单的逐步引导填写
- 移动端受限场景下的数据采集
- 老年人或儿童友好型交互界面
- 客户服务中的信息收集场景
LangChain框架凭借其强大的链式调用能力和多模型集成特性,为构建对话式表单系统提供了理想解决方案。本文将详细介绍如何基于LangChain实现一个完整的对话式表单系统。
2. 系统架构设计
2.1 核心组件
2.2 技术选型
组件 | 技术方案 | 备选方案 |
---|---|---|
框架核心 | LangChain | Rasa, Dialogflow |
大语言模型 | DeepSeek/Qianfan | GPT-4, Claude |
表单定义 | Pydantic模型 | JSON Schema |
持久化存储 | SQLite/内存 | MongoDB |
3. 核心实现代码
3.1 表单模型定义
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any, Type
from lang_chain_base.tools.search_tool import RobustJsonParser
class BaseDialogModel(BaseModel):
"""对话式表单基类"""
def get_empty_fields(self) -> Dict[str, str]:
"""获取未填写的字段及其描述"""
return {
name: field.description
for name, field in type(self).model_fields.items()
if getattr(self, name) is None or getattr(self, name) == ""
}
def update_from_model(self, other: 'BaseDialogModel') -> None:
"""
从另一个 BaseModel 实例更新当前实例的字段值
:param other: 另一个 UserModel 实例
"""
for field_name in type(self).model_fields.keys():
value = getattr(other, field_name)
if value is not None:
setattr(self, field_name, value)
def is_complete(self) -> bool:
"""检查表单是否填写完整(所有非可选字段均有值)"""
for name, field in type(self).model_fields.items():
# 检查字段值是否为 None
value = getattr(self, name)
if value is None or value == "":
return False
return True
# 示例:用户信息收集表单
class UserInfoForm(BaseDialogModel):
name: Optional[str] = Field(None, description="您的姓名")
age: Optional[int] = Field(None, description="年龄")
gender: Optional[str] = Field(None, description="性别(男/女/其他)")
email: Optional[str] = Field(None, description="电子邮箱")
address: Optional[str] = Field(None, description="居住地址")
3.2 对话管理核心
from langchain_core.language_models import BaseLanguageModel
from langchain_core.prompts import PromptTemplate
from langchain.chains import LLMChain
import json
class DialogFormManager:
def __init__(self, llm: BaseLanguageModel, typeForm: type[BaseDialogModel]):
self.llm = llm
self.typeForm = typeForm
self.setup_chains()
def setup_chains(self):
"""初始化对话链"""
# 字段提问生成链
self.ask_chain = LLMChain(
llm=self.llm,
prompt=PromptTemplate.from_template(
"您需要收集以下信息,请用自然语言提问获取其中一个缺失字段的值:"
"缺失字段: {empty_fields}\n"
"请只输出问题,不要包含其他文字。"
)
)
# 答案解析链
self.parse_chain = LLMChain(
llm=self.llm,
prompt=self._generate_prompt_from_model(self.typeForm)
)
def _generate_prompt_from_model(self,model: Type[BaseDialogModel]) -> PromptTemplate:
"""从 Pydantic 模型自动生成 PromptTemplate(包含字段描述和示例 JSON)"""
field_descriptions = []
example_data: Dict[str, Any] = {}
# 提取字段名、描述,并生成示例值
for field_name, field_info in model.model_fields.items():
description = field_info.description or "无描述"
field_descriptions.append(field_name + ":" + description)
# 构建 PromptTemplate
fields_str = "\n".join(field_descriptions)
template = f"""
请从以下文本'{{user_input}}'中提取信息,并返回严格的 JSON 格式:
字段说明:
{fields_str}
注意:若输入文本不包含某字段,则JSON中不返回该字段
"""
return PromptTemplate(input_variables=["input_text"], template=template)
def generate_question(self, empty_fields: Dict[str, str]) -> str:
"""生成下一个提问"""
prompt_input = {
"empty_fields": json.dumps(list(empty_fields.values()), ensure_ascii=False)
}
print("📝 生成问题提示词:")
print(self.ask_chain.prompt.format(**prompt_input))
return self.ask_chain.invoke(prompt_input)['text']
def parse_answer(self, user_input: str) -> Optional[BaseDialogModel]:
"""从对话中解析特定字段的值"""
try:
prompt_input = {
"user_input": user_input
}
# print("📝 用户信息转换为LLM 的提示词:")
# print(self.parse_chain.prompt.format(**prompt_input))
result = self.parse_chain.invoke(prompt_input)
# print("LLM 解析结果:", result)
return RobustJsonParser.parseToModel(result['text'], self.typeForm)
except:
return None
3.3 完整对话系统
class ConversationalFormSystem:
def __init__(self, form_model: type[BaseDialogModel], llm: BaseLanguageModel):
self.form_model = form_model()
self.manager = DialogFormManager(llm, form_model)
self.dialog_history = []
def run(self):
"""
对话式表单运行
:return:
"""
print("对话式表单系统已启动(输入'退出'结束对话)")
# 模拟对话流程
while True:
if self.form_model.is_complete():
self._handle_complete()
break
question = self._generate_next_question()
print(question)
# 获取用户输入
user_answer = input("您的回答: ").strip()
if user_answer == "退出":
print("当前填写进度:", self.form_model.model_dump())
break
user_input = f"{question} {user_answer}"
print("输入的问题:", user_input)
self._process_answer(user_input)
def _generate_next_question(self) -> str:
"""生成下一个问题"""
empty_fields = self.form_model.get_empty_fields()
question = self.manager.generate_question(empty_fields)
self.dialog_history.append(f"系统: {question}")
return question
def _process_answer(self, user_input: str) -> None:
"""处理用户回答"""
# 记录对话历史
self.dialog_history.append(f"用户: {user_input}")
new_model = self.manager.parse_answer(
user_input
)
if new_model:
self.form_model.update_from_model(new_model)
else:
print("⚠️ 解析失败,请重新回答!")
def _handle_complete(self) -> str:
"""处理表单完成"""
result = json.dumps(self.form_model.model_dump(), ensure_ascii=False)
self.dialog_history.append(f"系统: 表单收集完成: {result}")
print("信息收集完成!",result)
4. 系统演示与效果
4.1 演示代码
if __name__ == "__main__":
from lang_chain_base.chat_models.model_language import getLLM
# 初始化系统(使用DeepSeek模型)
llm = getLLM("deepseek")
form_system = ConversationalFormSystem(UserInfoForm, llm)
form_system.run()
4.2 实际对话示例
对话式表单系统已启动(输入'退出'结束对话)
系统: 您需要收集以下信息,请用自然语言提问获取其中一个缺失字段的值:["您的姓名", "年龄(数字)", "性别(男/女/其他)", "电子邮箱", "居住地址"]
您的回答: 请问您的姓名是?
用户: 张三
系统: 您需要收集以下信息,请用自然语言提问获取其中一个缺失字段的值:["年龄(数字)", "性别(男/女/其他)", "电子邮箱", "居住地址"]
您的回答: 您今年多大了?
用户: 30岁
系统: 您需要收集以下信息,请用自然语言提问获取其中一个缺失字段的值:["性别(男/女/其他)", "电子邮箱", "居住地址"]
您的回答: 您的性别是?
用户: 男
系统: 您需要收集以下信息,请用自然语言提问获取其中一个缺失字段的值:["电子邮箱", "居住地址"]
您的回答: 您的邮箱是?
用户: zhangsan@example.com
系统: 您需要收集以下信息,请用自然语言提问获取其中一个缺失字段的值:["居住地址"]
您的回答: 您住在哪里?
用户: 北京市海淀区
系统: 信息收集完成!
{"name":"张三","age":30,"gender":"男","email":"zhangsan@example.com","address":"北京市海淀区"}
5. 高级功能扩展
5.1 多轮对话历史管理
class EnhancedDialogManager(DialogFormManager):
def __init__(self, llm: BaseLanguageModel, memory_size: int = 5):
super().__init__(llm)
self.memory_size = memory_size
self.short_term_memory = []
def get_relevant_history(self, current_field: str) -> str:
"""获取相关对话历史"""
# 只保留最近的记忆片段
relevant = [h for h in self.short_term_memory
if current_field in h.lower()]
return "\n".join(relevant[-self.memory_size:])
def enhanced_parse(self, field_name: str, user_input: str) -> Optional[str]:
"""增强版答案解析"""
context = self.get_relevant_history(field_name)
prompt = PromptTemplate.from_template(
"你正在从对话中提取特定信息。以下是相关对话历史和当前用户输入:\n"
"历史对话:\n{context}\n"
"当前输入: {user_input}\n"
"请提取{field_name}的值,如果没有则返回'无'。"
)
chain = LLMChain(llm=self.llm, prompt=prompt)
result = chain.invoke({
"context": context,
"user_input": user_input,
"field_name": field_name
})
return result['text'].strip()
6. 性能优化与最佳实践
6.1 提示词工程优化
# 优化后的提问生成提示词
OPTIMIZED_ASK_PROMPT = """
你是一位专业的访谈者,需要收集用户信息。以下是待收集的字段信息:
{field_descriptions}
已收集的信息:
{collected_data}
请用友好自然的方式提出下一个问题,要求:
1. 每次只问一个字段
2. 问题要清晰明确
3. 避免重复提问
4. 使用口语化表达
当前最合适的字段是:{suggested_field}
请根据以上信息生成问题(不需要问候语):
"""
# 优化后的解析提示词
OPTIMIZED_PARSE_PROMPT = """
你是一位专业的信息提取专家,需要从对话中提取特定字段的值。
字段定义:
{field_description}
对话历史(相关部分):
{relevant_history}
用户最新输入:
{user_input}
请严格按照以下JSON格式返回提取结果:
{{
"{field_name}": "提取的值",
"confidence": "置信度(高/中/低)",
"reason": "判断依据"
}}
如果没有找到有效值,confidence应为"低",value返回。
"""
6.2 缓存与会话管理
from functools import lru_cache
import pickle
import os
class CachedDialogSystem:
def __init__(self, system: ConversationalFormSystem, cache_dir: str = ".dialog_cache"):
self.system = system
self.cache_dir = cache_dir
os.makedirs(cache_dir, exist_ok=True)
def _get_cache_key(self, session_id: str) -> str:
return os.path.join(self.cache_dir, f"{session_id}.pkl")
def save_session(self, session_id: str):
"""保存会话状态"""
with open(self._get_cache_key(session_id), "wb") as f:
state = {
"form_model": self.system.form_model.model_dump(),
"dialog_history": self.system.dialog_history
}
pickle.dump(state, f)
def load_session(self, session_id: str) -> bool:
"""加载会话状态"""
cache_file = self._get_cache_key(session_id)
if not os.path.exists(cache_file):
return False
with open(cache_file, "rb") as f:
state = pickle.load(f)
self.system.form_model.update_from_dict(state["form_model"])
self.system.dialog_history = state["dialog_history"]
return True
@lru_cache(maxsize=100)
def cached_generate_question(self, empty_fields: tuple) -> str:
"""带缓存的问题生成"""
return self.system.manager.generate_question(dict(empty_fields))
7. 总结与展望
本文详细介绍了基于LangChain框架构建对话式表单系统的完整方案,从基础实现到高级功能扩展,涵盖了:
- 核心架构设计:基于Pydantic的表单模型定义
- 对话管理:多轮对话状态维护
- 智能解析:利用LLM从自然语言中提取结构化信息
- 增强功能:对话历史管理、字段验证、性能优化
7.1 实际应用建议
- 模型选择:
- 对准确性要求高的场景:使用GPT-4或Claude
- 成本敏感场景:使用国产模型(DeepSeek、Qianfan等)
- 私有部署场景:考虑Llama2或ChatGLM
- 领域适配:
- 医疗领域:添加专业术语校验
- 金融领域:强化数据隐私保护提示
- 教育领域:简化问题表述
- 性能优化:
- 对高频字段使用专用解析器
- 实现冷热数据分离的缓存策略
- 添加用户输入预处理(纠错、意图识别)
7.2 未来发展方向
- 多模态交互:集成语音识别和TTS实现全双工对话
- 主动学习:根据用户反馈自动优化提问策略
- 情感计算:识别用户情绪调整对话策略
- 多语言支持:添加国际化能力
通过本文介绍的方案,开发者可以快速构建出高效、灵活的对话式表单系统,显著提升用户体验和数据收集效率。完整代码已开源,欢迎读者根据实际需求进行修改和扩展。
链接: 基于langchain实现AI智能对话式表单