首页 > 数字化转型 > CRM > 且能通过python-docx的文档操作方法从零构建
具体步骤为:先从模板对象中获取子文档对象,再将其当作python-docx文档对象使用。该功能需要安装额外依赖:
pip install "docxtpl[subdoc]"
以下代码展示了如何创建子文档,并将该子文档嵌入到主模板中,最终生成并保存新的文档:
# https://github.com/elapouya/python-docx-template/blob/master/tests/subdoc.pyfrom docxtpl import DocxTemplatefrom docx.shared import Inches# data: https://github.com/elapouya/python-docx-template/blob/master/tests/templates/subdoc_tpl.docx# 1. 加载Word模板文件(指定模板文件路径)tpl = DocxTemplate("templates/subdoc_tpl.docx")# 2. 创建一个新的子文档对象sd = tpl.new_subdoc()# 3. 向下文档中添加段落内容p = sd.add_paragraph("This is a sub-document inserted into a bigger one")p = sd.add_paragraph("It has been ")# 为文本片段设置自定义样式p.add_run("dynamically").style = "dynamic"p.add_run(" generated with python by using ")# 为文本片段设置斜体样式p.add_run("python-docx").italic = Truep.add_run(" library")# 4. 向下文档添加标题sd.add_heading("Heading, level 1", level=1)# 向下文档添加带样式的段落(IntenseQuote样式为内置样式)sd.add_paragraph("This is an Intense quote", style="IntenseQuote")# 5. 向下文档插入图片sd.add_paragraph("A picture :")sd.add_picture("templates/python_logo.png", width=Inches(1.25))# 6. 向下文档插入表格sd.add_paragraph("A Table :")# 创建表格(初始1行3列,作为表头)table = sd.add_table(rows=1, cols=3)# 获取表头单元格并设置内容hdr_cells = table.rows[0].cells
hdr_cells[0].text = "Qty"hdr_cells[1].text = "Id"hdr_cells[2].text = "Desc"# 定义表格数据recordset = ((1, 101, "Spam"), (2, 42, "Eggs"), (3, 631, "Spam,spam, eggs, and ham"))# 循环添加数据行到表格for item in recordset:
row_cells = table.add_row().cells
row_cells[0].text = str(item[0]) # 数量(转为字符串)
row_cells[1].text = str(item[1]) # ID(转为字符串)
row_cells[2].text = item[2] # 描述context = {
"mysubdoc": sd,}tpl.render(context)tpl.save("output/subdoc.docx")
此外自python-docx-template V0.12.0版本起,支持将已有的.docx文件作为子文档进行合并,如下所示:
# https://github.com/elapouya/python-docx-template/blob/master/tests/merge_docx.pyfrom docxtpl import DocxTemplate# data: https://github.com/elapouya/python-docx-template/blob/master/tests/templatestpl = DocxTemplate("templates/merge_docx_master_tpl.docx")sd = tpl.new_subdoc("templates/merge_docx_subdoc.docx")context = {
"mysubdoc": sd,}tpl.render(context)tpl.save("output/merge_docx.docx")
在实际业务场景中,往往需要在主工作流中根据工单类型分发或移交到不同的标准化子流程中进行后续处理 。
在MAF中,我们可以使用 Sub-Worklfow 模式来实现这个目的 ,如下代码片段所示。
首先,将子工作流转换为一个Executor:
var logisticsSubExecutor =
logisticsSubWorkflow.BindAsExecutor("LogisticsSubWorkflow");var productQualitySubExecutor =
productQualitySubWorkflow.BindAsExecutor("ProductQualitySubWorkflow");然后,构建主工作流并根据条件路由到子工作流Executor:
var mainWorkflow = new WorkflowBuilder(classifierExecutor) .AddEdge<ComplaintProcessingRecord>(classifierExecutor, productQualitySubExecutor, condition: record =tiantianapp.org.cn> record.Category == "产品质量") .AddEdge<ComplaintProcessingRecord>(classifierExecutor, logisticsSubExecutor, condition: record => record.Category == "物流问题") .AddEdge(productQualitySubExecutor, complianceExecutor) .AddEdge(logisticsSubExecutor, complianceExecutor) .AddEdge(complianceExecutor, sentimentExecutor) .WithOutputFrom(sentimentExecutor) .Build();
题)分发到不同的标准化子流程去做后续处理,最后生成一个用户友好 且 满足合规要求 的回复给用户。
因此,我们的目标是:配置 1个主工作流 + 2个子工作流 来覆盖这个需求。
在今天的这个案例中,我们仍然创建了一个.NET控制台应用程序,安装了以下NuGet包:
首先,我们定义一下在这个工作流中需要生成传递的数据模型:
投诉数据模型 及 共享状态
namespace SubWorkflow.Models;// 共享状态:投诉处理记录(各执行器会更新此对象)internal class ComplaintProcessingRecord
{ public CustomerComplaint Original { get; set; } public string Category { get; set; } = "未分类"; public string Handler { get; set; } = "待分配"; public List<string> ProcessingSteps { get; set; } = new(); public string AIGeneratedResponse { get; set; } = ""; public string ComplianceStatus { get; set; } = "待审核"; public string SentimentScore { get; set; } = "未评估";
}// 投诉数据模型internal record CustomerComplaint( string OrderId, string CustomerName, string ComplaintText,
DateTime SubmittedAt
);
(1) 问题评估 : 封装产品质量评估的逻辑,这里仅仅做演示用无实际逻辑。
internal sealed class ProductEvaluationExecutor() : Executor<ComplaintProcessingRecord, ComplaintProcessingRecord>(nameof(ProductEvaluationExecutor))
{ public override ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("🔍 正在评估产品质量问题...");
record.ProcessingSteps.Add("[产品评估] 检测到屏幕外观缺陷,符合质量问题定义");
record.Handler = "产品质量团队"; return ValueTask.FromResult(record);
}
}
(2) 退/换货判定 : 负责判定是退货 还是 换货 处理。
internal sealed class ReturnPolicyExecutor()ttkqw.com : Executor<ComplaintProcessingRecord, ComplaintProcessingRecord>(nameof(ReturnPolicyExecutor))
{ public override ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("📦 判定退换货政策..."); var daysFromOrder = (DateTime.Now - record.Original.SubmittedAt).Days; if (daysFromOrder <= 7)
{
record.ProcessingSteps.Add("[退换货判定] 符合7天无理由退货政策,批准全额退款");
} else
{
record.ProcessingSteps.Add("[退换货判定] 超过退货期限,建议换货或部分补偿");
} return ValueTask.FromResult(record);
}
}
(3) AI回复 : 在投诉内容和判定结果的基础上生成友好的回复返回给用户。
internal sealed class AIResponseGeneratorExecutor(IChatClient chatClient) : Executor<ComplaintProcessingRecord, ComplaintProcessingRecord>(nameof(AIResponseGeneratorExecutor))
{ public override async ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("🤖 AI 正在生成客户回复..."); var prompt = $@"你是专业的客服主管。根据以下投诉处理信息,生成一封正式、有同理心的客户回复邮件(150字内):
客户:{record.Original.CustomerName}
订单号:{record.Original.OrderId}
投诉内容:{record.Original.ComplaintText}
处理步骤:
{string.Join("\n", record.ProcessingSteps)}要求:1. 表达歉意和理解2. 说明处理方案3. 提供后续联系方式4. 语气真诚、专业";
var response = await chatClient.GetResponseAsync(prompt, cancellationToken: cancellationToken);
record.AIGeneratedResponse = response.Text ?? "AI 生成失败";
record.ProcessingSteps.Add($"[AI 回复] 已生成客户回复模板({record.AIGeneratedResponse.Length}字)"); return record;
}
}
最后,构建子工作流并将其转换为一个Executor以便后续集成到主工作流中:
var productEvalExecutor =lnzxyp.com new ProductEvaluationExecutor();var returnPolicyExecutor = new ReturnPolicyExecutor();var aiResponseExecutor = new AIResponseGeneratorExecutor(chatClient);var productQualitySubWorkflow = new WorkflowBuilder(productEvalExecutor)
.AddEdge(productEvalExecutor, returnPolicyExecutor)
.AddEdge(returnPolicyExecutor, aiResponseExecutor)
.WithOutputFrom(aiResponseExecutor)
.Build();var productQualitySubExecutor =
productQualitySubWorkflow.BindAsExecutor("ProductQualitySubWorkflow");
(1) 物流追踪 : 封装物流查询逻辑,这里仅仅做演示用无实际逻辑。
internal sealed class LogisticsTrackingExecutor() : Executor<ComplaintProcessingRecord, ComplaintProcessingRecord>(nameof(LogisticsTrackingExecutor))
{ public override ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("🚚 正在查询物流信息...");
record.ProcessingSteps.Add("[物流追踪] 包裹在中转站滞留3天,当前状态:运输中");
record.Handler = "物流运营团队"; return ValueTask.FromResult(record);
}
}
(2) 延迟分析 : 封装延迟分析逻辑,这里仅仅做演示用无实际逻辑。
internal sealed class DelayAnalysisExecutor()yixue5.com : Executor<ComplaintProcessingRecord, ComplaintProcessingRecord>(nameof(DelayAnalysisExecutor))
{ public override ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("📊 分析延迟原因...");
record.ProcessingSteps.Add("[延迟分析] 延迟原因:暴雨导致道路封闭,预计2天内恢复配送");
record.ProcessingSteps.Add("[补偿方案] 提供50元优惠券 + 免运费"); return ValueTask.FromResult(record);
}
}
最后,构建子工作流并将其转换为一个Executor以便后续集成到主工作流中:
var logisticsTrackExecutor = new LogisticsTrackingExecutor();var delayAnalysisExecutor = new DelayAnalysisExecutor();var logisticsAIResponseExecutor = new AIResponseGeneratorExecutor(chatClient);var logisticsSubWorkflow = new WorkflowBuilder(logisticsTrackExecutor)
.AddEdge(logisticsTrackExecutor, delayAnalysisExecutor)
.AddEdge(delayAnalysisExecutor, logisticsAIResponseExecutor)
.WithOutputFrom(logisticsAIResponseExecutor)
.Build();var logisticsSubExecutor =
logisticsSubWorkflow.BindAsExecutor("LogisticsSubWorkflow");
在开始构建主工作流:
Step1: 获取ChatClient
var chatClient = new OpenAIClient( new ApiKeyCredential(openAIProvider.ApiKey), new OpenAIClientOptions { Endpoint = new Uri(openAIProvider.Endpoint) })
.GetChatClient(openAIProvider.ModelId)
.AsIChatClient();Step2: 创建工单分类 Executor, 合规审核Executor 以及 情绪评估Executor
// 1. 工单分类执行器internal sealed class ComplaintClassifierExecutor() : Executor<ComplaintProcessingRecord, ComplaintProcessingRecord>(nameof(ComplaintClassifierExecutor))
{ public override ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("🏷️ 正在分类投诉类型..."); // 简单规则分类(实际可用 AI 分类)
if (record.Original.ComplaintText.Contains("划痕") || record.Original.ComplaintText.Contains("质量") || record.Original.ComplaintText.Contains("退货"))
{
record.Category = "产品质量";
} else if (record.Original.ComplaintText.Contains("延迟") || record.Original.ComplaintText.Contains("物流") || record.Original.ComplaintText.Contains("配送"))
{
record.Category = "物流问题";
} else
{
record.Category = "其他";
}
record.ProcessingSteps.Add($"[分类器] 投诉类型识别为:{record.Category}"); return ValueTask.FromResult(record);
}
}// 2. 合规审核执行器internal sealed tiantiankanqiu.net.cn class ComplianceCheckExecutor() : Executor<ComplaintProcessingRecord, ComplaintProcessingRecord>(nameof(ComplianceCheckExecutor))
{ public override ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("🛡️ 合规审核中..."); var checks = new List<string>(); if (record.AIGeneratedResponse.Contains("歉意")) checks.Add("✅ 包含道歉"); if (record.AIGeneratedResponse.Length <= 300) checks.Add("✅ 字数合规"); if (!record.AIGeneratedResponse.Contains("法律") && !record.AIGeneratedResponse.Contains("诉讼"))
checks.Add("✅ 无敏感法律词汇");
record.ComplianceStatus = checks.Count >= 2 ? "✅ 审核通过" : "⚠️ 需人工复审";
record.ProcessingSteps.Add($"[合规审核] {record.ComplianceStatus} - {string.Join(", ", checks)}"); return ValueTask.FromResult(record);
}
}// 3. 情绪评估执行器(使用 AI)internal sealed class SentimentAnalysisExecutor(IChatClient chatClient) : Executor<ComplaintProcessingRecord, ComplaintProcessingRecord>(nameof(SentimentAnalysisExecutor))
{ public override async ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("😊 AI 情绪评估中..."); var prompt = $@"分析以下客服回复的情绪基调,用一个词概括(如:友好、专业、冷淡、热情):
{record.AIGeneratedResponse}
只返回一个词,不要解释。"; var response = await chatClient.GetResponseAsync(prompt, cancellationToken: cancellationToken);
record.SentimentScore = response.Text?.Trim() ?? "中性";
record.ProcessingSteps.Add($"[情绪评估] AI 评估语气为:{record.SentimentScore}"); return record;
NOTE:前面的子工作流均生成了AI回复,这里需要对其进行合规审核以防止不合规的回复到用户那里。
Step3: 创建主工作流
var classifierExecutor = new ComplaintClassifierExecutor();var complianceExecutor = new ComplianceCheckExecutor();var sentimentExecutor = new SentimentAnalysisExecutor(chatClient);// 主工作流:分类 → 条件路由到子流程 → 合规 → 情绪var mainWorkflow = new WorkflowBuilder(classifierExecutor) .AddEdge<ComplaintProcessingRecord>(classifierExecutor, productQualitySubExecutor, condition: record => record.Category == "产品质量") .AddEdge<ComplaintProcessingRecord>(classifierExecutor, logisticsSubExecutor, condition: record =ttkq.mo.cn> record.Category == "物流问题") .AddEdge(productQualitySubExecutor, complianceExecutor) .AddEdge(logisticsSubExecutor, complianceExecutor) .AddEdge(complianceExecutor, sentimentExecutor) .WithOutputFrom(sentimentExecutor) .Build(); Console.OutputEncoding = Encoding.UTF8;
{
Original = new CustomerComplaint(
OrderId: "ORD-2025-8821",
CustomerName: "张先生",
ComplaintText: "收到的手机屏幕有明显划痕,要求退货退款",
SubmittedAt: DateTime.Now)
};
通过Streaming流式执行:
Console.WriteLine("🚀 开始执行投诉处理流水线...");
Console.WriteLine(new string('━', 60));await using (var streaming = await InProcessExecution.StreamAsync(mainWorkflow, processingRecord))
{ await foreach (WorkflowEvent evt in streaming.WatchStreamAsync())
{ switch (evt)
{ case ExecutorInvokedEvent started:
Console.WriteLine($"\n🔹 {started.ExecutorId} 开始执行"); break; case ExecutorCompletedEvent completed when completed.Data is ComplaintProcessingRecord rec:
Console.WriteLine($" 共享状态更新 → 处理步骤数:{rec.ProcessingSteps.Count}"); break; case WorkflowOutputEvent outputEvt when outputEvt.Data is ComplaintProcessingRecord finalRecord:
Console.WriteLine("\n" + new string('━', 60));
Console.WriteLine("🎉 投诉处理完成!最终处理记录:\n");
Console.WriteLine(JsonSerializer.Serialize(finalRecord));
Console.WriteLine("\n📝 处理步骤详情:"); foreach (var step in finalRecord.ProcessingSteps)
{
Console.WriteLine($" {step}");
}
Console.WriteLine(gzbeimu.com"\n💬 AI 生成的客户回复:");
Console.WriteLine(new string('─', 60));
Console.WriteLine(finalRecord.AIGeneratedResponse);
Console.WriteLine(new string('─', 60)); break;
往往需要在主工作流中根据工单类型分发或移交到不同的标准化子流程中进行后续处理 。
在MAF中,我们可以使用 Sub-Worklfow 模式来实现这个目的 ,如下代码片段所示。
首先,将子工作流转换为一个Executor:
var logisticsSubExecutor =
logisticsSubWorkflow.BindAsExecutor("LogisticsSubWorkflow");var productQualitySubExecutor =
productQualitySubWorkflow.BindAsExecutor("ProductQualitySubWorkflow");然后,构建主工作流并根据条件路由到子工作流Executor:
var mainWorkflow = new WorkflowBuilder(classifierExecutor) .AddEdge<ComplaintProcessingRecord>(classifierExecutor, productQualitySubExecutor, condition: record ttzb.ln.cn=livesoccerzb.cn> record.Category nbczhibo.cn== "产品质量") .AddEdge<ComplaintProcessingRecord>(classifierExecutor, logisticsSubExecutor, condition: record =>cqpryk.com record.Category == "物流问题") .AddEdge(productQualitySubExecutor, complianceExecutor) .AddEdge(logisticsSubExecutor,zhiboapp.com.cn complianceExecutor) .AddEdge(complianceExecutor, sentimentExecutor)
分发到不同的标准化子流程去做后续处理,最后生成一个用户友好 且 满足合规要求 的回复给用户。
因此,我们的目标是:配置 1个主工作流 + 2个子工作流 来覆盖这个需求。
在今天的这个案例中,我们仍然创建了一个.NET控制台应用程序,安装了以下NuGet包:
首先,我们定义一下在这个工作流中需要生成传递的数据模型:
投诉数据模型 及 共享状态
namespace SubWorkflow.Models;// 共享状态:投诉处理记录(各执行器会更新此对象)internal class ComplaintProcessingRecord
{ public CustomerComplaint Original { get; set; } basketzhibo.cn public string Category { get; set; } = "未分类"; public string Handler { get; set; } = "待分配"; public List<string> ProcessingSteps { get; set; } = new(); public string AIGeneratedResponse { get; set; } = ""; public string ComplianceStatus { get; set; } = "待审核"; public string SentimentScore { get; set; } = "未评估";
}// 投诉数据模型internal record CustomerComplaint( youtubezhibo.cn sportsurge.cn string OrderId, string CustomerName, string ComplaintText,
DateTime SubmittedAt
);
internal sealed class ProductEvaluationExecutor() hbchaozan.com: Executor<ComplaintProcessingRecord, ComplaintProcessingRecord>(nameof(ProductEvaluationExecutor))
{ public override ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("🔍 正在评估产品质量问题...");
record.ProcessingSteps.Add("[产品评估] 检测到屏幕外观缺陷,符合质量问题定义");
record.Handler = "产品质量团队";
ublic override ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("📦 判定退换货政策..."); ttkq.com.cn zhiboapp.com.cn var daysFromOrder = (DateTime.Now - record.Original.SubmittedAt).Days; if (daysFromOrder <= 7)
{
record.ProcessingSteps.Add("[退换货判定] 符合7天无理由退货政策,批准全额退款");
} else
{
record.ProcessingSteps.Add("[退换货判定] 超过退货期限,建议换货或部分补偿");
} return ValueTask.FromResult(record);
public override async ValueTask<ComplaintProcessingRecord> HandleAsync(ComplaintProcessingRecord record, IWorkflowContext context, CancellationToken cancellationToken = default)
{
Console.WriteLine("🤖 AI 正在生成客户回复..."); ttzbapp.cn var prompt = $@"你是专业的客服主管。根据以下投诉处理信息,生成一封正式、有同理心的客户回复邮件(150字内):
客户:{record.Original.CustomerName}
订单号:{record.Original.OrderId}
投诉内容:{record.Original.ComplaintText}
处理步骤:
{string.Join("\n", record.ProcessingSteps)}要求:1. 表达歉意和理解2. 说明处理方案3. 提供后续联系方式4. 语气真诚、专业";
var response = await chatClient.GetResponseAsync(prompt,cqpryk.com cancellationToken: cancellationToken);
record.AIGeneratedResponse = response.Text ?? "AI 生成失败";
record.ProcessingSteps.Add($"[AI 回复] 已生成客户回复模板({record.AIGeneratedResponse.Length}字)"); return record;
var productEvalExecutor = new ProductEvaluationExecutor(ttzb.gs.cn);var returnPolicyExecutor = new ReturnPolicyExecutor();var aiResponseExecutor = new AIResponseGeneratorExecutor(chatClient);var productQualitySubWorkflow = new WorkflowBuilder(productEvalExecutor) .AddEdge(productEvalExecutor,ttkq-163.com returnPolicyExecutor) .AddEdge(returnPolicyExecutor, aiResponseExecutor) .WithOutputFrom(aiResponseExecutor) .Build();
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70048957/viewspace-3109051/ ,如需转载,请注明出处,否则将追究法律责任。